Compare commits

...

7 Commits

Author SHA1 Message Date
dependabot[bot]
cbb05f88c9 merge(release/v3.8.51): refresh onto 3b752f9d4c
Bring inherited Fast Quality Gates / ESLint / unit-test / docs-sync fixes from #11940/#11955/#11975.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-08-29 04:07:06 -03:00
Diego Rodrigues de Sa e Souza
3b752f9d4c chore(quality): type the 55 no-explicit-any sites frozen under #11924 (#11975)
Production (open-sse/utils/socksConnectorWithFamily.ts, 4 sites): every cast was
redundant — undici's buildConnector.BuildOptions already has `timeout?: number | null`,
socks' SocksClientOptions has `timeout?: number`, and Agent.Options' `connect` /
`connectTimeout` narrow to the connector's parameter types on their own. Behaviour
unchanged; check:open-sse-typecheck stays at the frozen 5.

Tests (51 sites): the socks-timeout mocks now carry the real types — the patched
SocksClient.createConnection is typed as the static it replaces, the fake
buildConnector returns buildConnector.connector, the proxy is a SocksProxy, the
dynamic import is typed as the module it loads; the e2e suite passes a SocksProxy and
Agent.Options and no longer casts undici's fetch init (its RequestInit already has
`dispatcher`); the isFree suites narrow getCustomModels()' JSON to a declared row
shape, feed deliberately-wrong values through `unknown`, and stop casting for
zod's safeParse, which takes unknown.

The six files' suppression entries are removed: 1238 → 1232 files, 5487 → 5432
suppressed. ESLint without the suppressions file reports 0 problems on all six;
with it, no stale entry is left. The five suites pass (4, 2, 5, 4, 4).
2026-08-29 03:06:50 -03:00
Diego Rodrigues de Sa e Souza
757cc3bb9d fix(release): let the Electron workflow start again — grant actions:read to the npm leg (release/v3.8.51 twin of #11973) (#11974)
Same three changes as #11973 on main, applied to this branch's newer copy of the
workflow so the v3.8.51 tag does not repeat v3.8.50's zero-asset release:
publish-npm grants actions:read (the called publish job requests it — a caller that
grants less is refused at startup and the release job dies with it), a publish_npm
dispatch input gates the npm leg, and web-build/build/release check out the tag
named by the dispatch. actionlint clean; the five workflow-pinning suites pass.
2026-08-29 02:42:13 -03:00
Diego Rodrigues de Sa e Souza
60bbf0f8f0 fix(ci): stop a stalled Codecov upload from cancelling the Coverage job and the main run (#11972)
The job has timeout-minutes: 20; the c8 merge across 8 shards takes ~10 min and the
Codecov upload (declared informational) then hung for the rest of the budget on two
consecutive main runs (33207760653, 33215115341) — GitHub cancels the step, the job
ends cancelled, and the run's conclusion turns cancelled although every blocking job
was green. The upload step now has its own 5-minute ceiling and continue-on-error;
the job budget is 30 min. check-workflows suite 32/32; zizmor ratchet unchanged.
2026-08-29 02:10:25 -03:00
Diego Rodrigues de Sa e Souza
3d4f3e4960 test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) (#11968)
* test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966)

Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.

One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.

Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.

* fix(quality): let check-forgotten-sibling-tests read a 1,000-file diff

The gate shells out to `git diff` through execFileSync with Node's default 1 MB
maxBuffer; the 1,292-file codemod in this PR is the first diff large enough to
overflow it, and the gate died with `spawnSync git ENOBUFS` before comparing
anything. 64 MB is far above any real PR and costs nothing when unused.
2026-08-29 01:17:40 -03:00
Diego Rodrigues de Sa e Souza
751710616a fix(ci): run the build-bearing nightly jobs on the box's light pool (#11965) (#11967)
Four nightly jobs run a backend-only `next build` on ubuntu-latest (7 GB):
Schemathesis, promptfoo injection guard, garak probes and the axe a11y suite
(self-building webServer). On release/v3.8.51 three of them died with the hosted
VM shutdown signature and nobody saw it — nightlies have no audience — and the
fourth passes by a margin of minutes. They now target [self-hosted, omni-light]
(hosted fallback when USE_VPS_RUNNER is off), a new two-listener label on the .113
box for jobs that need ~6 GB, not the 14-16 GB of a full build; they run once a day
in the 04:00-06:00 UTC window, when the box is idle.

Fleet reshaped the same day and documented in docs/ops/RUNNER_BOX.md: 4 active
OmniRoute listeners (omniroute-113-5/-6 omni-build, omniroute-113/-2 omni-light),
omniroute-113-3/-4/-7/-8 disabled (systemctl enable --now brings one back), janitor
ceiling MAX_ACTIVE_RUNNERS=4. The remaining headroom limit is the VM's 31 GB of RAM
(2 heavy + 2 light ≈ 42 GB peak, inside the 16 GB swap); more RAM on the Proxmox VM
is the lever that turns the label ceilings into 3 heavy + 2 light.

check:workflows --ratchet unchanged (194/194); check-workflows and
backend-only-smoke-workflows suites pass; docs-sync PASS.
2026-08-28 23:26:52 -03:00
dependabot[bot]
1ecd83c6af chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.37.7 to 4.37.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v4.37.7...v4.37.8)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-28 18:26:32 +00:00
1311 changed files with 6588 additions and 5317 deletions

View File

@@ -979,7 +979,11 @@ jobs:
# 10min was sized before #7114 added the lcov reporter (Codecov/Sonar need it);
# merging 8 shard JSONs + text+json+lcov now takes ~10-12min — three consecutive
# release-tip runs died at exactly 10m as job-timeout "cancelled" (2026-07-15/16).
timeout-minutes: 20
# 30, not 20 (2026-08-29): the informational Codecov upload below hung for the rest of
# the budget on two consecutive main runs (33207760653, 33215115341); the job ended
# `cancelled` and dragged the whole run's conclusion to `cancelled` although every
# blocking job was green. The upload step now has its own ceiling; this is headroom.
timeout-minutes: 30
needs: test-unit
if: ${{ !cancelled() && needs.test-unit.result == 'success' && !contains(github.event.pull_request.labels.*.name, 'hotfix') }}
env:
@@ -1058,6 +1062,10 @@ jobs:
# (if-no-files-found: warn) — Sonar consumes the same file.
- name: Upload coverage to Codecov (informational)
if: always()
# Informational means informational: its own ceiling and continue-on-error, so a
# stalled upload can neither eat the job's budget nor turn a green job cancelled.
timeout-minutes: 5
continue-on-error: true
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
files: coverage/lcov.info

View File

@@ -499,7 +499,7 @@ jobs:
- name: Upload Trivy SARIF to Security tab
if: needs.prepare.outputs.version != 'main'
continue-on-error: true
uses: github/codeql-action/upload-sarif@v4.37.7
uses: github/codeql-action/upload-sarif@v4.37.8
with:
sarif_file: trivy-results.sarif
category: trivy-image

View File

@@ -10,6 +10,11 @@ on:
description: "Release version (e.g., v1.6.8)"
required: true
type: string
publish_npm:
description: "Also run the npm publish leg (turn off when re-attaching desktop assets to a release whose npm package already shipped)"
required: false
default: true
type: boolean
# Least-privilege default: read-only at the top level; each job grants the writes it
# needs (build/release upload assets, publish-npm forwards npm provenance / packages
@@ -76,6 +81,9 @@ jobs:
- uses: actions/checkout@v7
with:
persist-credentials: false
# workflow_dispatch: build the tag being (re)built, not the dispatching branch. On a
# tag push this resolves to the same commit.
ref: ${{ needs.validate.outputs.version }}
- name: Setup Node
uses: actions/setup-node@v7
with:
@@ -161,6 +169,9 @@ jobs:
- uses: actions/checkout@v7
with:
persist-credentials: false
# workflow_dispatch: build the tag being (re)built, not the dispatching branch. On a
# tag push this resolves to the same commit.
ref: ${{ needs.validate.outputs.version }}
- name: Setup Node
uses: actions/setup-node@v7
with:
@@ -347,6 +358,8 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
# Source archives + SBOM come from the tag being released, not the dispatching branch.
ref: ${{ needs.validate.outputs.version }}
# `merge-multiple` is deliberately OFF. It resolves same-name collisions by ARRIVAL
# ORDER, and the two macOS jobs each emit their own `latest-mac.yml` listing only their
@@ -462,11 +475,20 @@ jobs:
publish-npm:
name: Publish to npm
needs: [validate, release]
# A re-dispatch that only re-attaches desktop assets must not publish the npm package again.
if: ${{ github.event_name != 'workflow_dispatch' || inputs.publish_npm }}
permissions:
# Must be `write`, not `read`: this job calls the reusable npm-publish.yml whose
# `publish` job needs `contents: write` (gh release upload — attach the SBOM, #3874).
# A reusable workflow's job cannot request more permission than the caller grants,
# so a `read` here makes GitHub reject the run at startup (startup_failure).
#
# `actions: read` for the same reason: the called `publish` job downloads the next-build
# artefact and requests it. v3.8.50 (run 33005490476) died at startup with "The nested
# job 'publish' is requesting 'actions: read', but is only allowed 'actions: none'" — and
# because `release` lives in this same workflow, the tag shipped with ZERO assets. Keep
# this block a superset of every job's permissions in npm-publish.yml.
actions: read
contents: write
id-token: write # npm provenance (forwarded to the reusable workflow)
packages: write # publish to npm.pkg.github.com

View File

@@ -10,7 +10,10 @@ permissions:
jobs:
promptfoo-guard:
name: promptfoo — injection guard (block mode, no secret)
runs-on: ubuntu-latest
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v7
with:
@@ -46,7 +49,10 @@ jobs:
garak:
name: garak probes (skip without provider secret)
runs-on: ubuntu-latest
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
# NOTE: the `secrets` context is NOT available in a job-level `if:` — referencing
# it there makes GitHub reject the file on push (startup_failure on every push).
# Map the secret into a job-level env and gate each step on a presence check, so

View File

@@ -78,7 +78,10 @@ jobs:
a11y:
name: A11y axe (nightly, freeze-and-alert)
runs-on: ubuntu-latest
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
# The Playwright webServer (`start` mode) builds Next via build-next-isolated.mjs and
# boots the standalone server itself (waits on /api/monitoring/health, 15min webServer
# timeout). Unlike the per-PR test-e2e job, this nightly job has no pre-built artifact,

View File

@@ -10,7 +10,10 @@ permissions:
jobs:
schemathesis:
name: Schemathesis — OpenAPI contract fuzz (advisory)
runs-on: ubuntu-latest
# #11965: this job runs a backend-only `next build`; the hosted 7 GB runner cannot build
# release/v3.8.51 (VM shutdown ~7 min in), so it targets the box's light pool (`omni-light`:
# two listeners, jobs ≤ ~6 GB). Falls back to hosted when USE_VPS_RUNNER is off.
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v7

View File

@@ -0,0 +1 @@
- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets

View File

@@ -0,0 +1 @@
- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924)

View File

@@ -0,0 +1 @@
- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965)

View File

@@ -0,0 +1 @@
- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it

View File

@@ -837,11 +837,6 @@
"count": 5
}
},
"open-sse/utils/socksConnectorWithFamily.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 4
}
},
"open-sse/utils/stream.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 2
@@ -4952,11 +4947,6 @@
"count": 20
}
},
"tests/unit/free-models-isfree.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 2
}
},
"tests/unit/functional-gateway-mirrors-append.test.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
@@ -5243,11 +5233,6 @@
"count": 3
}
},
"tests/unit/models-db-isfree.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 18
}
},
"tests/unit/modelsDevSync-extended.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 2
@@ -5547,11 +5532,6 @@
"count": 4
}
},
"tests/unit/providerModelMutationSchema-isfree.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 2
}
},
"tests/unit/providers-route-managed-catalog.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 4
@@ -6021,16 +6001,6 @@
"count": 7
}
},
"tests/unit/socks-connect-timeout-e2e.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 11
}
},
"tests/unit/socks-connect-timeout.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 18
}
},
"tests/unit/spend-batch-writer.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 1
@@ -6552,4 +6522,4 @@
"count": 2
}
}
}
}

View File

