Skip to content

feat(server): per-suite config resolution with overrides - #224

Merged
07prajwal2000 merged 2 commits into
Fluxify-rest:mainfrom
07prajwal2000:feat/test-suite-config-resolution
Aug 8, 2026
Merged

feat(server): per-suite config resolution with overrides#224
07prajwal2000 merged 2 commits into
Fluxify-rest:mainfrom
07prajwal2000:feat/test-suite-config-resolution

Conversation

@07prajwal2000

Copy link
Copy Markdown
Collaborator

Closes #216. Part 2 of 6 of the sandboxed test suite runner.

Why

Test suites will run in an ephemeral child process (#217). That child starts cold — setupContextVars runs no queries, it reads module caches that are empty in a fresh process. So the parent has to resolve app config and integrations up front and ship them over IPC.

What

apps/server/src/modules/testRunner/resolve.tsresolveSuiteConfig(projectId, overrides).

Returns a ProjectConfigPayload rather than the ResolvedIntegration[] the issue sketched. That is the shape the compiled worker already hydrates from (hydrateAppConfig + hydrateIntegrations), so the child needs no new loader and no second hydration path.

Order is load-bearing

  1. app config overrides applied first
  2. integrations resolved second, against the overridden map

Integration configs hold cfg: references. Resolving them first expands pre-swap values and the override silently does nothing — a test quietly pointed at the real database instead of the sandbox one. The ordering test fails if the two steps are swapped.

For the same reason integrations are re-resolved from their rows instead of copied out of dbIntegrationsCache: the cached ones were already expanded against the un-overridden config, so copying would make the rule unenforceable.

Loader refactor

Per-row resolution extracted into the exported resolveIntegrationConfig(row, appConfig) in integrationsLoader.ts. The app config map is a parameter now instead of a getAppConfig cache read; loadFromDB passes the live one, the test runner passes the overridden one. No new decryption — loadFromDB behaviour is unchanged.

Bug fixed on the way

The three near-identical mapIntegrationTo{Pg,Mysql,Mongo}ConnectionData helpers wrote the expanded url back onto the row (config.url = ...), destroying the cfg: reference. Harmless when a row is resolved once; fatal here, where the same row is resolved twice (live config, then overridden). Collapsed into one mapIntegrationToConnectionData(appConfig, config, parseUrl) that expands into a new object. A missing cfg key now logs "Failed to load integration" instead of throwing a TypeError on undefined.match.

Tests

tests/resolve.spec.ts — ordering (fails if flipped), no-override identity, id swap, unknown override target throws.

tests/resolve.integration.spec.ts + tests/fixtures/hydrateChild.tsspawns a real child process, ships the payload over IPC, child hydrates through the production loaders and reads values back through getAppConfig / dbIntegrationsCache. Proves three things the in-process spec cannot:

  • the payload is structured-cloneable — no class instances, no functions, so child.send() cannot fail on it
  • the loaders work with a cold module cache
  • a child with no Postgres and no NATS builds its entire view from the message

Pre-commit green on both commits: lint 9/9, 662 pass / 0 fail across 121 files.

Notes for #217

The spawn mechanism is now verified end to end, but the child here is a test stub — it only hydrates. The real testExecutionProcess.ts (instantiate, run, watchdog, rlimits) is still #217. One gotcha worth carrying over: new URL(...).pathname yields /D:/... on Windows and the child dies with Module not found — use fileURLToPath.

Out of scope

No ownership check here — assertOverridesOwned is called by the orchestrator in #219, in the parent, before dispatch. Only this project's rows (and unowned ones) are loaded, so an override naming a foreign integration throws.

🤖 Generated with Claude Code

https://claude.ai/code/session_018pjR2Xuyxu5UTUDFo9smSd

07prajwal2000 and others added 2 commits August 8, 2026 09:02
…nner

Test suites will run in a cold child process (Fluxify-rest#217) whose caches are empty, so
the parent has to resolve app config and integrations up front and ship them
over IPC.

- `modules/testRunner/resolve.ts` returns a `ProjectConfigPayload` — the shape
  the compiled worker already hydrates from, so the child needs no new loader.
- App config overrides are applied FIRST, then integrations are resolved
  against the overridden map. Integration configs hold `cfg:` references, so
  the other order expands pre-swap values and the override silently does
  nothing. The test fails if the order is flipped.
- Integrations are re-resolved from their rows instead of copied out of the
  live cache, which is already expanded against the un-overridden config.
- `integrationsLoader`: per-row resolution extracted into the exported
  `resolveIntegrationConfig(row, appConfig)`; the app config map is a parameter
  now instead of a module cache read. `loadFromDB` calls it.
- Fixes a latent mutation: the three near-identical `mapIntegrationTo*`
  helpers wrote the expanded url back onto the row, destroying the `cfg:`
  reference for any second resolve. Collapsed into one function that expands
  into a new object, and a missing cfg key now logs instead of throwing.

Closes Fluxify-rest#216

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018pjR2Xuyxu5UTUDFo9smSd
The spec only proved the resolution rules in-process. This adds an integration
test that spawns a cold child with Bun.spawn + IPC, ships the resolved payload
across, and has the child hydrate through the same loaders the compiled worker
uses, then read the values back through getAppConfig/dbIntegrationsCache.

Proves what an in-process assertion cannot: the payload is structured-cloneable
(no class instances, no functions), the loaders work with a cold module cache,
and a child with no database or NATS can build its whole view from the message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018pjR2Xuyxu5UTUDFo9smSd
@07prajwal2000
07prajwal2000 added this pull request to the merge queue Aug 8, 2026
Merged via the queue into Fluxify-rest:main with commit 6e8237c Aug 8, 2026
12 checks passed
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 runner (2/6): per-suite config resolution with overrides

1 participant