GoBlog has an extensive test suite. Tests are written in Go, use the standard library testing package, and mostly run against real SQLite databases with all migrations applied.
go test -tags=linux,libsqlite3,sqlite_fts5,gomailnotpl -timeout 600s ./...The build tags are required. Without them, compilation fails:
linux: Linux-specific code pathslibsqlite3: System SQLite via cgosqlite_fts5: Full-text search supportgomailnotpl: GoMail without templates
Run a single test:
go test -tags=linux,libsqlite3,sqlite_fts5,gomailnotpl -run TestName ./...Each test initializes the app independently using a shared config helper:
app := &goBlog{cfg: createDefaultTestConfig(t)}
_ = app.initConfig(false)
_ = app.initTemplateStrings()
app.d = app.buildRouter()createDefaultTestConfig(t) points all file paths (database, caches, profile image) into t.TempDir(), so tests are isolated and cleaned up automatically.
Tests run against real SQLite databases on disk, not in-memory databases. On initConfig(false), all embedded migrations from dbmigrations/*.sql are applied. This means database code is exercised exactly as it runs in production.
Most HTTP tests route requests directly into the chi router without a TCP layer:
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/path", nil)
app.d.ServeHTTP(rec, req)The helper newHandlerClient(handler) in utils.go wraps the router into an *http.Client, so tests can use the project's fluent HTTP library (carlmjohnson/requests) for request construction.
When a test needs to simulate an external service (e.g., a remote server for link checking or webmention fetching), it spins up an httptest.Server.
The testdata/ directory contains HTML pages and GPX track files used as fixture data in parsing and validation tests.
testify: assertions (assertandrequire)carlmjohnson/requests: fluent HTTP test requests
Some test files spin up Docker containers to test against real software:
activityPub_integration_test.go (gated behind //go:build !skipIntegration) launches a real GoToSocial instance in Docker and tests full ActivityPub federation:
- Follow, unfollow
- Post create, update, delete: verified from the federated side
- Likes, reposts (boosts)
- Mentions and replies
- Profile updates
- Moving followers to another account (
Moveactivity) - Domain migration via
altDomain
The test creates a Docker network, uses socat containers for port forwarding, and performs OAuth2 against GoToSocial to obtain Mastodon API tokens. Federation events are verified using require.Eventually to wait for asynchronous delivery.
acme_integration_test.go (also gated behind //go:build !skipIntegration) tests TLS certificate management against Pebble, Let's Encrypt's ACME test server:
- Certificate issuance via TLS-ALPN-01 challenge
- Certificate renewal
- HTTPS content serving with acquired certificates
- Host whitelist enforcement
- Certificate and account key persistence in the database
mediaStorage_test.go spawns a real FTP server (goftp.io/server/v2) on a random port and tests the full upload/retrieve/delete cycle against FTP media storage.
These integration tests need a Docker daemon. They are skipped in Dockerfile builds (skipIntegration is set in the Dockerfile's GOFLAGS to avoid Docker-in-Docker complexity), but run fine in CI and locally. Run them with:
go test -tags=linux,libsqlite3,sqlite_fts5,gomailnotpl -run Integration -timeout 600s ./...Currently, there are just very few fuzz tests, but they can be run with:
go test -tags=linux,libsqlite3,sqlite_fts5,gomailnotpl -fuzz=Fuzz_urlize -fuzztime=30s ./...A couple of benchmarks exist, but they are not yet fully fleshed out. Run them with:
go test -tags=linux,libsqlite3,sqlite_fts5,gomailnotpl -bench=. -benchtime=5s ./...- Use
assertfor normal assertions,requirewhen the test can't continue on failure - Tests that can run independently use
t.Parallel() - New features should include tests
- Test helpers live alongside the code they test (not in a separate package)
Continuous integration runs on every push and pull request:
- Lint:
golangci-lintchecks for code quality and security issues - Test:
go test -timeout 600s -cover ./...runs all tests including integration tests
golangci-lint runConfiguration is in .golangci.yml.