Move 36 root-level audit/analysis documents and 7 web app audit documents into docs/audits/ directory to declutter the project root. Remove stale EXPLORATION_SUMMARY.txt. Co-Authored-By: Paperclip <noreply@paperclip.ing>
9.1 KiB
9.1 KiB
Test Coverage Quick Reference Guide
17 Untested Source Files Overview
INQUIRIES MODULE (4 files)
1. prisma-inquiry.repository.ts — 6 methods to test + 1 private mapper
2. inquiries.controller.ts — 4 endpoints to test + guard validation
3. create-inquiry.dto.ts — 3 field validations
4. list-inquiries.dto.ts — 2 field validations (pagination)
LEADS MODULE (6 files)
5. prisma-lead.repository.ts — 6 methods + stats aggregation
6. lead-score.vo.ts — Value object: range 0-100
7. leads.controller.ts — 5 endpoints + class-level role guard
8. create-lead.dto.ts — 6 field validations
9. list-leads.dto.ts — Status enum + pagination
10. update-lead-status.dto.ts — Status enum validation
REVIEWS MODULE (5 files)
11. prisma-review.repository.ts — 7 methods + stats with distribution
12. rating.vo.ts — Value object: range 1-5 (integers only)
13. reviews.controller.ts — 5 endpoints + mixed auth
14. create-review.dto.ts — 4 field validations
15. list-reviews.dto.ts — 2 DTOs: ListReviewsByTargetDto, ReviewStatsDto
REFERENCE PATTERNS (2 test files)
16. create-inquiry.handler.spec.ts — Handler test pattern
17. create-lead.handler.spec.ts — Handler test pattern
18. reviews.controller.spec.ts — Controller test pattern
Quick Test Scenarios by Type
REPOSITORIES (3 files)
Test checklist for each repository:
- findById() returns entity or null
- save() creates record with correct data mapping
- Paginated methods respect limit cap (100 max)
- Pagination calculation: skip = (page - 1) * take
- Relationships are joined correctly
- Aggregations calculate correctly (stats methods)
- Optional fields handling (null coercion)
- ISO date formatting in DTOs
Specific to PrismaInquiryRepository:
- countUnreadByAgent() aggregation
- findByListing() includes property.title
- findByAgent() joins through listing.agentId
Specific to PrismaLeadRepository:
- findByAgent() optional status filter
- getStatsByAgent() calculates:
- totalLeads count
- byStatus object (dict of counts)
- conversionRate: (CONVERTED / total) * 100 (2 decimals)
- avgScore: average of non-null scores (1 decimal)
Specific to PrismaReviewRepository:
- findByUserAndTarget() unique constraint query
- getStats() builds distribution object (keys 1-5)
- averageRating calculation: (sum / total) * 10 / 10 (1 decimal)
VALUE OBJECTS (2 files)
Test checklist:
- create() with valid value returns Result.ok()
- create() with invalid value returns Result.err() with correct message
- Getter returns props.value
- Invalid cases covered (negative, > max, non-integer, null)
LeadScore validation:
✓ Valid: 0, 50, 100
✗ Invalid: -1, 101, 2.5, null, "50"
Error: "Điểm lead phải từ 0 đến 100"
Rating validation:
✓ Valid: 1, 2, 3, 4, 5
✗ Invalid: 0, 6, 2.5, null, "5"
Error: "Đánh giá phải từ 1 đến 5 sao"
CONTROLLERS (2 files)
Test checklist:
- Each endpoint dispatches correct command/query type
- Parameters are mapped correctly from DTO
- Optional fields become null (e.g., phone ?? null → null)
- Default pagination values applied (page: 1, limit: 20)
- CurrentUser decorator extracts user.sub correctly
- Guard enforcement (@UseGuards, @Roles)
- Return types match (e.g., { deleted: true })
InquiriesController specifics:
- POST /inquiries: phone optional → null
- GET /inquiries/listing/:listingId: requires JWT
- GET /inquiries/agent/me: requires JWT + AGENT role
- PATCH /inquiries/:id/read: requires JWT + AGENT role
LeadsController specifics:
- ALL endpoints require JWT + AGENT role (class-level @Roles)
- POST /leads: score optional, score range validation in command
- GET /leads: status filter optional
- GET /leads/stats: aggregation query
- PATCH /leads/:id/status: only status field in command
- DELETE /leads/🆔 agentId verification in command
ReviewsController specifics:
- POST /reviews: requires JWT (AuthGuard)
- GET /reviews: NO auth required (stats are public)
- GET /reviews/stats: NO auth required
- GET /reviews/me: requires JWT
- DELETE /reviews/🆔 requires JWT + ownership check in command
DTOs (10 files)
Test checklist:
- Required fields throw ValidationException if missing
- String max/min length validated
- Number min/max validated
- Enum @IsIn() validates allowed values
- Type transformation (class-transformer @Type)
- Email format validated (@IsEmail)
- Optional fields (@IsOptional) don't throw if omitted
Pagination standard (used in 5 DTOs):
- page: optional, @Min(1), default 1
- limit: optional, @Min(1), @Max(100), default 20
- Both use @Type(() => Number) for string→number transformation
Enum validations:
- LeadStatus: ['NEW', 'CONTACTED', 'QUALIFIED', 'NEGOTIATING', 'CONVERTED', 'LOST']
- Used in: ListLeadsDto, UpdateLeadStatusDto
Test Priority Matrix
🔴 CRITICAL (Business Logic)
- PrismaReviewRepository.getStats() - distribution calculation
- PrismaLeadRepository.getStatsByAgent() - conversion rate formula
- Rating.vo - must be 1-5 integers only
- LeadScore.vo - must be 0-100 range
🟡 HIGH (Data Integrity)
- All repository CRUD methods
- Pagination calculations
- Relationship mapping (user joins, listing joins)
- Controller parameter mapping
🟢 MEDIUM (Validation)
- DTO field validations
- Enum constraints
- Optional field handling
- Guard enforcement
Test Execution Order Recommendation
- Value Objects (2 files) - 2 simple test files
- DTOs (10 files) - Use class-validator testing patterns
- Controllers (2 files) - Command/query dispatch tests
- Repositories (3 files) - Data layer tests with mocked Prisma
Mock Setup Template
For Repositories:
const mockPrisma = {
inquiry: { findUnique: vi.fn(), findMany: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), count: vi.fn() },
};
const repo = new PrismaInquiryRepository(mockPrisma as any);
For Controllers:
const mockCommandBus = { execute: vi.fn() };
const mockQueryBus = { execute: vi.fn() };
const controller = new InquiriesController(mockCommandBus as any, mockQueryBus as any);
For Value Objects:
const result = LeadScore.create(75);
expect(result.isOk()).toBe(true);
expect(result.unwrap().value).toBe(75);
For DTOs:
import { validate } from 'class-validator';
const dto = new CreateLeadDto();
dto.name = 'Nguyễn Văn A';
dto.phone = '0901234567';
// ... set other required fields
const errors = await validate(dto);
expect(errors).toHaveLength(0);
Key Formulas to Verify
Pagination:
skip = (page - 1) * take
totalPages = Math.ceil(total / take)
take = Math.min(limit, 100)
Lead Conversion Rate:
conversionRate = (convertedCount / totalLeads) * 100
Result: rounded to 2 decimals (e.g., 33.33)
Lead Average Score:
avgScore = scoreSum / scoreCount (where score !== null)
Result: rounded to 1 decimal (e.g., 75.5)
Review Average Rating:
averageRating = (sum / totalReviews)
Result: rounded to 1 decimal (e.g., 4.5)
Review Distribution:
distribution: { 1: count, 2: count, 3: count, 4: count, 5: count }
Must initialize all 5 keys, even if 0
File Locations for Reference
/Users/velikho/Desktop/WORKING/goodgo-platform-ai/apps/api/
Inquiries:
src/modules/inquiries/infrastructure/repositories/prisma-inquiry.repository.ts
src/modules/inquiries/presentation/controllers/inquiries.controller.ts
src/modules/inquiries/presentation/dto/create-inquiry.dto.ts
src/modules/inquiries/presentation/dto/list-inquiries.dto.ts
Leads:
src/modules/leads/infrastructure/repositories/prisma-lead.repository.ts
src/modules/leads/domain/value-objects/lead-score.vo.ts
src/modules/leads/presentation/controllers/leads.controller.ts
src/modules/leads/presentation/dto/create-lead.dto.ts
src/modules/leads/presentation/dto/list-leads.dto.ts
src/modules/leads/presentation/dto/update-lead-status.dto.ts
Reviews:
src/modules/reviews/infrastructure/repositories/prisma-review.repository.ts
src/modules/reviews/domain/value-objects/rating.vo.ts
src/modules/reviews/presentation/controllers/reviews.controller.ts
src/modules/reviews/presentation/dto/create-review.dto.ts
src/modules/reviews/presentation/dto/list-reviews.dto.ts
Next Steps
-
Copy reference test patterns:
- Create inquiry and lead handlers already have good test patterns
- Controller test for reviews shows endpoint testing approach
-
Start with repositories:
- Most complex (Prisma mocking)
- Most critical (data layer)
- Established patterns in existing tests
-
Test DTOs second:
- Quick feedback (class-validator validation)
- 10 files but mostly simple
-
Controllers and VOs last:
- Build on repository/DTO tests
- Depend on handler tests (if testing full flow)