Files
training-tracker/backend/claude.md
2026-01-19 19:49:47 +01:00

3.7 KiB

General

  • Before scaffolding any service, verify current stable versions from official sources (go.dev, rust-lang.org, nodejs.org, etc.)
  • Prefer stdlib over dependencies where reasonable
  • No vendoring unless explicitly requested

Go

  • Use latest stable Go version (check https://go.dev/dl/ before starting)
  • Use slog for structured logging
  • context.Context first param, always
  • No init() unless unavoidable
  • Errors: prefer errors.Join for multiple errors, fmt.Errorf with %w for wrapping

Containers

  • Multi-stage builds, distroless/scratch final images
  • ubuntu based images for the development builds
  • Non-root user, read-only rootfs for production builds
  • prepare dockerfiles for both development and production
  • Pin base image digests in prod Dockerfiles

K8s Manifests

  • Always include resource requests/limits
  • Liveness/readiness probes with sane defaults
  • PodDisruptionBudgets for replicated workloads

Code Style

  • Fail fast, return early
  • Table-driven tests
  • No obvious comments

CI/CD

  • Git remote: Gitea at https://gitea.yourdomain.com
  • CI: Gitea Actions (workflow syntax is GitHub Actions compatible)
  • Workflows go in .gitea/workflows/
  • Use runs-on: linux-amd64 (or whatever labels your runners have)
  • Registry: gitea.home.hrajfrisbee.cz (use ${{ secrets.REGISTRY_TOKEN }} for auth)

Testing (Go)

Go Test Requirements

  • MANDATORY: All new Go code must have corresponding tests
  • Use stdlib testing package, no testify unless already in project
  • Table-driven tests as default pattern
  • Test file next to source: foo.gofoo_test.go
  • Name tests Test<Function>_<scenario> (e.g., TestCreateUser_duplicateEmail)
  • Use t.Parallel() where safe
  • Subtests for table entries: t.Run(tc.name, func(t *testing.T) { ... })
  • Test behavior, not implementation
  • Use testing.Short() to skip slow integration tests: if testing.Short() { t.Skip() }

Test Structure

  • Each test function should test ONE behavior
  • Use descriptive test names: TestExerciseHandler_Create_ValidInput
  • Table-driven test format:
    tests := []struct {
        name    string
        input   InputType
        want    ExpectedType
        wantErr bool
    }{
        {"valid input", validInput, expectedOutput, false},
        {"empty name", emptyName, nil, true},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // test logic
        })
    }
    

Handler Tests (Unit)

  • Mock external dependencies (repositories, services)
  • Use httptest.NewRecorder() and httptest.NewRequest()
  • Test: success path, validation errors, not found, internal errors
  • Mock interfaces defined in internal/api/interfaces.go
  • Mock implementations in internal/api/mocks_test.go

Repository Tests (Integration)

  • Use testcontainers-go for database tests
  • Integration tests in _test.go with build tag //go:build integration
  • Clean database state between tests using truncateTables()
  • Test: CRUD operations, edge cases, transactions, cascades
  • Test helpers in internal/storage/testhelpers_test.go

What to Test

  • All public functions/methods
  • Happy path AND error paths
  • Edge cases: empty inputs, nil values, not found
  • Validation logic
  • Transaction rollback scenarios

What NOT to Test

  • Simple getters/setters
  • Third-party library code
  • Private helper functions (test through public API)

Running Tests

# Run all unit tests (fast)
go test ./...

# Run with verbose output
go test -v ./...

# Run with coverage
go test -cover ./...

# Run only unit tests, skip integration
go test -short ./...

# Run integration tests (requires Docker)
go test -v -tags=integration ./internal/storage/...

# Run specific package
go test -v ./internal/api/...