Skip to content

test: add full-stack integration test suite (real Nest + mocked AWS SDK)#124

Merged
CoderCoco merged 13 commits into
mainfrom
worktree-claude+issue-75-full-stack-integration-tests
May 8, 2026
Merged

test: add full-stack integration test suite (real Nest + mocked AWS SDK)#124
CoderCoco merged 13 commits into
mainfrom
worktree-claude+issue-75-full-stack-integration-tests

Conversation

@CoderCoco
Copy link
Copy Markdown
Owner

Closes #75

Summary

  • Adds a tier-2 integration test suite (npm run app:test:integration) where a real Nest server runs on :3002, AWS SDK calls are intercepted by aws-sdk-client-mock, and the Vite preview on :4174 proxies /api to it — no real AWS required
  • New ServerMocks Playwright fixture class + extended test object that resets MockStore before/after each spec; mock responses pushed to the server via POST /api/test/mocks/*
  • 6 spec files covering API token guard (401/200 auth), ConfigService tfstate parsing, start/stop browser flows, status-badge polling transitions, and ECS error propagation; canRun() spec stubbed pending Discord integration
  • Integration Vite config injects VITE_STATUS_POLL_MS=3000 so poll-based assertions complete in < 10 s; Nest server startup timeout raised to 120 s for WSL2/DrvFs ESM loading latency
  • CLAUDE.md two-tier strategy table updated to three-tier; integration conventions section added; reference docs table updated
  • docs/docs/components/integration-tests.md added: architecture diagram, key-file table, mock response reference, spec inventory, design constraints

Test plan

  • npm run app:test:integration — 11 pass, 1 skip (canRun.spec.ts placeholder)
  • npm run app:test (unit) — all pass, unaffected
  • npm run app:test:e2e (tier-1) — all pass, unaffected

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 8, 2026 00:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new tier-2 full-stack integration test suite for the management app, running Playwright against a real Nest server (locally on :3002) while intercepting AWS SDK calls via aws-sdk-client-mock, and serving the frontend via a dedicated Vite preview build (on :4174) that proxies /api to the test server.

Changes:

  • Adds app:test:integration and supporting Playwright/Vite configs to run browser + API integration specs against a real Nest server.
  • Introduces a server-side mock-control surface (/api/test/mocks/*) backed by an in-process FIFO MockStore, plus Playwright fixtures to drive it.
  • Adds new integration specs + documentation/CI workflow for running and maintaining the suite.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
package.json Adds root-level app:test:integration script (build server + run web integration suite).
.github/workflows/integration.yml New CI workflow to run the integration suite and upload Playwright report artifacts on failure.
.gitignore Ignores app/packages/web/dist-integration/ build output.
CLAUDE.md Updates testing strategy docs to include a third tier (integration) and adds integration conventions.
docs/docs/components/integration-tests.md New documentation for integration test architecture, fixtures, and spec inventory.
docs/superpowers/plans/2026-05-07-full-stack-integration-tests.md Adds a detailed implementation plan reference for the integration suite.
app/packages/server/src/services/ConfigService.ts Allows TF_STATE_PATH env var override so tests can point at a fixture tfstate.
app/packages/server/src/test-main.ts New Nest test entry point that installs ECS client mocks before boot and runs on port 3002.
app/packages/server/src/test-mocks/mock-store.ts In-process FIFO queues for per-command mocked ECS responses.
app/packages/server/src/test-mocks/test-mocks.controller.ts Mock-control endpoints (POST /api/test/mocks/*) for Playwright to seed responses.
app/packages/server/src/test-mocks/test-mocks.module.ts Test-only module to register the mock-control controller.
app/packages/web/package.json Adds test:integration Playwright script using the integration config.
app/packages/web/vite.integration.config.ts New Vite config for integration build/preview (dist-integration, port 4174, /api proxy, faster polling).
app/packages/web/playwright.integration.config.ts New Playwright config that boots Nest test server + Vite preview as webServer deps (single worker).
app/packages/web/e2e/fixtures/tfstate.fixture.json Synthetic Terraform state fixture used by the test server.
app/packages/web/e2e/fixtures/server-mocks.ts Playwright fixtures for serverMocks plus authedPage and dashboard helpers.
app/packages/web/e2e/integration-specs/index.ts Shared re-export entry point for integration specs.
app/packages/web/e2e/integration-specs/api-token-guard.spec.ts Integration coverage for ApiTokenGuard (401/200 + query param fallback).
app/packages/web/e2e/integration-specs/config-service.spec.ts Integration coverage for tfstate parsing and core read endpoints.
app/packages/web/e2e/integration-specs/start-stop.spec.ts Browser-level start/stop UI flow coverage against real Nest server.
app/packages/web/e2e/integration-specs/status-polling.spec.ts Browser-level polling transition coverage (STOPPED→RUNNING) using queued mocks.
app/packages/web/e2e/integration-specs/error-propagation.spec.ts Integration coverage for AWS error propagation via the start endpoint.
app/packages/web/e2e/integration-specs/can-run.spec.ts Placeholder skipped spec for future canRun() enforcement integration.

Comment thread package.json Outdated
Comment on lines +20 to +23
"app:lint": "npm run lint -w game-server-manager",
"app:lint:fix": "npm run lint:fix -w game-server-manager",
"app:test:e2e": "npm run test:e2e -w @gsd/web",
"app:test:integration": "npm run build -w @gsd/server && npm run test:integration -w @gsd/web",
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — title updated to "test: add full-stack integration test suite (real Nest + mocked AWS SDK)" (imperative verb added).


Generated by Claude Code

CoderCoco and others added 9 commits May 7, 2026 20:56
…trol via HTTP

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck setup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add the Playwright-side of the full-stack integration test suite:

- ServerMocks fixture class with auto-reset before/after each spec;
  extends Playwright base with serverMocks, authedPage, and dashboard
- 6 spec files: api-token-guard (4 HTTP), config-service (3 HTTP),
  start-stop (2 browser), status-polling (1 browser),
  error-propagation (1 HTTP), can-run (skipped placeholder)
- Integration spec index re-exporting the extended test/expect
- Vite integration config: embed VITE_STATUS_POLL_MS=3000 so status
  poller fires every 3s rather than 20s during integration runs
- Playwright integration config: raise Nest startup timeout 30s→120s
  to accommodate ESM loading latency on WSL2/DrvFs (~50s observed)
- gitignore: exclude dist-integration/ build artifacts
- CLAUDE.md: promote two-tier table to three-tier; add integration
  test conventions section; update reference docs table
- docs/docs/components/integration-tests.md: full architecture doc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@CoderCoco CoderCoco force-pushed the worktree-claude+issue-75-full-stack-integration-tests branch from 03b0629 to 27022d1 Compare May 8, 2026 00:56
app:test:integration called `npm run build -w @gsd/server` directly,
bypassing the game-server-manager prebuild hook that runs
embed-tfstate.mjs. Since generated/tfstate.ts is in .gitignore and
only created by that hook, tsc -b failed with "Cannot find module
'../generated/tfstate.js'". Prepend the embed script so the stub
(null in CI) is written before tsc runs.

https://claude.ai/code/session_01UAVwJvuBtUaDYFXtJRheww
Copilot AI review requested due to automatic review settings May 8, 2026 01:05
@CoderCoco CoderCoco changed the title test: full-stack integration test suite (real Nest + mocked AWS SDK) test: add full-stack integration test suite (real Nest + mocked AWS SDK) May 8, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.

},
define: {
// Speed up the status poller so browser integration tests don't wait 20 s per cycle.
'import.meta.env.VITE_STATUS_POLL_MS': '"3000"',
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declined — this works correctly in practice. Vite replaces import.meta.env itself with the full env object (which includes all define entries), so import.meta.env?.VITE_STATUS_POLL_MS accesses VITE_STATUS_POLL_MS from that object just fine — the ?. is on the env object, not on a missing key. The integration CI confirms it: status-polling.spec.ts asserts STOPPED→RUNNING within a 10 s window, which is only achievable at the configured 3 s cadence, and it passed. The ?. is a TypeScript guard against VITE_STATUS_POLL_MS not being declared in ImportMetaEnv — it has zero runtime impact with Vite.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout. resolveRuntimePath and its ugly path lists are pre-existing — this PR only added the process.env['TF_STATE_PATH'] ?? override on line 29 so the integration test server can point at the fixture file.

The env-var approach is the right direction: inject the path from outside rather than path-walking at runtime. The next logical step would be to give CONFIG_PATH the same treatment (e.g. a SERVER_CONFIG_PATH env var) and then resolveRuntimePath could be deleted entirely.

Happy to open a follow-up issue for that cleanup, or fold it into this PR if you'd prefer — just let me know.


Generated by Claude Code

Comment on lines 29 to 37
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's up with the weird relative paths? Should we make a better function or pass things in to reduce the amount of relative paths?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout. resolveRuntimePath and its ugly path lists are pre-existing — this PR only added the process.env['TF_STATE_PATH'] ?? override on line 29 so the integration test server can point at the fixture file.

The env-var approach is the right direction: inject the path from outside rather than path-walking at runtime. The next logical step would be to give CONFIG_PATH the same treatment (e.g. a SERVER_CONFIG_PATH env var) and then resolveRuntimePath could be deleted entirely.

Happy to open a follow-up issue for that cleanup, or fold it into this PR if you'd prefer — just let me know.


Generated by Claude Code

Comment on lines +11 to +13
test.skip('canRun() permission enforcement — pending Discord integration', () => {
// placeholder
});
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this be implemented now?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

canRun() is a pure function that lives in @gsd/shared and is called from the Discord Lambda layer, not from the Nest HTTP server. The integration test tier only covers the HTTP API (bearer-token auth, tfstate parsing, ECS start/stop), so there's no real hook to call canRun() from a spec here. Testing it here would mean either adding a purpose-built test endpoint to the server or mocking DynamoDB for the DiscordConfig — both of which exceed the scope of this tier.

The right home is a unit test in @gsd/lambda-interactions (or @gsd/shared) where you can call the pure function directly with a canned DiscordConfig and an interaction object. Leaving this as a skipped placeholder so the slot is visible in the spec inventory.


Generated by Claude Code

…resolution

Remove the fragile multi-candidate path-probing helper. Both runtime
environments (local repo and Docker /workspace) have the same dist/
depth, so a single APP_ROOT anchor (4 hops up from dist/services/) is
enough. Adds SERVER_CONFIG_PATH env var alongside the existing
TF_STATE_PATH so operators can override either path at runtime — the
same pattern the integration test server already uses for TF_STATE_PATH.

https://claude.ai/code/session_01UAVwJvuBtUaDYFXtJRheww

test.describe('ApiTokenGuard', () => {
test('should reject requests with no Authorization header with 401', async ({ request, serverMocks: _ }) => {
void _;
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on with the void _; here?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Playwright fixtures only run when referenced in the test's parameter list. These tests don't push any mock responses, but they still need the serverMocks fixture to fire so the MockStore gets cleared before/after each test.

serverMocks: _ grabs the fixture to trigger it; void _ is the TypeScript idiom for an intentionally unused variable (suppresses "declared but never read" lint warnings). It's a bit noisy — I'll clean it up by using the conventional _-prefix rename instead.


Generated by Claude Code

…pecs

Playwright fixtures only run when referenced in the test parameter list.
Tests that need MockStore auto-reset but don't push responses were using
`serverMocks: _` + `void _` to force the fixture and silence lint.
Rename to `serverMocks: _reset` (underscore prefix is the standard
TypeScript convention for intentionally-unused variables) and drop the
now-redundant void lines.

https://claude.ai/code/session_01UAVwJvuBtUaDYFXtJRheww
Copilot AI review requested due to automatic review settings May 8, 2026 01:38
The _reset pattern used in Playwright fixtures (to trigger setup/teardown
without using the value) requires argsIgnorePattern/varsIgnorePattern to
be set. Without it, @typescript-eslint/no-unused-vars flags every _reset
binding. This is the standard TypeScript convention for intentionally
unused variables.

https://claude.ai/code/session_01UAVwJvuBtUaDYFXtJRheww
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated no new comments.

@CoderCoco CoderCoco merged commit 63f73bd into main May 8, 2026
9 checks passed
@CoderCoco CoderCoco deleted the worktree-claude+issue-75-full-stack-integration-tests branch May 8, 2026 02:01
CoderCoco added a commit that referenced this pull request May 8, 2026
## Summary

- Deletes
`docs/superpowers/plans/2026-05-07-full-stack-integration-tests.md` — a
1288-line superpowers planning document that was accidentally bundled
into PR #124
- Planning artifacts from the `docs/superpowers/` tree should never be
committed to the repo

## Test plan

- [ ] Confirm `docs/superpowers/` directory is absent after merge
- [ ] No functional code changed; no tests required

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: full-stack integration test suite (real Nest server + mocked AWS SDK)

3 participants