Skip to main content
← Back to list
01Issue
FeatureClosedUAT
AssigneesNone

Relationships

#555 feat: giga-swamp comprehensive UAT coverage

Opened by stack72 · 6/4/2026

Problem

Giga-swamp is feature-complete (all 7 phases + extensions shipped) but has zero UAT coverage. The existing datastore tests (sync_test.ts, lock_test.ts, setup_test.ts, status_test.ts, compact_test.ts) all operate in solo mode. Namespace commands, namespace-scoped sync, cross-namespace queries, and data migration are untested in the UAT suite.

Previous scoped sync PRs (#1386, #134) passed all unit tests but failed in production. The UAT suite is the only reliable validation for cross-repo datastore scenarios. Without namespace UAT coverage, regressions in giga-swamp will be caught by users, not CI.

Test Infrastructure

All cloud-dependent tests use local containers — no real AWS/GCS credentials needed for development:

  • S3 tests use a ministack container (ministack.org) for S3-compatible local testing
  • GCS tests use fake-gcs-server for GCS-compatible local testing
  • The existing getTestBackends() pattern runs tests against both backends automatically
  • Tests skip gracefully when a backend is unavailable

Each phase should be its own commit so git bisect can isolate regressions.

Phase A: Infrastructure + Helpers (commit 1)

Extend the helper infrastructure before writing any tests. Zero risk — purely additive.

New file: src/cli/helpers/datastore_namespace_helpers.ts

Helper functions:

  • namespaceSet(runner, cwd, slug) — runs swamp datastore namespace set
  • namespaceUnset(runner, cwd, opts?) — runs swamp datastore namespace unset, with optional --migrate --confirm flags
  • namespaceMigrate(runner, cwd, opts?) — runs swamp datastore namespace migrate, with optional --confirm and --reverse flags
  • namespaceList(runner, cwd) — runs swamp datastore namespaces --json
  • catalogPull(runner, cwd, namespaces) — runs swamp datastore catalog pull --namespaces

Extend DatastoreBackend interface:

  • setupDatastoreWithNamespace(runner, cwd, prefix, namespace) — same as setupDatastore but adds namespace to the config JSON

Add JSON schemas to schemas.ts:

  • NamespaceListSchema, NamespaceSetSchema, NamespaceMigrateSchema

Export everything from mod.ts.

Phase B: Namespace Commands — filesystem only (commit 2)

File: tests/cli/datastore/namespace/commands_test.ts

No cloud credentials needed. Tests against filesystem datastores.

  1. namespace set happy path — fresh repo + filesystem datastore, verify .swamp.yaml updated, manifest exists
  2. namespace set validation — invalid slugs (uppercase, special chars, 65 chars, leading hyphen) rejected
  3. namespace set already set — same value errors
  4. namespace set conflict — two repos same datastore, second claims same slug, conflict error
  5. namespace set different slugs — two repos, infra and security, both succeed
  6. namespaces list empty — no namespaces, empty output
  7. namespaces list with entries — two namespaces, both listed, current marked
  8. namespaces JSON mode — validates against NamespaceListSchema
  9. namespace unset happy path — single namespace, succeeds
  10. namespace unset with multiple — two namespaces exist, error
  11. namespace unset when not set — no namespace configured, error

Phase C: Data Migration — filesystem only (commit 3)

File: tests/cli/datastore/namespace/migrate_test.ts

No cloud credentials needed.

  1. Forward migration round-trip — solo repo with data, namespace set, verify orphaned (total: 0), migrate --confirm, data list shows all data with namespace
  2. Data integrity — diff pre/post migration JSON (minus namespace field), identical
  3. Dry-run accuracy — migrate without --confirm shows preview, no files moved, then confirm, preview matched
  4. Reverse migration round-trip — after forward, unset --migrate --confirm, data list byte-identical to pre-migration
  5. Conflict on reverse — forward migrate, create file at un-namespaced path, reverse refuses
  6. Data written after set before migrate — new data goes to namespaced path, old orphaned, migrate brings both together

Phase D: Namespace-Scoped Sync — S3/GCS required (commit 4)

File: tests/cli/datastore/namespace/sync_test.ts

Uses getTestBackends() against ministack (S3) and fake-gcs-server (GCS). This is the critical phase — the PR #1386 failure mode lives here.

  1. THE KILL SWITCH — Repo A (ns: infra) writes + syncs. Repo B (ns: security) writes + syncs. Repo B wipes cache, pulls. data list returns own data. If this returns 0 rows, catalog rebuild didnt fire after pull.
  2. Namespace isolation in bucket — listObjects shows infra/ and security/ prefixes only, no un-namespaced keys
  3. Per-namespace index — infra/.datastore-index.json and security/.datastore-index.json exist, no global
  4. Cross-namespace deletion safety — Repo A pushes, Repo B pushes, Repo A pushes again, Repo B keys untouched
  5. Namespace fast-path — push twice with no changes, second is no-op (sidecar works per-namespace)
  6. Foreign catalog export/pull — Repo A exports catalog, Repo B pulls foreign catalog, Repo B queries ns == infra, sees metadata
  7. Solo mode regression — solo repo against same backend, push/pull/data list unchanged

Phase E: CEL Cross-Namespace Queries — filesystem only (commit 5)

File: tests/cli/data/namespace/query_test.ts

No cloud credentials needed.

  1. Own-namespace default — namespaced repo, data.latest returns own namespace only
  2. Cross-namespace ns:model — two namespaces seeded, data query ns == security returns foreign data
  3. Wildcard ambiguity — same model name in two namespaces, wildcard errors
  4. Wildcard unambiguous — model in one namespace, wildcard succeeds
  5. data.query() spans all — no ns predicate returns all namespaces
  6. NAMESPACE column in output — namespaced repo shows column
  7. Solo mode no column — solo repo hides column
  8. ns == empty string in solo — returns all data

Phase F: Feature Interactions — mostly filesystem (commit 6)

File: tests/cli/e2e/namespace_features_test.ts

  1. Workflow in namespaced mode — multi-step workflow, all data carries correct namespace
  2. Data GC in namespaced mode — only own namespace cleaned, foreign rows untouched
  3. Data delete in namespaced mode — only own namespace affected
  4. Datastore compact — works with namespaced catalog
  5. Lock status — shows per-namespace lock key
  6. Extension model in namespaced mode — test fixture model writes data with correct namespace

Phase G: Adversarial + Regressions (commit 7)

File: tests/cli/adversarial/namespace_isolation_test.ts File: tests/cli/datastore/namespace/regressions_test.ts

Named regression tests for specific bugs found during implementation: 39. PR #1386 regression — explicit: writer pushes, reader pulls, catalog rebuilt, data visible 40. Bug #534 regression — explicit sync --push in namespaced mode lands data under ns/ prefix 41. Bug #536 regression — datastore setup extension in namespaced mode respects namespace

Adversarial scenarios: 42. Concurrent namespace push — two repos different namespaces push simultaneously via runConcurrent, no corruption 43. Corrupt .namespace.json — invalid JSON in manifest, namespaces list handles gracefully 44. Missing manifest but namespace configured — system works without conflict detection 45. Half-migrated state — some dirs moved, migrate handles remaining or errors clearly 46. Catalog rebuild preserves own data loses foreign — delete catalog, backfill, own data survives, catalog pull restores foreign

Upgrade path: 47. Solo to namespace upgrade journey — solo repo with data, upgrade binary, data list identical, namespace set, migrate, push to S3, pull from fresh cache, data visible

Phase H: User Journeys — S3/GCS required (commit 8)

File: tests/cli/e2e/namespace_journey_test.ts

Full end-to-end user journeys against real (local) S3/GCS:

  1. Two teams one bucket — Repo A (infra) and Repo B (security) against same bucket. Both write, push, pull foreign catalogs, query cross-namespace. Complete isolation + visibility.
  2. Solo repo joins a giga-swamp — existing solo repo with data, namespace set, migrate, push to shared bucket where another namespaced repo has data. Both visible.
  3. Leave the giga-swamp — namespaced repo, unset --migrate, back to solo. New datastore. Push. Data intact.

Rollout Order

A (helpers) ships first, unblocks everything. B + C + E can develop in parallel (filesystem only, no cloud deps). D is the critical cloud phase — gates on ministack + fake-gcs-server. F builds on B. G builds on D. H builds on everything.

Ship order: A, B, C, E, D, F, G, H — each as its own commit.

Verification

Each phase must pass deno test locally before committing. The full suite runs in CI against ministack and fake-gcs-server containers. Cloud-dependent tests (D, G adversarial, H) skip gracefully when containers are unavailable.

02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED

Closed

6/6/2026, 9:46:43 PM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

stack72 commented 6/6/2026, 9:46:41 PM

Sign in to post a ripple.