Files
atlas/docs/TESTING.md
othman.suseno 507961716e
Some checks failed
CI / test-build (push) Failing after 2m26s
add tui features
2025-12-15 01:08:17 +07:00

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 code
  • AssertJSONResponse: Validate JSON response
  • AssertErrorResponse: Check error response format
  • AssertSuccessResponse: Validate success response
  • AssertHeader: 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

  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