infra/.planning/codebase/TESTING.md
2026-02-23 20:54:27 +00:00

7.8 KiB

Testing Patterns

Analysis Date: 2026-02-23

Test Framework

Language-Specific Runners:

Go:

  • Runner: go test (standard library testing package)
  • Config: No config file (uses built-in conventions)
  • Run Commands:
    go test ./...                    # Run all tests
    go test -v ./...                 # Verbose output
    go test -run TestContains ./...  # Run specific test
    go test -cover ./...             # Show coverage
    

Bash:

  • Runner: Custom shell scripts in scripts/
  • No formal test framework; uses set -euo pipefail for error handling
  • Manual health checks via bash scripts/cluster_healthcheck.sh

Terraform:

  • Framework: No automated testing detected (no terraform test files, no tftest.hcl)
  • Validation: Manual terraform validate, terraform plan, visual inspection
  • Integration: Terragrunt applies validate before execution

Test File Organization

Location:

  • Go tests: Co-located with source code: <service>/files/internal/scraper/validate_test.go
  • Shell/Infrastructure: No test files (manual validation/health checks only)

Naming:

  • Go: *_test.go suffix
  • Script tests: .sh for check/validation scripts

Structure:

stacks/f1-stream/files/internal/scraper/
├── main.go
├── validate.go
└── validate_test.go           # Test file co-located

Test Structure

Go Table-Driven Tests:

func TestContainsVideoMarkers(t *testing.T) {
	tests := []struct {
		name string
		body string
		want bool
	}{
		{
			name: "video tag",
			body: `<div><video src="stream.mp4"></video></div>`,
			want: true,
		},
		// ... more test cases
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := containsVideoMarkers(tt.body)
			if got != tt.want {
				t.Errorf("containsVideoMarkers(%q) = %v, want %v", truncate(tt.body, 60), got, tt.want)
			}
		})
	}
}

Patterns:

  • Slice of anonymous structs with name, input fields, and want for expected result
  • Loop with t.Run(tt.name, ...) for individual test case execution and reporting
  • Descriptive test case names: "video tag", "HLS manifest reference", "empty string"
  • Separate positive cases (upper) and negative cases (lower) with comments

Bash Health Check Structure:

check_nodes() {
    section 1 "Node Status"
    local nodes not_ready versions unique_versions detail=""

    nodes=$($KUBECTL get nodes --no-headers 2>&1) || { fail "Cannot reach cluster"; json_add "node_status" "FAIL" "Cannot reach cluster"; return 0; }
    # ... processing
    if [[ -n "$not_ready" ]]; then
        fail "NotReady nodes: $not_ready"
        json_add "node_status" "FAIL" "$detail"
    elif [[ "$unique_versions" -gt 1 ]]; then
        warn "Version mismatch..."
        json_add "node_status" "WARN" "$detail"
    else
        pass "All nodes Ready..."
        json_add "node_status" "PASS" "$detail"
    fi
}

Patterns:

  • Each check function follows same structure: setup → validation → status reporting
  • Status reported via pass(), warn(), fail() helper functions
  • JSON output optional via json_add() for programmatic consumption
  • Error handling inline with || fallback and graceful degradation

Mocking

Framework:

  • Go: No mocking framework detected (table-driven tests use real function calls)
  • Bash: External commands mocked implicitly (KUBECONFIG override, kubectl invocation through $KUBECTL variable)

Patterns (Go):

  • No mock objects or stubs
  • Real function behavior tested directly
  • Test data provided as input in struct fields

Patterns (Bash):

# Kubeconfig override allows testing against different clusters
KUBECTL="kubectl --kubeconfig $KUBECONFIG_PATH"
nodes=$($KUBECTL get nodes --no-headers 2>&1) || { fail "Cannot reach cluster"; return 0; }

What NOT to Mock:

  • Core functionality being tested (test actual behavior)
  • Standard library functions (test integration)

