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

342 lines
8.0 KiB
Markdown

# 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/<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**
```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)