test(integration): add end-to-end integration tests #518

Merged
fuzzy merged 2 commits from test/integration into main 2026-08-04 07:00:16 +00:00
Owner

What

Add a new internal/integration test package that wires the real site.Service, webhook.Receiver, and metrics.Registry through the HTTP router exactly as buildRouter does in cmd/thwap-pagesd/main.go, backed by in-memory repositories and fake Forgejo/Kubernetes clients.

  • Registration flow (#105)POST /api/v1/sites creates the Forgejo repository and webhook, configures the Kubernetes ingress, persists the site, and exposes it via the status and metrics endpoints; duplicate registrations conflict.
  • Webhook push events (#106) — release-tag pushes publish the highest v* tag; test/* pushes publish previews; test/* deletions remove them; bad signatures are rejected with 401.
  • Rollback scenario (#107) — a tag-deletion webhook reselects the highest remaining v* tag, republishes, records a rollback audit event, and the status API reflects the new current deployment.

Why

Phase 8 task #108 — closes the gap between the isolated handler/service unit tests by exercising the real end-to-end wiring through HTTP.

Testing

  • go build ./...
  • go vet ./...
  • go test -race -count=1 ./... — full suite incl. new integration tests
  • gofmt -l ./cmd ./internal ./pkg clean
  • golangci-lint run — 0 issues
  • pre-commit hooks all pass

Breaking Changes

None.

Notes

  • Issues #105, #106, #107, #108 closed via the Forgejo API as part of this task per the workflow.
  • RegisterSiteRequest has no JSON tags, so preview_enabled in the request body does not bind; the preview tests set it directly on the repository as a pre-existing limitation (out of scope here).

Closes #105
Closes #106
Closes #107
Closes #108

## What Add a new `internal/integration` test package that wires the real `site.Service`, `webhook.Receiver`, and `metrics.Registry` through the HTTP router exactly as `buildRouter` does in `cmd/thwap-pagesd/main.go`, backed by in-memory repositories and fake Forgejo/Kubernetes clients. - **Registration flow (#105)** — `POST /api/v1/sites` creates the Forgejo repository and webhook, configures the Kubernetes ingress, persists the site, and exposes it via the status and metrics endpoints; duplicate registrations conflict. - **Webhook push events (#106)** — release-tag pushes publish the highest `v*` tag; `test/*` pushes publish previews; `test/*` deletions remove them; bad signatures are rejected with 401. - **Rollback scenario (#107)** — a tag-deletion webhook reselects the highest remaining `v*` tag, republishes, records a rollback audit event, and the status API reflects the new current deployment. ## Why Phase 8 task #108 — closes the gap between the isolated handler/service unit tests by exercising the real end-to-end wiring through HTTP. ## Testing - [x] `go build ./...` - [x] `go vet ./...` - [x] `go test -race -count=1 ./...` — full suite incl. new integration tests - [x] `gofmt -l ./cmd ./internal ./pkg` clean - [x] `golangci-lint run` — 0 issues - [x] pre-commit hooks all pass ## Breaking Changes None. ## Notes - Issues #105, #106, #107, #108 closed via the Forgejo API as part of this task per the workflow. - `RegisterSiteRequest` has no JSON tags, so `preview_enabled` in the request body does not bind; the preview tests set it directly on the repository as a pre-existing limitation (out of scope here). Closes #105 Closes #106 Closes #107 Closes #108
test(integration): add end-to-end integration tests
All checks were successful
Test and Release / lint (pull_request) Successful in 7m9s
Test and Release / test (pull_request) Successful in 7m20s
a29b36b20c
Add a new internal/integration test package that wires the real
site.Service, webhook.Receiver, and metrics.Registry through the HTTP
router exactly as buildRouter does in main.go, backed by in-memory
repositories and fake Forgejo/Kubernetes clients.

- Simulate registration flow: POST /api/v1/sites creates the Forgejo
  repository and webhook, configures the Kubernetes ingress, persists
  the site, and exposes it via the status and metrics endpoints (#105)
- Simulate webhook push events: release-tag pushes publish the highest
  v* tag; test/* pushes publish previews; test/* deletions remove them;
  bad signatures are rejected (#106)
- Simulate rollback scenario: a tag-deletion webhook reselects the
  highest remaining v* tag, republishes, and records a rollback audit
  event (#107)

Mark task done in ROADMAP.md.

closes #105
closes #106
closes #107
closes #108
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-08-04 02:50:03 +00:00
the.auditor requested changes 2026-08-04 02:53:54 +00:00
Dismissed
the.auditor left a comment

Review Summary

Verified locally on the PR head (a29b36b): go build, go vet, gofmt, golangci-lint run all clean; full suite incl. the new integration package passes with -race.

The wiring is well done — newApp mirrors buildRouter faithfully (real site.Service, webhook.Receiver, metrics.Registry through the gin router with in-memory repos and recording fakes), and the scenarios cover registration, duplicate 409, tag-push publish, preview push/delete, bad signature, and the rollback flow including audit + status reflection. These close the gap the unit tests left open.

Blocking Issue

  1. The PR's stated purpose is exercising the real end-to-end wiring through HTTP, but the preview tests don't do that for registration — because RegisterSiteRequest has no JSON tags, so preview_enabled and custom_domain in the request body are silently ignored. Empirically confirmed: POST /api/v1/sites with {"preview_enabled":true,"custom_domain":"blog.example.org"} yields a persisted site with PreviewEnabled=false, CustomDomain="". TestWebhookPreviewPush/TestWebhookPreviewDelete therefore bypass the API with a direct a.sites.Update(...), leaving the real registration path for previews untested — and codifying the workaround instead of surfacing the defect.

    This also makes the README (#517, merged) inaccurate: it documents preview_enabled/custom_domain as POST /api/v1/sites request fields.

    Fix (one line): add JSON tags to RegisterSiteRequest (json:"name", json:"owner", json:"visibility", json:"custom_domain", json:"preview_enabled"), then drive the preview tests through the API (registerSite with preview_enabled: true) and assert the persisted site has it set. The tests should catch this class of bug, not route around it.

Non-blocking Suggestions

  1. registerSite could take options (e.g. preview flag / custom domain) so the preview scenarios share the real path cleanly.
  2. Minor: TestRollbackScenario reuses tag pushes with fixed SHAs — fine, but a brief comment noting the fake's tags is the source of truth for "remaining" would help future readers.

The blocking item is a one-line struct-tag addition that makes the tests genuinely end-to-end.

## Review Summary Verified locally on the PR head (`a29b36b`): `go build`, `go vet`, `gofmt`, `golangci-lint run` all clean; full suite incl. the new integration package passes with `-race`. The wiring is well done — `newApp` mirrors `buildRouter` faithfully (real `site.Service`, `webhook.Receiver`, `metrics.Registry` through the gin router with in-memory repos and recording fakes), and the scenarios cover registration, duplicate 409, tag-push publish, preview push/delete, bad signature, and the rollback flow including audit + status reflection. These close the gap the unit tests left open. ## Blocking Issue 1. The PR's stated purpose is exercising the real end-to-end wiring through HTTP, but the preview tests don't do that for registration — because `RegisterSiteRequest` has no JSON tags, so `preview_enabled` and `custom_domain` in the request body are silently ignored. Empirically confirmed: `POST /api/v1/sites` with `{"preview_enabled":true,"custom_domain":"blog.example.org"}` yields a persisted site with `PreviewEnabled=false, CustomDomain=""`. `TestWebhookPreviewPush`/`TestWebhookPreviewDelete` therefore bypass the API with a direct `a.sites.Update(...)`, leaving the real registration path for previews untested — and codifying the workaround instead of surfacing the defect. This also makes the README (#517, merged) inaccurate: it documents `preview_enabled`/`custom_domain` as `POST /api/v1/sites` request fields. **Fix (one line):** add JSON tags to `RegisterSiteRequest` (`json:"name"`, `json:"owner"`, `json:"visibility"`, `json:"custom_domain"`, `json:"preview_enabled"`), then drive the preview tests through the API (`registerSite` with `preview_enabled: true`) and assert the persisted site has it set. The tests should catch this class of bug, not route around it. ## Non-blocking Suggestions 1. `registerSite` could take options (e.g. preview flag / custom domain) so the preview scenarios share the real path cleanly. 2. Minor: `TestRollbackScenario` reuses tag pushes with fixed SHAs — fine, but a brief comment noting the fake's `tags` is the source of truth for "remaining" would help future readers. The blocking item is a one-line struct-tag addition that makes the tests genuinely end-to-end.
test(integration): bind preview and custom domain via JSON tags
All checks were successful
Test and Release / lint (pull_request) Successful in 5m34s
Test and Release / test (pull_request) Successful in 23m15s
d7f1164a53
RegisterSiteRequest carried no JSON tags, so custom_domain and
preview_enabled in the POST /api/v1/sites body were silently ignored.
This made the documented registration fields untestable end to end and
left the preview integration tests routing around the real registration
path with a direct repository update.

Add the missing json tags and drive the preview scenarios through the
API: registerSite now accepts options and registers preview_enabled via
the HTTP endpoint. Add a registration test asserting custom_domain and
preview_enabled persist, and note in the rollback test that the fake
tags slice is the source of truth for remaining tags.
Author
Owner

Addressed the blocking review item.

Blocking fix: RegisterSiteRequest now carries JSON tags (name, owner, visibility, custom_domain, preview_enabled) so those fields bind from the POST /api/v1/sites body instead of being silently ignored. The preview tests no longer route around the real registration path: registerSite takes options and registers preview_enabled through the HTTP API, and a new TestRegistrationFlowOptions asserts custom_domain + preview_enabled persist. This also makes the README's documented request fields accurate.

Non-blocking suggestions: registerSite now accepts a siteOptions struct (preview flag / custom domain); TestRollbackScenario carries a comment noting the fake's tags slice is the source of truth for remaining tags.

Verification: go build ./..., go vet ./..., go test -race -count=1 ./..., gofmt -l clean, golangci-lint run 0 issues, pre-commit hooks pass. Re-requesting review.

Addressed the blocking review item. **Blocking fix:** `RegisterSiteRequest` now carries JSON tags (`name`, `owner`, `visibility`, `custom_domain`, `preview_enabled`) so those fields bind from the `POST /api/v1/sites` body instead of being silently ignored. The preview tests no longer route around the real registration path: `registerSite` takes options and registers `preview_enabled` through the HTTP API, and a new `TestRegistrationFlowOptions` asserts `custom_domain` + `preview_enabled` persist. This also makes the README's documented request fields accurate. **Non-blocking suggestions:** `registerSite` now accepts a `siteOptions` struct (preview flag / custom domain); `TestRollbackScenario` carries a comment noting the fake's `tags` slice is the source of truth for remaining tags. Verification: `go build ./...`, `go vet ./...`, `go test -race -count=1 ./...`, `gofmt -l` clean, `golangci-lint run` 0 issues, pre-commit hooks pass. Re-requesting review.
the.auditor left a comment

Review Summary

The blocking issue from the previous review is resolved.

  1. Blocker fixedRegisterSiteRequest now carries JSON tags (name, owner, visibility, custom_domain, preview_enabled), so preview_enabled/custom_domain bind from the POST /api/v1/sites body. New TestRegistrationFlowOptions asserts both persist correctly.
  2. Truly end-to-end preview testsTestWebhookPreviewPush/TestWebhookPreviewDelete now register a preview-enabled site through the API via registerSite(t, a, "blog", siteOptions{previewEnabled: true}) instead of mutating the repository directly, exercising the real registration path.
  3. Suggestion addressedregisterSite takes optional siteOptions, and the rollback scenario now comments that the fake's tags slice is the source of truth for "remaining" tags.

Verified locally on the PR head (d7f1164): go build, go vet, gofmt, golangci-lint run all clean; full suite passes with -race -count=1.

No blocking issues. Approving.

## Review Summary The blocking issue from the previous review is resolved. 1. **Blocker fixed** — `RegisterSiteRequest` now carries JSON tags (`name`, `owner`, `visibility`, `custom_domain`, `preview_enabled`), so `preview_enabled`/`custom_domain` bind from the `POST /api/v1/sites` body. New `TestRegistrationFlowOptions` asserts both persist correctly. 2. **Truly end-to-end preview tests** — `TestWebhookPreviewPush`/`TestWebhookPreviewDelete` now register a preview-enabled site through the API via `registerSite(t, a, "blog", siteOptions{previewEnabled: true})` instead of mutating the repository directly, exercising the real registration path. 3. **Suggestion addressed** — `registerSite` takes optional `siteOptions`, and the rollback scenario now comments that the fake's tags slice is the source of truth for "remaining" tags. Verified locally on the PR head (`d7f1164`): `go build`, `go vet`, `gofmt`, `golangci-lint run` all clean; full suite passes with `-race -count=1`. **No blocking issues.** Approving.
fuzzy merged commit d7f1164a53 into main 2026-08-04 07:00:16 +00:00
fuzzy deleted branch test/integration 2026-08-04 07:00:17 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/thwap-pagesd!518
No description provided.