Imported from rusq/aklapi (
AGENTS.md). Install upstream withnpx skills add rusq/aklapi. Copyright stays with the author.
AGENTS.md — Coding Agent Instructions for aklapi
This document provides guidance for agentic coding assistants operating in this repository.
Project Overview
aklapi is a Go library and HTTP server that exposes Auckland Council APIs
(rubbish collection schedules, property address lookup) as a simple REST service.
- Module:
github.com/rusq/aklapi(go 1.25) - Library package: root (
aklapi) - Binary:
cmd/aklapi/— standard HTTP server on port 8080 - Language: Go only — no TypeScript, JavaScript, or Node tooling
Build, Run & Test Commands
# Build the server binary
go build -o server ./cmd/aklapi
# Run the server (port defaults to 8080)
./server
# Run all tests
go test ./...
# Run all tests with verbose output
go test -v ./...
# Run a single test by name (supports regex)
go test -v -run TestFunctionName ./...
# Run a single test in this package
go test -v -run TestCollectionDayDetail .
# Run tests with race detector
go test -race ./...
# Build all packages (verify compilation)
go build -v ./...
# Format code (use goimports, not gofmt)
goimports -w .
# Lint (golangci-lint with default config)
golangci-lint run ./...
# Docker build
docker build -t aklapi .
# Make targets
make server # go build -o server ./cmd/aklapi
make test # go test ./... -race
make docker # docker build -t aklapi .
To run a single test: use
go test -v -run <TestName> <./package/path>Example:go test -v -run TestNextRubbish .
Code Style Guidelines
Formatting
- Use
goimports(not plaingofmt) — it manages imports automatically. - Indentation: tabs (Go standard).
- VS Code devcontainer is configured with
"editor.formatOnSave": trueusinggoimports. - No trailing whitespace; no blank lines at end of file.
Imports
Group imports in two blocks separated by a blank line:
- Standard library
- Third-party packages
import (
"context"
"encoding/json"
"net/http"
"github.com/PuerkitoBio/goquery"
)
- Use blank imports only where required:
_ "time/tzdata",_ "embed". - Never use dot imports (
.). - Alias imports only when disambiguation is genuinely needed.
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Exported types | PascalCase | AddrRequest, RubbishCollection |
| Unexported types | camelCase | refuseParser, lruCache |
| Exported functions | PascalCase | AddressLookup, CollectionDayDetail |
| Unexported functions | camelCase | fetchandparse, oneAddress |
| Receiver names | Short (1–2 chars) | (r *RubbishCollection), (c *lruCache[K,V]) |
| Package-level vars | camelCase | addrCache, defaultLoc |
| Unexported constants | camelCase | defCacheSz, dateLayout |
| Acronyms | Go convention | addrURI (not addrUrl), ID (not Id) |
Types & Structs
- Add JSON struct tags to all exported response types:
json:"field,omitempty". - Prefer pointer receivers for types that may mutate state or are large.
- Use generics for reusable containers (see
lruCache[K comparable, V any]). - Use a stateful parser type (struct with fields for state, error, and results) when
parsing multi-step data (see
refuseParser).
Error Handling
- Always check errors:
if err != nil { return nil, err }. - No
panicin production code. - Use
errors.New("...")for static error messages. - Use string concatenation (not
fmt.Sprintf) for simple dynamic error strings:errors.New("address API returned status code: " + strconv.Itoa(resp.StatusCode)) - Prefer
fmt.Errorf("context: %w", err)for wrapping errors that need context. - Use package-level sentinel errors for flow control:
var errSkip = errors.New("skip this date") - Use
errors.Isfor sentinel error comparisons. - HTTP handlers: use
http.Error(w, msg, code)or a typedrespond(w, body, code)helper.
HTTP & Networking
- Use standard library
net/httponly — no external router (no Gin, Echo, Chi). - Register routes with
http.HandleFuncon the default mux. - Always pass context to outgoing HTTP requests:
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) - Always
defer resp.Body.Close()immediately after a successful response. - Decode JSON responses with
json.NewDecoder(resp.Body).Decode(&v).
Logging
- Use
log/slogfor all logging — notlog.Printf,fmt.Println, etc. - Prefer context-aware variants:
slog.DebugContext(ctx, ...),slog.InfoContext(ctx, ...). - Add structured key-value pairs for observability:
start := time.Now() // ... operation ... slog.DebugContext(ctx, "fetched addresses", "count", len(results), "duration", time.Since(start))
Dependency Injection & Testability
- Declare external URLs as package-level
var(notconst) so tests can override them:var addrURI = `https://example.com/api/addresses` - Inject time via a replaceable variable:
var now = time.Now. - Injectable function-type variables enable handler testing without real upstream calls:
var addressLookup = aklapi.AddressLookup - Restore overridden vars with
deferin top-level tests, ort.Cleanupin subtests:old := addrURI addrURI = ts.URL t.Cleanup(func() { addrURI = old })
Upstream Request Shape
- The Auckland Council collection HTML endpoint may return
406unless requests look like a modern browser. - Reuse the shared HTTP client / transport helpers for outbound requests instead
of creating bare
http.Clientinstances. - For the collection-day HTML page, preserve the browser-style document headers
set by helper functions such as
setBrowserDocumentHeaders.
Testing Guidelines
Style
- Use table-driven tests for all non-trivial functions.
- Table entry struct fields:
name string,args,want,wantErr bool. - Field names may be omitted for the
namefield in composite literals. - Prefer
github.com/stretchr/testify/assertfor assertions in new tests (avoid rawreflect.DeepEqual+t.Errorfpatterns from older tests). - Use
t.Context()(Go 1.24+) for context in subtests. - Use
t.Cleanup(func() {...})for teardown instead ofdeferin the test function body when working with subtests.
HTTP Testing
- Use
net/http/httptest.NewServerto mock upstream APIs. - Use
httptest.NewRequest+httptest.NewRecorderfor handler unit tests.
Test Fixtures
- Embed HTML fixture files with
//go:embed://go:embed test_assets/some-page.html var fixtureHTML []byte - Fixtures are refreshed by
//go:generatedirectives thatcurlthe live page.
Subtests
- Always run subtests with
t.Run(tt.name, func(t *testing.T) { ... }). - Use
t.Helper()in assertion helper functions.
Repository Conventions
- One concern per file:
addr.go,rubbish.go,caches.go,time.go. - Library in root, binary in
cmd/: follows standard Go project layout. - CI runs on
pushandpull_requesttomaster(see.github/workflows/go.yml):go build -v ./...thengo test -v ./.... - Docker images are published to
ffffuuu/aklapion GitHub Release events.