Files
web-hosts/domains/coppertone.tech/docs/TESTING.md
2025-12-26 13:38:04 +01:00

8.0 KiB

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

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

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

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

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

cd backend/functions/<service-name>

# 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

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

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):

- 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):

- 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/:

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:

cd frontend
npm run test:unit -- --coverage
# Open coverage/index.html in browser

Backend Coverage

Generate HTML coverage report:

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