@@ -7,13 +7,13 @@ title: Self-Hosted Runner Box Operations
The self-hosted pool (`self-hosted, omni-release` on all eight runners; `omni-build` on two) runs on the **.113** box.
Measured 2026-08-28 (v3.8.50 postmortem, Parte III):
| resource | value | what it means for scheduling |
| --------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| RAM / CPU | **31 GB / 32 cores** (was 16 GB when this doc was first written) | one `next-build` peaks at **~14 GB** → 2 concurrent heavy builds saturate the box, 3 take it down (2026-08-28 06:42Z: load 56, two jobs lost) |
| swap | 15 GB | it swapped its way through the v3.8.50 publish; pressure shows in `/proc/pressure/memory` |
| `/tmp` | **12 GB tmpfs = RAM** | anything parked there is memory; leftovers are swept after 3 h |
| disk | 188 GB | `_work` checkouts of 8 runners reach ~70 GB with no cap |
| runners | **10 listeners**: 8 OmniRoute + OmniHeuris + OmniMind | all share the memory above |
| resource | value | what it means for scheduling |
| --------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| RAM / CPU | **31 GB / 32 cores** (was 16 GB when this doc was first written) | one `next-build` peaks at **~14 GB** → 2 concurrent heavy builds saturate the box, 3 take it down (2026-08-28 06:42Z: load 56, two jobs lost) |
| swap | 15 GB | it swapped its way through the v3.8.50 publish; pressure shows in `/proc/pressure/memory` |
| `/tmp` | **12 GB tmpfs = RAM** | anything parked there is memory; leftovers are swept after 3 h |
| disk | 188 GB | `_work` checkouts of 8 runners reach ~70 GB with no cap |
| runners | **6 listeners**: 4 OmniRoute (2 `omni-build` + 2 `omni-light`) + OmniHeuris + OmniMind | all share the memory above; `omniroute-113-3/-4/-7/-8` are disabled (`systemctl enable --now` brings one back) |
## Install the janitor (one-time, on the box)
@@ -21,7 +21,7 @@ Measured 2026-08-28 (v3.8.50 postmortem, Parte III):
scp scripts/ops/runner-janitor.sh root@192.168.0.113:/opt/omniroute-ops/runner-janitor.sh
ssh root@192.168.0.113 'chmod +x /opt/omniroute-ops/runner-janitor.sh; apt-get install -y lsof'
# cron (root): every 30 min, log to /var/log/runner-janitor.log
*/30 * * * * MAX_ACTIVE_RUNNERS=8 /opt/omniroute-ops/runner-janitor.sh >> /var/log/runner-janitor.log 2>&1
*/30 * * * * MAX_ACTIVE_RUNNERS=4 /opt/omniroute-ops/runner-janitor.sh >> /var/log/runner-janitor.log 2>&1
```
`lsof` is required: the janitor proves a path is idle with one snapshot of open
@@ -57,6 +57,15 @@ a time, only when idle**, with the idle check and the restart in the same comman
queues a third build instead of the kernel killing one. Pair with the `heavy-build-*`
concurrency lanes in `ci.yml`. To add capacity, label another runner — never raise
the count past what 31 GB holds (one next-build ≈ 1416 GB).
- **Light pool: `omni-light` (2026-08-29, #11965).** `omniroute-113` and `omniroute-113-2` carry
`omni-light` for jobs that need a backend-only `next build` (~56 GB) but not a full one: the
nightly Schemathesis, promptfoo, garak and axe-a11y jobs. They ran on the hosted 7 GB runner and
died on `release/v3.8.51` with nobody watching. Worst case on the box is 2 heavy + 2 light ≈
30 + 12 GB — over 31 GB of RAM, inside the 16 GB of swap; the real fix for headroom is more RAM
on the Proxmox VM (`tomni-proxmox-113`), which turns the label ceilings into 3 heavy + 2 light.
- **Fewer listeners on purpose.** Four OmniRoute units were disabled on 2026-08-29 — with only
`ci.yml` `Build` and the nightlies using the box, 8 listeners were idle and each extra one is a
potential 14 GB tenant. The janitor ceiling is 4 (`MAX_ACTIVE_RUNNERS=4` in cron).
- **Never clean `/tmp` or `_work` by hand while any runner is busy.** A
check-then-delete with a gap between the two is how a live Build job lost its
`_work` on 2026-08-27. The janitor does the check and the removal in one step;

View File

@@ -49,13 +49,15 @@ export function socksConnectorWithFamily(
const isDisabled = connectTimeout === 0;
// SOCKS lib: 0 throws (isValidTimeoutValue: value>0) and undefined → DEFAULT_TIMEOUT 30s;
// undici: 0 disables (core/util.js: if (!opts.timeout) return noop), undefined → 10s. Divergence intentional.
const handshakeTimeout = isDisabled ? undefined : (connectTimeout ?? resolveSocksHandshakeTimeoutMs());
const handshakeTimeout = isDisabled
? undefined
: (connectTimeout ?? resolveSocksHandshakeTimeoutMs());
const tlsTimeout = connectTimeout;
// Sequential budget: both phases bounded by the same connectTimeout → wall-time up to 60s for https
// (vs 30s direct). Shared-deadline alternative rejected as unjustified complexity.
const build = _buildConnectorForTest ?? buildConnector;
const undiciConnect = build(
tlsTimeout !== undefined ? ({ ...tlsOpts, timeout: tlsTimeout } as any) : tlsOpts
tlsTimeout !== undefined ? { ...tlsOpts, timeout: tlsTimeout } : tlsOpts
);
const socketOptions = buildSocksFamilySocketOptions(family);
return async (options, callback) => {
@@ -69,7 +71,7 @@ export function socksConnectorWithFamily(
const r = await SocksClient.createConnection({
command: "connect",
proxy,
timeout: handshakeTimeout as any,
timeout: handshakeTimeout,
destination: { host: hostname, port: resolvePort(protocol, port) },
existing_socket: httpSocket as never,
socket_options: socketOptions as never,
@@ -97,6 +99,6 @@ export function createSocksDispatcherWithFamily(
};
return new Agent({
...rest,
connect: socksConnectorWithFamily(proxy, family, connect as any, connectTimeout as any),
connect: socksConnectorWithFamily(proxy, family, connect, connectTimeout),
});
}

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env node
/**
* One-shot codemod (#11966): give every recursive temp-dir removal in tests the retry
* options Node already supports, so a WAL/backup/worker still writing into the directory
* turns into a retried delete instead of a red shard:
*
* rmSync(dir, { recursive: true, force: true })
* → rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
*
* Applies to `rmSync(`, `fs.rmSync(`, `rm(` / `fs.rm(` / `fs.promises.rm(` (async) and
* `rmdirSync(` calls whose option object literal contains `recursive: true` and no
* `maxRetries`. Only the option object is touched — call sites, assertions and imports are
* left as they are. Usage: node scripts/ad-hoc/codemod-rm-maxretries.mjs [dir=tests]
*/
import fs from "node:fs";
import path from "node:path";
const root = process.argv[2] || "tests";
const CALL = /\b(?:fs\.promises\.|fsp\.|fs\.|promises\.)?(?:rmSync|rmdirSync|rm)\(/g;
let files = 0;
let sites = 0;
function walk(dir, out = []) {
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, e.name);
if (e.isDirectory()) {
if (e.name === "node_modules" || e.name === "fixtures") continue;
walk(p, out);
} else if (/\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(e.name)) out.push(p);
}
return out;
}
// Find the closing brace of the option object literal that starts at `open`.
function objectEnd(src, open) {
let depth = 0;
for (let i = open; i < src.length; i++) {
const c = src[i];
if (c === "{") depth++;
else if (c === "}") {
depth--;
if (depth === 0) return i;
} else if (c === '"' || c === "'" || c === "`") {
const q = c;
i++;
while (i < src.length && src[i] !== q) {
if (src[i] === "\\") i++;
i++;
}
}
}
return -1;
}
for (const file of walk(root)) {
const src = fs.readFileSync(file, "utf8");
let out = "";
let last = 0;
let touched = 0;
for (const m of src.matchAll(CALL)) {
const callStart = m.index + m[0].length;
// Locate the option object: the first `{` before the call's closing paren at depth 0.
let depth = 0;
let objOpen = -1;
for (let i = callStart; i < src.length; i++) {
const c = src[i];
if (c === "(" || c === "[") depth++;
else if (c === ")" || c === "]") {
if (depth === 0) break;
depth--;
} else if (c === "{" && depth === 0) {
objOpen = i;
break;
}
}
if (objOpen === -1) continue;
const objClose = objectEnd(src, objOpen);
if (objClose === -1) continue;
const obj = src.slice(objOpen, objClose + 1);
if (!/\brecursive:\s*true\b/.test(obj) || /\bmaxRetries\b/.test(obj)) continue;
// Insert before the closing brace, respecting an existing trailing comma / newline.
const inner = obj.slice(1, -1);
const trimmed = inner.replace(/\s+$/, "");
const trailing = inner.slice(trimmed.length);
const sep = trimmed.endsWith(",") ? " " : ", ";
const multiline = /\n/.test(trailing);
const insert = multiline
? `${trimmed}${trimmed.endsWith(",") ? "" : ","}\n${trailing.replace(/\n$/, "")} maxRetries: 5,\n retryDelay: 100,${trailing}`
: `${trimmed}${sep}maxRetries: 5, retryDelay: 100${trailing}`;
out += src.slice(last, objOpen + 1) + insert;
last = objClose;
touched++;
}
if (touched) {
out += src.slice(last);
fs.writeFileSync(file, out);
files++;
sites += touched;
}
}
console.log(`[codemod-rm-maxretries] ${sites} call site(s) in ${files} file(s) under ${root}`);

View File

@@ -173,7 +173,7 @@ function arg(name, fallback = "") {
}
function git(root, args) {
return execFileSync("git", args, { cwd: root, encoding: "utf8" });
return execFileSync("git", args, { cwd: root, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
}
function changedEntries(root, base) {

View File

@@ -33,7 +33,7 @@ if (!process.env.DATA_DIR) {
// Best-effort cleanup so a long suite run does not leak hundreds of temp DBs.
process.on("exit", () => {
try {
fs.rmSync(dir, { recursive: true, force: true });
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {
// ignore — the OS reaps its temp dir eventually.
}

View File

@@ -366,7 +366,7 @@ test.after(async () => {
await serverA.stop();
await serverB.stop();
core.closeDbInstance();
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("primary healthy: request routes to Server A only", async () => {

View File

@@ -286,7 +286,7 @@ export async function createChatPipelineHarness(prefix) {
clearSkillState();
await new Promise((resolve) => setTimeout(resolve, 20));
core.resetDbInstance();
fs.rmSync(testDataDir, { recursive: true, force: true });
fs.rmSync(testDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(testDataDir, { recursive: true });
initTranslators();
}
@@ -300,7 +300,7 @@ export async function createChatPipelineHarness(prefix) {
clearSkillState();
resetAllCircuitBreakers();
core.resetDbInstance();
fs.rmSync(testDataDir, { recursive: true, force: true });
fs.rmSync(testDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
async function seedConnection(provider: string, overrides: SeedConnectionOverrides = {}) {

View File

@@ -24,7 +24,7 @@ const DEFAULT_PATTERNS = [".bank.", ".gov.", "okta.com", "auth0.com"];
function resetDb() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -34,7 +34,11 @@ test.beforeEach(() => {
});
test.after(() => {
try { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); } catch { /* noop */ }
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {
/* noop */
}
});
// ── POST patterns ──────────────────────────────────────────────────────────
@@ -48,7 +52,10 @@ test("POST /bypass: stores user patterns", async () => {
})
);
assert.equal(res.status, 200);
const body = await res.json() as { ok: boolean; patterns: Array<{ pattern: string; source: string }> };
const body = (await res.json()) as {
ok: boolean;
patterns: Array<{ pattern: string; source: string }>;
};
assert.equal(body.ok, true);
assert.ok(Array.isArray(body.patterns));
const userPatterns = body.patterns.filter((p) => p.source === "user");
@@ -64,7 +71,7 @@ test("POST /bypass: invalid body returns 400", async () => {
})
);
assert.equal(res.status, 400);
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
const errMsg = (body.error as Record<string, unknown>)?.message as string;
assert.ok(!errMsg.includes("at /"), "stack trace leaked in 400 error");
});
@@ -83,7 +90,7 @@ test("GET /bypass: shows default + user patterns", async () => {
const res = await bypassRoute.GET();
assert.equal(res.status, 200);
const body = await res.json() as { patterns: Array<{ pattern: string; source: string }> };
const body = (await res.json()) as { patterns: Array<{ pattern: string; source: string }> };
assert.ok(Array.isArray(body.patterns));
const sources = new Set(body.patterns.map((p) => p.source));
@@ -119,7 +126,10 @@ test("DELETE /bypass?pattern=X: removes a user pattern", async () => {
})
);
assert.equal(deleteRes.status, 200);
const deleteBody = await deleteRes.json() as { ok: boolean; patterns: Array<{ pattern: string; source: string }> };
const deleteBody = (await deleteRes.json()) as {
ok: boolean;
patterns: Array<{ pattern: string; source: string }>;
};
assert.equal(deleteBody.ok, true);
// Verify it's gone
@@ -142,19 +152,18 @@ test("DELETE /bypass: missing pattern param returns 400", async () => {
})
);
assert.equal(res.status, 400);
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
const errMsg = (body.error as Record<string, unknown>)?.message as string;
assert.ok(!errMsg.includes("at /"), "stack trace leaked in DELETE 400");
});
test("DELETE /bypass?pattern=X: no-op when pattern not in user list", async () => {
const res = await bypassRoute.DELETE(
new Request(
"http://localhost/api/tools/agent-bridge/bypass?pattern=not-in-list.com",
{ method: "DELETE" }
)
new Request("http://localhost/api/tools/agent-bridge/bypass?pattern=not-in-list.com", {
method: "DELETE",
})
);
assert.equal(res.status, 200);
const body = await res.json() as { ok: boolean };
const body = (await res.json()) as { ok: boolean };
assert.equal(body.ok, true);
});

View File

@@ -19,7 +19,8 @@ process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
const certRoute = await import("../../src/app/api/tools/agent-bridge/cert/route.ts");
const downloadRoute = await import("../../src/app/api/tools/agent-bridge/cert/download/route.ts");
const regenerateRoute = await import("../../src/app/api/tools/agent-bridge/cert/regenerate/route.ts");
const regenerateRoute =
await import("../../src/app/api/tools/agent-bridge/cert/regenerate/route.ts");
function certDir() {
return path.join(TEST_DATA_DIR, "mitm");
@@ -30,7 +31,7 @@ function certFilePath() {
}
function resetCertDir() {
fs.rmSync(certDir(), { recursive: true, force: true });
fs.rmSync(certDir(), { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(certDir(), { recursive: true });
}
@@ -39,7 +40,11 @@ test.beforeEach(() => {
});
test.after(() => {
try { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); } catch { /* noop */ }
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {
/* noop */
}
});
// ── GET /cert ─────────────────────────────────────────────────────────────
@@ -47,7 +52,7 @@ test.after(() => {
test("GET /cert: returns exists:false when no cert file", async () => {
const res = await certRoute.GET();
assert.equal(res.status, 200);
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
assert.equal(body.exists, false);
assert.equal(body.trusted, false);
assert.equal(body.path, null);
@@ -59,7 +64,7 @@ test("GET /cert: returns exists:true when cert file present", async () => {
const res = await certRoute.GET();
assert.equal(res.status, 200);
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
assert.equal(body.exists, true);
// trusted may be false in test env (no system store)
assert.ok(typeof body.trusted === "boolean");
@@ -83,7 +88,7 @@ test("POST /cert: returns 404 when no cert file", async () => {
})
);
assert.equal(res.status, 404);
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
const errMsg = (body.error as Record<string, unknown>)?.message as string;
assert.ok(!errMsg.includes("at /"), "stack trace leaked in 404 error message");
});
@@ -106,7 +111,7 @@ MIIBpDCCAQ2gAwIBAgIUFakeMITMCertForTestingOnlyXX==
// In test env: installCert may throw because the PEM is fake; we accept
// either 200 (mocked) or 500 (real OS failure) — NOT a 500 with stack trace
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
const errMsg = (body.error as Record<string, unknown>)?.message as string | undefined;
if (errMsg) {
assert.ok(!errMsg.includes("at /"), "stack trace leaked in POST /cert error");
@@ -118,7 +123,7 @@ MIIBpDCCAQ2gAwIBAgIUFakeMITMCertForTestingOnlyXX==
test("GET /cert/download: 404 when no cert file", async () => {
const res = await downloadRoute.GET();
assert.equal(res.status, 404);
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
const errMsg = (body.error as Record<string, unknown>)?.message as string;
assert.ok(!errMsg.includes("at /"), "stack trace leaked in download 404");
});

View File

@@ -18,13 +18,12 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
const core = await import("../../src/lib/db/core.ts");
const mappingsRoute = await import(
"../../src/app/api/tools/agent-bridge/agents/[id]/mappings/route.ts"
);
const mappingsRoute =
await import("../../src/app/api/tools/agent-bridge/agents/[id]/mappings/route.ts");
function resetDb() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -33,18 +32,21 @@ test.beforeEach(() => {
});
test.after(() => {
try { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); } catch { /* noop */ }
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {
/* noop */
}
});
// ── GET (empty) ────────────────────────────────────────────────────────────
test("GET /mappings: returns empty array for new agent", async () => {
const res = await mappingsRoute.GET(
new Request("http://localhost/"),
{ params: { id: "copilot" } }
);
const res = await mappingsRoute.GET(new Request("http://localhost/"), {
params: { id: "copilot" },
});
assert.equal(res.status, 200);
const body = await res.json() as { mappings: unknown[] };
const body = (await res.json()) as { mappings: unknown[] };
assert.ok(Array.isArray(body.mappings));
assert.equal(body.mappings.length, 0);
});
@@ -66,17 +68,21 @@ test("PUT → GET round-trip: stores and retrieves mappings", async () => {
{ params: { id: "copilot" } }
);
assert.equal(putRes.status, 200);
const putBody = await putRes.json() as { ok: boolean; mappings: Array<{ agent_id: string; source_model: string; target_model: string }> };
const putBody = (await putRes.json()) as {
ok: boolean;
mappings: Array<{ agent_id: string; source_model: string; target_model: string }>;
};
assert.equal(putBody.ok, true);
assert.equal(putBody.mappings.length, 2);
// GET reads back the same data
const getRes = await mappingsRoute.GET(
new Request("http://localhost/"),
{ params: { id: "copilot" } }
);
const getRes = await mappingsRoute.GET(new Request("http://localhost/"), {
params: { id: "copilot" },
});
assert.equal(getRes.status, 200);
const getBody = await getRes.json() as { mappings: Array<{ source_model: string; target_model: string }> };
const getBody = (await getRes.json()) as {
mappings: Array<{ source_model: string; target_model: string }>;
};
assert.equal(getBody.mappings.length, 2);
const sources = getBody.mappings.map((m) => m.source_model).sort();
@@ -108,11 +114,10 @@ test("PUT: replaces all previous mappings", async () => {
);
assert.equal(putRes.status, 200);
const getRes = await mappingsRoute.GET(
new Request("http://localhost/"),
{ params: { id: "cursor" } }
);
const body = await getRes.json() as { mappings: Array<{ source_model: string }> };
const getRes = await mappingsRoute.GET(new Request("http://localhost/"), {
params: { id: "cursor" },
});
const body = (await getRes.json()) as { mappings: Array<{ source_model: string }> };
assert.equal(body.mappings.length, 1);
assert.equal(body.mappings[0].source_model, "new-model");
});
@@ -136,7 +141,7 @@ test("PUT: empty mappings array clears all mappings", async () => {
{ params: { id: "zed" } }
);
assert.equal(putRes.status, 200);
const body = await putRes.json() as { mappings: unknown[] };
const body = (await putRes.json()) as { mappings: unknown[] };
assert.equal(body.mappings.length, 0);
});
@@ -152,7 +157,7 @@ test("PUT: invalid body (missing mappings) returns 400", async () => {
{ params: { id: "antigravity" } }
);
assert.equal(res.status, 400);
const body = await res.json() as Record<string, unknown>;
const body = (await res.json()) as Record<string, unknown>;
const errMsg = (body.error as Record<string, unknown>)?.message as string;
assert.ok(!errMsg.includes("at /"), "stack trace leaked in 400 error");
});
@@ -183,10 +188,9 @@ test("PUT: error responses do not leak stack traces", async () => {
});
test("GET: error responses do not leak stack traces", async () => {
const res = await mappingsRoute.GET(
new Request("http://localhost/"),
{ params: { id: "antigravity" } }
);
const res = await mappingsRoute.GET(new Request("http://localhost/"), {
params: { id: "antigravity" },
});
const text = await res.text();
assert.ok(!text.includes("at /"), "stack trace leaked in GET /mappings response");
});

View File

@@ -39,7 +39,7 @@ const routeGuard = await import("../../src/server/authz/routeGuard.ts");
function resetDb() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -49,7 +49,7 @@ test.beforeEach(() => {
test.after(() => {
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {
/* noop */
}

View File

@@ -39,7 +39,7 @@ async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -55,7 +55,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Auth tests ────────────────────────────────────────────────────────────────
@@ -288,6 +288,6 @@ test("grok-build status uses GROK_HOME and returns its managed endpoint", async
} finally {
if (original === undefined) delete process.env.GROK_HOME;
else process.env.GROK_HOME = original;
fs.rmSync(grokHome, { recursive: true, force: true });
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});

View File

@@ -25,14 +25,13 @@ process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-8491-antigravit
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const { AntigravityExecutor } = await import("../../open-sse/executors/antigravity.ts");
const { clearAntigravityProjectCache } = await import(
"../../open-sse/services/antigravityProjectBootstrap.ts"
);
const { clearAntigravityProjectCache } =
await import("../../open-sse/services/antigravityProjectBootstrap.ts");
test.after(() => {
core.resetDbInstance();
if (fs.existsSync(TEST_DATA_DIR)) {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -91,7 +90,11 @@ test("#8491 PART A: runtime-discovered projectId must be persisted to the connec
throw new Error(`Expected an envelope but got a ${result.status} Response`);
}
assert.equal(loadCodeAssistCalls, 1, "loadCodeAssist must be called to recover the project");
assert.equal(result.project, DISCOVERED_PROJECT_ID, "the in-flight request uses the discovered id");
assert.equal(
result.project,
DISCOVERED_PROJECT_ID,
"the in-flight request uses the discovered id"
);
const persisted = await providersDb.getProviderConnectionById(connection.id);
assert.equal(

View File

@@ -25,7 +25,7 @@ async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -63,7 +63,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("API keys routes require management auth when login protection is enabled", async () => {

View File

@@ -24,7 +24,7 @@ async function resetStorage() {
delete process.env.ENABLE_SOCKS5_PROXY;
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -59,7 +59,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("critical routes: v1 management proxies covers auth, lookup, where-used, patch, and delete branches", async () => {

View File

@@ -20,7 +20,7 @@ const auditRoute = await import("../../src/app/api/compliance/audit-log/route.ts
function resetDb() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -38,7 +38,7 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
/**

View File

@@ -268,7 +268,7 @@ async function stopProcess(child: ReturnType<typeof spawn>) {
async function removeDirWithRetry(dir: string) {
for (let attempt = 0; attempt < 5; attempt++) {
try {
fs.rmSync(dir, { recursive: true, force: true });
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
return;
} catch (error) {
if (attempt === 4) throw error;

View File

@@ -373,7 +373,7 @@ async function resetStorage() {
invalidateMemorySettingsCache();
await new Promise((resolve) => setTimeout(resolve, 20));
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
initTranslators();
}
@@ -512,7 +512,7 @@ test.after(async () => {
clearInflight();
resetAllCircuitBreakers();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("chat pipeline handles OpenAI passthrough with valid API key auth", async () => {

View File

@@ -28,7 +28,7 @@ async function resetStorage() {
readCacheDb.invalidateDbCache();
await new Promise((resolve) => setTimeout(resolve, 20));
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -40,7 +40,7 @@ test.after(async () => {
globalThis.fetch = originalFetch;
core.closeDbInstance();
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {}
});

View File

@@ -21,7 +21,7 @@ test.after(async () => {
globalThis.fetch = originalFetch;
core.closeDbInstance();
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {}
});

View File

@@ -22,12 +22,13 @@ process.env.JWT_SECRET = "test-jwt-secret-codewhale";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/codewhale-settings/route.ts");
const { GET, POST, DELETE } =
await import("../../src/app/api/cli-tools/codewhale-settings/route.ts");
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -126,7 +127,7 @@ test("codewhale-settings POST: writes primary ~/.codewhale/config.toml for a fre
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -173,7 +174,7 @@ test("codewhale-settings POST: syncs an existing legacy ~/.deepseek/config.toml"
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -201,7 +202,7 @@ test("codewhale-settings GET: falls back to legacy ~/.deepseek/config.toml when
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -238,7 +239,7 @@ test("codewhale-settings DELETE: removes primary and legacy config files", async
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -272,7 +273,7 @@ test("codewhale-settings route.ts: does not call exec() or spawn() directly", ()
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -8,9 +8,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(
path.join(os.tmpdir(), "omniroute-deepseek-tui-settings-")
);
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-deepseek-tui-settings-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-api-key-secret-deepseek-tui";
process.env.JWT_SECRET = "test-jwt-secret-deepseek-tui";
@@ -18,14 +16,13 @@ process.env.JWT_SECRET = "test-jwt-secret-deepseek-tui";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { GET, POST, DELETE } = await import(
"../../src/app/api/cli-tools/deepseek-tui-settings/route.ts"
);
const { GET, POST, DELETE } =
await import("../../src/app/api/cli-tools/deepseek-tui-settings/route.ts");
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -103,10 +100,7 @@ test("deepseek-tui-settings POST: writes config.toml with valid body", async ()
}),
})
);
assert.ok(
[200, 403, 500].includes(res.status),
`Unexpected status ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
if (res.status === 200) {
const body = await res.json();
assert.equal(body.success, true);
@@ -120,7 +114,7 @@ test("deepseek-tui-settings POST: writes config.toml with valid body", async ()
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -136,23 +130,20 @@ test("deepseek-tui-settings DELETE: removes config file", async () => {
fs.mkdirSync(configDir, { recursive: true });
fs.writeFileSync(
path.join(configDir, "config.toml"),
"# managed by OmniRoute (plan 14)\n[openai]\nbase_url = \"http://localhost:20128\"\n"
'# managed by OmniRoute (plan 14)\n[openai]\nbase_url = "http://localhost:20128"\n'
);
const res = await DELETE(
new Request("http://localhost/api/cli-tools/deepseek-tui-settings", { method: "DELETE" })
);
assert.ok(
[200, 403, 500].includes(res.status),
`Expected 200/403/500, got ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
if (res.status === 200) {
const body = await res.json();
assert.equal(body.success, true);
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -186,7 +177,7 @@ test("deepseek-tui-settings route.ts: does not call exec() or spawn() directly",
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -19,14 +19,12 @@ const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
// Import route handlers
const { GET, POST, DELETE } = await import(
"../../src/app/api/cli-tools/forge-settings/route.ts"
);
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/forge-settings/route.ts");
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -107,10 +105,7 @@ test("forge-settings POST: writes config.toml with valid body", async () => {
);
// 200 = success; 403 = write guard active (test env); 500 = backup dir issue
assert.ok(
[200, 403, 500].includes(res.status),
`Unexpected status ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
if (res.status === 200) {
const body = await res.json();
@@ -126,7 +121,7 @@ test("forge-settings POST: writes config.toml with valid body", async () => {
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -143,16 +138,13 @@ test("forge-settings DELETE: removes config file when it exists", async () => {
fs.mkdirSync(forgeDir, { recursive: true });
fs.writeFileSync(
path.join(forgeDir, "config.toml"),
"# managed by OmniRoute (plan 14)\n[openai]\nbase_url = \"http://localhost:20128\"\n"
'# managed by OmniRoute (plan 14)\n[openai]\nbase_url = "http://localhost:20128"\n'
);
const res = await DELETE(
new Request("http://localhost/api/cli-tools/forge-settings", { method: "DELETE" })
);
assert.ok(
[200, 403, 500].includes(res.status),
`Expected 200/403/500, got ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
if (res.status === 200) {
const body = await res.json();
@@ -160,7 +152,7 @@ test("forge-settings DELETE: removes config file when it exists", async () => {
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -194,7 +186,7 @@ test("forge-settings route.ts: does not call exec() or spawn() directly", () =>
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -39,7 +39,7 @@ const { GET, POST, DELETE } =
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -163,7 +163,7 @@ test("grok-build-settings POST: writes [model.omniroute] section and preserves e
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -219,7 +219,7 @@ test("grok-build-settings DELETE: removes our section, preserves the rest, resto
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -237,7 +237,7 @@ test("grok-build-settings DELETE: no-op success when no config file exists", asy
assert.equal(body.success, true);
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -289,7 +289,7 @@ test("grok-build-settings: honors GROK_HOME and rejects a relative value", async
} finally {
if (original === undefined) delete process.env.GROK_HOME;
else process.env.GROK_HOME = original;
fs.rmSync(grokHome, { recursive: true, force: true });
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -313,7 +313,7 @@ test("grok-build-settings POST: returns 409 for an unowned omniroute slot", asyn
} finally {
if (original === undefined) delete process.env.GROK_HOME;
else process.env.GROK_HOME = original;
fs.rmSync(grokHome, { recursive: true, force: true });
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -342,7 +342,7 @@ test("grok-build-settings POST: resolves keyId to an unmasked key", async () =>
} finally {
if (original === undefined) delete process.env.GROK_HOME;
else process.env.GROK_HOME = original;
fs.rmSync(grokHome, { recursive: true, force: true });
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -376,7 +376,7 @@ test("grok-build-settings route.ts: does not call exec() or spawn() directly", (
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -21,7 +21,7 @@ const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/jcode-se
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -115,7 +115,7 @@ test("jcode-settings POST: writes [providers.omniroute] into config.toml", async
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -159,7 +159,7 @@ test("jcode-settings DELETE: removes only the OmniRoute-managed block", async ()
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -193,7 +193,7 @@ test("jcode-settings route.ts: does not call exec() or spawn() directly", () =>
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -22,9 +22,7 @@ process.env.JWT_SECRET = "test-jwt-secret-letta";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { GET, POST, DELETE } = await import(
"../../src/app/api/cli-tools/letta-settings/route.ts"
);
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/letta-settings/route.ts");
let tmpHome: string;
let origHome: string | undefined;
@@ -40,7 +38,7 @@ function req(init?: RequestInit) {
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -58,7 +56,7 @@ test.beforeEach(async () => {
test.afterEach(() => {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Test 1: GET without auth → 401 ──────────────────────────────────────────
@@ -189,7 +187,7 @@ test("letta-settings: error responses do not leak stack traces", async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -59,7 +59,7 @@ function seedOmpDb() {
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -77,7 +77,7 @@ test.beforeEach(async () => {
test.afterEach(() => {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Test 1: GET without auth → 401 ──────────────────────────────────────────
@@ -190,7 +190,7 @@ test("omp-settings: error responses do not leak stack traces", async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -16,14 +16,12 @@ process.env.JWT_SECRET = "test-jwt-secret-pi";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { GET, POST, DELETE } = await import(
"../../src/app/api/cli-tools/pi-settings/route.ts"
);
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/pi-settings/route.ts");
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -101,10 +99,7 @@ test("pi-settings POST: writes config.json with valid body", async () => {
}),
})
);
assert.ok(
[200, 403, 500].includes(res.status),
`Unexpected status ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
if (res.status === 200) {
const body = await res.json();
assert.equal(body.success, true);
@@ -118,7 +113,7 @@ test("pi-settings POST: writes config.json with valid body", async () => {
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -145,17 +140,14 @@ test("pi-settings DELETE: removes OmniRoute fields from existing config", async
const res = await DELETE(
new Request("http://localhost/api/cli-tools/pi-settings", { method: "DELETE" })
);
assert.ok(
[200, 403, 500].includes(res.status),
`Expected 200/403/500, got ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
if (res.status === 200) {
const body = await res.json();
assert.equal(body.success, true);
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -189,7 +181,7 @@ test("pi-settings route.ts: does not call exec() or spawn() directly", () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -16,14 +16,12 @@ process.env.JWT_SECRET = "test-jwt-secret-smelt";
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const { GET, POST, DELETE } = await import(
"../../src/app/api/cli-tools/smelt-settings/route.ts"
);
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/smelt-settings/route.ts");
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -101,10 +99,7 @@ test("smelt-settings POST: writes config.json with valid body", async () => {
}),
})
);
assert.ok(
[200, 403, 500].includes(res.status),
`Unexpected status ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
if (res.status === 200) {
const body = await res.json();
assert.equal(body.success, true);
@@ -118,7 +113,7 @@ test("smelt-settings POST: writes config.json with valid body", async () => {
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -145,17 +140,14 @@ test("smelt-settings DELETE: removes OmniRoute fields from existing config", asy
const res = await DELETE(
new Request("http://localhost/api/cli-tools/smelt-settings", { method: "DELETE" })
);
assert.ok(
[200, 403, 500].includes(res.status),
`Expected 200/403/500, got ${res.status}`
);
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
if (res.status === 200) {
const body = await res.json();
assert.equal(body.success, true);
}
} finally {
process.env.HOME = origHome;
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -189,7 +181,7 @@ test("smelt-settings route.ts: does not call exec() or spawn() directly", () =>
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
delete process.env.DATA_DIR;
delete process.env.API_KEY_SECRET;
delete process.env.JWT_SECRET;

View File

@@ -44,6 +44,6 @@ test("Codex Spark cooldown survives a fresh process without creating child conne
assert.equal(after.connectionId, before.connectionId);
assert.deepEqual(after.upstreamModels, ["gpt-5.5"]);
} finally {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});

View File

@@ -305,6 +305,6 @@ test("chat completions streams Codex Responses reasoning through real route HTTP
globalThis.fetch = originalFetch;
if (routeServer) await closeServer(routeServer);
core.closeDbInstance({ checkpointMode: null });
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});

View File

@@ -46,18 +46,18 @@ const IN_SCOPE_PROVIDERS = new Set([
// Provider → sensible default model (fallback when default_model is null).
const PROVIDER_DEFAULT_MODELS: Record<string, string> = {
"claude": "claude-3-5-haiku-20241022",
"glm": "glm-4-flash",
"minimax": "minimax-text-01",
claude: "claude-3-5-haiku-20241022",
glm: "glm-4-flash",
minimax: "minimax-text-01",
"kimi-coding-apikey": "moonshot-v1-8k",
"ollama-cloud": "llama3.2:3b",
"opencode-go": "gpt-4o-mini",
"gemini": "gemini-2.0-flash-lite",
"deepseek": "deepseek-chat",
"groq": "llama-3.1-8b-instant",
"cerebras": "llama-3.1-8b",
"openrouter": "openai/gpt-4o-mini",
"together": "meta-llama/Llama-3-8b-chat-hf",
gemini: "gemini-2.0-flash-lite",
deepseek: "deepseek-chat",
groq: "llama-3.1-8b-instant",
cerebras: "llama-3.1-8b",
openrouter: "openai/gpt-4o-mini",
together: "meta-llama/Llama-3-8b-chat-hf",
};
// ---------------------------------------------------------------------------
@@ -79,9 +79,11 @@ export type ComboModelEntry = {
connectionId: string;
};
export type LiveHarness = {
LIVE_ENABLED: false;
} | LiveHarnessEnabled;
export type LiveHarness =
| {
LIVE_ENABLED: false;
}
| LiveHarnessEnabled;
export type LiveHarnessEnabled = {
LIVE_ENABLED: true;
@@ -146,12 +148,12 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
}
}
} catch (err: any) {
fs.rmSync(snapshotDir, { recursive: true, force: true });
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
throw new Error(`[liveHarness] Failed to fetch VPS secrets via ssh: ${err.message}`);
}
if (!storageEncryptionKey || !apiKeySecret) {
fs.rmSync(snapshotDir, { recursive: true, force: true });
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
throw new Error(
"[liveHarness] Could not parse STORAGE_ENCRYPTION_KEY or API_KEY_SECRET from VPS .env"
);
@@ -176,13 +178,11 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
const snapshotDbPath = path.join(snapshotDir, "storage.sqlite");
try {
execFileSync(
"scp",
["root@192.168.0.15:/root/.omniroute/storage.sqlite", snapshotDbPath],
{ timeout: 60_000 }
);
execFileSync("scp", ["root@192.168.0.15:/root/.omniroute/storage.sqlite", snapshotDbPath], {
timeout: 60_000,
});
} catch (err: any) {
fs.rmSync(snapshotDir, { recursive: true, force: true });
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
throw new Error(`[liveHarness] Failed to scp production DB: ${err.message}`);
}
@@ -262,7 +262,10 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
});
}
function liveBody(model: string, overrides: Record<string, unknown> = {}): Record<string, unknown> {
function liveBody(
model: string,
overrides: Record<string, unknown> = {}
): Record<string, unknown> {
return {
model,
stream: false,
@@ -400,7 +403,7 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
resetAllCircuitBreakers();
core.resetDbInstance();
// Destroy the snapshot — targets only the temp dir, NEVER /root/.omniroute.
fs.rmSync(snapshotDir, { recursive: true, force: true });
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
// Populate the map eagerly so servedProvider (sync) works right after

View File

@@ -280,7 +280,7 @@ test.after(async () => {
if (app) await stopProcess(app.child);
await upstream.stop();
core.closeDbInstance();
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Tests ──────────────────────────────────────────────────────────────────

View File

@@ -14,7 +14,7 @@ const { createSSEStream } = await import("../../open-sse/utils/stream.ts");
test.after(() => {
core.resetDbInstance();
if (fs.existsSync(TEST_DATA_DIR)) {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});

View File

@@ -88,7 +88,7 @@ test.afterEach(() => {
clearInflight();
resetAllCircuitBreakers();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
});
@@ -112,7 +112,10 @@ test("llama-cpp provider: routes request to custom baseUrl with no auth header",
headers: toPlainHeaders(init.headers),
body: init.body ? JSON.parse(String(init.body)) : null,
});
return buildLlamaResponse("Why did the programmer go broke? Because he used up all his cache!", "unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M");
return buildLlamaResponse(
"Why did the programmer go broke? Because he used up all his cache!",
"unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M"
);
};
const response = await handleChat(
@@ -135,7 +138,10 @@ test("llama-cpp provider: routes request to custom baseUrl with no auth header",
assert.equal(upstream.headers.Authorization, undefined, "no auth header for local provider");
assert.equal(upstream.body.messages[0].content, "Tell me a joke.");
assert.equal(upstream.body.model, "unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M");
assert.equal(json.choices[0].message.content, "Why did the programmer go broke? Because he used up all his cache!");
assert.equal(
json.choices[0].message.content,
"Why did the programmer go broke? Because he used up all his cache!"
);
});
test("llama-cpp provider: alias matching works via model catalog prefix", async () => {
@@ -152,7 +158,12 @@ test("llama-cpp provider: alias matching works via model catalog prefix", async
const fetchCalls: FetchCall[] = [];
globalThis.fetch = async (url, init: RequestInit = {}) => {
fetchCalls.push({ url: String(url), method: init.method, headers: toPlainHeaders(init.headers), body: init.body ? JSON.parse(String(init.body)) : null });
fetchCalls.push({
url: String(url),
method: init.method,
headers: toPlainHeaders(init.headers),
body: init.body ? JSON.parse(String(init.body)) : null,
});
return buildLlamaResponse("42", "unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M");
};
@@ -167,7 +178,11 @@ test("llama-cpp provider: alias matching works via model catalog prefix", async
);
const json = (await response.json()) as any;
assert.equal(response.status, 200, `expected 200, got ${response.status}: ${JSON.stringify(json)}`);
assert.equal(
response.status,
200,
`expected 200, got ${response.status}: ${JSON.stringify(json)}`
);
assert.equal(json.choices[0].message.content, "42");
});

View File

@@ -22,16 +22,15 @@ const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
// Import route AFTER setting DATA_DIR
const embeddingProvidersRoute = await import(
"../../src/app/api/memory/embedding-providers/route.ts"
);
const embeddingProvidersRoute =
await import("../../src/app/api/memory/embedding-providers/route.ts");
const { GET } = embeddingProvidersRoute;
// ── Helpers ──
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -44,7 +43,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Tests ──

View File

@@ -22,16 +22,14 @@ const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
// Import route AFTER setting DATA_DIR
const engineStatusRoute = await import(
"../../src/app/api/memory/engine-status/route.ts"
);
const engineStatusRoute = await import("../../src/app/api/memory/engine-status/route.ts");
const { GET } = engineStatusRoute;
// ── Helpers ──
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -44,7 +42,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Tests ──
@@ -67,7 +65,11 @@ test("GET /api/memory/engine-status — 200 + valid MemoryEngineStatusSchema sha
assert.strictEqual(body.keyword.backend, "FTS5", "keyword.backend should be FTS5");
assert.ok(body.embedding, "should have embedding section");
assert.strictEqual(typeof body.embedding.available, "boolean", "embedding.available should be boolean");
assert.strictEqual(
typeof body.embedding.available,
"boolean",
"embedding.available should be boolean"
);
assert.ok(typeof body.embedding.reason === "string", "embedding.reason should be a string");
assert.ok(body.embedding.cacheStats, "should have cacheStats in embedding");
assert.strictEqual(typeof body.embedding.cacheStats.hits, "number");
@@ -77,7 +79,7 @@ test("GET /api/memory/engine-status — 200 + valid MemoryEngineStatusSchema sha
assert.ok(body.vectorStore, "should have vectorStore section");
assert.ok(
["sqlite-vec", "qdrant", "none"].includes(body.vectorStore.backend),
`vectorStore.backend should be valid: ${body.vectorStore.backend}`,
`vectorStore.backend should be valid: ${body.vectorStore.backend}`
);
assert.strictEqual(typeof body.vectorStore.available, "boolean");
assert.strictEqual(typeof body.vectorStore.rowCount, "number");

View File

@@ -13,9 +13,7 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
makeManagementSessionRequest,
} from "../helpers/managementSession.ts";
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-reindex-"));
process.env.DATA_DIR = TEST_DATA_DIR;
@@ -25,16 +23,14 @@ const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const memoryStore = await import("../../src/lib/memory/store.ts");
const reindexRoute = await import(
"../../src/app/api/memory/reindex/route.ts"
);
const reindexRoute = await import("../../src/app/api/memory/reindex/route.ts");
const { POST } = reindexRoute;
// ── Helpers ──
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -66,7 +62,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Tests ──

View File

@@ -12,9 +12,7 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
makeManagementSessionRequest,
} from "../helpers/managementSession.ts";
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-retrieve-preview-"));
process.env.DATA_DIR = TEST_DATA_DIR;
@@ -24,16 +22,14 @@ const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
// Import route AFTER setting DATA_DIR
const retrieveRoute = await import(
"../../src/app/api/memory/retrieve-preview/route.ts"
);
const retrieveRoute = await import("../../src/app/api/memory/retrieve-preview/route.ts");
const { POST } = retrieveRoute;
// ── Helpers ──
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -53,7 +49,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Tests ──
@@ -106,9 +102,7 @@ test("POST /api/memory/retrieve-preview — 401 without auth when requireLogin=t
test("POST /api/memory/retrieve-preview — error path: no stack trace (invalid JSON)", async () => {
// Test via invalid JSON body — the parse step should return 400 without a stack trace
const { createManagementSessionHeaders } = await import(
"../helpers/managementSession.ts"
);
const { createManagementSessionHeaders } = await import("../helpers/managementSession.ts");
const headers = await createManagementSessionHeaders();
const req = new Request("http://localhost/api/memory/retrieve-preview", {

View File

@@ -33,7 +33,7 @@ const { createMemory, getMemory } = memoryStore;
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -69,7 +69,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Tests ──

View File

@@ -12,9 +12,7 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
makeManagementSessionRequest,
} from "../helpers/managementSession.ts";
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-summarize-"));
process.env.DATA_DIR = TEST_DATA_DIR;
@@ -24,16 +22,14 @@ const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const memoryStore = await import("../../src/lib/memory/store.ts");
const summarizeRoute = await import(
"../../src/app/api/memory/summarize/route.ts"
);
const summarizeRoute = await import("../../src/app/api/memory/summarize/route.ts");
const { POST } = summarizeRoute;
// ── Helpers ──
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -61,7 +57,7 @@ async function seedOldMemory(daysAgo: number, apiKeyId = "api-key-1") {
db.prepare("UPDATE memories SET created_at = ?, updated_at = ? WHERE id = ?").run(
oldTs,
oldTs,
mem.id,
mem.id
);
return mem;
}
@@ -75,7 +71,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Tests ──

View File

@@ -19,7 +19,7 @@ const healthRoute = await import("../../src/app/api/health/ping/route.ts");
async function resetStorage() {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
modelsCatalog.__resetCatalogBuilderRunsForTest();
}
@@ -30,7 +30,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test(

View File

@@ -23,7 +23,7 @@ after(() => {
globalThis.fetch = originalFetch;
if (originalHome === undefined) delete process.env.HOME;
else process.env.HOME = originalHome;
fs.rmSync(testHome, { recursive: true, force: true });
fs.rmSync(testHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
function runOpencode(binary: string, args: string[]) {

View File

@@ -226,7 +226,7 @@ describe("Performance: memory API route handler (1000 records)", () => {
db.prepare("DELETE FROM memories WHERE api_key_id = ?").run(TEST_API_KEY_ID);
// Final cleanup: reset DB instance and remove temp dir
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
it(`should handle GET /api/memory?limit=50 in <${THRESHOLD_API_ROUTE_MS}ms`, async () => {

View File

@@ -19,16 +19,12 @@ import os from "node:os";
import path from "node:path";
// Set up a temp DATA_DIR so getDbInstance() initialises cleanly
const TEST_DATA_DIR = fs.mkdtempSync(
path.join(os.tmpdir(), "omniroute-improve-prompt-")
);
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-improve-prompt-"));
process.env.DATA_DIR = TEST_DATA_DIR;
// Disable mandatory auth for most tests
process.env.REQUIRE_API_KEY = "false";
const { POST, OPTIONS } = await import(
"../../src/app/api/playground/improve-prompt/route.ts"
);
const { POST, OPTIONS } = await import("../../src/app/api/playground/improve-prompt/route.ts");
const BASE_URL = "http://localhost:20128";
@@ -61,7 +57,7 @@ function postRequest(body: unknown): Request {
// ─── Cleanup ─────────────────────────────────────────────────────────────────
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ─── OPTIONS ─────────────────────────────────────────────────────────────────
@@ -84,7 +80,11 @@ test("happy path: system + prompt both provided", async () => {
) as typeof fetch;
const res = await POST(
postRequest({ system: "You are a helper.", prompt: "Tell me about AI.", model: "gpt-4o-mini" })
postRequest({
system: "You are a helper.",
prompt: "Tell me about AI.",
model: "gpt-4o-mini",
})
);
assert.equal(res.status, 200);
@@ -161,15 +161,13 @@ test("happy path: usage defaults to 0 when not in upstream response", async () =
try {
// Return response without usage field
globalThis.fetch = (async (_url: unknown, _opts: unknown) => {
return new Response(
JSON.stringify({ choices: [{ message: { content: "improved" } }] }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
return new Response(JSON.stringify({ choices: [{ message: { content: "improved" } }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}) as typeof fetch;
const res = await POST(
postRequest({ prompt: "Hello world", model: "gpt-4o-mini" })
);
const res = await POST(postRequest({ prompt: "Hello world", model: "gpt-4o-mini" }));
assert.equal(res.status, 200);
const body = (await res.json()) as { tokensIn: number; tokensOut: number };
@@ -245,9 +243,7 @@ test("upstream error returns sanitized error message — no stack trace in body"
"Internal error\n at /home/user/project/src/handler.ts:42:10\n at process.nextTick"
) as typeof fetch;
const res = await POST(
postRequest({ prompt: "Hello", model: "gpt-4o-mini" })
);
const res = await POST(postRequest({ prompt: "Hello", model: "gpt-4o-mini" }));
// Should be an error response (not 200)
assert.ok(res.status >= 400);
@@ -270,9 +266,7 @@ test("upstream network error is sanitized", async () => {
throw new Error("ECONNREFUSED connect ECONNREFUSED 127.0.0.1:20128");
}) as typeof fetch;
const res = await POST(
postRequest({ prompt: "Hello", model: "gpt-4o-mini" })
);
const res = await POST(postRequest({ prompt: "Hello", model: "gpt-4o-mini" }));
assert.ok(res.status >= 500);
const body = (await res.json()) as { error: { message: string } };
@@ -289,9 +283,7 @@ test("401 when REQUIRE_API_KEY=true and no key provided", async () => {
const originalRequired = process.env.REQUIRE_API_KEY;
try {
process.env.REQUIRE_API_KEY = "true";
const res = await POST(
postRequest({ prompt: "Test", model: "gpt-4o-mini" })
);
const res = await POST(postRequest({ prompt: "Test", model: "gpt-4o-mini" }));
assert.equal(res.status, 401);
const body = (await res.json()) as { error: { message: string } };

View File

@@ -22,26 +22,28 @@ import os from "node:os";
import path from "node:path";
// Isolated DB per test file
const TEST_DATA_DIR = fs.mkdtempSync(
path.join(os.tmpdir(), "omniroute-presets-crud-")
);
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-presets-crud-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.REQUIRE_API_KEY = "false";
const core = await import("../../src/lib/db/core.ts");
// Import route handlers
const { GET: listGet, POST: createPost, OPTIONS: listOptions } = await import(
"../../src/app/api/playground/presets/route.ts"
);
const { GET: idGet, PUT: idPut, DELETE: idDelete, OPTIONS: idOptions } = await import(
"../../src/app/api/playground/presets/[id]/route.ts"
);
const {
GET: listGet,
POST: createPost,
OPTIONS: listOptions,
} = await import("../../src/app/api/playground/presets/route.ts");
const {
GET: idGet,
PUT: idPut,
DELETE: idDelete,
OPTIONS: idOptions,
} = await import("../../src/app/api/playground/presets/[id]/route.ts");
const BASE_URL = "http://localhost:20128";
const UUID_V4_REGEX =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
// ─── Helpers ─────────────────────────────────────────────────────────────────
@@ -86,7 +88,7 @@ test.beforeEach(async () => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ─── OPTIONS ─────────────────────────────────────────────────────────────────
@@ -186,7 +188,12 @@ test("PUT /presets/[id] partial patch (name only) updates correctly", async () =
);
assert.equal(putRes.status, 200);
const updated = (await putRes.json()) as { id: string; name: string; endpoint: string; model: string };
const updated = (await putRes.json()) as {
id: string;
name: string;
endpoint: string;
model: string;
};
assert.equal(updated.id, created.id);
assert.equal(updated.name, "Updated Name");
// Other fields should be preserved
@@ -295,10 +302,7 @@ test("GET /presets/[id] with non-UUID id → 400", async () => {
test("PUT /presets/[id] with non-UUID id → 400", async () => {
const badId = "also-not-a-uuid";
const res = await idPut(
putReq(badId, { name: "Whatever" }),
await resolveParams(badId)
);
const res = await idPut(putReq(badId, { name: "Whatever" }), await resolveParams(badId));
assert.equal(res.status, 400);
const body = (await res.json()) as { error: { message: string } };

View File

@@ -19,20 +19,18 @@ import os from "node:os";
import path from "node:path";
// Isolated DB per test file
const TEST_DATA_DIR = fs.mkdtempSync(
path.join(os.tmpdir(), "omniroute-presets-zod-")
);
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-presets-zod-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.REQUIRE_API_KEY = "false";
const core = await import("../../src/lib/db/core.ts");
const { POST: createPost } = await import(
"../../src/app/api/playground/presets/route.ts"
);
const { GET: idGet, PUT: idPut, DELETE: idDelete } = await import(
"../../src/app/api/playground/presets/[id]/route.ts"
);
const { POST: createPost } = await import("../../src/app/api/playground/presets/route.ts");
const {
GET: idGet,
PUT: idPut,
DELETE: idDelete,
} = await import("../../src/app/api/playground/presets/[id]/route.ts");
const BASE_URL = "http://localhost:20128";
@@ -83,7 +81,7 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ─── POST validation ─────────────────────────────────────────────────────────
@@ -133,9 +131,7 @@ test("POST with missing model → 400", async () => {
});
test("POST with empty model → 400", async () => {
const res = await createPost(
postReq({ name: "Test", endpoint: "chat.completions", model: "" })
);
const res = await createPost(postReq({ name: "Test", endpoint: "chat.completions", model: "" }));
assert.equal(res.status, 400);
const body = (await res.json()) as { error: { message: string } };
assert.ok(body.error);
@@ -145,7 +141,12 @@ test("POST with empty model → 400", async () => {
test("POST with system > 50000 chars → 400", async () => {
const longSystem = "x".repeat(50001);
const res = await createPost(
postReq({ name: "Big System", endpoint: "chat.completions", model: "gpt-4o", system: longSystem })
postReq({
name: "Big System",
endpoint: "chat.completions",
model: "gpt-4o",
system: longSystem,
})
);
assert.equal(res.status, 400);
const body = (await res.json()) as { error: { message: string } };
@@ -156,7 +157,12 @@ test("POST with system > 50000 chars → 400", async () => {
test("POST with system exactly 50000 chars → 201 (boundary: valid)", async () => {
const maxSystem = "x".repeat(50000);
const res = await createPost(
postReq({ name: "Max System", endpoint: "chat.completions", model: "gpt-4o", system: maxSystem })
postReq({
name: "Max System",
endpoint: "chat.completions",
model: "gpt-4o",
system: maxSystem,
})
);
assert.equal(res.status, 201);
});
@@ -205,10 +211,7 @@ test("PUT with empty name → 400", async () => {
test("PUT with system > 50000 chars → 400", async () => {
const validId = "00000000-0000-4000-8000-000000000001";
const longSystem = "y".repeat(50001);
const res = await idPut(
putReq(validId, { system: longSystem }),
await resolveParams(validId)
);
const res = await idPut(putReq(validId, { system: longSystem }), await resolveParams(validId));
assert.equal(res.status, 400);
const body = (await res.json()) as { error: { message: string } };
assert.ok(body.error);

View File

@@ -20,7 +20,11 @@ const { pluginManager } = await import("../../src/lib/plugins/manager.ts");
// Scanner expects: sourceDir/<plugin-name>/plugin.json + index.js
// Returns the sourceDir (parent) to pass to pluginManager.install()
function writeTestPlugin(opts?: { name?: string; onRequest?: boolean; enabledByDefault?: boolean }) {
function writeTestPlugin(opts?: {
name?: string;
onRequest?: boolean;
enabledByDefault?: boolean;
}) {
const name = opts?.name ?? "test-lifecycle-plugin";
const onRequest = opts?.onRequest ?? true;
const enabledByDefault = opts?.enabledByDefault ?? false;
@@ -57,7 +61,7 @@ function writeTestPlugin(opts?: { name?: string; onRequest?: boolean; enabledByD
// ── Helpers ──
function cleanupDir(dir: string) {
fs.rmSync(dir, { recursive: true, force: true });
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
// Track temp source dirs for cleanup
@@ -65,7 +69,9 @@ const activeSourceDirs: string[] = [];
function cleanupSourceDirs() {
for (const dir of activeSourceDirs) {
try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
try {
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {}
}
activeSourceDirs.length = 0;
}
@@ -83,7 +89,9 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
cleanupSourceDirs();
try { cleanupDir(TEST_DATA_DIR); } catch {}
try {
cleanupDir(TEST_DATA_DIR);
} catch {}
});
// ── Tests: Install ──
@@ -230,7 +238,11 @@ test("deactivate: unregisters all hooks for the plugin", async () => {
// Hook should be gone
const after = hooks.getHooks("onRequest");
assert.equal(after.find((r) => r.pluginName === name), undefined, "hook should be unregistered");
assert.equal(
after.find((r) => r.pluginName === name),
undefined,
"hook should be unregistered"
);
await pluginManager.uninstall(name);
});
@@ -300,7 +312,10 @@ test("uninstall: deactivates before removing if active", async () => {
// Plugin should be fully gone
assert.equal(dbPlugins.getPluginByName(name), null);
assert.equal(hooks.getHooks("onRequest").find((r) => r.pluginName === name), undefined);
assert.equal(
hooks.getHooks("onRequest").find((r) => r.pluginName === name),
undefined
);
});
test("uninstall: throws for nonexistent plugin", async () => {
@@ -322,7 +337,10 @@ test("full lifecycle: install -> activate -> hook fires -> deactivate -> uninsta
await pluginManager.activate(name);
const afterActivate = dbPlugins.getPluginByName(name);
assert.equal(afterActivate!.status, "active");
assert.ok(hooks.getHooks("onRequest").find((r) => r.pluginName === name), "hook registered");
assert.ok(
hooks.getHooks("onRequest").find((r) => r.pluginName === name),
"hook registered"
);
// 3. Fire hook (use emitHookBlocking — child-process isolation means plugins cannot
// mutate the parent's in-memory payload object; check the returned merged result).
@@ -370,8 +388,14 @@ test("multiple plugins: hooks are isolated per plugin", async () => {
await pluginManager.deactivate("multi-p1");
const afterDeactivate = hooks.getHooks("onRequest");
assert.equal(afterDeactivate.find((r) => r.pluginName === "multi-p1"), undefined);
assert.ok(afterDeactivate.find((r) => r.pluginName === "multi-p2"), "p2 hook still registered");
assert.equal(
afterDeactivate.find((r) => r.pluginName === "multi-p1"),
undefined
);
assert.ok(
afterDeactivate.find((r) => r.pluginName === "multi-p2"),
"p2 hook still registered"
);
// Cleanup
await pluginManager.uninstall("multi-p1");

View File

@@ -118,7 +118,7 @@ async function fetchCatalog(
test.before(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
// requireLogin + requireAuthForModels ON so the API-key surface is gated.
await localDb.updateSettings({ requireLogin: true, requireAuthForModels: true, password: "" });
@@ -127,7 +127,7 @@ test.before(async () => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test.describe("provider journey — in-process contract (#8330)", () => {

View File

@@ -23,13 +23,13 @@ const proxyLogger = await import("../../src/lib/proxyLogger.ts");
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("integration: proxy create with inline assignment is atomic and clears legacy config", async () => {

View File

@@ -45,7 +45,7 @@ const asNextRequest = (req: Request) => req as unknown as import("next/server").
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
// #5597 follow-up: the memory-settings cache is a module-level singleton that
// survives per-test DB resets — bust it so each test starts from a clean read.
@@ -88,7 +88,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── Settings GET ──

View File

@@ -35,7 +35,7 @@ async function enableManagementAuth() {
function resetDb() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -46,7 +46,7 @@ test.beforeEach(async () => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ---------------------------------------------------------------------------
@@ -91,7 +91,9 @@ test("GET /api/quota/plans includes DB override plans", async () => {
// List should include the override
const listReq = await makeManagementSessionRequest("http://localhost/api/quota/plans");
const listRes = await plansRoute.GET(listReq);
const body = (await listRes.json()) as { plans: Array<{ connectionId: string | null; source: string }> };
const body = (await listRes.json()) as {
plans: Array<{ connectionId: string | null; source: string }>;
};
const override = body.plans.find((p) => p.connectionId === "conn-override-1");
assert.ok(override, "Override plan should appear in list");
assert.equal(override?.source, "manual");
@@ -160,13 +162,10 @@ test("PUT /api/quota/plans/[connectionId] without auth → 401", async () => {
});
test("PUT /api/quota/plans/[connectionId] with invalid body → 400", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/quota/plans/conn-bad-body",
{
method: "PUT",
body: { dimensions: [] }, // PlanUpsertSchema requires min(1) dimensions
}
);
const req = await makeManagementSessionRequest("http://localhost/api/quota/plans/conn-bad-body", {
method: "PUT",
body: { dimensions: [] }, // PlanUpsertSchema requires min(1) dimensions
});
const res = await planIdRoute.PUT(req, {
params: Promise.resolve({ connectionId: "conn-bad-body" }),
});
@@ -226,7 +225,9 @@ test("DELETE /api/quota/plans/[connectionId] clears override → 204; GET revert
`http://localhost/api/quota/plans/${connectionId}`,
{ method: "DELETE" }
);
const deleteRes = await planIdRoute.DELETE(deleteReq, { params: Promise.resolve({ connectionId }) });
const deleteRes = await planIdRoute.DELETE(deleteReq, {
params: Promise.resolve({ connectionId }),
});
assert.equal(deleteRes.status, 204);
// GET should now return auto/empty plan (no DB override)
@@ -250,7 +251,10 @@ test("DELETE /api/quota/plans/[connectionId] clears override → 204; GET revert
(e as Record<string, unknown>).target === connectionId &&
(e as { metadata?: { reverted?: boolean } }).metadata?.reverted === true
);
assert.ok(deleteEvt, "quota.plan.updated audit event (reverted=true) must be present after DELETE");
assert.ok(
deleteEvt,
"quota.plan.updated audit event (reverted=true) must be present after DELETE"
);
});
test("DELETE /api/quota/plans/[connectionId] is idempotent → 204 even when not found", async () => {

View File

@@ -36,7 +36,7 @@ type Db = {
function resetDb() {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -95,7 +95,7 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("DELETE pool waits for scoped quota-combo cleanup before returning 204", async () => {

View File

@@ -35,7 +35,7 @@ const usageRoute = await import("../../src/app/api/quota/pools/[id]/usage/route.
function resetDb() {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -46,7 +46,7 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("GET /usage surfaces catalog dimensions for a catalog-only pool (provider resolved from connection)", async () => {

View File

@@ -38,7 +38,7 @@ async function enableManagementAuth() {
function resetDb() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -49,7 +49,7 @@ test.beforeEach(async () => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ---------------------------------------------------------------------------
@@ -91,7 +91,7 @@ test("POST /api/quota/pools with auth + valid body → 201 + pool returned", asy
});
const res = await poolsRoute.POST(req);
assert.equal(res.status, 201);
const body = await res.json() as { pool: { id: string; name: string; connectionId: string } };
const body = (await res.json()) as { pool: { id: string; name: string; connectionId: string } };
assert.ok(body.pool.id, "Pool should have an id");
assert.equal(body.pool.name, "Test Pool Alpha");
assert.equal(body.pool.connectionId, "conn-test-1");
@@ -109,7 +109,10 @@ test("POST /api/quota/pools → audit event logged", async () => {
const events = Array.isArray(logs) ? logs : [];
assert.ok(events.length >= 1, "Should have at least one quota.pool.created audit event");
const evt = events.find(
(e) => typeof e === "object" && e !== null && (e as Record<string, unknown>).action === "quota.pool.created"
(e) =>
typeof e === "object" &&
e !== null &&
(e as Record<string, unknown>).action === "quota.pool.created"
);
assert.ok(evt, "quota.pool.created audit event must be present");
});
@@ -267,9 +270,7 @@ test("DELETE /api/quota/pools/[id] → 204 + audit event; subsequent GET → 404
assert.ok(evt, "quota.pool.deleted audit event must be present");
// Subsequent GET → 404
const getReq = await makeManagementSessionRequest(
`http://localhost/api/quota/pools/${poolId}`
);
const getReq = await makeManagementSessionRequest(`http://localhost/api/quota/pools/${poolId}`);
const getRes = await poolIdRoute.GET(getReq, { params: Promise.resolve({ id: poolId }) });
assert.equal(getRes.status, 404);
});

View File

@@ -42,7 +42,7 @@ async function enableManagementAuth() {
function resetDb() {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -54,7 +54,7 @@ test.beforeEach(async () => {
test.after(() => {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("GET /api/quota/pools/[id]/usage without auth → 401", async () => {
@@ -137,11 +137,7 @@ test("GET /api/quota/pools/[id]/usage → PoolUsageSnapshot shape with correct f
// Even with no plan dimensions (empty plan for unknown provider), the response
// is valid with an empty dimensions array — endpoint falls back to poolUsage()
// which returns what's available from the store.
assert.doesNotMatch(
JSON.stringify(body),
/\s+at\s+\//,
"No stack trace in usage response"
);
assert.doesNotMatch(JSON.stringify(body), /\s+at\s+\//, "No stack trace in usage response");
});
test("GET /api/quota/pools/[id]/usage response has required PoolUsageSnapshot fields", async () => {

View File

@@ -40,7 +40,7 @@ async function enableManagementAuth() {
function resetDb() {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -52,14 +52,12 @@ test.beforeEach(async () => {
test.after(() => {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("GET /api/quota/preview without auth → 401", async () => {
await enableManagementAuth();
const req = new Request(
"http://localhost/api/quota/preview?apiKeyId=k1&poolId=p1"
);
const req = new Request("http://localhost/api/quota/preview?apiKeyId=k1&poolId=p1");
const res = await previewRoute.GET(req);
assert.equal(res.status, 401);
});
@@ -89,9 +87,7 @@ test("GET /api/quota/preview with nonexistent poolId → 404", async () => {
test("GET /api/quota/preview with valid params → { decision } with kind", async () => {
// Create a real pool
const pool = createPool({ connectionId: "conn-preview", name: "Preview Pool" });
upsertAllocations(pool.id, [
{ apiKeyId: "preview-key-1", weight: 100, policy: "soft" },
]);
upsertAllocations(pool.id, [{ apiKeyId: "preview-key-1", weight: 100, policy: "soft" }]);
const req = await makeManagementSessionRequest(
`http://localhost/api/quota/preview?apiKeyId=preview-key-1&poolId=${pool.id}&estimatedTokens=100`
@@ -110,9 +106,7 @@ test("GET /api/quota/preview with valid params → { decision } with kind", asyn
test("GET /api/quota/preview is dry-run: store counters unchanged after call", async () => {
// Create pool and seed some consumption
const pool = createPool({ connectionId: "conn-dryrun", name: "Dry Run Pool" });
upsertAllocations(pool.id, [
{ apiKeyId: "dryrun-key", weight: 100, policy: "hard" },
]);
upsertAllocations(pool.id, [{ apiKeyId: "dryrun-key", weight: 100, policy: "hard" }]);
const store = getSqliteQuotaStore();
const dim = { poolId: pool.id, unit: "tokens" as const, window: "daily" as const };

View File

@@ -17,9 +17,7 @@ import os from "node:os";
import path from "node:path";
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
const TEST_DATA_DIR = fs.mkdtempSync(
path.join(os.tmpdir(), "omniroute-quota-err-sanitization-")
);
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-quota-err-sanitization-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-quota-sanitization-secret";
process.env.QUOTA_STORE_DRIVER = "sqlite";
@@ -42,7 +40,7 @@ const settingsRoute = await import("../../src/app/api/settings/quota-store/route
function resetDb() {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -68,16 +66,8 @@ async function assertNoStackTrace(res: Response, label: string) {
// Helper to assert secret URL not in response body text
function assertNoSecretUrlText(text: string, label: string) {
assert.doesNotMatch(
text,
/secret-host/,
`${label}: Response must not contain secret Redis host`
);
assert.doesNotMatch(
text,
/redis:\/\/secret/,
`${label}: Response must not contain Redis URL`
);
assert.doesNotMatch(text, /secret-host/, `${label}: Response must not contain secret Redis host`);
assert.doesNotMatch(text, /redis:\/\/secret/, `${label}: Response must not contain Redis URL`);
}
// Reads the response body once and runs both assertions (body cannot be read twice)
@@ -96,7 +86,7 @@ test.after(() => {
core.resetDbInstance();
resetQuotaStoreSingleton();
delete process.env.QUOTA_STORE_REDIS_URL;
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ---------------------------------------------------------------------------
@@ -118,9 +108,7 @@ test("POST /api/quota/pools 400 error response has no stack trace", async () =>
// ---------------------------------------------------------------------------
test("GET /api/quota/pools/[id] 404 response has no stack trace", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/quota/pools/does-not-exist"
);
const req = await makeManagementSessionRequest("http://localhost/api/quota/pools/does-not-exist");
const res = await poolIdRoute.GET(req, {
params: Promise.resolve({ id: "does-not-exist" }),
});
@@ -179,13 +167,10 @@ test("GET /api/quota/plans 200 response has no stack trace or path leak", async
// ---------------------------------------------------------------------------
test("PUT /api/quota/plans/[connectionId] 400 error response has no stack trace", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/quota/plans/conn-bad",
{
method: "PUT",
body: { dimensions: [] }, // PlanUpsertSchema requires min(1)
}
);
const req = await makeManagementSessionRequest("http://localhost/api/quota/plans/conn-bad", {
method: "PUT",
body: { dimensions: [] }, // PlanUpsertSchema requires min(1)
});
const res = await planIdRoute.PUT(req, {
params: Promise.resolve({ connectionId: "conn-bad" }),
});
@@ -212,9 +197,7 @@ test("GET /api/quota/preview 400 error response has no stack trace", async () =>
// ---------------------------------------------------------------------------
test("GET /api/settings/quota-store response does not contain Redis URL (Hard Rule #12/#1)", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store"
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store");
const res = await settingsRoute.GET(req);
assert.equal(res.status, 200);
await assertNoStackTraceAndNoSecretUrl(res, "GET /api/settings/quota-store 200");
@@ -225,13 +208,10 @@ test("GET /api/settings/quota-store response does not contain Redis URL (Hard Ru
// ---------------------------------------------------------------------------
test("PUT /api/settings/quota-store 400 error response has no stack trace", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store",
{
method: "PUT",
body: { driver: "baddriver" },
}
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
method: "PUT",
body: { driver: "baddriver" },
});
const res = await settingsRoute.PUT(req);
assert.equal(res.status, 400);
await assertNoStackTrace(res, "PUT /api/settings/quota-store 400");
@@ -242,13 +222,10 @@ test("PUT /api/settings/quota-store 400 error response has no stack trace", asyn
// ---------------------------------------------------------------------------
test("PUT /api/settings/quota-store redis+no-URL error response does not leak Redis URL", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store",
{
method: "PUT",
body: { driver: "redis" }, // No URL provided
}
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
method: "PUT",
body: { driver: "redis" }, // No URL provided
});
const res = await settingsRoute.PUT(req);
assert.equal(res.status, 400);
await assertNoStackTraceAndNoSecretUrl(res, "PUT /api/settings/quota-store redis-no-url 400");

View File

@@ -41,7 +41,7 @@ function resetDb() {
resetQuotaStoreSingleton();
delete process.env.QUOTA_STORE_REDIS_URL;
delete process.env.INITIAL_PASSWORD;
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -53,7 +53,7 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
resetQuotaStoreSingleton();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ---------------------------------------------------------------------------
@@ -68,9 +68,7 @@ test("GET /api/settings/quota-store without auth → 401", async () => {
});
test("GET /api/settings/quota-store returns driver + redisUrlConfigured (not URL)", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store"
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store");
const res = await settingsRoute.GET(req);
assert.equal(res.status, 200);
const body = (await res.json()) as {
@@ -93,9 +91,7 @@ test("GET /api/settings/quota-store returns driver + redisUrlConfigured (not URL
test("GET /api/settings/quota-store redisUrlConfigured=false when no URL configured", async () => {
delete process.env.QUOTA_STORE_REDIS_URL;
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store"
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store");
const res = await settingsRoute.GET(req);
const body = (await res.json()) as { redisUrlConfigured: boolean };
assert.equal(body.redisUrlConfigured, false);
@@ -117,13 +113,10 @@ test("PUT /api/settings/quota-store without auth → 401", async () => {
});
test("PUT /api/settings/quota-store driver=sqlite → 200", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store",
{
method: "PUT",
body: { driver: "sqlite" },
}
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
method: "PUT",
body: { driver: "sqlite" },
});
const res = await settingsRoute.PUT(req);
assert.equal(res.status, 200);
const body = (await res.json()) as { driver: string; redisUrl: null };
@@ -132,13 +125,10 @@ test("PUT /api/settings/quota-store driver=sqlite → 200", async () => {
});
test("PUT /api/settings/quota-store driver=redis without URL → 400", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store",
{
method: "PUT",
body: { driver: "redis" }, // No redisUrl
}
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
method: "PUT",
body: { driver: "redis" }, // No redisUrl
});
const res = await settingsRoute.PUT(req);
assert.equal(res.status, 400);
const body = await res.json();
@@ -147,13 +137,10 @@ test("PUT /api/settings/quota-store driver=redis without URL → 400", async ()
});
test("PUT /api/settings/quota-store driver=redis with valid URL → 200 + audit event", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store",
{
method: "PUT",
body: { driver: "redis", redisUrl: "redis://localhost:6379" },
}
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
method: "PUT",
body: { driver: "redis", redisUrl: "redis://localhost:6379" },
});
const res = await settingsRoute.PUT(req);
assert.equal(res.status, 200);
const body = (await res.json()) as {
@@ -187,13 +174,10 @@ test("PUT /api/settings/quota-store driver=redis with valid URL → 200 + audit
});
test("PUT /api/settings/quota-store with invalid driver → 400 (Zod)", async () => {
const req = await makeManagementSessionRequest(
"http://localhost/api/settings/quota-store",
{
method: "PUT",
body: { driver: "memcached" }, // Not in enum
}
);
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
method: "PUT",
body: { driver: "memcached" }, // Not in enum
});
const res = await settingsRoute.PUT(req);
assert.equal(res.status, 400);
const body = await res.json();

View File

@@ -547,7 +547,7 @@ test.after(async () => {
}
await relay.stop();
core.closeDbInstance();
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("resilience API only exposes configuration, not runtime breaker state", async () => {

View File

@@ -102,7 +102,7 @@ async function seedRateLimitedConnection(provider: string) {
/** Reset DB state between tests. */
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -116,7 +116,7 @@ test.beforeEach(async () => {
test.after(async () => {
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ---------------------------------------------------------------------------

View File

@@ -37,7 +37,7 @@ async function resetStorage() {
readCacheDb.invalidateDbCache();
await new Promise((resolve) => setTimeout(resolve, 20));
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
@@ -49,7 +49,7 @@ test.after(async () => {
globalThis.fetch = originalFetch;
core.closeDbInstance();
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch {
// best-effort cleanup
}

View File

@@ -19,30 +19,25 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ti-captur
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.INSPECTOR_HTTP_PROXY_PORT = "0"; // ephemeral port
const captureModesRoute = await import(
"../../src/app/api/tools/traffic-inspector/capture-modes/route.ts"
);
const httpProxyRoute = await import(
"../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts"
);
const systemProxyRoute = await import(
"../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts"
);
const tlsInterceptRoute = await import(
"../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts"
);
const { setHttpProxyHandle, getHttpProxyHandle, clearSystemProxy } = await import(
"../../src/lib/inspector/captureState.ts"
);
const { __setExec } = await import(
"../../src/mitm/inspector/systemProxyConfig.ts"
);
const captureModesRoute =
await import("../../src/app/api/tools/traffic-inspector/capture-modes/route.ts");
const httpProxyRoute =
await import("../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts");
const systemProxyRoute =
await import("../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts");
const tlsInterceptRoute =
await import("../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts");
const { setHttpProxyHandle, getHttpProxyHandle, clearSystemProxy } =
await import("../../src/lib/inspector/captureState.ts");
const { __setExec } = await import("../../src/mitm/inspector/systemProxyConfig.ts");
test.beforeEach(() => {
// Ensure no running proxy handle leaks between tests
const handle = getHttpProxyHandle();
if (handle) {
handle.stop().catch(() => {/* ignore */});
handle.stop().catch(() => {
/* ignore */
});
setHttpProxyHandle(null);
}
clearSystemProxy();
@@ -52,10 +47,12 @@ test.after(() => {
// Clean up any running proxy
const handle = getHttpProxyHandle();
if (handle) {
handle.stop().catch(() => {/* ignore */});
handle.stop().catch(() => {
/* ignore */
});
setHttpProxyHandle(null);
}
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── GET /capture-modes ──────────────────────────────────────────────────────
@@ -63,7 +60,7 @@ test.after(() => {
test("GET /capture-modes: returns status of all modes", async () => {
const res = await captureModesRoute.GET();
assert.equal(res.status, 200);
const body = await res.json() as {
const body = (await res.json()) as {
agentBridge: boolean;
httpProxy: { running: boolean; port: number | null };
systemProxy: { applied: boolean };
@@ -78,17 +75,14 @@ test("GET /capture-modes: returns status of all modes", async () => {
// ── POST /capture-modes/http-proxy ─────────────────────────────────────────
test("http-proxy: start binds an ephemeral port", async () => {
const req = new Request(
"http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy",
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ action: "start" }),
}
);
const req = new Request("http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ action: "start" }),
});
const res = await httpProxyRoute.POST(req);
assert.equal(res.status, 201);
const body = await res.json() as { ok: boolean; running: boolean; port: number };
const body = (await res.json()) as { ok: boolean; running: boolean; port: number };
assert.equal(body.ok, true);
assert.equal(body.running, true);
assert.ok(body.port > 0, "should have a bound port");
@@ -102,17 +96,14 @@ test("http-proxy: start binds an ephemeral port", async () => {
});
test("http-proxy: stop when not running returns ok", async () => {
const req = new Request(
"http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy",
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ action: "stop" }),
}
);
const req = new Request("http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ action: "stop" }),
});
const res = await httpProxyRoute.POST(req);
assert.equal(res.status, 200);
const body = await res.json() as { ok: boolean; running: boolean };
const body = (await res.json()) as { ok: boolean; running: boolean };
assert.equal(body.ok, true);
assert.equal(body.running, false);
});
@@ -139,16 +130,14 @@ test("http-proxy: start then stop lifecycle", async () => {
);
const stopRes = await httpProxyRoute.POST(stopReq);
assert.equal(stopRes.status, 200);
const body = await stopRes.json() as { running: boolean };
const body = (await stopRes.json()) as { running: boolean };
assert.equal(body.running, false);
});
test("http-proxy: EADDRINUSE returns 409 with structured error", async () => {
// Import startHttpProxyServer directly so we can test the low-level error path
// without depending on the module-cached DEFAULT_PORT.
const { startHttpProxyServer } = await import(
"../../src/mitm/inspector/httpProxyServer.ts"
);
const { startHttpProxyServer } = await import("../../src/mitm/inspector/httpProxyServer.ts");
// Occupy a random port
const blocker = net.createServer();
@@ -173,7 +162,10 @@ test("http-proxy: EADDRINUSE returns 409 with structured error", async () => {
// ── POST /capture-modes/system-proxy ───────────────────────────────────────
test("system-proxy: apply with mocked OS commands", async () => {
const restore = __setExec(async (_file, _args) => ({ stdout: "Enabled: No\nServer: \nPort: 0", stderr: "" }));
const restore = __setExec(async (_file, _args) => ({
stdout: "Enabled: No\nServer: \nPort: 0",
stderr: "",
}));
try {
const req = new Request(
"http://localhost/api/tools/traffic-inspector/capture-modes/system-proxy",
@@ -185,7 +177,7 @@ test("system-proxy: apply with mocked OS commands", async () => {
);
const res = await systemProxyRoute.POST(req);
assert.equal(res.status, 200);
const body = await res.json() as { ok: boolean; applied: boolean };
const body = (await res.json()) as { ok: boolean; applied: boolean };
assert.equal(body.ok, true);
assert.equal(body.applied, true);
} finally {
@@ -207,7 +199,7 @@ test("system-proxy: revert without prior apply is a no-op", async () => {
);
const res = await systemProxyRoute.POST(req);
assert.equal(res.status, 200);
const body = await res.json() as { applied: boolean };
const body = (await res.json()) as { applied: boolean };
assert.equal(body.applied, false);
} finally {
restore();
@@ -240,7 +232,7 @@ test("tls-intercept: toggle on/off", async () => {
);
const enableRes = await tlsInterceptRoute.POST(enableReq);
assert.equal(enableRes.status, 200);
const enableBody = await enableRes.json() as { tlsIntercept: { enabled: boolean } };
const enableBody = (await enableRes.json()) as { tlsIntercept: { enabled: boolean } };
assert.equal(enableBody.tlsIntercept.enabled, true);
const disableReq = new Request(
@@ -253,6 +245,6 @@ test("tls-intercept: toggle on/off", async () => {
);
const disableRes = await tlsInterceptRoute.POST(disableReq);
assert.equal(disableRes.status, 200);
const disableBody = await disableRes.json() as { tlsIntercept: { enabled: boolean } };
const disableBody = (await disableRes.json()) as { tlsIntercept: { enabled: boolean } };
assert.equal(disableBody.tlsIntercept.enabled, false);
});

View File

@@ -17,53 +17,36 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const { globalTrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
const requestsRoute = await import(
"../../src/app/api/tools/traffic-inspector/requests/route.ts"
);
const requestDetailRoute = await import(
"../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts"
);
const annotationRoute = await import(
"../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts"
);
const hostsRoute = await import(
"../../src/app/api/tools/traffic-inspector/hosts/route.ts"
);
const hostDetailRoute = await import(
"../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts"
);
const sessionsRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/route.ts"
);
const sessionDetailRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts"
);
const ingestRoute = await import(
"../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts"
);
const httpProxyRoute = await import(
"../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts"
);
const systemProxyRoute = await import(
"../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts"
);
const tlsInterceptRoute = await import(
"../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts"
);
const requestsRoute = await import("../../src/app/api/tools/traffic-inspector/requests/route.ts");
const requestDetailRoute =
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts");
const annotationRoute =
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts");
const hostsRoute = await import("../../src/app/api/tools/traffic-inspector/hosts/route.ts");
const hostDetailRoute =
await import("../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts");
const sessionsRoute = await import("../../src/app/api/tools/traffic-inspector/sessions/route.ts");
const sessionDetailRoute =
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts");
const ingestRoute =
await import("../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts");
const httpProxyRoute =
await import("../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts");
const systemProxyRoute =
await import("../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts");
const tlsInterceptRoute =
await import("../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts");
function noStackTrace(msg: string, label: string): void {
assert.ok(
!msg.includes("at /"),
`${label}: error message must not contain stack trace (found "at /")`
);
assert.ok(
!msg.includes(".ts:"),
`${label}: error message must not include TS file paths`
);
assert.ok(!msg.includes(".ts:"), `${label}: error message must not include TS file paths`);
}
async function getErrorMessage(res: Response): Promise<string> {
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
return body.error?.message ?? "";
}
@@ -72,23 +55,20 @@ test.beforeEach(() => {
});
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("requests: invalid profile param does not leak stack", async () => {
const req = new Request(
"http://localhost/api/tools/traffic-inspector/requests?profile=BAD"
);
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?profile=BAD");
const res = await requestsRoute.GET(req);
assert.equal(res.status, 400);
noStackTrace(await getErrorMessage(res), "GET /requests");
});
test("requests/[id]: unknown id does not leak stack", async () => {
const res = await requestDetailRoute.GET(
new Request("http://localhost/"),
{ params: Promise.resolve({ id: randomUUID() }) }
);
const res = await requestDetailRoute.GET(new Request("http://localhost/"), {
params: Promise.resolve({ id: randomUUID() }),
});
assert.equal(res.status, 404);
noStackTrace(await getErrorMessage(res), "GET /requests/[id]");
});
@@ -148,40 +128,33 @@ test("hosts/[host] PATCH: invalid body does not leak stack", async () => {
});
test("sessions: 404 does not leak stack", async () => {
const res = await sessionDetailRoute.GET(
new Request("http://localhost/"),
{ params: Promise.resolve({ id: randomUUID() }) }
);
const res = await sessionDetailRoute.GET(new Request("http://localhost/"), {
params: Promise.resolve({ id: randomUUID() }),
});
assert.equal(res.status, 404);
noStackTrace(await getErrorMessage(res), "GET /sessions/[id]");
});
test("ingest: 403 does not leak stack", async () => {
const req = new Request(
"http://localhost/api/tools/traffic-inspector/internal/ingest",
{
method: "POST",
headers: {
"content-type": "application/json",
authorization: "Bearer wrong-token",
},
body: JSON.stringify({}),
}
);
const req = new Request("http://localhost/api/tools/traffic-inspector/internal/ingest", {
method: "POST",
headers: {
"content-type": "application/json",
authorization: "Bearer wrong-token",
},
body: JSON.stringify({}),
});
const res = await ingestRoute.POST(req);
assert.equal(res.status, 403);
noStackTrace(await getErrorMessage(res), "POST /internal/ingest (403)");
});
test("http-proxy: invalid action does not leak stack", async () => {
const req = new Request(
"http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy",
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ action: "invalid" }),
}
);
const req = new Request("http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ action: "invalid" }),
});
const res = await httpProxyRoute.POST(req);
assert.equal(res.status, 400);
noStackTrace(await getErrorMessage(res), "POST /capture-modes/http-proxy");

View File

@@ -18,29 +18,26 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const hostsRoute = await import(
"../../src/app/api/tools/traffic-inspector/hosts/route.ts"
);
const hostDetailRoute = await import(
"../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts"
);
const hostsRoute = await import("../../src/app/api/tools/traffic-inspector/hosts/route.ts");
const hostDetailRoute =
await import("../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts");
test.beforeEach(async () => {
resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
// Re-init DB with fresh migrations
await import("../../src/lib/db/core.ts").then((m) => m.getDbInstance());
});
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("GET /hosts: returns empty list initially", async () => {
const res = await hostsRoute.GET();
assert.equal(res.status, 200);
const body = await res.json() as { hosts: unknown[] };
const body = (await res.json()) as { hosts: unknown[] };
assert.deepEqual(body.hosts, []);
});
@@ -52,13 +49,13 @@ test("POST /hosts: adds a host", async () => {
});
const res = await hostsRoute.POST(req);
assert.equal(res.status, 201);
const body = await res.json() as { ok: boolean; host: string };
const body = (await res.json()) as { ok: boolean; host: string };
assert.equal(body.ok, true);
assert.equal(body.host, "api.openai.com");
// Verify it appears in list
const listRes = await hostsRoute.GET();
const list = await listRes.json() as { hosts: Array<{ host: string }> };
const list = (await listRes.json()) as { hosts: Array<{ host: string }> };
assert.ok(list.hosts.some((h) => h.host === "api.openai.com"));
});
@@ -70,7 +67,7 @@ test("POST /hosts: rejects empty host string", async () => {
});
const res = await hostsRoute.POST(req);
assert.equal(res.status, 400);
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
});
@@ -94,15 +91,14 @@ test("DELETE /hosts/[host]: removes existing host", async () => {
await hostsRoute.POST(addReq);
// Now delete it
const delRes = await hostDetailRoute.DELETE(
new Request("http://localhost/"),
{ params: Promise.resolve({ host: "remove-me.example.com" }) }
);
const delRes = await hostDetailRoute.DELETE(new Request("http://localhost/"), {
params: Promise.resolve({ host: "remove-me.example.com" }),
});
assert.equal(delRes.status, 204);
// Verify gone
const listRes = await hostsRoute.GET();
const list = await listRes.json() as { hosts: Array<{ host: string }> };
const list = (await listRes.json()) as { hosts: Array<{ host: string }> };
assert.ok(!list.hosts.some((h) => h.host === "remove-me.example.com"));
});
@@ -125,7 +121,7 @@ test("PATCH /hosts/[host]: toggles enabled flag", async () => {
{ params: Promise.resolve({ host: "toggle-me.example.com" }) }
);
assert.equal(patchRes.status, 200);
const body = await patchRes.json() as { enabled: boolean };
const body = (await patchRes.json()) as { enabled: boolean };
assert.equal(body.enabled, false);
});

View File

@@ -23,9 +23,8 @@ const VALID_TOKEN = "test-ingest-token-abc123xyz789-longer-than-16";
process.env.INSPECTOR_INTERNAL_INGEST_TOKEN = VALID_TOKEN;
const { globalTrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
const ingestRoute = await import(
"../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts"
);
const ingestRoute =
await import("../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts");
function makeIngestRequest(token: string | null, body: unknown): Request {
const headers: Record<string, string> = {
@@ -34,14 +33,11 @@ function makeIngestRequest(token: string | null, body: unknown): Request {
if (token !== null) {
headers["authorization"] = `Bearer ${token}`;
}
return new Request(
"http://localhost/api/tools/traffic-inspector/internal/ingest",
{
method: "POST",
headers,
body: JSON.stringify(body),
}
);
return new Request("http://localhost/api/tools/traffic-inspector/internal/ingest", {
method: "POST",
headers,
body: JSON.stringify(body),
});
}
function minimalEntry(overrides: Record<string, unknown> = {}): Record<string, unknown> {
@@ -66,14 +62,14 @@ test.beforeEach(() => {
});
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("ingest: POST without Authorization header → 403", async () => {
const req = makeIngestRequest(null, minimalEntry());
const res = await ingestRoute.POST(req);
assert.equal(res.status, 403);
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
});
@@ -94,7 +90,7 @@ test("ingest: POST with valid token + valid body → 200 + buffer push", async (
const req = makeIngestRequest(VALID_TOKEN, minimalEntry({ id }));
const res = await ingestRoute.POST(req);
assert.equal(res.status, 200);
const body = await res.json() as { ok: boolean; id: string };
const body = (await res.json()) as { ok: boolean; id: string };
assert.equal(body.ok, true);
assert.equal(body.id, id);
@@ -113,23 +109,20 @@ test("ingest: valid token + missing required field → 400", async () => {
});
const res = await ingestRoute.POST(req);
assert.equal(res.status, 400);
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
});
test("ingest: valid token + invalid JSON → 400", async () => {
const headers: Record<string, string> = {
"content-type": "application/json",
"authorization": `Bearer ${VALID_TOKEN}`,
authorization: `Bearer ${VALID_TOKEN}`,
};
const req = new Request(
"http://localhost/api/tools/traffic-inspector/internal/ingest",
{
method: "POST",
headers,
body: "not valid json",
}
);
const req = new Request("http://localhost/api/tools/traffic-inspector/internal/ingest", {
method: "POST",
headers,
body: "not valid json",
});
const res = await ingestRoute.POST(req);
assert.equal(res.status, 400);
});

View File

@@ -14,12 +14,10 @@ import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ti-local-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const { isLocalOnlyPath, isLoopbackHost } = await import(
"../../src/server/authz/routeGuard.ts"
);
const { isLocalOnlyPath, isLoopbackHost } = await import("../../src/server/authz/routeGuard.ts");
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
// ── isLocalOnlyPath assertions ──────────────────────────────────────────────

View File

@@ -16,23 +16,21 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ti-reqs-"
process.env.DATA_DIR = TEST_DATA_DIR;
const { globalTrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
const requestsRoute = await import(
"../../src/app/api/tools/traffic-inspector/requests/route.ts"
);
const requestDetailRoute = await import(
"../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts"
);
const annotationRoute = await import(
"../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts"
);
const requestsRoute = await import("../../src/app/api/tools/traffic-inspector/requests/route.ts");
const requestDetailRoute =
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts");
const annotationRoute =
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts");
function makeEntry(overrides: Partial<{
id: string;
host: string;
detectedKind: "llm" | "app" | "unknown";
status: number | "in-flight" | "error";
source: "agent-bridge" | "custom-host" | "http-proxy" | "system-proxy";
}> = {}) {
function makeEntry(
overrides: Partial<{
id: string;
host: string;
detectedKind: "llm" | "app" | "unknown";
status: number | "in-flight" | "error";
source: "agent-bridge" | "custom-host" | "http-proxy" | "system-proxy";
}> = {}
) {
return {
id: randomUUID(),
source: "agent-bridge" as const,
@@ -57,14 +55,14 @@ test.beforeEach(() => {
});
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("GET /requests: returns empty list when buffer is empty", async () => {
const req = new Request("http://localhost/api/tools/traffic-inspector/requests");
const res = await requestsRoute.GET(req);
assert.equal(res.status, 200);
const body = await res.json() as { requests: unknown[]; total: number };
const body = (await res.json()) as { requests: unknown[]; total: number };
assert.deepEqual(body.requests, []);
assert.equal(body.total, 0);
});
@@ -76,7 +74,7 @@ test("GET /requests: returns all entries without filter", async () => {
const req = new Request("http://localhost/api/tools/traffic-inspector/requests");
const res = await requestsRoute.GET(req);
assert.equal(res.status, 200);
const body = await res.json() as { requests: unknown[]; total: number };
const body = (await res.json()) as { requests: unknown[]; total: number };
assert.equal(body.total, 2);
});
@@ -84,12 +82,10 @@ test("GET /requests: filters by profile=llm", async () => {
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), detectedKind: "llm" }));
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), detectedKind: "app" }));
const req = new Request(
"http://localhost/api/tools/traffic-inspector/requests?profile=llm"
);
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?profile=llm");
const res = await requestsRoute.GET(req);
assert.equal(res.status, 200);
const body = await res.json() as { requests: unknown[]; total: number };
const body = (await res.json()) as { requests: unknown[]; total: number };
assert.equal(body.total, 1);
});
@@ -97,23 +93,19 @@ test("GET /requests: filters by host", async () => {
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), host: "target.com" }));
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), host: "other.com" }));
const req = new Request(
"http://localhost/api/tools/traffic-inspector/requests?host=target.com"
);
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?host=target.com");
const res = await requestsRoute.GET(req);
assert.equal(res.status, 200);
const body = await res.json() as { requests: Array<{ host: string }>; total: number };
const body = (await res.json()) as { requests: Array<{ host: string }>; total: number };
assert.equal(body.total, 1);
assert.equal(body.requests[0]?.host, "target.com");
});
test("GET /requests: rejects invalid profile param with 400", async () => {
const req = new Request(
"http://localhost/api/tools/traffic-inspector/requests?profile=invalid"
);
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?profile=invalid");
const res = await requestsRoute.GET(req);
assert.equal(res.status, 400);
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
});
@@ -134,19 +126,17 @@ test("GET /requests/[id]: returns entry by id", async () => {
params: Promise.resolve({ id: entry.id }),
});
assert.equal(res.status, 200);
const body = await res.json() as { id: string };
const body = (await res.json()) as { id: string };
assert.equal(body.id, entry.id);
});
test("GET /requests/[id]: returns 404 for unknown id", async () => {
const req = new Request(
`http://localhost/api/tools/traffic-inspector/requests/${randomUUID()}`
);
const req = new Request(`http://localhost/api/tools/traffic-inspector/requests/${randomUUID()}`);
const res = await requestDetailRoute.GET(req, {
params: Promise.resolve({ id: randomUUID() }),
});
assert.equal(res.status, 404);
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
});
@@ -166,7 +156,7 @@ test("PUT /requests/[id]/annotation: attaches annotation", async () => {
params: Promise.resolve({ id: entry.id }),
});
assert.equal(res.status, 200);
const body = await res.json() as { annotation: string };
const body = (await res.json()) as { annotation: string };
assert.equal(body.annotation, "my note");
// Confirm buffer was updated

View File

@@ -17,20 +17,16 @@ const { resetDbInstance, getDbInstance } = await import("../../src/lib/db/core.t
async function resetStorage() {
resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
getDbInstance();
}
const sessionsRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/route.ts"
);
const sessionDetailRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts"
);
const sessionRequestsRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/[id]/requests/route.ts"
);
const sessionsRoute = await import("../../src/app/api/tools/traffic-inspector/sessions/route.ts");
const sessionDetailRoute =
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts");
const sessionRequestsRoute =
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/requests/route.ts");
async function createSession(name?: string): Promise<string> {
const res = await sessionsRoute.POST(
@@ -61,7 +57,7 @@ test.beforeEach(async () => {
test.after(() => {
resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("POST /sessions/[id]/requests: seq increments 1, 2, 3", async () => {
@@ -131,7 +127,7 @@ test("POST /sessions/[id]/requests: error response does not leak stack trace", a
// POST to non-existent session — exercises the 404 path error body
const res = await postRequest("00000000-0000-4000-8000-000000000099", "data");
assert.equal(res.status, 404);
const body = await res.json() as { error?: { message?: string } };
const body = (await res.json()) as { error?: { message?: string } };
const msg = body?.error?.message ?? "";
assert.ok(!msg.includes("at /"), "should not contain stack trace");
});

View File

@@ -18,21 +18,17 @@ const { resetDbInstance, getDbInstance } = await import("../../src/lib/db/core.t
async function resetStorage() {
resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
// Re-initialize db
getDbInstance();
}
const sessionsRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/route.ts"
);
const sessionDetailRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts"
);
const sessionHarRoute = await import(
"../../src/app/api/tools/traffic-inspector/sessions/[id]/export.har/route.ts"
);
const sessionsRoute = await import("../../src/app/api/tools/traffic-inspector/sessions/route.ts");
const sessionDetailRoute =
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts");
const sessionHarRoute =
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/export.har/route.ts");
const { appendSessionRequest } = await import("../../src/lib/db/inspectorSessions.ts");
test.beforeEach(async () => {
@@ -40,7 +36,7 @@ test.beforeEach(async () => {
});
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("POST /sessions: creates a session", async () => {
@@ -51,7 +47,7 @@ test("POST /sessions: creates a session", async () => {
});
const res = await sessionsRoute.POST(req);
assert.equal(res.status, 201);
const body = await res.json() as { id: string; started_at: string };
const body = (await res.json()) as { id: string; started_at: string };
assert.ok(body.id, "should have an id");
assert.ok(body.started_at, "should have started_at");
});
@@ -85,7 +81,7 @@ test("GET /sessions: lists all sessions", async () => {
const res = await sessionsRoute.GET();
assert.equal(res.status, 200);
const body = await res.json() as { sessions: unknown[] };
const body = (await res.json()) as { sessions: unknown[] };
assert.equal(body.sessions.length, 2);
});
@@ -97,7 +93,7 @@ test("PATCH /sessions/[id]: stop adds ended_at", async () => {
body: JSON.stringify({}),
})
);
const session = await createRes.json() as { id: string };
const session = (await createRes.json()) as { id: string };
const patchReq = new Request("http://localhost/", {
method: "PATCH",
@@ -108,7 +104,7 @@ test("PATCH /sessions/[id]: stop adds ended_at", async () => {
params: Promise.resolve({ id: session.id }),
});
assert.equal(patchRes.status, 200);
const body = await patchRes.json() as { ended_at: string | null };
const body = (await patchRes.json()) as { ended_at: string | null };
assert.ok(body.ended_at !== null, "ended_at should be set after stop");
});
@@ -120,7 +116,7 @@ test("PATCH /sessions/[id]: rename updates name", async () => {
body: JSON.stringify({ name: "old-name" }),
})
);
const session = await createRes.json() as { id: string };
const session = (await createRes.json()) as { id: string };
const patchRes = await sessionDetailRoute.PATCH(
new Request("http://localhost/", {
@@ -131,7 +127,7 @@ test("PATCH /sessions/[id]: rename updates name", async () => {
{ params: Promise.resolve({ id: session.id }) }
);
assert.equal(patchRes.status, 200);
const body = await patchRes.json() as { name: string };
const body = (await patchRes.json()) as { name: string };
assert.equal(body.name, "new-name");
});
@@ -143,7 +139,7 @@ test("GET /sessions/[id]: returns session with requests", async () => {
body: JSON.stringify({ name: "with-reqs" }),
})
);
const session = await createRes.json() as { id: string };
const session = (await createRes.json()) as { id: string };
// Append a fake request
const payload = JSON.stringify({
@@ -163,12 +159,11 @@ test("GET /sessions/[id]: returns session with requests", async () => {
});
appendSessionRequest(session.id, payload);
const getRes = await sessionDetailRoute.GET(
new Request("http://localhost/"),
{ params: Promise.resolve({ id: session.id }) }
);
const getRes = await sessionDetailRoute.GET(new Request("http://localhost/"), {
params: Promise.resolve({ id: session.id }),
});
assert.equal(getRes.status, 200);
const body = await getRes.json() as { session: { id: string }; requests: unknown[] };
const body = (await getRes.json()) as { session: { id: string }; requests: unknown[] };
assert.equal(body.session.id, session.id);
assert.equal(body.requests.length, 1);
});
@@ -181,21 +176,19 @@ test("DELETE /sessions/[id]: cascades requests", async () => {
body: JSON.stringify({}),
})
);
const session = await createRes.json() as { id: string };
const session = (await createRes.json()) as { id: string };
appendSessionRequest(session.id, JSON.stringify({ note: "test" }));
const delRes = await sessionDetailRoute.DELETE(
new Request("http://localhost/"),
{ params: Promise.resolve({ id: session.id }) }
);
const delRes = await sessionDetailRoute.DELETE(new Request("http://localhost/"), {
params: Promise.resolve({ id: session.id }),
});
assert.equal(delRes.status, 204);
// Session should be gone
const getRes = await sessionDetailRoute.GET(
new Request("http://localhost/"),
{ params: Promise.resolve({ id: session.id }) }
);
const getRes = await sessionDetailRoute.GET(new Request("http://localhost/"), {
params: Promise.resolve({ id: session.id }),
});
assert.equal(getRes.status, 404);
});
@@ -207,7 +200,7 @@ test("GET /sessions/[id]/export.har: returns HAR file", async () => {
body: JSON.stringify({ name: "har-test" }),
})
);
const session = await createRes.json() as { id: string };
const session = (await createRes.json()) as { id: string };
const reqPayload = {
id: randomUUID(),
@@ -226,16 +219,15 @@ test("GET /sessions/[id]/export.har: returns HAR file", async () => {
};
appendSessionRequest(session.id, JSON.stringify(reqPayload));
const harRes = await sessionHarRoute.GET(
new Request("http://localhost/"),
{ params: Promise.resolve({ id: session.id }) }
);
const harRes = await sessionHarRoute.GET(new Request("http://localhost/"), {
params: Promise.resolve({ id: session.id }),
});
assert.equal(harRes.status, 200);
assert.ok(
harRes.headers.get("content-disposition")?.includes(".har"),
"should have .har filename"
);
const har = await harRes.json() as { log: { entries: unknown[] } };
const har = (await harRes.json()) as { log: { entries: unknown[] } };
assert.ok(har.log, "should be a HAR object");
assert.equal(har.log.entries.length, 1);
});

View File

@@ -18,9 +18,7 @@ process.env.DATA_DIR = TEST_DATA_DIR;
process.env.INSPECTOR_BUFFER_SIZE = "100";
const { TrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
const wsRoute = await import(
"../../src/app/api/tools/traffic-inspector/ws/route.ts"
);
const wsRoute = await import("../../src/app/api/tools/traffic-inspector/ws/route.ts");
function makeRequest(upgrade = "websocket", clientKey = "dGhlIHNhbXBsZSBub25jZQ=="): Request {
return new Request("http://localhost/api/tools/traffic-inspector/ws", {
@@ -33,14 +31,14 @@ function makeRequest(upgrade = "websocket", clientKey = "dGhlIHNhbXBsZSBub25jZQ=
}
test.after(() => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("ws/route: rejects non-WebSocket GET with 426", async () => {
const req = new Request("http://localhost/api/tools/traffic-inspector/ws");
const res = await wsRoute.GET(req);
assert.equal(res.status, 426);
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
assert.ok(body.error.message.includes("Upgrade"), "should mention upgrade");
});
@@ -57,7 +55,7 @@ test("ws/route: rejects when no raw socket available with 500", async () => {
// No `.socket` property injected — Next.js standalone would attach it
const res = await wsRoute.GET(req);
assert.equal(res.status, 500);
const body = await res.json() as { error: { message: string } };
const body = (await res.json()) as { error: { message: string } };
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
});

View File

@@ -81,7 +81,7 @@ function productionShapedSynchronousRefresh() {
}
test.after(() => {
fs.rmSync(CACHE_DATA_DIR, { recursive: true, force: true });
fs.rmSync(CACHE_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("the /v1/models route wires Next after() as its response-flush-safe scheduler", () => {
@@ -152,7 +152,7 @@ test("an external client receives the stale body before synchronous refresh fini
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "EPERM") {
t.skip("sandbox does not permit opening HTTP listener sockets");
fs.rmSync(socketDir, { recursive: true, force: true });
fs.rmSync(socketDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
return;
}
throw error;
@@ -177,7 +177,7 @@ test("an external client receives the stale body before synchronous refresh fini
);
} finally {
await close(server);
fs.rmSync(socketDir, { recursive: true, force: true });
fs.rmSync(socketDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
catalogCache.__resetCatalogBuilderRunsForTest();
}
});

View File

@@ -112,7 +112,9 @@ test(
{ skip: REAL_FFMPEG_SKIP },
async (context) => {
const directory = await mkdtemp(join(tmpdir(), "omniroute-video-sampler-fixtures-"));
context.after(async () => rm(directory, { force: true, recursive: true }));
context.after(async () =>
rm(directory, { force: true, recursive: true, maxRetries: 5, retryDelay: 100 })
);
const rapidCuts = await createRapidEdgeCutFixture(directory);
await context.test("rapid cuts near both ends retain coverage within the cap", async () => {

View File

@@ -49,7 +49,7 @@ async function readTransformed(chunks: string[], options: object): Promise<strin
test.after(() => {
core.resetDbInstance();
if (fs.existsSync(TEST_DATA_DIR)) {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
}
});
@@ -59,7 +59,8 @@ function leakedUpstreamControlLines(output: string): string[] {
.trim()
.split("\n")
.filter(
(l) => /^(?:id:|event:|retry:)/i.test(l) || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))
(l) =>
/^(?:id:|event:|retry:)/i.test(l) || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))
);
}
@@ -166,7 +167,9 @@ test("#10017: OpenAI Responses passthrough KEEPS event framing (regression guard
"Responses output_text.delta event framing must be preserved"
);
assert.ok(
!lines.some((l) => l.startsWith("id:") || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))),
!lines.some(
(l) => l.startsWith("id:") || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))
),
"Responses passthrough must still strip id:/comment control lines"
);
});
@@ -191,5 +194,8 @@ test("#10017: Claude Messages passthrough KEEPS event framing", async () => {
const lines = text.trim().split("\n");
assert.ok(lines.includes("event: message_start"), "Claude event framing must be preserved");
assert.ok(lines.includes("event: content_block_delta"), "Claude delta event framing must be preserved");
});
assert.ok(
lines.includes("event: content_block_delta"),
"Claude delta event framing must be preserved"
);
});

View File

@@ -33,13 +33,13 @@ const NODE_B_ID = `openai-compatible-chat-558d982b-0000-4000-8000-000000000000`;
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
async function seedNode() {

View File

@@ -37,7 +37,7 @@ async function resetStorage() {
globalThis.fetch = originalFetch;
apiKeysDb.resetApiKeyState();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
}
@@ -68,7 +68,7 @@ test.after(() => {
globalThis.fetch = originalFetch;
apiKeysDb.resetApiKeyState();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("#10197 v1 image edit POST forwards built-in openrouter edits to the unified Image API", async () => {
@@ -95,10 +95,14 @@ test("#10197 v1 image edit POST forwards built-in openrouter edits to the unifie
else if (raw instanceof Uint8Array) hitBody = Buffer.from(raw).toString("utf8");
else if (raw instanceof ArrayBuffer) hitBody = Buffer.from(raw).toString("utf8");
else if (raw && typeof (raw as { arrayBuffer?: unknown }).arrayBuffer === "function") {
hitBody = Buffer.from(await (raw as { arrayBuffer(): Promise<ArrayBuffer> }).arrayBuffer()).toString("utf8");
hitBody = Buffer.from(
await (raw as { arrayBuffer(): Promise<ArrayBuffer> }).arrayBuffer()
).toString("utf8");
}
return new Response(
JSON.stringify({ data: [{ b64_json: Buffer.from([0x89, 0x50, 0x4e, 0x47]).toString("base64") }] }),
JSON.stringify({
data: [{ b64_json: Buffer.from([0x89, 0x50, 0x4e, 0x47]).toString("base64") }],
}),
{ status: 200, headers: { "content-type": "application/json" } }
);
};

View File

@@ -18,7 +18,7 @@ const SECRET = "sk-live-PROBE-10313-SUPER-SECRET-TOKEN";
test.beforeEach(() => {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
});
@@ -26,7 +26,7 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
function captureMapKeys(): { keys: string[]; restore: () => void } {
@@ -118,14 +118,26 @@ test("cache keys embed the sha256 digest of the secret, never the raw secret (#1
// The hashed fingerprint, not the raw secret, rides in the cache keys.
const keysWithDigestA = catalogKeys.filter((k) => k.includes(digestA));
const keysWithDigestB = catalogKeys.filter((k) => k.includes(digestB));
assert.ok(keysWithDigestA.length > 0, `expected a cache key embedding the fingerprint of A: ${catalogKeys.join(",")}`);
assert.ok(keysWithDigestB.length > 0, `expected a cache key embedding the fingerprint of B: ${catalogKeys.join(",")}`);
assert.ok(
keysWithDigestA.length > 0,
`expected a cache key embedding the fingerprint of A: ${catalogKeys.join(",")}`
);
assert.ok(
keysWithDigestB.length > 0,
`expected a cache key embedding the fingerprint of B: ${catalogKeys.join(",")}`
);
// Raw secrets must never appear (issue #10313 root cause).
assert.ok(!catalogKeys.some((k) => k.includes(rawA) || k.includes(rawB)));
// Identical secrets ⇒ identical key (memoized reuse); different ⇒ distinct.
assert.ok(keysWithDigestA.every((k) => k === keysWithDigestA[0]), "all A keys must be identical");
assert.ok(keysWithDigestB.every((k) => k === keysWithDigestB[0]), "all B keys must be identical");
assert.ok(
keysWithDigestA.every((k) => k === keysWithDigestA[0]),
"all A keys must be identical"
);
assert.ok(
keysWithDigestB.every((k) => k === keysWithDigestB[0]),
"all B keys must be identical"
);
assert.notEqual(keysWithDigestA[0], keysWithDigestB[0]);
});
});

View File

@@ -27,17 +27,19 @@ const { handleEmbedding } = await import("../../open-sse/handlers/embeddings.ts"
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
function readConnectionRow(connId: string) {
const db = core.getDbInstance() as unknown as {
prepare: (sql: string) => {
get: (id: string) => {
test_status: unknown;
rate_limited_until: unknown;
last_error_type: unknown;
} | undefined;
get: (id: string) =>
| {
test_status: unknown;
rate_limited_until: unknown;
last_error_type: unknown;
}
| undefined;
};
};
return db
@@ -100,4 +102,4 @@ test("embed 402 marks the connection terminal credits_exhausted (stops re-select
} finally {
globalThis.fetch = originalFetch;
}
});
});

View File

@@ -79,7 +79,7 @@ test.before(async () => {
test.after(() => {
proxyServer?.close();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("#7993 getProviderCredentials('opencode-zen') hydrates the proxy saved under the sibling 'opencode' connection", async () => {

View File

@@ -20,13 +20,13 @@ const auth = await import("../../src/sse/services/auth.ts");
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("BUG #8200: single perplexity-web 401 (cookie expiry) does not terminal-expire the only connection", async () => {

View File

@@ -29,9 +29,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const routeModule = await import("../../src/app/api/v1/providers/[provider]/models/route.ts");
const { isCompatibleProviderConnectionId } = await import(
"../../src/shared/utils/compatibleProviderId.ts"
);
const { isCompatibleProviderConnectionId } =
await import("../../src/shared/utils/compatibleProviderId.ts");
const { getProviderDisplayName } = await import("../../src/lib/display/names.ts");
function makeRequest(provider: string) {
@@ -50,7 +49,7 @@ test.beforeEach(() => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
const UUID = "02669115-2545-4896-b003-cb4dac09d441";
@@ -105,10 +104,7 @@ test("GET /v1/providers/:provider/models still rejects unrelated look-alike pref
});
test("getProviderDisplayName simplifies all 4 generated compatible id shapes", () => {
assert.equal(
getProviderDisplayName("openai-compatible-chat-" + UUID),
"Compatible (openai)"
);
assert.equal(getProviderDisplayName("openai-compatible-chat-" + UUID), "Compatible (openai)");
assert.equal(
getProviderDisplayName("openai-compatible-responses-" + UUID),
"Compatible (openai)"

View File

@@ -39,7 +39,7 @@ const CONFIGURED_PREFIX = "pix4k-talk";
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
}
@@ -50,7 +50,7 @@ test.beforeEach(async () => {
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("#8327: synced models on a compatible provider node expose the configured prefix as owned_by, not the raw UUID", async () => {
@@ -361,10 +361,7 @@ test("#9416: provider with configured prefix still uses the configured prefix (r
// Must still use the configured prefix, NOT slugified name
const entry = body.data.find((m) => m.id === `${CONFIGURED_PREFIX}/glm-5.2`);
assert.ok(
entry,
`expected entry with configured prefix "${CONFIGURED_PREFIX}/glm-5.2"`
);
assert.ok(entry, `expected entry with configured prefix "${CONFIGURED_PREFIX}/glm-5.2"`);
assert.equal(entry!.owned_by, CONFIGURED_PREFIX);
assert.notEqual(entry!.owned_by, "pix4k-talk-probe"); // not slugified
});

View File

@@ -96,7 +96,7 @@ test.after(() => {
clearModelsDevCapabilities();
settingsDb.clearAllLKGP();
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
if (ORIGINAL_DATA_DIR === undefined) {
delete process.env.DATA_DIR;
} else {
@@ -153,7 +153,11 @@ test(
[],
"vision-incapable rr-blind must never receive the image_url body, even as a last-resort fallback"
);
assert.notEqual(result.status, 200, "must not silently succeed via the vision-incapable target");
assert.notEqual(
result.status,
200,
"must not silently succeed via the vision-incapable target"
);
}
);

View File

@@ -36,7 +36,7 @@ const originalGetCookieStore = loginRoute.authRouteInternals.getCookieStore;
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
process.env.INITIAL_PASSWORD = "correct-secret-8336";
}
@@ -52,7 +52,7 @@ test.afterEach(() => {
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
if (ORIGINAL_INITIAL_PASSWORD === undefined) {
delete process.env.INITIAL_PASSWORD;
} else {

View File

@@ -44,7 +44,7 @@ before(() => {
after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
if (originalDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = originalDataDir;
});

View File

@@ -27,13 +27,13 @@ async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.after(async () => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("issue #8385: global perKeyProxyEnabled=false must override a connection's per_key_proxy_enabled=1", async () => {

View File

@@ -18,17 +18,15 @@ import path from "node:path";
const tmpDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-8388-"));
process.env.DATA_DIR = tmpDataDir;
const { compressionSettingsUpdateSchema } = await import(
"../../src/shared/validation/compressionConfigSchemas.ts"
);
const { compressionSettingsUpdateSchema } =
await import("../../src/shared/validation/compressionConfigSchemas.ts");
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
const { getCompressionSettings, updateCompressionSettings } = await import(
"../../src/lib/db/compression.ts"
);
const { getCompressionSettings, updateCompressionSettings } =
await import("../../src/lib/db/compression.ts");
test.after(() => {
resetDbInstance();
fs.rmSync(tmpDataDir, { recursive: true, force: true });
fs.rmSync(tmpDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("#8388: PUT body carrying ccr detail (minChars/retrievalRampFactor) is ACCEPTED by the schema", () => {

View File

@@ -26,7 +26,7 @@ test(
t.after(async () => {
loaded?.cleanup();
await rm(pluginDir, { recursive: true, force: true });
await rm(pluginDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
await writeFile(
@@ -98,23 +98,20 @@ export async function onRequest(ctx) {
}
);
test(
"loadPlugin no longer spawns the plugin host with stdout/stderr fully ignored",
async () => {
const source = await readFile(
join(import.meta.dirname, "../../src/lib/plugins/loader.ts"),
"utf-8"
);
// The original bug: stdio: ["ignore", "ignore", "ignore", "ipc"] discards
// stdout (fd 1) and stderr (fd 2) at the OS level unconditionally.
assert.doesNotMatch(
source,
/stdio:\s*\[\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ipc["']\s*\]/,
"loader.ts must not spawn the plugin host with stdout+stderr both set to " +
"'ignore' — that silently discards all plugin console.log/console.error output"
);
}
);
test("loadPlugin no longer spawns the plugin host with stdout/stderr fully ignored", async () => {
const source = await readFile(
join(import.meta.dirname, "../../src/lib/plugins/loader.ts"),
"utf-8"
);
// The original bug: stdio: ["ignore", "ignore", "ignore", "ipc"] discards
// stdout (fd 1) and stderr (fd 2) at the OS level unconditionally.
assert.doesNotMatch(
source,
/stdio:\s*\[\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ipc["']\s*\]/,
"loader.ts must not spawn the plugin host with stdout+stderr both set to " +
"'ignore' — that silently discards all plugin console.log/console.error output"
);
});
// Secondary #8395 finding: runPluginOnResponseHook was only wired into chatCore.ts's
// STREAMING success path — the non-streaming (stream:false) JSON-return branch
@@ -131,7 +128,9 @@ test("chatCore.ts calls runPluginOnResponseHook from both the non-streaming and
"utf-8"
);
const nonStreamingReturnIndex = source.indexOf("buildNonStreamingJsonResponse(translatedResponse");
const nonStreamingReturnIndex = source.indexOf(
"buildNonStreamingJsonResponse(translatedResponse"
);
const hookCallNeedle = "await runPluginOnResponseHook({";
const hookCallIndex = source.indexOf(hookCallNeedle);
const secondHookCallIndex = source.indexOf(hookCallNeedle, hookCallIndex + 1);

View File

@@ -35,10 +35,17 @@ const quotaCache = await import("../../src/domain/quotaCache.ts");
test.after(() => {
coreDb.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
const COLD_WINDOWS = ["Bonus Pack 1", "Bonus Pack 2", "Bonus Pack 3", "Bonus Pack 4", "Weekly", "Daily"];
const COLD_WINDOWS = [
"Bonus Pack 1",
"Bonus Pack 2",
"Bonus Pack 3",
"Bonus Pack 4",
"Weekly",
"Daily",
];
const HOT_WINDOWS = ["Monthly", "Bonus Pack 5", "Bonus Pack 6"];
test("#8431 idle healthy windows survive rehydration even when hot windows accumulate >200 rows", () => {

Some files were not shown because too many files have changed in this diff Show More