# Testing Guide for Copper Tone Technologies This document describes the testing strategy and implementation for the Copper Tone Technologies platform. ## Testing Stack ### Frontend Testing - **Unit Tests:** Vitest + Vue Test Utils - **E2E Tests:** Cypress - **Coverage:** Configured for comprehensive test coverage reporting ### Backend Testing - **Unit Tests:** Go standard testing library (`testing` package) - **Integration Tests:** Testing against real database with test fixtures - **Test Database:** Separate database for testing (configured via environment) --- ## Frontend Testing ### Running Tests ```bash cd frontend # Run unit tests npm run test:unit # Run unit tests in watch mode npm run test:unit -- --watch # Run unit tests with coverage npm run test:unit -- --coverage # Run E2E tests (headless) npm run test:e2e # Run E2E tests (interactive) npm run test:e2e:dev ``` ### Unit Test Structure Unit tests are located in `src/**/__tests__/` directories, co-located with the code they test. **Example: Store Test** ```typescript import { describe, it, expect, beforeEach, vi } from 'vitest' import { setActivePinia, createPinia } from 'pinia' import { useAuthStore } from '../auth' describe('Auth Store', () => { beforeEach(() => { setActivePinia(createPinia()) vi.clearAllMocks() }) it('initializes with correct default state', () => { const store = useAuthStore() expect(store.user).toBeNull() expect(store.token).toBeNull() }) }) ``` **Example: Component Test** ```typescript import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import HelloWorld from '../HelloWorld.vue' describe('HelloWorld', () => { it('renders properly', () => { const wrapper = mount(HelloWorld, { props: { msg: 'Hello' } }) expect(wrapper.text()).toContain('Hello') }) }) ``` ### E2E Test Structure E2E tests are located in `cypress/e2e/` directory. **Example: Authentication Flow Test** ```typescript describe('Authentication Flow', () => { it('can login with email and password', () => { cy.intercept('POST', '**/login-email-password', { statusCode: 200, body: { token: 'mock-jwt-token' } }).as('login') cy.visit('/login') cy.get('input[type="email"]').type('test@example.com') cy.get('input[type="password"]').type('password123') cy.get('button[type="submit"]').click() cy.wait('@login') cy.url().should('include', '/dashboard') }) }) ``` ### Test Coverage Goals - **Stores:** 100% coverage (critical business logic) - **Components:** 80%+ coverage - **Views:** 70%+ coverage (covered by E2E) - **Utilities:** 100% coverage --- ## Backend Testing ### Running Tests ```bash cd backend/functions/ # Run all tests go test ./... # Run tests with verbose output go test -v ./... # Run tests with coverage go test -cover ./... # Generate coverage report go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out ``` ### Test Structure Tests are in `*_test.go` files co-located with the code they test. **Example: Handler Test** ```go func TestHandleRegisterEmailPassword(t *testing.T) { // Setup test database testDB := setupTestDB(t) defer testDB.Close() // Create test request reqBody := RegisterEmailPasswordRequest{ Email: "test@example.com", Password: "SecurePass123!", Name: "Test User", } body, _ := json.Marshal(reqBody) req := httptest.NewRequest("POST", "/register-email-password", bytes.NewReader(body)) w := httptest.NewRecorder() // Execute handler handleRegisterEmailPassword(w, req) // Assert response if w.Code != http.StatusCreated { t.Errorf("Expected status 201, got %d", w.Code) } } ``` **Example: Service Function Test** ```go func TestVerifyEthereumSignature(t *testing.T) { testCases := []struct { name string address string message string signature string expected bool }{ { name: "valid signature", address: "0x1234...", message: "Test message", signature: "0xabcd...", expected: true, }, { name: "invalid signature", address: "0x1234...", message: "Test message", signature: "0xinvalid", expected: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := verifyEthereumSignature(tc.address, tc.message, tc.signature) if result != tc.expected { t.Errorf("Expected %v, got %v", tc.expected, result) } }) } } ``` ### Test Coverage Goals - **Handlers:** 90%+ coverage - **Business Logic:** 100% coverage - **Utilities:** 100% coverage - **Middleware:** 95%+ coverage --- ## CI/CD Integration ### Gitea Actions Workflows Tests are automatically run on: - Pull requests to `develop` - Commits to `develop`, `testing`, `main` **Frontend Workflow** (`.gitea/workflows/frontend.yml`): ```yaml - name: Run Unit Tests run: | cd frontend npm run test:unit -- --run - name: Run E2E Tests run: | cd frontend npm run build npm run test:e2e ``` **Backend Workflow** (`.gitea/workflows/backend.yml`): ```yaml - name: Run Tests run: | cd backend/functions/auth-service go test -v -cover ./... cd ../work-management-service go test -v -cover ./... cd ../payment-service go test -v -cover ./... ``` --- ## Test Data & Fixtures ### Frontend Test Data Mock data for frontend tests is in `src/**/__tests__/fixtures/`: ```typescript export const mockUser = { id: 1, name: 'Test User', email: 'test@example.com', roles: ['CLIENT'], createdAt: new Date().toISOString() } export const mockProjects = [ { id: 1, name: 'Project Alpha', status: 'ACTIVE', ... }, { id: 2, name: 'Project Beta', status: 'PLANNING', ... } ] ``` ### Backend Test Data Test fixtures in `testdata/` directories: ``` backend/functions/auth-service/ testdata/ users.json identities.json ``` --- ## Best Practices ### Frontend Testing 1. **Use Descriptive Test Names:** `it('redirects to login when accessing protected route while unauthenticated')` 2. **Mock External Dependencies:** Use `vi.mock()` for API calls, localStorage, etc. 3. **Test User Interactions:** Focus on user behavior, not implementation details 4. **Avoid Testing Framework Internals:** Don't test Vue Router, Pinia, etc. - test YOUR code 5. **Use Semantic Queries:** `cy.contains('button', 'Login')` instead of CSS selectors when possible ### Backend Testing 1. **Use Table-Driven Tests:** Test multiple scenarios with a single test function 2. **Test Error Paths:** Don't just test happy path - test failures, edge cases 3. **Use Subtests:** `t.Run(name, func(t *testing.T) { ... })` for organized output 4. **Clean Up Resources:** Use `defer db.Close()`, `defer cleanup()` 5. **Isolate Tests:** Each test should be independent and idempotent --- ## Coverage Reports ### Frontend Coverage Generate HTML coverage report: ```bash cd frontend npm run test:unit -- --coverage # Open coverage/index.html in browser ``` ### Backend Coverage Generate HTML coverage report: ```bash cd backend/functions/auth-service go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out -o coverage.html # Open coverage.html in browser ``` --- ## Continuous Improvement - **Review Coverage Weekly:** Identify untested code paths - **Add Tests for Bug Fixes:** Every bug fix should include a regression test - **Update E2E Tests:** When adding new features, add corresponding E2E tests - **Performance Testing:** Monitor test execution time, keep tests fast - **Flaky Tests:** Fix or remove tests that intermittently fail --- ## Resources - [Vitest Documentation](https://vitest.dev/) - [Vue Test Utils](https://test-utils.vuejs.org/) - [Cypress Documentation](https://docs.cypress.io/) - [Go Testing Package](https://pkg.go.dev/testing) - [Go Table Driven Tests](https://dave.cheney.net/2019/05/07/prefer-table-driven-tests)