setup
This commit is contained in:
672
docs/checklist-phase7-testing.md
Normal file
672
docs/checklist-phase7-testing.md
Normal file
@@ -0,0 +1,672 @@
|
||||
# Phase 7: Testing - Checklist
|
||||
|
||||
## 📋 Overview
|
||||
|
||||
This phase covers comprehensive testing of the refactored CRM backend system, including unit tests, integration tests, and manual API testing.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Completed Tasks
|
||||
|
||||
None yet - Phase 7 is the final phase and has not been started.
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Strategy
|
||||
|
||||
### Testing Pyramid
|
||||
|
||||
```
|
||||
/\
|
||||
/ \ E2E Tests (Manual/API)
|
||||
/____\
|
||||
/ \ Integration Tests
|
||||
/________\
|
||||
/ \ Unit Tests
|
||||
/____________\
|
||||
```
|
||||
|
||||
### Test Categories
|
||||
|
||||
1. **Unit Tests** - Test individual functions in isolation
|
||||
2. **Integration Tests** - Test service layer with database
|
||||
3. **API Tests** - Test endpoints with requests/responses
|
||||
4. **Manual Tests** - Postman/curl testing
|
||||
|
||||
---
|
||||
|
||||
## 📝 Unit Tests
|
||||
|
||||
### Customer Service Tests
|
||||
|
||||
- [ ] `generateCrmCustomerCode()`
|
||||
- [ ] Generates unique code per branch
|
||||
- [ ] Increments correctly
|
||||
- [ ] Handles empty branch
|
||||
|
||||
- [ ] `getCustomersByBranch()`
|
||||
- [ ] Returns only branch customers
|
||||
- [ ] Filters by status correctly
|
||||
- [ ] Includes contacts
|
||||
- [ ] Handles empty results
|
||||
|
||||
- [ ] `getCustomerById()`
|
||||
- [ ] Returns correct customer
|
||||
- [ ] Enforces branch access
|
||||
- [ ] Returns null for non-existent
|
||||
- [ ] Returns null for wrong branch
|
||||
|
||||
- [ ] `createCustomer()`
|
||||
- [ ] Creates customer with correct branch
|
||||
- [ ] Auto-generates CRM code
|
||||
- [ ] Validates required fields
|
||||
- [ ] Sets createdBy and updatedBy
|
||||
|
||||
- [ ] `updateCustomer()`
|
||||
- [ ] Updates customer correctly
|
||||
- [ ] Enforces branch access
|
||||
- [ ] Updates erpCustomerCode
|
||||
- [ ] Sets updatedBy
|
||||
|
||||
- [ ] `deleteCustomer()`
|
||||
- [ ] Soft deletes customer
|
||||
- [ ] Enforces branch access
|
||||
- [ ] Returns error if already deleted
|
||||
|
||||
### Contact Service Tests
|
||||
|
||||
- [ ] `getVisibleContactsForCustomer()`
|
||||
- [ ] Returns only user's contacts (createdBy == userId)
|
||||
- [ ] Returns public contacts (isPublic == true)
|
||||
- [ ] Enforces branch access
|
||||
- [ ] Filters by customerId
|
||||
|
||||
- [ ] `createContact()`
|
||||
- [ ] Creates contact with correct branch
|
||||
- [ ] Sets createdBy to current user
|
||||
- [ ] Sets isPublic to false by default
|
||||
- [ ] Validates required fields
|
||||
|
||||
- [ ] `updateContact()`
|
||||
- [ ] Updates contact correctly
|
||||
- [ ] Only allows creator to update
|
||||
- [ ] Can update isPublic flag
|
||||
- [ ] Enforces branch access
|
||||
|
||||
- [ ] `shareContact()`
|
||||
- [ ] Sets isPublic to true
|
||||
- [ ] Only allows creator to share
|
||||
- [ ] Returns error for non-existent contact
|
||||
|
||||
- [ ] `unshareContact()`
|
||||
- [ ] Sets isPublic to false
|
||||
- [ ] Only allows creator to unshare
|
||||
- [ ] Returns error for non-existent contact
|
||||
|
||||
- [ ] `deleteContact()`
|
||||
- [ ] Deletes contact correctly
|
||||
- [ ] Only allows creator to delete
|
||||
- [ ] Enforces branch access
|
||||
|
||||
### Quotation Service Tests
|
||||
|
||||
- [ ] `generateQuotationCode()`
|
||||
- [ ] Generates unique code
|
||||
- [ ] Follows pattern (Q-YYYY-XXXXX)
|
||||
|
||||
- [ ] `calculateBaseCurrencyAmount()`
|
||||
- [ ] Converts to THB correctly
|
||||
- [ ] Handles THB (exchangeRate = 1)
|
||||
- [ ] Handles different currencies
|
||||
- [ ] Returns null for invalid currency
|
||||
|
||||
- [ ] `validateQuotationStatus()`
|
||||
- [ ] Allows editing DRAFT
|
||||
- [ ] Prevents editing SENT
|
||||
- [ ] Validates status transitions
|
||||
|
||||
- [ ] `createQuotation()`
|
||||
- [ ] Creates quotation with correct branch
|
||||
- [ ] Validates currency code
|
||||
- [ ] Validates exchange rate
|
||||
- [ ] Calculates baseCurrencyAmount
|
||||
- [ ] Validates customer has visible contacts
|
||||
- [ ] Sets revisionNo to null (initial)
|
||||
- [ ] Sets parentQuotationId to null (initial)
|
||||
|
||||
- [ ] `updateQuotation()`
|
||||
- [ ] Updates quotation correctly
|
||||
- [ ] Enforces branch access
|
||||
- [ ] Validates status (only DRAFT)
|
||||
- [ ] Sets updatedBy
|
||||
|
||||
- [ ] `createRevision()`
|
||||
- [ ] Clones quotation correctly
|
||||
- [ ] Increments revisionNo
|
||||
- [ ] Sets parentQuotationId
|
||||
- [ ] Sets status to DRAFT
|
||||
- [ ] Preserves currency and exchange rate
|
||||
|
||||
- [ ] `getQuotationVersions()`
|
||||
- [ ] Returns all versions by code
|
||||
- [ ] Includes different currencies
|
||||
- [ ] Orders by revisionNo
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Integration Tests
|
||||
|
||||
### Database Integration
|
||||
|
||||
- [ ] Test database connection
|
||||
- [ ] Test transaction rollback
|
||||
- [ ] Test concurrent access
|
||||
- [ ] Test foreign key constraints
|
||||
|
||||
### Service Integration
|
||||
|
||||
- [ ] Test customer creation with contacts
|
||||
- [ ] Test quotation creation with items
|
||||
- [ ] Test revision creation
|
||||
- [ ] Test contact visibility rules
|
||||
- [ ] Test branch scoping
|
||||
|
||||
### API Integration
|
||||
|
||||
- [ ] Test authentication flow
|
||||
- [ ] Test branch context injection
|
||||
- [ ] Test error handling
|
||||
- [ ] Test response formats
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Manual API Tests
|
||||
|
||||
### Customer Endpoints
|
||||
|
||||
#### Get All Customers
|
||||
|
||||
```bash
|
||||
GET /api/customers
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
|
||||
Query Params (optional):
|
||||
status: active | inactive | pending
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": [...],
|
||||
"count": 10,
|
||||
"message": "Found 10 customer(s)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Customer by ID
|
||||
|
||||
```bash
|
||||
GET /api/customers/:id
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "...",
|
||||
"branchId": "...",
|
||||
"name": "...",
|
||||
"crmCustomerCode": "...",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Create Customer
|
||||
|
||||
```bash
|
||||
POST /api/customers
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"name": "Test Customer",
|
||||
"email": "test@example.com",
|
||||
"phone": "1234567890",
|
||||
"company": "Test Company",
|
||||
"address": "123 Test St",
|
||||
"customerStatus": "active",
|
||||
"customerType": "corporate",
|
||||
"taxId": "1234567890123"
|
||||
}
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "...",
|
||||
"crmCustomerCode": "CUST-001", // Auto-generated
|
||||
...
|
||||
},
|
||||
"message": "Customer created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Update Customer
|
||||
|
||||
```bash
|
||||
PUT /api/customers/:id
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"name": "Updated Customer",
|
||||
"erpCustomerCode": "ERP-001" // Optional
|
||||
}
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {...},
|
||||
"message": "Customer updated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Delete Customer
|
||||
|
||||
```bash
|
||||
DELETE /api/customers/:id
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "...",
|
||||
"deletedAt": "2026-04-24T..."
|
||||
},
|
||||
"message": "Customer deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### Contact Endpoints
|
||||
|
||||
#### Get Visible Contacts
|
||||
|
||||
```bash
|
||||
GET /api/customers/:customerId/contacts
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "...",
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"isPublic": false,
|
||||
...
|
||||
}
|
||||
],
|
||||
"count": 5,
|
||||
"message": "Found 5 contact(s)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Create Contact
|
||||
|
||||
```bash
|
||||
POST /api/customers/:customerId/contacts
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"name": "John Doe",
|
||||
"position": "Manager",
|
||||
"phone": "9876543210",
|
||||
"email": "john@example.com",
|
||||
"isPrimary": true
|
||||
}
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "...",
|
||||
"name": "John Doe",
|
||||
"isPublic": false, // Default
|
||||
...
|
||||
},
|
||||
"message": "Contact created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Share Contact
|
||||
|
||||
```bash
|
||||
POST /api/contacts/:contactId/share
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "...",
|
||||
"name": "John Doe",
|
||||
"isPublic": true, // Now shared
|
||||
...
|
||||
},
|
||||
"message": "Contact shared successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### Quotation Endpoints
|
||||
|
||||
#### Create Quotation
|
||||
|
||||
```bash
|
||||
POST /api/quotations
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"customerId": "customer-uuid",
|
||||
"quotationDate": "2026-04-24T10:00:00Z",
|
||||
"validUntil": "2026-05-24T10:00:00Z",
|
||||
"currencyCode": "USD",
|
||||
"exchangeRate": 35.5,
|
||||
"subtotal": "1000.00",
|
||||
"discount": "50.00",
|
||||
"taxRate": 7.0,
|
||||
"taxAmount": "66.50",
|
||||
"totalAmount": "1016.50",
|
||||
"notes": "Test quotation"
|
||||
}
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "...",
|
||||
"code": "Q-2026-00001", // Auto-generated
|
||||
"baseCurrencyAmount": "36085.75", // THB equivalent
|
||||
"status": "new_job_draft",
|
||||
"revisionNo": null,
|
||||
...
|
||||
},
|
||||
"message": "Quotation created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Update Quotation (Draft Only)
|
||||
|
||||
```bash
|
||||
PUT /api/quotations/:id
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"subtotal": "1200.00",
|
||||
"totalAmount": "1219.80"
|
||||
}
|
||||
|
||||
Expected Response (Success):
|
||||
{
|
||||
"success": true,
|
||||
"data": {...},
|
||||
"message": "Quotation updated successfully"
|
||||
}
|
||||
|
||||
Expected Response (Error if SENT):
|
||||
{
|
||||
"success": false,
|
||||
"error": "Quotation cannot be edited. Create a revision instead."
|
||||
}
|
||||
```
|
||||
|
||||
#### Create Revision
|
||||
|
||||
```bash
|
||||
POST /api/quotations/:id/revision
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "...",
|
||||
"code": "Q-2026-00001", // Same code
|
||||
"revisionNo": 1, // Incremented
|
||||
"parentQuotationId": "original-quotation-id",
|
||||
"status": "new_job_draft", // Reset to draft
|
||||
...
|
||||
},
|
||||
"message": "Revision created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Quotation Versions
|
||||
|
||||
```bash
|
||||
GET /api/quotations/code/:code/versions
|
||||
Headers:
|
||||
Authorization: Bearer <token>
|
||||
x-branch-id: <branch-uuid>
|
||||
|
||||
Expected Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "...",
|
||||
"code": "Q-2026-00001",
|
||||
"revisionNo": 0,
|
||||
"currencyCode": "THB",
|
||||
"totalAmount": "1000.00",
|
||||
...
|
||||
},
|
||||
{
|
||||
"id": "...",
|
||||
"code": "Q-2026-00001",
|
||||
"revisionNo": 1,
|
||||
"currencyCode": "THB",
|
||||
"totalAmount": "1200.00",
|
||||
...
|
||||
},
|
||||
{
|
||||
"id": "...",
|
||||
"code": "Q-2026-00001",
|
||||
"revisionNo": 0, // Different currency version
|
||||
"currencyCode": "USD",
|
||||
"totalAmount": "30.00",
|
||||
...
|
||||
}
|
||||
],
|
||||
"count": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Error Scenarios
|
||||
|
||||
### Authentication Errors
|
||||
|
||||
- [ ] Missing Authorization header
|
||||
- [ ] Invalid token
|
||||
- [ ] Expired token
|
||||
- [ ] Missing x-branch-id header
|
||||
- [ ] Invalid branch ID
|
||||
- [ ] User has no access to branch
|
||||
|
||||
### Validation Errors
|
||||
|
||||
- [ ] Missing required fields
|
||||
- [ ] Invalid email format
|
||||
- [ ] Invalid currency code
|
||||
- [ ] Negative exchange rate
|
||||
- [ ] Invalid status transition
|
||||
- [ ] Creating quotation without visible contacts
|
||||
|
||||
### Permission Errors
|
||||
|
||||
- [ ] Accessing customer from wrong branch
|
||||
- [ ] Updating contact not owned by user
|
||||
- [ ] Sharing contact not owned by user
|
||||
- [ ] Deleting contact not owned by user
|
||||
- [ ] Editing sent quotation (without revision)
|
||||
- [ ] Accessing quotation from wrong branch
|
||||
|
||||
### Business Logic Errors
|
||||
|
||||
- [ ] Duplicate CRM customer code
|
||||
- [ ] Duplicate ERP customer code
|
||||
- [ ] Quotation with no items
|
||||
- [ ] Invalid discount amount
|
||||
- [ ] Invalid tax calculation
|
||||
- [ ] Revision of revision (not allowed)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Test Coverage Goals
|
||||
|
||||
### Minimum Coverage Targets
|
||||
|
||||
- **Unit Tests**: 80% code coverage
|
||||
- **Integration Tests**: 60% code coverage
|
||||
- **API Tests**: 100% endpoint coverage
|
||||
|
||||
### Critical Path Coverage
|
||||
|
||||
- [ ] Customer CRUD flow
|
||||
- [ ] Contact visibility flow
|
||||
- [ ] Quotation creation flow
|
||||
- [ ] Revision creation flow
|
||||
- [ ] Multi-currency conversion
|
||||
- [ ] Branch scoping enforcement
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Testing Tools
|
||||
|
||||
### Recommended Tools
|
||||
|
||||
- **Unit Tests**: Jest, Vitest
|
||||
- **Integration Tests**: Supertest, @elysiajs/testing
|
||||
- **API Testing**: Postman, Insomnia, curl
|
||||
- **Database Testing**: testcontainers, docker-compose
|
||||
|
||||
### Test Data
|
||||
|
||||
Create test data fixtures:
|
||||
|
||||
- [ ] Sample customers
|
||||
- [ ] Sample contacts
|
||||
- [ ] Sample quotations
|
||||
- [ ] Sample quotation items
|
||||
- [ ] Test users with different branch access
|
||||
|
||||
---
|
||||
|
||||
## 📝 Test Execution
|
||||
|
||||
### Run All Tests
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
### Run Unit Tests Only
|
||||
|
||||
```bash
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
### Run Integration Tests Only
|
||||
|
||||
```bash
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
### Run with Coverage
|
||||
|
||||
```bash
|
||||
npm run test:coverage
|
||||
```
|
||||
|
||||
### Run Specific Test File
|
||||
|
||||
```bash
|
||||
npm test customers.service.test.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Success Criteria
|
||||
|
||||
Phase 7 is complete when:
|
||||
|
||||
- [ ] All unit tests pass (80% coverage)
|
||||
- [ ] All integration tests pass (60% coverage)
|
||||
- [ ] All API endpoints tested manually
|
||||
- [ ] All error scenarios tested
|
||||
- [ ] Critical path scenarios tested
|
||||
- [ ] Test documentation complete
|
||||
- [ ] CI/CD pipeline configured
|
||||
|
||||
---
|
||||
|
||||
## 📋 Remaining Tasks
|
||||
|
||||
- [ ] Set up test framework (Jest/Vitest)
|
||||
- [ ] Write unit tests for customer service
|
||||
- [ ] Write unit tests for quotation service
|
||||
- [ ] Write integration tests
|
||||
- [ ] Create Postman collection
|
||||
- [ ] Execute manual API tests
|
||||
- [ ] Document test results
|
||||
- [ ] Fix any bugs found
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Next Steps
|
||||
|
||||
After Phase 7:
|
||||
|
||||
- [ ] Create final project documentation
|
||||
- [ ] Deploy to staging environment
|
||||
- [ ] Conduct user acceptance testing (UAT)
|
||||
- [ ] Deploy to production
|
||||
- [ ] Monitor and maintain
|
||||
|
||||
---
|
||||
|
||||
**Phase 7 Status**: 🚧 **NOT STARTED**
|
||||
**Overall Project Status**: 85% Complete (Phases 1-6 Done)
|
||||
Reference in New Issue
Block a user