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 271 StormReport structs |
TestMockDataTypes |
79 hail, 149 tornado, 43 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 |
San Saba TX 1.25" hail report matches expected field values |
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 271 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 (271 total), filter by type (79 hail) |
TestGraphQLAggregations |
Full GraphQL response: totalCount, byType with counts, byState with county sub-groups, byHour bucket totals, lastUpdated, dataLagMinutes
|
TestKafkaConsumerIntegration |
Produce 271 mock messages to Kafka, consume them, insert to Postgres, verify all 271 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:
-
bodyclose,errorlint,gosec,noctx,sqlclosecheck-- security and correctness -
gocritic,revive-- style -
misspell,unparam,errname,testifylint-- hygiene -
prealloc,unconvert-- performance -
exhaustive-- completeness -
gocyclo-- complexity (threshold: 15)
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
- Large file detection
- Private key detection (gitleaks)
-
gofmtandgoimports -
go vetandgo build golangci-lint- GraphQL schema linting
yamllintmarkdownlint
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) |
sonarcloud |
Unit tests with coverage + SonarCloud scan |
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. -
Concrete store dependency: Resolvers depend on
*store.Storedirectly. The store is the single source of all data access logic. -
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.
- System Development -- multi-repo workflow, CI conventions, and cross-service patterns
- Shared Development -- shared library development and versioning
- Architecture -- schema-first GraphQL, query protection, and embedded migrations
- Configuration -- environment variables and operational endpoints
- API Reference -- GraphQL types, queries, filter options
Storm Data API
Related Wikis