Files
nextjs-elysia-allaos/docs/checklist-phase7-testing.md
phaichayon 043edff93a setup
2026-04-26 00:15:22 +07:00

13 KiB

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

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

GET /api/customers/:id
Headers:
  Authorization: Bearer <token>
  x-branch-id: <branch-uuid>

Expected Response:
{
  "success": true,
  "data": {
    "id": "...",
    "branchId": "...",
    "name": "...",
    "crmCustomerCode": "...",
    ...
  }
}

Create Customer

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

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

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

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

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

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

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)

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

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

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

  • 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

npm test

Run Unit Tests Only

npm run test:unit

Run Integration Tests Only

npm run test:integration

Run with Coverage

npm run test:coverage

Run Specific Test File

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)