7.2 KiB
7.2 KiB
Testing Infrastructure
Overview
AtlasOS includes a comprehensive testing infrastructure with unit tests, integration tests, and test utilities to ensure code quality and reliability.
Test Structure
atlas/
├── internal/
│ ├── validation/
│ │ └── validator_test.go # Unit tests for validation
│ ├── errors/
│ │ └── errors_test.go # Unit tests for error handling
│ └── testing/
│ └── helpers.go # Test utilities and helpers
└── test/
└── integration_test.go # Integration tests
Running Tests
Run All Tests
go test ./...
Run Tests for Specific Package
# Validation tests
go test ./internal/validation -v
# Error handling tests
go test ./internal/errors -v
# Integration tests
go test ./test -v
Run Tests with Coverage
go test ./... -cover
Generate Coverage Report
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out
Unit Tests
Validation Tests
Tests for input validation functions:
go test ./internal/validation -v
Coverage:
- ZFS name validation
- Username validation
- Password validation
- Email validation
- Share name validation
- IQN validation
- Size format validation
- Path validation
- CIDR validation
- String sanitization
- Path sanitization
Example:
func TestValidateZFSName(t *testing.T) {
err := ValidateZFSName("tank")
if err != nil {
t.Errorf("expected no error for valid name")
}
}
Error Handling Tests
Tests for error handling and API errors:
go test ./internal/errors -v
Coverage:
- Error code validation
- HTTP status code mapping
- Error message formatting
- Error details attachment
Integration Tests
Test Server
The integration test framework provides a test server:
ts := NewTestServer(t)
defer ts.Close()
Features:
- In-memory database for tests
- Test HTTP client
- Authentication helpers
- Request helpers
Authentication Testing
// Login and get token
ts.Login(t, "admin", "admin")
// Make authenticated request
resp := ts.Get(t, "/api/v1/pools")
Request Helpers
// GET request
resp := ts.Get(t, "/api/v1/pools")
// POST request
resp := ts.Post(t, "/api/v1/pools", map[string]interface{}{
"name": "tank",
"vdevs": []string{"/dev/sda"},
})
Test Utilities
Test Helpers Package
The internal/testing package provides utilities:
MakeRequest: Create and execute HTTP requests
recorder := MakeRequest(t, handler, TestRequest{
Method: "GET",
Path: "/api/v1/pools",
})
Assertions:
AssertStatusCode: Check HTTP status codeAssertJSONResponse: Validate JSON responseAssertErrorResponse: Check error response formatAssertSuccessResponse: Validate success responseAssertHeader: Check response headers
Example:
recorder := MakeRequest(t, handler, TestRequest{
Method: "GET",
Path: "/api/v1/pools",
})
AssertStatusCode(t, recorder, http.StatusOK)
response := AssertJSONResponse(t, recorder)
Mock Clients
MockZFSClient: Mock ZFS client for testing
mockClient := NewMockZFSClient()
mockClient.AddPool(map[string]interface{}{
"name": "tank",
"size": "10TB",
})
Writing Tests
Unit Test Template
func TestFunctionName(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{"valid input", "valid", false},
{"invalid input", "invalid", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := FunctionName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("FunctionName(%q) error = %v, wantErr %v",
tt.input, err, tt.wantErr)
}
})
}
}
Integration Test Template
func TestEndpoint(t *testing.T) {
ts := NewTestServer(t)
defer ts.Close()
ts.Login(t, "admin", "admin")
resp := ts.Get(t, "/api/v1/endpoint")
if resp.StatusCode != http.StatusOK {
t.Errorf("expected status 200, got %d", resp.StatusCode)
}
}
Test Coverage Goals
Current Coverage
- Validation Package: ~95% coverage
- Error Package: ~90% coverage
- Integration Tests: Core endpoints covered
Target Coverage
- Unit Tests: >80% coverage for all packages
- Integration Tests: All API endpoints
- Edge Cases: Error conditions and boundary cases
Best Practices
1. Test Naming
Use descriptive test names:
func TestValidateZFSName_ValidName_ReturnsNoError(t *testing.T) {
// ...
}
2. Table-Driven Tests
Use table-driven tests for multiple cases:
tests := []struct {
name string
input string
wantErr bool
}{
// test cases
}
3. Test Isolation
Each test should be independent:
func TestSomething(t *testing.T) {
// Setup
ts := NewTestServer(t)
defer ts.Close() // Cleanup
// Test
// ...
}
4. Error Testing
Test both success and error cases:
// Success case
err := ValidateZFSName("tank")
if err != nil {
t.Error("expected no error")
}
// Error case
err = ValidateZFSName("")
if err == nil {
t.Error("expected error for empty name")
}
5. Use Test Helpers
Use helper functions for common patterns:
recorder := MakeRequest(t, handler, TestRequest{
Method: "GET",
Path: "/api/v1/pools",
})
AssertStatusCode(t, recorder, http.StatusOK)
Continuous Integration
GitHub Actions Example
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '1.21'
- run: go test ./... -v
- run: go test ./... -coverprofile=coverage.out
- run: go tool cover -func=coverage.out
Future Enhancements
- More Unit Tests: Expand coverage for all packages
- Integration Tests: Complete API endpoint coverage
- Performance Tests: Benchmark critical paths
- Load Tests: Stress testing with high concurrency
- Mock Services: Mock external dependencies
- Test Fixtures: Reusable test data
- Golden Files: Compare outputs to expected results
- Fuzzing: Property-based testing
- Race Detection: Test for race conditions
- Test Documentation: Generate test documentation
Troubleshooting
Tests Failing
- Check Test Output: Run with
-vflag for verbose output - Check Dependencies: Ensure all dependencies are available
- Check Environment: Verify test environment setup
- Check Test Data: Ensure test data is correct
Coverage Issues
- Run Coverage:
go test ./... -cover - View Report:
go tool cover -html=coverage.out - Identify Gaps: Look for untested code paths
- Add Tests: Write tests for uncovered code
Integration Test Issues
- Check Server: Verify test server starts correctly
- Check Database: Ensure in-memory database works
- Check Auth: Verify authentication in tests
- Check Cleanup: Ensure proper cleanup after tests