## 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](https://gitea.home.hrajfrisbee.cz/) - 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.go` → `foo_test.go` - Name tests `Test_` (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: ```go 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 ```bash # 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/... ```