Repository navigation
Development
- Go 1.25+
- Docker and Docker Compose (for integration tests and local infrastructure)
- golangci-lint (for linting)
- pre-commit (optional, for git hooks)
git clone <repo-url>
cd storm-data-api
make docker-up # Start Postgres + Kafka
make run # Start server (runs migrations automatically)Install pre-commit hooks (optional):
pre-commit installGraphQL types and execution engine are generated by gqlgen:
make generateThis regenerates internal/graph/generated.go and internal/graph/models_gen.go from the schema in internal/graph/schema.graphqls. Resolver implementations in schema.resolvers.go are preserved.
make test-unitRuns unit tests with the race detector enabled (-race -count=1). Unit tests validate model deserialization and enum behavior. They do not require Docker or any external services.
| Test | What it verifies |
|---|---|
TestLoadMockData |
Mock JSON deserializes into 30 StormReport structs |
TestMockDataTypes |
10 hail, 10 tornado, 10 wind reports |
TestMockDataFields |
All required fields are populated (ID, coordinates, state, event_time, source_office) |
TestMockDataOptionalFields |
Optional fields (severity, distance) are present on some but not all records |
TestMockDataHailReport |
Specific field values for hail-1 match expected data |
TestSortFieldIsValid |
SortField enum validates known values and rejects invalid/lowercase strings |
TestSortFieldString |
SortField.String() returns correct uppercase representation |
TestSortOrderIsValid |
SortOrder enum validates ASC/DESC and rejects invalid values |
TestSortOrderString |
SortOrder.String() returns correct uppercase representation |
make test-coverGenerates coverage.out and opens an HTML coverage report in the browser.
Integration tests use testcontainers-go to spin up real PostgreSQL and Kafka containers. Docker must be running.
make test-integration| Test | What it verifies |
|---|---|
TestStoreInsertAndQuery |
Insert all 30 mock reports, then test: get by ID, list all, filter by type, filter by state, geo radius search, get non-existent returns nil |
TestStoreAggregations |
CountByType (3 groups, max magnitude), CountByState (with county sub-groups, sum validation), CountByHour (bucket totals), LastUpdated, CountByType with type filter |
TestStoreFilters |
Severity filter, multiple severities, counties, minMagnitude, combined filters (type + state + severity), empty result, multiple types |
TestStoreSortingAndPagination |
Sort by magnitude DESC/ASC, sort by state, limit, offset with page comparison, offset beyond total |
TestGraphQLEndpoint |
Full GraphQL query: list all (30), filter by type (10 hail), get single report by ID with nested fields |
TestGraphQLAggregations |
Full GraphQL response: totalCount, byType with counts, byState with county sub-groups, byHour bucket totals, lastUpdated, dataLagMinutes
|
TestKafkaConsumerIntegration |
Produce 30 mock messages to Kafka, consume them, insert to Postgres, verify all 30 are in the database |
| Service | Image |
|---|---|
| PostgreSQL | postgres:16 |
| Kafka | confluentinc/confluent-local:7.6.0 |
The Kafka integration tests use the Confluent image (not apache/kafka) because the testcontainers Kafka module requires Confluent's startup scripts.
Sample storm report JSON files live in data/mock/. These are used by unit tests to verify model deserialization against realistic data for all three event types (hail, tornado, wind).
make lintUses golangci-lint with the configuration in .golangci.yml. Enabled linters include:
-
errcheck,govet,staticcheck-- correctness -
gosec,sqlclosecheck,noctx-- security -
gocritic,revive-- style -
misspell,unparam,errname-- hygiene -
prealloc,unconvert-- performance -
exhaustive-- completeness
make fmtRuns gofmt and goimports across the project.
The .pre-commit-config.yaml configures hooks that run on every commit:
- Trailing whitespace removal
- End-of-file newline
- YAML and JSON validation
- Merge conflict markers
- Private key detection (gitleaks)
-
gofmtandgoimports -
go vetandgo build golangci-lint- GraphQL schema linting
yamllint
The .github/workflows/ci.yml workflow runs on pushes and pull requests to main:
| Job | What It Does |
|---|---|
test-unit |
make test-unit (unit tests with race detector) |
lint |
make lint (golangci-lint with the project config) |
build |
make build (compile check) |
A separate release.yml workflow (triggered by CI success on main) handles versioning, GitHub releases, and Docker image publishing.
-
Schema-first GraphQL: The schema in
schema.graphqlsis the source of truth. Runmake generateafter schema changes. -
Domain logic is pure: The
modelpackage has no infrastructure imports. -
Interfaces defined by consumers: The
storepackage defines the query interface used by GraphQL resolvers. -
Adapter constructor injection: All adapters (Kafka, HTTP, database) accept
*slog.Loggervia their constructors for consistent, testable logging. -
Embedded migrations: SQL migrations in
internal/database/migrations/are embedded via//go:embedand run automatically on startup.
Storm Data API
Related Wikis