# 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 ```bash go test ./... ``` ### Run Tests for Specific Package ```bash # 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 ```bash go test ./... -cover ``` ### Generate Coverage Report ```bash go test ./... -coverprofile=coverage.out go tool cover -html=coverage.out ``` ## Unit Tests ### Validation Tests Tests for input validation functions: ```bash 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:** ```go 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: ```bash 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: ```go ts := NewTestServer(t) defer ts.Close() ``` **Features:** - In-memory database for tests - Test HTTP client - Authentication helpers - Request helpers ### Authentication Testing ```go // Login and get token ts.Login(t, "admin", "admin") // Make authenticated request resp := ts.Get(t, "/api/v1/pools") ``` ### Request Helpers ```go // 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 ```go recorder := MakeRequest(t, handler, TestRequest{ Method: "GET", Path: "/api/v1/pools", }) ``` **Assertions**: - `AssertStatusCode`: Check HTTP status code - `AssertJSONResponse`: Validate JSON response - `AssertErrorResponse`: Check error response format - `AssertSuccessResponse`: Validate success response - `AssertHeader`: Check response headers **Example:** ```go 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 ```go mockClient := NewMockZFSClient() mockClient.AddPool(map[string]interface{}{ "name": "tank", "size": "10TB", }) ``` ## Writing Tests ### Unit Test Template ```go 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 ```go 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: ```go func TestValidateZFSName_ValidName_ReturnsNoError(t *testing.T) { // ... } ``` ### 2. Table-Driven Tests Use table-driven tests for multiple cases: ```go tests := []struct { name string input string wantErr bool }{ // test cases } ``` ### 3. Test Isolation Each test should be independent: ```go func TestSomething(t *testing.T) { // Setup ts := NewTestServer(t) defer ts.Close() // Cleanup // Test // ... } ``` ### 4. Error Testing Test both success and error cases: ```go // 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: ```go recorder := MakeRequest(t, handler, TestRequest{ Method: "GET", Path: "/api/v1/pools", }) AssertStatusCode(t, recorder, http.StatusOK) ``` ## Continuous Integration ### GitHub Actions Example ```yaml 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 1. **More Unit Tests**: Expand coverage for all packages 2. **Integration Tests**: Complete API endpoint coverage 3. **Performance Tests**: Benchmark critical paths 4. **Load Tests**: Stress testing with high concurrency 5. **Mock Services**: Mock external dependencies 6. **Test Fixtures**: Reusable test data 7. **Golden Files**: Compare outputs to expected results 8. **Fuzzing**: Property-based testing 9. **Race Detection**: Test for race conditions 10. **Test Documentation**: Generate test documentation ## Troubleshooting ### Tests Failing 1. **Check Test Output**: Run with `-v` flag for verbose output 2. **Check Dependencies**: Ensure all dependencies are available 3. **Check Environment**: Verify test environment setup 4. **Check Test Data**: Ensure test data is correct ### Coverage Issues 1. **Run Coverage**: `go test ./... -cover` 2. **View Report**: `go tool cover -html=coverage.out` 3. **Identify Gaps**: Look for untested code paths 4. **Add Tests**: Write tests for uncovered code ### Integration Test Issues 1. **Check Server**: Verify test server starts correctly 2. **Check Database**: Ensure in-memory database works 3. **Check Auth**: Verify authentication in tests 4. **Check Cleanup**: Ensure proper cleanup after tests