fix: remaining lint auto-fixes and rate-limit guard test fixes

- Import ordering auto-fixes from `pnpm lint --fix` for remaining API modules
- Fix rate-limit guard test specs: override NODE_ENV to 'development'
  so guards don't skip rate limiting in test mode
- Unused import removal (UnauthorizedException in login-user handler)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-11 23:12:45 +07:00
parent 154aed5440
commit 9e2bf9a4b5
24 changed files with 392 additions and 145 deletions

View File

@@ -1,7 +1,23 @@
import { AgentEntity } from '../../domain/entities/agent.entity';
import type { IAgentRepository, AgentDashboardData } from '../../domain/repositories/agent.repository';
import { QualityScore } from '../../domain/value-objects/quality-score.vo';
import { GetAgentDashboardHandler } from '../queries/get-agent-dashboard/get-agent-dashboard.handler';
import { GetAgentDashboardQuery } from '../queries/get-agent-dashboard/get-agent-dashboard.query';
function makeAgent(id: string, userId: string, qualityScore: number): AgentEntity {
return new AgentEntity(id, {
userId,
licenseNumber: null,
agency: null,
qualityScore: QualityScore.fromPersistence(qualityScore),
totalDeals: 0,
responseTimeAvg: null,
bio: null,
serviceAreas: [],
isVerified: false,
});
}
describe('GetAgentDashboardHandler', () => {
let handler: GetAgentDashboardHandler;
let mockAgentRepo: { [K in keyof IAgentRepository]: ReturnType<typeof vi.fn> };
@@ -10,19 +26,17 @@ describe('GetAgentDashboardHandler', () => {
mockAgentRepo = {
findByUserId: vi.fn(),
findById: vi.fn(),
updateQualityScore: vi.fn(),
save: vi.fn(),
getDashboard: vi.fn(),
getPublicProfile: vi.fn(),
getQualityScoreInputs: vi.fn(),
};
handler = new GetAgentDashboardHandler(mockAgentRepo as any);
});
it('returns dashboard data', async () => {
mockAgentRepo.findByUserId.mockResolvedValue({
id: 'agent-1',
userId: 'user-1',
qualityScore: 85,
});
mockAgentRepo.findByUserId.mockResolvedValue(makeAgent('agent-1', 'user-1', 85));
const mockDashboard: AgentDashboardData = {
agentId: 'agent-1',

View File

@@ -1,69 +1,69 @@
import type { EventBus } from '@nestjs/cqrs';
import { AgentEntity } from '../../domain/entities/agent.entity';
import type { IAgentRepository } from '../../domain/repositories/agent.repository';
import { QualityScore } from '../../domain/value-objects/quality-score.vo';
import { RecalculateQualityScoreCommand } from '../commands/recalculate-quality-score/recalculate-quality-score.command';
import { RecalculateQualityScoreHandler } from '../commands/recalculate-quality-score/recalculate-quality-score.handler';
function makeAgent(id: string, userId: string, qualityScore: number): AgentEntity {
return new AgentEntity(id, {
userId,
licenseNumber: null,
agency: null,
qualityScore: QualityScore.fromPersistence(qualityScore),
totalDeals: 0,
responseTimeAvg: null,
bio: null,
serviceAreas: [],
isVerified: false,
});
}
describe('RecalculateQualityScoreHandler', () => {
let handler: RecalculateQualityScoreHandler;
let mockAgentRepo: { [K in keyof IAgentRepository]: ReturnType<typeof vi.fn> };
let mockPrisma: {
review: { aggregate: ReturnType<typeof vi.fn> };
lead: { count: ReturnType<typeof vi.fn> };
listing: { count: ReturnType<typeof vi.fn> };
agent: { findUnique: ReturnType<typeof vi.fn> };
};
let mockEventBus: { publish: ReturnType<typeof vi.fn> };
beforeEach(() => {
mockAgentRepo = {
findByUserId: vi.fn(),
findById: vi.fn(),
updateQualityScore: vi.fn(),
save: vi.fn(),
getDashboard: vi.fn(),
getPublicProfile: vi.fn(),
getQualityScoreInputs: vi.fn(),
};
mockPrisma = {
review: { aggregate: vi.fn() },
lead: { count: vi.fn() },
listing: { count: vi.fn() },
agent: { findUnique: vi.fn() },
};
mockEventBus = { publish: vi.fn() };
const mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), verbose: vi.fn() };
handler = new RecalculateQualityScoreHandler(
mockAgentRepo as any,
mockPrisma as any,
mockEventBus as unknown as EventBus,
mockLogger as any,
);
});
it('recalculates quality score successfully', async () => {
mockAgentRepo.findById.mockResolvedValue({ id: 'agent-1', userId: 'user-1', qualityScore: 50 });
mockPrisma.review.aggregate.mockResolvedValue({
_avg: { rating: 4.5 },
_count: { rating: 10 },
mockAgentRepo.findById.mockResolvedValue(makeAgent('agent-1', 'user-1', 50));
mockAgentRepo.getQualityScoreInputs.mockResolvedValue({
avgRating: 4.5,
totalReviews: 10,
responseTimeAvg: 900,
conversionRate: 0.25,
activeListingRatio: 0.7,
});
mockPrisma.lead.count
.mockResolvedValueOnce(20) // totalLeads
.mockResolvedValueOnce(5); // convertedLeads
mockPrisma.listing.count
.mockResolvedValueOnce(10) // totalListings
.mockResolvedValueOnce(7); // activeListings
mockPrisma.agent.findUnique.mockResolvedValue({ responseTimeAvg: 900 }); // 15 min
const command = new RecalculateQualityScoreCommand('agent-1');
await handler.execute(command);
expect(mockAgentRepo.updateQualityScore).toHaveBeenCalledTimes(1);
expect(mockAgentRepo.updateQualityScore).toHaveBeenCalledWith(
'agent-1',
expect.any(Number),
);
expect(mockAgentRepo.save).toHaveBeenCalledTimes(1);
// Verify the score value is reasonable
const actualScore = mockAgentRepo.updateQualityScore.mock.calls[0][1];
expect(actualScore).toBeGreaterThan(0);
expect(actualScore).toBeLessThanOrEqual(100);
const savedAgent = mockAgentRepo.save.mock.calls[0][0] as AgentEntity;
expect(savedAgent.qualityScore.value).toBeGreaterThan(0);
expect(savedAgent.qualityScore.value).toBeLessThanOrEqual(100);
});
it('skips recalculation when agent not found', async () => {
@@ -73,46 +73,61 @@ describe('RecalculateQualityScoreHandler', () => {
await handler.execute(command);
expect(mockAgentRepo.updateQualityScore).not.toHaveBeenCalled();
expect(mockPrisma.review.aggregate).not.toHaveBeenCalled();
expect(mockAgentRepo.save).not.toHaveBeenCalled();
expect(mockAgentRepo.getQualityScoreInputs).not.toHaveBeenCalled();
});
it('updates agent record with calculated score', async () => {
mockAgentRepo.findById.mockResolvedValue({ id: 'agent-1', userId: 'user-1', qualityScore: 0 });
mockPrisma.review.aggregate.mockResolvedValue({
_avg: { rating: 5 },
_count: { rating: 20 },
it('updates agent with calculated score', async () => {
mockAgentRepo.findById.mockResolvedValue(makeAgent('agent-1', 'user-1', 0));
mockAgentRepo.getQualityScoreInputs.mockResolvedValue({
avgRating: 5,
totalReviews: 20,
responseTimeAvg: 0,
conversionRate: 1,
activeListingRatio: 1,
});
mockPrisma.lead.count
.mockResolvedValueOnce(10)
.mockResolvedValueOnce(10); // 100% conversion
mockPrisma.listing.count
.mockResolvedValueOnce(5)
.mockResolvedValueOnce(5); // 100% active
mockPrisma.agent.findUnique.mockResolvedValue({ responseTimeAvg: 0 }); // instant response
const command = new RecalculateQualityScoreCommand('agent-1');
await handler.execute(command);
expect(mockAgentRepo.updateQualityScore).toHaveBeenCalledWith('agent-1', 100);
const savedAgent = mockAgentRepo.save.mock.calls[0][0] as AgentEntity;
expect(savedAgent.qualityScore.value).toBe(100);
});
it('handles null response time avg from agent record', async () => {
mockAgentRepo.findById.mockResolvedValue({ id: 'agent-1', userId: 'user-1', qualityScore: 0 });
mockPrisma.review.aggregate.mockResolvedValue({
_avg: { rating: null },
_count: { rating: 0 },
it('publishes domain events after save', async () => {
mockAgentRepo.findById.mockResolvedValue(makeAgent('agent-1', 'user-1', 50));
mockAgentRepo.getQualityScoreInputs.mockResolvedValue({
avgRating: 4.5,
totalReviews: 10,
responseTimeAvg: 900,
conversionRate: 0.25,
activeListingRatio: 0.7,
});
mockPrisma.lead.count.mockResolvedValue(0);
mockPrisma.listing.count.mockResolvedValue(0);
mockPrisma.agent.findUnique.mockResolvedValue({ responseTimeAvg: null });
const command = new RecalculateQualityScoreCommand('agent-1');
await handler.execute(command);
expect(mockEventBus.publish).toHaveBeenCalled();
});
it('handles null response time avg', async () => {
mockAgentRepo.findById.mockResolvedValue(makeAgent('agent-1', 'user-1', 0));
mockAgentRepo.getQualityScoreInputs.mockResolvedValue({
avgRating: 0,
totalReviews: 0,
responseTimeAvg: null,
conversionRate: 0,
activeListingRatio: 0,
});
const command = new RecalculateQualityScoreCommand('agent-1');
await handler.execute(command);
const savedAgent = mockAgentRepo.save.mock.calls[0][0] as AgentEntity;
// no reviews (50*0.4=20), null response (50*0.3=15), 0 conversion, 0 listing
expect(mockAgentRepo.updateQualityScore).toHaveBeenCalledWith('agent-1', 35);
expect(savedAgent.qualityScore.value).toBe(35);
});
});