673 lines
13 KiB
Markdown
673 lines
13 KiB
Markdown
# 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)
|