What to Mock (Bash):

  • External kubectl calls via variable indirection: allows KUBECONFIG override
  • Conditional output by flag: --json, --quiet flags change output, not behavior

Fixtures and Factories

Test Data (Go):

  • Inline strings in struct fields: HTML content, MIME types
  • Examples from validate_test.go:
    {
        name: "HLS manifest reference",
        body: `var url = "https://cdn.example.com/live.m3u8";`,
        want: true,
    },
    

Location:

  • Embedded directly in test file as struct field values
  • No separate fixture files or factories

Bash Fixtures:

  • Real cluster fixtures: tests run against actual Kubernetes cluster
  • No data files; tests fetch live state via kubectl

Coverage

Requirements: None enforced (no coverage thresholds, targets, or CI/CD gates detected)

View Coverage (Go):

go test -cover ./...              # Show coverage percentages
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out  # Open HTML report

Note: Coverage tools not integrated into CI/CD pipeline; manual check only.

Test Types

Unit Tests (Go):

  • Scope: Single function validation
  • Approach: Table-driven with parameterized inputs
  • Example: TestContainsVideoMarkers() tests HTML content detection
  • Example: TestIsDirectVideoContentType() tests MIME type classification
  • In file: stacks/f1-stream/files/internal/scraper/validate_test.go

Integration Tests:

  • Bash health checks (scripts/cluster_healthcheck.sh) serve as integration tests
  • Tests 24 separate checks against live Kubernetes cluster:
    • Node status and readiness
    • Node resource utilization
    • Container metrics
    • Pod crash loops
    • Persistent volume health
    • DNS resolution
    • Networking
    • RBAC
    • Logs aggregation
  • Can run with --fix flag for auto-remediation
  • Can output JSON for CI integration

E2E Tests:

  • Not formally implemented
  • Manual validation via Terragrunt apply → cluster state verification

Infrastructure Testing:

  • Terraform: terraform validate and terraform plan provide syntax/logic validation
  • Application health: Manual checks via scripts and cluster_healthcheck.sh
  • No automated test suite for infrastructure code

Common Patterns

Async Testing (Go):

  • Not applicable (synchronous function testing only)

Error Testing (Go):

{
    name: "empty string",
    body: "",
    want: false,
},
  • Negative test cases included in same table
  • Error/edge cases named descriptively: "empty string", "reddit link page"
  • Expected failure behavior verified: want: false for invalid inputs

Error Reporting (Go):

t.Errorf("containsVideoMarkers(%q) = %v, want %v", truncate(tt.body, 60), got, tt.want)
  • Formatted message includes: function name, input (truncated), actual, expected
  • Test name automatically prefixed by t.Run(tt.name, ...)

Status Reporting (Bash):

  • Color-coded status: ${GREEN}[PASS]${NC}, ${YELLOW}[WARN]${NC}, ${RED}[FAIL]${NC}
  • Counter incremented per status
  • Optional quiet mode (--quiet) suppresses PASS output
  • Optional JSON output (--json) for CI integration
  • Summary printed at end: $PASS_COUNT/$WARN_COUNT/$FAIL_COUNT

Running Tests

Go Tests:

# From service directory containing *_test.go
go test -v ./...

Bash Health Checks:

# Comprehensive checks
bash scripts/cluster_healthcheck.sh

# Quiet mode (WARN/FAIL only)
bash scripts/cluster_healthcheck.sh --quiet

# Auto-fix mode
bash scripts/cluster_healthcheck.sh --fix

# JSON output
bash scripts/cluster_healthcheck.sh --json

# Custom kubeconfig
bash scripts/cluster_healthcheck.sh --kubeconfig /path/to/config

Terraform Validation:

# Format check
terraform fmt -recursive

# Syntax validation
terraform validate

# Plan without apply
terraform plan

# From stack directory
cd stacks/<service> && terragrunt plan
cd stacks/<service> && terragrunt apply --non-interactive

Testing analysis: 2026-02-23