mirror of
https://github.com/NVIDIA/NemoClaw.git
synced 2026-07-03 03:37:16 +00:00
test: run CLI tests against source (#5904)
## Summary Make ordinary CLI tests execute the TypeScript source graph instead of potentially stale `dist/` output, while preserving a small clean-built lane for genuine compiled-package contracts. Add executable boundaries so the migration cannot silently regress or leave tests assigned to overlapping Vitest projects. ## Changes - Migrate CLI unit and integration tests from compiled internals to `src/`, including spawned fixture programs. - Move compiled-artifact assertions into `test/package-contract/` and clean-build CLI/plugin artifacts before that lane runs. - Split Vitest into explicit, disjoint source, integration, installer, package, plugin, support, and live-E2E projects. - Add `test:fast`, `test:integration`, `test:package`, and `test:live-e2e` commands. - Add repository checks that reject ordinary test imports from `dist/` and derive overlapping project membership from Vitest itself. - Collect CLI coverage against TypeScript source, remove the old compiled-coverage signal, and run package contracts in the build/typecheck gate. - Tighten three existing test-file size exceptions after the migration reduced those files. - Document the test lanes and source/package boundary for contributors. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [x] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [x] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [ ] Tests not applicable — justification: - [ ] Docs updated for user-facing behavior changes - [x] Docs not applicable — justification: no user-facing runtime behavior changed; contributor guidance was updated in `AGENTS.md` and `CONTRIBUTING.md`. - [x] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [x] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: production source is unchanged; migrated tests retain their assertions and the complete non-live suite passes against the new source graph. - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Verification - [x] PR description includes the DCO sign-off declaration and every commit appears as `Verified` in GitHub - [x] Git hooks passed during commit and push, or `npx prek run --from-ref main --to-ref HEAD` passes - [x] Targeted tests pass for changed behavior - [x] Full `npm test` passes (broad runtime changes only) - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) - [ ] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) Verification details on head `88360d617`: - `VITEST_MAX_WORKERS=16 npm test`: 808 files passed, 2 skipped; 9,507 tests passed, 18 skipped, after clean CLI/plugin builds. - `VITEST_MAX_WORKERS=16 npm run test:fast`: 505 files and 5,592 tests passed with `dist/` absent. - The clean-built package-contract project passed 13 files and 259 compiled-package assertions. - The source/package import boundary and disjoint Vitest-project guards passed in `static-checks`. - All five source-coverage shards passed; `cli-tests` merged coverage against `src/**/*.ts` and passed the coverage ratchet; required aggregate `checks` passed. - Pre-push CLI typecheck and version/tag synchronization passed. - CodeQL, both PR review advisors, all self-hosted E2Es, macOS E2E, WSL E2E, and codebase growth guardrails passed. --- Signed-off-by: Carlos Villela <cvillela@nvidia.com> --------- Signed-off-by: Carlos Villela <cvillela@nvidia.com>
This commit is contained in:
parent
a95e851d15
commit
7d6daa2b93
321 changed files with 2915 additions and 2666 deletions
|
|
@ -27,6 +27,10 @@ runs:
|
|||
shell: bash
|
||||
run: npm run build:cli
|
||||
|
||||
- name: Verify compiled package contracts
|
||||
shell: bash
|
||||
run: npx vitest run --project package-contract
|
||||
|
||||
- name: Typecheck CLI + tests
|
||||
shell: bash
|
||||
run: npm run typecheck:cli
|
||||
|
|
|
|||
|
|
@ -78,16 +78,6 @@ runs:
|
|||
- name: Merge CLI coverage
|
||||
shell: bash
|
||||
run: |
|
||||
coverage_include="dist/lib/**/*.js"
|
||||
# Keep the merge denominator aligned with the shard command during
|
||||
# the PR #5904 transition. That PR removes this probe after landing.
|
||||
integration_probe_output=""
|
||||
if integration_probe_output=$(npx vitest list --project integration --filesOnly 2>&1); then
|
||||
coverage_include="src/**/*.ts"
|
||||
elif ! grep -Fqx 'Error: No projects matched the filter "integration".' <<<"$integration_probe_output"; then
|
||||
printf '%s\n' "$integration_probe_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
npx vitest --mergeReports .vitest-reports \
|
||||
--reporter=json \
|
||||
--outputFile.json=coverage/cli/vitest-results.json \
|
||||
|
|
@ -97,7 +87,7 @@ runs:
|
|||
--coverage.reporter=cobertura \
|
||||
--coverage.reportsDirectory=coverage/cli \
|
||||
--coverage.include="bin/**/*.js" \
|
||||
--coverage.include="$coverage_include" \
|
||||
--coverage.include="src/**/*.ts" \
|
||||
--coverage.exclude="test/**/*.js" \
|
||||
--coverage.exclude="test/**/*.ts"
|
||||
npx tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage"
|
||||
|
|
|
|||
|
|
@ -66,21 +66,7 @@ runs:
|
|||
node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"
|
||||
npm run build:cli
|
||||
npx tsx scripts/check-dist-sourcemaps.ts dist
|
||||
coverage_include="dist/lib/**/*.js"
|
||||
additional_projects=()
|
||||
# Bootstrap the source-test migration without executing action code
|
||||
# from the PR branch. PR #5904 removes this probe once `integration`
|
||||
# is part of the trusted base configuration.
|
||||
integration_probe_output=""
|
||||
if integration_probe_output=$(npx vitest list --project integration --filesOnly 2>&1); then
|
||||
additional_projects+=(--project integration)
|
||||
coverage_include="src/**/*.ts"
|
||||
elif ! grep -Fqx 'Error: No projects matched the filter "integration".' <<<"$integration_probe_output"; then
|
||||
printf '%s\n' "$integration_probe_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
npx vitest run --project cli \
|
||||
"${additional_projects[@]}" \
|
||||
npx vitest run --project cli --project integration \
|
||||
--shard="${CLI_SHARD}/${CLI_SHARD_COUNT}" \
|
||||
--reporter=github-actions \
|
||||
--reporter=blob \
|
||||
|
|
@ -89,7 +75,7 @@ runs:
|
|||
--coverage.reporter=json-summary \
|
||||
--coverage.reportsDirectory="coverage/cli/shard-${CLI_SHARD}" \
|
||||
--coverage.include="bin/**/*.js" \
|
||||
--coverage.include="$coverage_include" \
|
||||
--coverage.include="src/**/*.ts" \
|
||||
--coverage.exclude="test/**/*.js" \
|
||||
--coverage.exclude="test/**/*.ts"
|
||||
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ repos:
|
|||
- id: test-cli
|
||||
name: Test (CLI)
|
||||
entry: >-
|
||||
bash -c 'node -e "require(\"node:fs\").rmSync(\"dist\", { recursive: true, force: true })" && npm run build:cli && npx tsx scripts/check-dist-sourcemaps.ts dist && npx vitest run --project cli --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/cli --coverage.include="bin/**/*.js" --coverage.include="dist/lib/**/*.js" --coverage.exclude="test/**/*.js" --coverage.exclude="test/**/*.ts" && npx tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage"'
|
||||
bash -c 'node -e "require(\"node:fs\").rmSync(\"dist\", { recursive: true, force: true })" && npm run build:cli && npx tsx scripts/check-dist-sourcemaps.ts dist && npx vitest run --project cli --project integration --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary --coverage.reportsDirectory=coverage/cli --coverage.include="bin/**/*.js" --coverage.include="src/**/*.ts" --coverage.exclude="test/**/*.js" --coverage.exclude="test/**/*.ts" && npx tsx scripts/check-coverage-ratchet.ts coverage/cli/coverage-summary.json ci/coverage-threshold-cli.json "CLI coverage"'
|
||||
language: system
|
||||
pass_filenames: false
|
||||
files: ^(bin/|src/.*\.(ts|tsx|js|mjs|cjs)$|test/.*\.(ts|tsx|js|mjs|cjs)$)
|
||||
|
|
|
|||
19
AGENTS.md
19
AGENTS.md
|
|
@ -45,6 +45,10 @@ Package-specific guides:
|
|||
| Build plugin | `cd nemoclaw && npm run build` |
|
||||
| Watch mode | `cd nemoclaw && npm run dev` |
|
||||
| Run all tests | `npm test` |
|
||||
| Run fast source tests | `npm run test:fast` |
|
||||
| Run integration tests | `npm run test:integration` |
|
||||
| Run package contracts | `npm run test:package` |
|
||||
| Run live E2E scenarios | `npm run test:live-e2e` |
|
||||
| Run plugin tests | `cd nemoclaw && npm test` |
|
||||
| Run all linters | `make check` |
|
||||
| Run all hooks manually | `npx prek run --all-files` |
|
||||
|
|
@ -66,16 +70,23 @@ The `bin/` directory uses CommonJS intentionally for the launcher and a few comp
|
|||
|
||||
### Testing Strategy
|
||||
|
||||
Tests are organized into three Vitest projects defined in `vitest.config.ts`:
|
||||
Tests are organized into disjoint Vitest projects defined in `vitest.config.ts`:
|
||||
|
||||
1. **`cli`** — `test/**/*.test.{js,ts}` — integration tests for CLI behavior
|
||||
2. **`plugin`** — `nemoclaw/src/**/*.test.ts` — unit tests co-located with source
|
||||
3. **`e2e-branch-validation`** — `test/e2e/brev-e2e.test.ts` — validates a branch from source on ephemeral Brev instance (requires `BREV_API_TOKEN`)
|
||||
1. **`cli`** — `src/**/*.test.ts` — CLI unit tests importing source
|
||||
2. **`integration`** — `test/**/*.test.{js,ts}` — root integration tests importing source; excludes the explicit lanes below
|
||||
3. **`installer-integration`** — installer tests that spawn real `install.sh` processes
|
||||
4. **`package-contract`** — `test/package-contract/**/*.test.ts` — the only non-live lane that imports compiled CLI/plugin artifacts
|
||||
5. **`plugin`** — `nemoclaw/src/**/*.test.ts` — plugin unit tests co-located with source
|
||||
6. **`e2e-vitest-support`** — fast tests for the E2E fixture/support layer
|
||||
7. **`e2e-scenarios-live`** — opt-in live scenarios that mutate real external state
|
||||
8. **`e2e-branch-validation`** — opt-in validation on an ephemeral Brev instance
|
||||
|
||||
When writing tests:
|
||||
|
||||
- Root-level tests (`test/`) use ESM imports
|
||||
- Plugin tests use TypeScript and are co-located with their source files
|
||||
- Import CLI source from ordinary tests. Put genuine compiled-artifact assertions under `test/package-contract/`.
|
||||
- Keep project globs disjoint; `npm run test:projects:check` derives membership from Vitest and rejects overlap.
|
||||
- Mock external dependencies; don't call real NVIDIA APIs in unit tests
|
||||
- E2E tests run on ephemeral Brev cloud instances
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,11 @@ These are the primary `make` and `npm` targets for day-to-day development:
|
|||
| `make lint` | Same as `make check` |
|
||||
| `make format` | Auto-format TypeScript and Python source |
|
||||
| `npm run typecheck:cli` | Type-check CLI TypeScript using `tsconfig.cli.json` (`bin/`, `scripts/`, `src/`, `test/`, `nemoclaw-blueprint/scripts/`) |
|
||||
| `npm test` | Run root-level tests (`test/*.test.js`) |
|
||||
| `npm test` | Build package artifacts and run every non-live Vitest project |
|
||||
| `npm run test:fast` | Clean `dist/` and run source CLI, plugin, and E2E-support tests |
|
||||
| `npm run test:integration` | Clean-build the CLI and run root integration and installer tests |
|
||||
| `npm run test:package` | Clean-build CLI/plugin artifacts and run compiled-package contracts |
|
||||
| `npm run test:live-e2e` | Opt into live E2E scenarios (mutates real external state) |
|
||||
| `cd nemoclaw && npm test` | Run plugin unit tests (Vitest) |
|
||||
| `npm run docs` | Validate Fern documentation with the pinned Fern CLI version |
|
||||
| `npm run docs:live` | Serve Fern docs locally with auto-rebuild |
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
"legacyMaxLines": {
|
||||
"nemoclaw/src/commands/migration-state.test.ts": 1566,
|
||||
"src/lib/inference/nim.test.ts": 2068,
|
||||
"src/lib/onboard/preflight.test.ts": 1905,
|
||||
"src/lib/onboard/preflight.test.ts": 1904,
|
||||
"test/channels-add-preset.test.ts": 1871,
|
||||
"test/generate-openclaw-config.test.ts": 1984,
|
||||
"test/install-preflight.test.ts": 4006,
|
||||
"test/nemoclaw-start.test.ts": 5043,
|
||||
"test/onboard-messaging.test.ts": 2062,
|
||||
"test/onboard-selection.test.ts": 6888,
|
||||
"test/onboard-selection.test.ts": 6867,
|
||||
"test/onboard.test.ts": 4774,
|
||||
"test/policies.test.ts": 2753
|
||||
"test/policies.test.ts": 2489
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
package.json
10
package.json
|
|
@ -20,8 +20,13 @@
|
|||
},
|
||||
"scripts": {
|
||||
"preinstall": "node scripts/check-node-version.js",
|
||||
"test": "vitest run",
|
||||
"coverage:cli:dist-signal": "tsx scripts/coverage-cli-dist-signal.ts",
|
||||
"test": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project cli --project integration --project installer-integration --project package-contract --project plugin --project e2e-vitest-support",
|
||||
"test:fast": "npm run clean:cli && vitest run --project cli --project plugin --project e2e-vitest-support",
|
||||
"test:integration": "npm run clean:cli && npm run build:cli && vitest run --project integration --project installer-integration",
|
||||
"test:package": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project package-contract",
|
||||
"test:live-e2e": "NEMOCLAW_RUN_E2E_SCENARIOS=1 vitest run --project e2e-scenarios-live",
|
||||
"test:imports:check": "tsx scripts/checks/no-test-dist-imports.ts",
|
||||
"test:projects:check": "tsx scripts/checks/vitest-project-overlap.ts",
|
||||
"check": "npx prek run --all-files",
|
||||
"checks": "tsx scripts/checks/run.ts",
|
||||
"lint": "npx @biomejs/biome lint . && npm run checks",
|
||||
|
|
@ -33,6 +38,7 @@
|
|||
"check:installer-hash": "bash scripts/check-installer-hash.sh",
|
||||
"typecheck": "tsc -p jsconfig.json",
|
||||
"build:cli": "tsc -p tsconfig.src.json && node dist/lib/cli/generate-oclif-metadata-manifest.js && if find nemoclaw-blueprint/scripts -name '*.ts' -print -quit | grep -q .; then tsc -p nemoclaw-blueprint/tsconfig.json; fi",
|
||||
"clean:cli": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
||||
"typecheck:cli": "tsc -p tsconfig.cli.json",
|
||||
"validate:configs": "tsx scripts/validate-configs.ts",
|
||||
"type-safety:hotspots": "tsx scripts/type-safety-hotspots.ts",
|
||||
|
|
|
|||
187
scripts/checks/no-test-dist-imports.ts
Normal file
187
scripts/checks/no-test-dist-imports.ts
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import ts from "typescript";
|
||||
|
||||
export type Violation = { file: string; line: number; detail: string };
|
||||
|
||||
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const SKIP_DIRS = new Set([".git", "coverage", "dist", "node_modules"]);
|
||||
// These tests intentionally construct fake dist/lib trees; they do not load
|
||||
// repository build output. The self-audit below prevents this list growing or
|
||||
// retaining an exemption after the fixture no longer needs one.
|
||||
const FIXTURE_EXCLUSIONS = new Set([
|
||||
"test/dist-sourcemaps.test.ts",
|
||||
"test/install-preflight.test.ts",
|
||||
"test/stale-dist-check.test.ts",
|
||||
]);
|
||||
const EXCLUDED_PREFIXES = [
|
||||
// Live/branch E2E validates installed artifacts rather than unit-test imports.
|
||||
"test/e2e/",
|
||||
"test/e2e-scenario/live/",
|
||||
// This is the sole non-live lane allowed to import compiled package artifacts.
|
||||
"test/package-contract/",
|
||||
];
|
||||
|
||||
function repoPath(absolutePath: string): string {
|
||||
return path.relative(REPO_ROOT, absolutePath).split(path.sep).join("/");
|
||||
}
|
||||
|
||||
export function isScannedTestPath(relativePath: string): boolean {
|
||||
if (FIXTURE_EXCLUSIONS.has(relativePath)) return false;
|
||||
if (EXCLUDED_PREFIXES.some((prefix) => relativePath.startsWith(prefix))) return false;
|
||||
if (relativePath.startsWith("src/")) return /\.(?:test|spec)\.[cm]?[jt]sx?$/.test(relativePath);
|
||||
return relativePath.startsWith("test/") && /\.[cm]?[jt]sx?$/.test(relativePath);
|
||||
}
|
||||
|
||||
function isScannedTestFile(absolutePath: string): boolean {
|
||||
return isScannedTestPath(repoPath(absolutePath));
|
||||
}
|
||||
|
||||
function* walk(directory: string): Generator<string> {
|
||||
if (!existsSync(directory)) return;
|
||||
for (const entry of readdirSync(directory)) {
|
||||
if (SKIP_DIRS.has(entry)) continue;
|
||||
const absolutePath = path.join(directory, entry);
|
||||
const stats = statSync(absolutePath);
|
||||
if (stats.isDirectory()) yield* walk(absolutePath);
|
||||
else if (stats.isFile() && isScannedTestFile(absolutePath)) yield absolutePath;
|
||||
}
|
||||
}
|
||||
|
||||
function isCompiledInternalSpecifier(specifier: string): boolean {
|
||||
const normalized = specifier.replaceAll("\\", "/");
|
||||
return (
|
||||
/(^|\/)dist\/(?:lib|commands)(?:\/|$)/.test(normalized) ||
|
||||
/(^|\/)dist\/nemoclaw(?:\.js)?$/.test(normalized)
|
||||
);
|
||||
}
|
||||
|
||||
export function findCompiledInternalViolations(file: string, source: string): Violation[] {
|
||||
const sourceFile = ts.createSourceFile(
|
||||
file,
|
||||
source,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
file.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
|
||||
);
|
||||
const violations: Violation[] = [];
|
||||
|
||||
function add(node: ts.Node, detail: string): void {
|
||||
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
||||
violations.push({ file, line: position.line + 1, detail });
|
||||
}
|
||||
|
||||
function checkSpecifier(node: ts.Node, specifier: string): void {
|
||||
if (isCompiledInternalSpecifier(specifier)) {
|
||||
add(node, `imports compiled CLI internals from ${JSON.stringify(specifier)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function visit(node: ts.Node): void {
|
||||
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
|
||||
checkSpecifier(node.moduleSpecifier, node.moduleSpecifier.text);
|
||||
} else if (
|
||||
ts.isExportDeclaration(node) &&
|
||||
node.moduleSpecifier &&
|
||||
ts.isStringLiteralLike(node.moduleSpecifier)
|
||||
) {
|
||||
checkSpecifier(node.moduleSpecifier, node.moduleSpecifier.text);
|
||||
} else if (ts.isCallExpression(node)) {
|
||||
const isRequire = ts.isIdentifier(node.expression) && node.expression.text === "require";
|
||||
const isDynamicImport = node.expression.kind === ts.SyntaxKind.ImportKeyword;
|
||||
const isRequireResolve =
|
||||
ts.isPropertyAccessExpression(node.expression) &&
|
||||
ts.isIdentifier(node.expression.expression) &&
|
||||
node.expression.expression.text === "require" &&
|
||||
node.expression.name.text === "resolve";
|
||||
const firstArgument = node.arguments[0];
|
||||
if (
|
||||
(isRequire || isDynamicImport || isRequireResolve) &&
|
||||
firstArgument &&
|
||||
ts.isStringLiteralLike(firstArgument)
|
||||
) {
|
||||
checkSpecifier(firstArgument, firstArgument.text);
|
||||
}
|
||||
|
||||
const isPathBuilder =
|
||||
ts.isPropertyAccessExpression(node.expression) &&
|
||||
ts.isIdentifier(node.expression.expression) &&
|
||||
node.expression.expression.text === "path" &&
|
||||
["join", "resolve"].includes(node.expression.name.text);
|
||||
if (isPathBuilder) {
|
||||
const parts = node.arguments.map((argument) =>
|
||||
ts.isStringLiteralLike(argument) ? argument.text : null,
|
||||
);
|
||||
const distIndex = parts.indexOf("dist");
|
||||
const compiledTarget = distIndex >= 0 ? parts[distIndex + 1] : null;
|
||||
if (compiledTarget === "lib" || compiledTarget === "commands") {
|
||||
add(node, `constructs a path into dist/${compiledTarget}`);
|
||||
} else if (distIndex >= 0 && compiledTarget === "nemoclaw.js") {
|
||||
add(node, "constructs a path to dist/nemoclaw.js");
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
|
||||
visit(sourceFile);
|
||||
for (const match of source.matchAll(/require\([^\n)]*(?:\.\/|\.\.\/)dist\/(?:lib|commands)\//g)) {
|
||||
const position = sourceFile.getLineAndCharacterOfPosition(match.index);
|
||||
violations.push({
|
||||
file,
|
||||
line: position.line + 1,
|
||||
detail: "embeds a compiled-internal require in generated test code",
|
||||
});
|
||||
}
|
||||
return violations.filter(
|
||||
(violation, index, all) =>
|
||||
all.findIndex(
|
||||
(candidate) => candidate.file === violation.file && candidate.line === violation.line,
|
||||
) === index,
|
||||
);
|
||||
}
|
||||
|
||||
function findViolations(absolutePath: string): Violation[] {
|
||||
return findCompiledInternalViolations(repoPath(absolutePath), readFileSync(absolutePath, "utf8"));
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const staleFixtureExclusions = [...FIXTURE_EXCLUSIONS].filter((relativePath) => {
|
||||
const absolutePath = path.join(REPO_ROOT, relativePath);
|
||||
return !existsSync(absolutePath) || findViolations(absolutePath).length === 0;
|
||||
});
|
||||
|
||||
if (staleFixtureExclusions.length > 0) {
|
||||
console.error("Fixture exclusions must exist and still construct a compiled-internal path:");
|
||||
for (const relativePath of staleFixtureExclusions) console.error(` ${relativePath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const violations = [
|
||||
...walk(path.join(REPO_ROOT, "src")),
|
||||
...walk(path.join(REPO_ROOT, "test")),
|
||||
].flatMap(findViolations);
|
||||
|
||||
if (violations.length > 0) {
|
||||
console.error(
|
||||
"Compiled CLI internals may only be imported by the package-contract test project:",
|
||||
);
|
||||
for (const violation of violations) {
|
||||
console.error(` ${violation.file}:${violation.line} ${violation.detail}`);
|
||||
}
|
||||
console.error(
|
||||
"Import src/ instead, or move a genuine compiled-package contract under test/package-contract/.",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Test imports respect the source/package boundary.");
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
||||
main();
|
||||
}
|
||||
|
|
@ -36,6 +36,16 @@ const CHECKS: readonly CheckCommand[] = [
|
|||
command: TSX,
|
||||
args: ["scripts/checks/layer-import-boundaries.ts"],
|
||||
},
|
||||
{
|
||||
name: "no-test-dist-imports",
|
||||
command: TSX,
|
||||
args: ["scripts/checks/no-test-dist-imports.ts"],
|
||||
},
|
||||
{
|
||||
name: "vitest-project-overlap",
|
||||
command: TSX,
|
||||
args: ["scripts/checks/vitest-project-overlap.ts"],
|
||||
},
|
||||
];
|
||||
|
||||
function main(): void {
|
||||
|
|
|
|||
80
scripts/checks/vitest-project-overlap.ts
Normal file
80
scripts/checks/vitest-project-overlap.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
export type ProjectListing = {
|
||||
projects: Set<string>;
|
||||
projectsByFile: Map<string, Set<string>>;
|
||||
};
|
||||
|
||||
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const VITEST = path.join(
|
||||
REPO_ROOT,
|
||||
"node_modules",
|
||||
".bin",
|
||||
process.platform === "win32" ? "vitest.cmd" : "vitest",
|
||||
);
|
||||
|
||||
export function parseProjectListing(output: string): ProjectListing {
|
||||
const projectsByFile = new Map<string, Set<string>>();
|
||||
const projects = new Set<string>();
|
||||
for (const line of output.split(/\r?\n/)) {
|
||||
if (!line.trim()) continue;
|
||||
const match = line.match(/^\[([^\]]+)\]\s+(.+)$/);
|
||||
if (!match) throw new Error(`Could not parse Vitest project listing line: ${line}`);
|
||||
const [, project, file] = match;
|
||||
projects.add(project);
|
||||
const memberships = projectsByFile.get(file) ?? new Set<string>();
|
||||
memberships.add(project);
|
||||
projectsByFile.set(file, memberships);
|
||||
}
|
||||
return { projects, projectsByFile };
|
||||
}
|
||||
|
||||
export function findProjectOverlaps(
|
||||
projectsByFile: ReadonlyMap<string, ReadonlySet<string>>,
|
||||
): Array<[string, ReadonlySet<string>]> {
|
||||
return [...projectsByFile]
|
||||
.filter(([, memberships]) => memberships.size > 1)
|
||||
.sort(([left], [right]) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const result = spawnSync(VITEST, ["list", "--filesOnly"], {
|
||||
cwd: REPO_ROOT,
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
NEMOCLAW_RUN_BRANCH_VALIDATION_E2E: "1",
|
||||
NEMOCLAW_RUN_E2E_SCENARIOS: "1",
|
||||
},
|
||||
});
|
||||
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) {
|
||||
process.stderr.write(result.stderr);
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
|
||||
const { projects, projectsByFile } = parseProjectListing(result.stdout);
|
||||
const overlaps = findProjectOverlaps(projectsByFile);
|
||||
|
||||
if (overlaps.length > 0) {
|
||||
console.error("Vitest files must belong to exactly one project:");
|
||||
for (const [file, memberships] of overlaps) {
|
||||
console.error(` ${file}: ${[...memberships].sort().join(", ")}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Vitest project membership is disjoint (${projectsByFile.size} files across ${projects.size} projects).`,
|
||||
);
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
||||
main();
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
#!/usr/bin/env -S npx tsx
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const DEFAULT_REPORT_DIR = "coverage/cli-dist-signal";
|
||||
const REPO_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
type SpawnResult = {
|
||||
status: number | null;
|
||||
signal?: NodeJS.Signals | null;
|
||||
error?: Error;
|
||||
};
|
||||
|
||||
type SpawnSyncLike = (
|
||||
command: string,
|
||||
args: string[],
|
||||
options: { stdio: "inherit"; env: NodeJS.ProcessEnv },
|
||||
) => SpawnResult;
|
||||
|
||||
type RunCliDistCoverageDeps = {
|
||||
spawn?: SpawnSyncLike;
|
||||
kill?: typeof process.kill;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
repoRoot?: string;
|
||||
};
|
||||
|
||||
export function buildCliDistCoverageArgs(extraArgs: string[] = []): string[] {
|
||||
return [
|
||||
"run",
|
||||
"--project",
|
||||
"cli",
|
||||
"--coverage",
|
||||
"--coverage.provider=v8",
|
||||
"--coverage.reporter=text-summary",
|
||||
"--coverage.reporter=json-summary",
|
||||
"--coverage.reporter=json",
|
||||
`--coverage.reportsDirectory=${DEFAULT_REPORT_DIR}`,
|
||||
"--coverage.reportOnFailure",
|
||||
"--coverage.include=src/**/*.ts",
|
||||
"--coverage.include=dist/**/*.js",
|
||||
"--coverage.include=bin/**/*.js",
|
||||
"--coverage.exclude=**/*.test.ts",
|
||||
"--coverage.exclude=**/*.test.js",
|
||||
"--coverage.exclude=node_modules/**",
|
||||
"--coverage.exclude=nemoclaw/**",
|
||||
...extraArgs,
|
||||
];
|
||||
}
|
||||
|
||||
export function resolveLocalVitestBin(repoRoot = REPO_ROOT): string {
|
||||
const suffix = process.platform === "win32" ? "vitest.cmd" : "vitest";
|
||||
return path.join(repoRoot, "node_modules", ".bin", suffix);
|
||||
}
|
||||
|
||||
export function runCliDistCoverage(
|
||||
extraArgs: string[] = [],
|
||||
deps: RunCliDistCoverageDeps = {},
|
||||
): number | null {
|
||||
const args = buildCliDistCoverageArgs(extraArgs);
|
||||
const result = (deps.spawn ?? spawnSync)(resolveLocalVitestBin(deps.repoRoot), args, {
|
||||
stdio: "inherit",
|
||||
env: deps.env ?? process.env,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
if (result.signal) {
|
||||
(deps.kill ?? process.kill)(process.pid, result.signal);
|
||||
return null;
|
||||
}
|
||||
return result.status ?? 1;
|
||||
}
|
||||
|
||||
export function isDirectExecution(metaUrl: string, argv1: string | undefined): boolean {
|
||||
if (!argv1) return false;
|
||||
return path.resolve(fileURLToPath(metaUrl)) === path.resolve(argv1);
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const exitCode = runCliDistCoverage(process.argv.slice(2));
|
||||
if (exitCode !== null) {
|
||||
process.exitCode = exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDirectExecution(import.meta.url, process.argv[1])) {
|
||||
main();
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import ResourcesCommand from "../../dist/commands/resources.js";
|
||||
import ResourcesCommand from "./resources.js";
|
||||
|
||||
const rootDir = process.cwd();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { runFixCoreDns, runSetupDnsProxy } from "../../../../dist/lib/actions/dns/index.js";
|
||||
import type { CommandResult } from "./index";
|
||||
import { runFixCoreDns, runSetupDnsProxy } from "./index.js";
|
||||
|
||||
function ok(stdout = ""): CommandResult {
|
||||
return { status: 0, stdout, stderr: "" };
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } fr
|
|||
|
||||
import type { OpenShellStateRpcIssue } from "../adapters/openshell/gateway-drift";
|
||||
|
||||
type BackupAll = typeof import("../../../dist/lib/actions/maintenance")["backupAll"];
|
||||
type UpgradeSandboxes =
|
||||
typeof import("../../../dist/lib/actions/upgrade-sandboxes")["upgradeSandboxes"];
|
||||
type BackupAll = typeof import("./maintenance")["backupAll"];
|
||||
type UpgradeSandboxes = typeof import("./upgrade-sandboxes")["upgradeSandboxes"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
|
||||
|
|
@ -57,14 +56,14 @@ describe("gateway drift preflight for maintenance actions", () => {
|
|||
exitSpy = mockExit();
|
||||
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
|
||||
const gatewayDrift = requireDist("../../../dist/lib/adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const registry = requireDist("../../../dist/lib/state/registry.js");
|
||||
const sandboxState = requireDist("../../../dist/lib/state/sandbox.js");
|
||||
const sandboxVersion = requireDist("../../../dist/lib/sandbox/version.js");
|
||||
const upgradeDomain = requireDist("../../../dist/lib/domain/maintenance/upgrade.js");
|
||||
const rebuild = requireDist("../../../dist/lib/actions/sandbox/rebuild.js");
|
||||
const gatewayRuntime = requireDist("../../../dist/lib/gateway-runtime-action.js");
|
||||
const gatewayDrift = requireDist("../adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../adapters/openshell/runtime.js");
|
||||
const registry = requireDist("../state/registry.js");
|
||||
const sandboxState = requireDist("../state/sandbox.js");
|
||||
const sandboxVersion = requireDist("../sandbox/version.js");
|
||||
const upgradeDomain = requireDist("../domain/maintenance/upgrade.js");
|
||||
const rebuild = requireDist("./sandbox/rebuild.js");
|
||||
const gatewayRuntime = requireDist("../gateway-runtime-action.js");
|
||||
|
||||
detectPreflightIssueSpy = vi
|
||||
.spyOn(gatewayDrift, "detectOpenShellStateRpcPreflightIssue")
|
||||
|
|
@ -113,8 +112,8 @@ describe("gateway drift preflight for maintenance actions", () => {
|
|||
vi.spyOn(rebuild, "rebuildSandbox").mockResolvedValue(undefined),
|
||||
);
|
||||
|
||||
({ backupAll } = requireDist("../../../dist/lib/actions/maintenance.js"));
|
||||
({ upgradeSandboxes } = requireDist("../../../dist/lib/actions/upgrade-sandboxes.js"));
|
||||
({ backupAll } = requireDist("./maintenance.js"));
|
||||
({ upgradeSandboxes } = requireDist("./upgrade-sandboxes.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -197,7 +196,7 @@ describe("gateway drift preflight for maintenance actions", () => {
|
|||
});
|
||||
|
||||
it("backup-all skips sandboxes that are not in Ready phase", async () => {
|
||||
const registry = requireDist("../../../dist/lib/state/registry.js");
|
||||
const registry = requireDist("../state/registry.js");
|
||||
(registry.listSandboxes as ReturnType<typeof vi.fn>).mockReturnValue({
|
||||
sandboxes: [
|
||||
{ name: "alpha", provider: "nvidia-prod", model: "nemotron" },
|
||||
|
|
|
|||
|
|
@ -6,11 +6,15 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type ConnectSandbox =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/connect")["connectSandbox"];
|
||||
type ConnectSandbox = typeof import("./connect")["connectSandbox"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const connectModulePath = "../../../../dist/lib/actions/sandbox/connect.js";
|
||||
const connectModulePath = "./connect.js";
|
||||
|
||||
// Warm the CommonJS source graph outside the first test's timeout. Each harness
|
||||
// still reloads the entry module after installing its dependency spies.
|
||||
requireDist(connectModulePath);
|
||||
delete require.cache[requireDist.resolve(connectModulePath)];
|
||||
|
||||
type ConnectHarness = {
|
||||
captureOpenshellSpy: MockInstance;
|
||||
|
|
@ -65,24 +69,18 @@ function createConnectHarness(options: ConnectHarnessOptions = {}): ConnectHarne
|
|||
signal: options.spawnSignal ?? null,
|
||||
} as never)) as never);
|
||||
|
||||
const runtime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const gatewayState = requireDist("../../../../dist/lib/actions/sandbox/gateway-state.js");
|
||||
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
|
||||
const autoPairApproval = requireDist(
|
||||
"../../../../dist/lib/actions/sandbox/auto-pair-approval.js",
|
||||
);
|
||||
const connectVllmPreflight = requireDist(
|
||||
"../../../../dist/lib/actions/sandbox/connect-vllm-preflight.js",
|
||||
);
|
||||
const gatewayFailureClassifier = requireDist(
|
||||
"../../../../dist/lib/actions/sandbox/gateway-failure-classifier.js",
|
||||
);
|
||||
const ollamaProxy = requireDist("../../../../dist/lib/inference/ollama/proxy.js");
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const runtime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
const gatewayState = requireDist("./gateway-state.js");
|
||||
const processRecovery = requireDist("./process-recovery.js");
|
||||
const autoPairApproval = requireDist("./auto-pair-approval.js");
|
||||
const connectVllmPreflight = requireDist("./connect-vllm-preflight.js");
|
||||
const gatewayFailureClassifier = requireDist("./gateway-failure-classifier.js");
|
||||
const ollamaProxy = requireDist("../../inference/ollama/proxy.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
|
||||
vi.spyOn(connectVllmPreflight, "preflightVllmModelEnvOrExit").mockImplementation(() => undefined);
|
||||
vi.spyOn(gatewayState, "ensureLiveSandboxOrExit").mockResolvedValue({
|
||||
|
|
@ -436,7 +434,7 @@ describe("connectSandbox flow", () => {
|
|||
secretBoundaryReason: "raw-secret",
|
||||
},
|
||||
});
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
vi.spyOn(agentRuntime, "getSessionAgent").mockReturnValue({ name: "hermes" });
|
||||
vi.spyOn(agentRuntime, "getAgentDisplayName").mockReturnValue("Hermes");
|
||||
const errorSpy = vi.spyOn(console, "error");
|
||||
|
|
@ -474,7 +472,7 @@ describe("connectSandbox flow", () => {
|
|||
secretBoundaryReason: "raw-secret",
|
||||
},
|
||||
});
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
vi.spyOn(agentRuntime, "getSessionAgent").mockReturnValue({ name: "hermes" });
|
||||
vi.spyOn(agentRuntime, "getAgentDisplayName").mockReturnValue("Hermes");
|
||||
const errorSpy = vi.spyOn(console, "error");
|
||||
|
|
@ -509,7 +507,7 @@ describe("connectSandbox flow", () => {
|
|||
secretBoundaryReason: "inconclusive",
|
||||
},
|
||||
});
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
vi.spyOn(agentRuntime, "getSessionAgent").mockReturnValue({ name: "hermes" });
|
||||
vi.spyOn(agentRuntime, "getAgentDisplayName").mockReturnValue("Hermes");
|
||||
const errorSpy = vi.spyOn(console, "error");
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type DestroySandbox =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/destroy")["destroySandbox"];
|
||||
type DestroySandbox = typeof import("./destroy")["destroySandbox"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const destroyModulePath = "../../../../dist/lib/actions/sandbox/destroy.js";
|
||||
const destroyModulePath = "./destroy.js";
|
||||
|
||||
type DestroyHarness = {
|
||||
cleanupGatewaySpy: MockInstance;
|
||||
|
|
@ -45,19 +44,17 @@ function createDestroyHarness(options: DestroyHarnessOptions = {}): DestroyHarne
|
|||
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
||||
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const runtime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const destroyGateway = requireDist("../../../../dist/lib/actions/sandbox/destroy-gateway.js");
|
||||
const sandboxProviderCleanup = requireDist(
|
||||
"../../../../dist/lib/onboard/sandbox-provider-cleanup.js",
|
||||
);
|
||||
const nim = requireDist("../../../../dist/lib/inference/nim.js");
|
||||
const ollamaProxy = requireDist("../../../../dist/lib/inference/ollama/proxy.js");
|
||||
const tunnelServices = requireDist("../../../../dist/lib/tunnel/services.js");
|
||||
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const timerControl = requireDist("../../../../dist/lib/shields/timer-control.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const runtime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const destroyGateway = requireDist("./destroy-gateway.js");
|
||||
const sandboxProviderCleanup = requireDist("../../onboard/sandbox-provider-cleanup.js");
|
||||
const nim = requireDist("../../inference/nim.js");
|
||||
const ollamaProxy = requireDist("../../inference/ollama/proxy.js");
|
||||
const tunnelServices = requireDist("../../tunnel/services.js");
|
||||
const onboardSession = requireDist("../../state/onboard-session.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
const timerControl = requireDist("../../shields/timer-control.js");
|
||||
|
||||
vi.spyOn(resolve, "resolveOpenshell").mockReturnValue("/usr/bin/openshell");
|
||||
vi.spyOn(sandboxSession, "getActiveSandboxSessions").mockReturnValue({
|
||||
|
|
|
|||
|
|
@ -2,12 +2,8 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
getSandboxDockerHealth,
|
||||
getSandboxDockerRuntime,
|
||||
} from "../../../../dist/lib/actions/sandbox/docker-health";
|
||||
import type { SandboxEntry } from "../../../../dist/lib/state/registry";
|
||||
import type { SandboxEntry } from "../../state/registry";
|
||||
import { getSandboxDockerHealth, getSandboxDockerRuntime } from "./docker-health";
|
||||
|
||||
function fixture({
|
||||
driver = "docker",
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type RunSandboxDoctor =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/doctor")["runSandboxDoctor"];
|
||||
type RunSandboxDoctor = typeof import("./doctor")["runSandboxDoctor"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const doctorModulePath = "../../../../dist/lib/actions/sandbox/doctor.js";
|
||||
const doctorModulePath = "./doctor.js";
|
||||
|
||||
function createDoctorHarness(): {
|
||||
captureHostCommandSpy: MockInstance;
|
||||
|
|
@ -23,29 +22,23 @@ function createDoctorHarness(): {
|
|||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
||||
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const runtime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const agentDefs = requireDist("../../../../dist/lib/agent/defs.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
|
||||
const health = requireDist("../../../../dist/lib/inference/health.js");
|
||||
const dockerDriverPlatform = requireDist(
|
||||
"../../../../dist/lib/onboard/docker-driver-platform.js",
|
||||
);
|
||||
const gatewayBinding = requireDist("../../../../dist/lib/onboard/gateway-binding.js");
|
||||
const sandboxVerificationExec = requireDist(
|
||||
"../../../../dist/lib/onboard/sandbox-verification-exec.js",
|
||||
);
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const shields = requireDist("../../../../dist/lib/shields/index.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const statusCommandDeps = requireDist("../../../../dist/lib/status-command-deps.js");
|
||||
const tunnelServices = requireDist("../../../../dist/lib/tunnel/services.js");
|
||||
const doctorHostCommand = requireDist(
|
||||
"../../../../dist/lib/actions/sandbox/doctor-host-command.js",
|
||||
);
|
||||
const doctorToolScope = requireDist("../../../../dist/lib/actions/sandbox/doctor-tool-scope.js");
|
||||
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const runtime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const agentDefs = requireDist("../../agent/defs.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
|
||||
const health = requireDist("../../inference/health.js");
|
||||
const dockerDriverPlatform = requireDist("../../onboard/docker-driver-platform.js");
|
||||
const gatewayBinding = requireDist("../../onboard/gateway-binding.js");
|
||||
const sandboxVerificationExec = requireDist("../../onboard/sandbox-verification-exec.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const shields = requireDist("../../shields/index.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const statusCommandDeps = requireDist("../../status-command-deps.js");
|
||||
const tunnelServices = requireDist("../../tunnel/services.js");
|
||||
const doctorHostCommand = requireDist("./doctor-host-command.js");
|
||||
const doctorToolScope = requireDist("./doctor-tool-scope.js");
|
||||
const processRecovery = requireDist("./process-recovery.js");
|
||||
|
||||
const getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue({
|
||||
name: "alpha",
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
import type { OpenShellStateRpcIssue } from "../../adapters/openshell/gateway-drift";
|
||||
|
||||
type GatewayStateModule = typeof import("../../../../dist/lib/actions/sandbox/gateway-state");
|
||||
type GatewayStateModule = typeof import("./gateway-state");
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
|
||||
|
|
@ -46,10 +46,10 @@ describe("sandbox gateway state drift guard", () => {
|
|||
exitSpy = mockExit();
|
||||
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
|
||||
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
|
||||
getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue(null);
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ describe("sandbox gateway state drift guard", () => {
|
|||
removeSandboxSpy,
|
||||
);
|
||||
|
||||
gatewayState = requireDist("../../../../dist/lib/actions/sandbox/gateway-state.js");
|
||||
gatewayState = requireDist("./gateway-state.js");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type GatewayStateModule = typeof import("../../../../dist/lib/actions/sandbox/gateway-state");
|
||||
type GatewayStateModule = typeof import("./gateway-state");
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
|
||||
|
|
@ -14,13 +14,13 @@ describe("printGatewayLifecycleHint multi-instance hints", () => {
|
|||
let getSandboxSpy: MockInstance;
|
||||
|
||||
beforeEach(async () => {
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue({
|
||||
name: "instance-a",
|
||||
gatewayName: "nemoclaw-8080",
|
||||
gatewayPort: 8080,
|
||||
});
|
||||
gatewayState = requireDist("../../../../dist/lib/actions/sandbox/gateway-state.js");
|
||||
gatewayState = requireDist("./gateway-state.js");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// Import from compiled dist for parity with the other CLI tests in this project.
|
||||
import {
|
||||
collectGatewayWedgeDiagnostics,
|
||||
sanitizeWedgeLogLine,
|
||||
} from "../../../../dist/lib/actions/sandbox/gateway-wedge-diagnostics";
|
||||
import { collectGatewayWedgeDiagnostics, sanitizeWedgeLogLine } from "./gateway-wedge-diagnostics";
|
||||
|
||||
describe("collectGatewayWedgeDiagnostics — #4710 wedge signature", () => {
|
||||
it("returns the matching gateway.log lines, trimmed", () => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
ensureHermesDashboardPortForwardIfEnabled,
|
||||
getHermesDashboardRecoveryConfig,
|
||||
recoverHermesDashboardProcessIfEnabled,
|
||||
} from "../../../../dist/lib/actions/sandbox/hermes-dashboard-recovery";
|
||||
} from "./hermes-dashboard-recovery";
|
||||
|
||||
describe("Hermes dashboard recovery helpers", () => {
|
||||
it("reads recovery config only for enabled Hermes dashboard sandboxes", () => {
|
||||
|
|
|
|||
|
|
@ -7,19 +7,16 @@
|
|||
// Without this gate, a destructive sandbox rebuild can run and fail late at
|
||||
// Dockerfile patching.
|
||||
//
|
||||
// Why dist + vi.spyOn (matches policy-channel-conflict.test.ts): the source
|
||||
// policy-channel.ts loads several deps via runtime CommonJS `require()`. In
|
||||
// this repo's vitest setup, `vi.mock` only intercepts ESM `import`, not plain
|
||||
// `require()`. We `require()` the COMPILED module + its real compiled
|
||||
// dependency modules from dist/ (one shared require cache) and `vi.spyOn`
|
||||
// the dependency exports. Run `npm run build:cli` first.
|
||||
// policy-channel.ts loads several dependencies through CommonJS `require()`.
|
||||
// Load the source module and its dependencies through the shared source hook
|
||||
// so `vi.spyOn` observes one require cache without depending on a CLI build.
|
||||
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
|
||||
const requireSource = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireSource(`../../${p}`);
|
||||
|
||||
const registry = D("state/registry.js");
|
||||
const providers = D("onboard/providers.js");
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { createRequire } from "node:module";
|
|||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
|
||||
const D = (p: string) => requireDist(`../../${p}`);
|
||||
|
||||
const registry = D("state/registry.js");
|
||||
const defs = D("agent/defs.js");
|
||||
|
|
|
|||
|
|
@ -6,14 +6,9 @@
|
|||
// assert only on the mocked module boundaries — never on the private helper
|
||||
// names — so they survive a refactor of the internal conflict-check plumbing.
|
||||
//
|
||||
// Why dist + vi.spyOn (the rebuild-shields-finally.test.ts pattern): the source
|
||||
// policy-channel.ts loads several deps via runtime CommonJS `require()`
|
||||
// (../../onboard, ../../onboard/providers, ./rebuild, ../../runner, ...). In
|
||||
// this repo's vitest setup, `vi.mock` only intercepts ESM `import`, not plain
|
||||
// `require()`, and those modules do extensionless sibling requires the TS
|
||||
// transform cannot resolve. So we require the COMPILED module + its real
|
||||
// compiled dependency modules from dist/ (one shared require cache) and
|
||||
// `vi.spyOn` the dependency exports. Run `npm run build:cli` first.
|
||||
// policy-channel.ts loads several dependencies through CommonJS `require()`.
|
||||
// Load the source module and its dependencies through the shared source hook
|
||||
// so `vi.spyOn` observes one require cache without depending on a CLI build.
|
||||
//
|
||||
// isNonInteractive is destructured at module load (`const { isNonInteractive }
|
||||
// = require("../../onboard")`), so it cannot be spied after load; it reads
|
||||
|
|
@ -25,12 +20,12 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
|
||||
const requireSource = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireSource(`../../${p}`);
|
||||
|
||||
type SandboxEntry = import("../../state/registry").SandboxEntry;
|
||||
|
||||
// Real compiled dependency modules (shared require cache with the SUT).
|
||||
// Real source dependency modules (shared require cache with the SUT).
|
||||
const store = D("credentials/store.js");
|
||||
const registry = D("state/registry.js");
|
||||
const providers = D("onboard/providers.js");
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { createRequire } from "node:module";
|
|||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
|
||||
const D = (p: string) => requireDist(`../../${p}`);
|
||||
|
||||
type PresetInfo = {
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@
|
|||
* silently let the in-sandbox POLICY.md drift.
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
|
||||
const D = (p: string) => requireDist(`../../${p}`);
|
||||
|
||||
type PresetInfo = { name: string };
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import { createRequire } from "node:module";
|
|||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const policyChannelModulePath = "../../../../dist/lib/actions/sandbox/policy-channel.js";
|
||||
const policyChannelModulePath = "./policy-channel.js";
|
||||
|
||||
type PolicyChannelModule = typeof import("../../../../dist/lib/actions/sandbox/policy-channel");
|
||||
type PolicyChannelModule = typeof import("./policy-channel");
|
||||
|
||||
describe("policy channel remove/enable flows", () => {
|
||||
let exitSpy: MockInstance;
|
||||
|
|
@ -52,7 +52,7 @@ describe("policy channel remove/enable flows", () => {
|
|||
});
|
||||
|
||||
it("supports stop dry runs for configured channels", async () => {
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
vi.spyOn(registry, "getSandbox").mockReturnValue({ name: "alpha" });
|
||||
vi.spyOn(registry, "getConfiguredMessagingChannelsFromEntry").mockReturnValue(["telegram"]);
|
||||
vi.spyOn(registry, "getDisabledChannels").mockReturnValue([]);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { afterEach, describe, it, expect } from "vitest";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
// Import from compiled dist for parity with the other CLI tests in this project.
|
||||
import {
|
||||
probeSandboxInferenceGatewayHealth,
|
||||
waitForRecoveredSandboxGateway,
|
||||
} from "../../../../dist/lib/actions/sandbox/process-recovery";
|
||||
} from "./process-recovery";
|
||||
|
||||
describe("probeSandboxInferenceGatewayHealth — #3265 gateway-chain subprobe", () => {
|
||||
const makeExec =
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ import path from "node:path";
|
|||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
type RebuildModule = typeof import("../../../../dist/lib/actions/sandbox/rebuild");
|
||||
type RebuildModule = typeof import("./rebuild");
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const { buildRefreshMutableOpenClawConfigHashCommand } = requireDist(
|
||||
"../../../../dist/lib/actions/sandbox/rebuild.js",
|
||||
"./rebuild.js",
|
||||
) as RebuildModule;
|
||||
|
||||
function sha256Hex(filePath: string): string {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
assessAmbientRecreateEnv,
|
||||
isolateAmbientRecreateEnv,
|
||||
sanitizeEnvValueForDisplay,
|
||||
} from "../../../../dist/lib/actions/sandbox/rebuild-env-isolation.js";
|
||||
} from "./rebuild-env-isolation.js";
|
||||
|
||||
describe("sanitizeEnvValueForDisplay (#5735 PRA-7)", () => {
|
||||
it("collapses a multi-line / ANSI value into a single safe line", () => {
|
||||
|
|
|
|||
|
|
@ -5,16 +5,14 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type RebuildFlowHelpersModule =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/rebuild-flow-helpers");
|
||||
type SandboxStateModule = typeof import("../../../../dist/lib/state/sandbox");
|
||||
type UserManagedFilesProbeModule =
|
||||
typeof import("../../../../dist/lib/state/user-managed-files-probe");
|
||||
type RebuildFlowHelpersModule = typeof import("./rebuild-flow-helpers");
|
||||
type SandboxStateModule = typeof import("../../state/sandbox");
|
||||
type UserManagedFilesProbeModule = typeof import("../../state/user-managed-files-probe");
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const rebuildFlowHelpersPath = "../../../../dist/lib/actions/sandbox/rebuild-flow-helpers.js";
|
||||
const sandboxStatePath = "../../../../dist/lib/state/sandbox.js";
|
||||
const userManagedFilesProbePath = "../../../../dist/lib/state/user-managed-files-probe.js";
|
||||
const rebuildFlowHelpersPath = "./rebuild-flow-helpers.js";
|
||||
const sandboxStatePath = "../../state/sandbox.js";
|
||||
const userManagedFilesProbePath = "../../state/user-managed-files-probe.js";
|
||||
|
||||
function loadRebuildFlowHelpers(): RebuildFlowHelpersModule {
|
||||
delete require.cache[requireDist.resolve(rebuildFlowHelpersPath)];
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type RebuildSandbox =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
|
||||
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const rebuildModulePath = "../../../../dist/lib/actions/sandbox/rebuild.js";
|
||||
const rebuildModulePath = "./rebuild.js";
|
||||
|
||||
// Warm the CommonJS source graph outside the first test's timeout. Each harness
|
||||
// still reloads the entry module after installing its dependency spies.
|
||||
requireDist(rebuildModulePath);
|
||||
delete require.cache[requireDist.resolve(rebuildModulePath)];
|
||||
|
||||
type RebuildFlowStep = {
|
||||
status: string;
|
||||
|
|
@ -157,28 +161,26 @@ function createRebuildFlowHarness(overrides: RebuildFlowOverrides = {}): Rebuild
|
|||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
|
||||
|
||||
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const sandboxList = requireDist("../../../../dist/lib/openshell-sandbox-list.js");
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const agentDefs = requireDist("../../../../dist/lib/agent/defs.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
|
||||
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const sandboxState = requireDist("../../../../dist/lib/state/sandbox.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
|
||||
const rebuildShields = requireDist("../../../../dist/lib/actions/sandbox/rebuild-shields.js");
|
||||
const nim = requireDist("../../../../dist/lib/inference/nim.js");
|
||||
const policies = requireDist("../../../../dist/lib/policy/index.js");
|
||||
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
|
||||
const messagingHostForwardLifecycle = requireDist(
|
||||
"../../../../dist/lib/actions/sandbox/messaging-host-forward-lifecycle.js",
|
||||
);
|
||||
const messaging = requireDist("../../../../dist/lib/messaging/index.js");
|
||||
const shields = requireDist("../../../../dist/lib/shields/index.js");
|
||||
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const sandboxList = requireDist("../../openshell-sandbox-list.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const agentDefs = requireDist("../../agent/defs.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
const onboardMod = requireDist("../../onboard.js");
|
||||
const onboardSession = requireDist("../../state/onboard-session.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const sandboxState = requireDist("../../state/sandbox.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const destroy = requireDist("./destroy.js");
|
||||
const rebuildShields = requireDist("./rebuild-shields.js");
|
||||
const nim = requireDist("../../inference/nim.js");
|
||||
const policies = requireDist("../../policy/index.js");
|
||||
const processRecovery = requireDist("./process-recovery.js");
|
||||
const messagingHostForwardLifecycle = requireDist("./messaging-host-forward-lifecycle.js");
|
||||
const messaging = requireDist("../../messaging/index.js");
|
||||
const shields = requireDist("../../shields/index.js");
|
||||
|
||||
const session = createRebuildFlowSession(onboardSession.MACHINE_SNAPSHOT_VERSION);
|
||||
const rebuildShieldsWindow = { relocked: false, wasLocked: false };
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } fr
|
|||
|
||||
import type { OpenShellStateRpcIssue } from "../../adapters/openshell/gateway-drift";
|
||||
|
||||
type RebuildSandbox =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
|
||||
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
|
||||
|
|
@ -45,15 +44,15 @@ describe("rebuild gateway drift preflight", () => {
|
|||
exitSpy = mockExit();
|
||||
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
|
||||
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
const onboardSession = requireDist("../../state/onboard-session.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
|
||||
printIssueSpy = vi
|
||||
.spyOn(gatewayDrift, "printOpenShellStateRpcIssue")
|
||||
|
|
@ -100,7 +99,7 @@ describe("rebuild gateway drift preflight", () => {
|
|||
checkAgentVersionSpy,
|
||||
);
|
||||
|
||||
({ rebuildSandbox } = requireDist("../../../../dist/lib/actions/sandbox/rebuild.js"));
|
||||
({ rebuildSandbox } = requireDist("./rebuild.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -156,8 +155,8 @@ describe("rebuild gateway drift preflight", () => {
|
|||
// preserved registry metadata. Stub the destructive steps + recreate handoff
|
||||
// so the path stays hermetic, and assert the recreate failure surfaces the
|
||||
// stale-recovery message instead of "not running".
|
||||
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
|
||||
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
|
||||
const destroy = requireDist("./destroy.js");
|
||||
const onboardMod = requireDist("../../onboard.js");
|
||||
spies.push(
|
||||
vi.spyOn(destroy, "removeSandboxRegistryEntry").mockImplementation(() => undefined),
|
||||
vi.spyOn(onboardMod, "onboard").mockRejectedValue(new Error("recreate-stub")),
|
||||
|
|
@ -189,15 +188,15 @@ describe("rebuild gateway drift preflight", () => {
|
|||
// recover the wrong (and possibly nonexistent) default gateway.
|
||||
for (const spy of spies) spy.mockRestore();
|
||||
spies.length = 0;
|
||||
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
const onboardSession = requireDist("../../state/onboard-session.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
|
||||
let listCalls = 0;
|
||||
detectPreflightIssueSpy = vi
|
||||
|
|
@ -239,8 +238,8 @@ describe("rebuild gateway drift preflight", () => {
|
|||
.spyOn(gatewayDrift, "printOpenShellStateRpcIssue")
|
||||
.mockImplementation(() => undefined);
|
||||
|
||||
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
|
||||
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
|
||||
const destroy = requireDist("./destroy.js");
|
||||
const onboardMod = requireDist("../../onboard.js");
|
||||
spies.push(
|
||||
detectPreflightIssueSpy,
|
||||
vi.spyOn(gatewayDrift, "detectOpenShellStateRpcResultIssue").mockReturnValue(null),
|
||||
|
|
@ -271,7 +270,7 @@ describe("rebuild gateway drift preflight", () => {
|
|||
vi.spyOn(onboardMod, "onboard").mockRejectedValue(new Error("recreate-stub")),
|
||||
);
|
||||
|
||||
({ rebuildSandbox } = requireDist("../../../../dist/lib/actions/sandbox/rebuild.js"));
|
||||
({ rebuildSandbox } = requireDist("./rebuild.js"));
|
||||
|
||||
await expect(rebuildSandbox("alpha", ["--yes"], { throwOnError: true })).rejects.toThrow(
|
||||
/stale-sandbox recovery/,
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
buildRebuildRecreateOnboardOpts,
|
||||
rebuildShouldOptOutGpu,
|
||||
} from "../../../../dist/lib/actions/sandbox/rebuild-gpu-opt-out";
|
||||
import { buildRebuildRecreateOnboardOpts, rebuildShouldOptOutGpu } from "./rebuild-gpu-opt-out";
|
||||
|
||||
describe("rebuildShouldOptOutGpu", () => {
|
||||
it("returns false when the registry entry is null", () => {
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@
|
|||
// channel manifests, so a non-messaging sandbox rebuild cannot
|
||||
// carry messaging-plan state into the Dockerfile patch step.
|
||||
//
|
||||
// Loaded from dist/ to match the rest of the rebuild test suite (runner.ts
|
||||
// loads './platform' via runtime CommonJS `require()` that vitest cannot
|
||||
// resolve from a TS source file). Run `npm run build:cli` first.
|
||||
// Loaded through the shared source require hook because the rebuild graph uses
|
||||
// runtime CommonJS dependencies that must share one cache for test spies.
|
||||
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireDist(`../../../../dist/lib/${p}`);
|
||||
const requireSource = createRequire(import.meta.url);
|
||||
const D = (p: string) => requireSource(`../../${p}`);
|
||||
|
||||
const defs = D("agent/defs.js");
|
||||
const messaging = D("messaging/index.js") as {
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import { createRequire } from "node:module";
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
|
||||
const onboardSession = requireDist("../../state/onboard-session.js");
|
||||
const {
|
||||
isLocalInferenceProvider,
|
||||
getRebuildCredentialEnvFromRegistry,
|
||||
getRebuildEndpointFromRegistry,
|
||||
prepareRebuildResumeConfig,
|
||||
} = requireDist("../../../../dist/lib/actions/sandbox/rebuild-resume-config.js");
|
||||
} = requireDist("./rebuild-resume-config.js");
|
||||
|
||||
const noopLog = () => undefined;
|
||||
const throwingBail = (msg: string): never => {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@ import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } fr
|
|||
|
||||
import type { Session } from "../../state/onboard-session";
|
||||
|
||||
type RebuildSandbox =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
|
||||
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const rebuildModulePath = "../../../../dist/lib/actions/sandbox/rebuild.js";
|
||||
const rebuildModulePath = "./rebuild.js";
|
||||
|
||||
function cloneSession(session: Session): Session {
|
||||
return JSON.parse(JSON.stringify(session));
|
||||
|
|
@ -44,22 +43,22 @@ describe("rebuild resume snapshot repair", () => {
|
|||
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
||||
|
||||
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const sandboxList = requireDist("../../../../dist/lib/openshell-sandbox-list.js");
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const agentDefs = requireDist("../../../../dist/lib/agent/defs.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const onboardMod = requireDist("../../../../dist/lib/onboard.js");
|
||||
const resumeRepair = requireDist("../../../../dist/lib/onboard/resume-machine-repair.js");
|
||||
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const sandboxState = requireDist("../../../../dist/lib/state/sandbox.js");
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const destroy = requireDist("../../../../dist/lib/actions/sandbox/destroy.js");
|
||||
const rebuildShields = requireDist("../../../../dist/lib/actions/sandbox/rebuild-shields.js");
|
||||
const nim = requireDist("../../../../dist/lib/inference/nim.js");
|
||||
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
|
||||
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const sandboxList = requireDist("../../openshell-sandbox-list.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const agentDefs = requireDist("../../agent/defs.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
const onboardMod = requireDist("../../onboard.js");
|
||||
const resumeRepair = requireDist("../../onboard/resume-machine-repair.js");
|
||||
const onboardSession = requireDist("../../state/onboard-session.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
const sandboxState = requireDist("../../state/sandbox.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const destroy = requireDist("./destroy.js");
|
||||
const rebuildShields = requireDist("./rebuild-shields.js");
|
||||
const nim = requireDist("../../inference/nim.js");
|
||||
|
||||
session = onboardSession.createSession({
|
||||
sandboxName: "alpha",
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type RebuildSandbox =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/rebuild")["rebuildSandbox"];
|
||||
type RebuildSandbox = typeof import("./rebuild")["rebuildSandbox"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const rebuildModulePath = "../../../../dist/lib/actions/sandbox/rebuild.js";
|
||||
const rebuildModulePath = "./rebuild.js";
|
||||
|
||||
describe("rebuild shields relock guard", () => {
|
||||
let rebuildSandbox: RebuildSandbox;
|
||||
|
|
@ -28,17 +27,17 @@ describe("rebuild shields relock guard", () => {
|
|||
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
||||
|
||||
const gatewayDrift = requireDist("../../../../dist/lib/adapters/openshell/gateway-drift.js");
|
||||
const gatewayRuntime = requireDist("../../../../dist/lib/gateway-runtime-action.js");
|
||||
const sandboxList = requireDist("../../../../dist/lib/openshell-sandbox-list.js");
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const onboardSession = requireDist("../../../../dist/lib/state/onboard-session.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const sandboxState = requireDist("../../../../dist/lib/state/sandbox.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const rebuildShields = requireDist("../../../../dist/lib/actions/sandbox/rebuild-shields.js");
|
||||
const gatewayDrift = requireDist("../../adapters/openshell/gateway-drift.js");
|
||||
const gatewayRuntime = requireDist("../../gateway-runtime-action.js");
|
||||
const sandboxList = requireDist("../../openshell-sandbox-list.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
const onboardSession = requireDist("../../state/onboard-session.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const sandboxState = requireDist("../../state/sandbox.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const rebuildShields = requireDist("./rebuild-shields.js");
|
||||
|
||||
relockSpy = vi
|
||||
.spyOn(rebuildShields, "relockRebuildShieldsWindow")
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
|
||||
type RoutingModule = typeof import("../../../../dist/lib/actions/sandbox/sandbox-gateway-routing");
|
||||
type RoutingModule = typeof import("./sandbox-gateway-routing");
|
||||
|
||||
describe("sandbox gateway routing helpers", () => {
|
||||
let routing: RoutingModule;
|
||||
|
|
@ -17,8 +17,8 @@ describe("sandbox gateway routing helpers", () => {
|
|||
let runOpenshellSpy: MockInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const openshellRuntime = requireDist("../../adapters/openshell/runtime.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
|
||||
spies = [];
|
||||
getSandboxSpy = vi.spyOn(registry, "getSandbox").mockReturnValue({
|
||||
|
|
@ -44,7 +44,7 @@ describe("sandbox gateway routing helpers", () => {
|
|||
} as never);
|
||||
spies.push(getSandboxSpy, captureOpenshellSpy, runOpenshellSpy);
|
||||
|
||||
routing = requireDist("../../../../dist/lib/actions/sandbox/sandbox-gateway-routing.js");
|
||||
routing = requireDist("./sandbox-gateway-routing.js");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -197,7 +197,9 @@ describe("callOpenclawGateway", () => {
|
|||
{ encoding: "utf8" },
|
||||
);
|
||||
|
||||
expect(result.status).toBe(126);
|
||||
// Login-shell teardown hooks may replace the explicit 126 status, but
|
||||
// the security rejection must remain non-zero and happen before Node.
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(result.stderr).toContain("[SECURITY]");
|
||||
expect(result.stderr).toContain("expected regular root-owned mode 444 file");
|
||||
expect(result.stderr).not.toContain("node should not run");
|
||||
|
|
|
|||
|
|
@ -5,11 +5,15 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type ShowSandboxStatus =
|
||||
typeof import("../../../../dist/lib/actions/sandbox/status")["showSandboxStatus"];
|
||||
type ShowSandboxStatus = typeof import("./status")["showSandboxStatus"];
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const statusModulePath = "../../../../dist/lib/actions/sandbox/status.js";
|
||||
const statusModulePath = "./status.js";
|
||||
|
||||
// Warm the CommonJS source graph outside the first test's timeout. Each harness
|
||||
// still reloads the entry module after installing its dependency spies.
|
||||
requireDist(statusModulePath);
|
||||
delete require.cache[requireDist.resolve(statusModulePath)];
|
||||
|
||||
type StatusFlowHarness = {
|
||||
checkAgentVersionSpy: MockInstance;
|
||||
|
|
@ -54,17 +58,17 @@ function createStatusFlowHarness(
|
|||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
||||
vi.spyOn(console, "error").mockImplementation(() => undefined);
|
||||
|
||||
const statusPreflight = requireDist("../../../../dist/lib/actions/sandbox/status-preflight.js");
|
||||
const statusSnapshot = requireDist("../../../../dist/lib/actions/sandbox/status-snapshot.js");
|
||||
const dockerHealth = requireDist("../../../../dist/lib/actions/sandbox/docker-health.js");
|
||||
const processRecovery = requireDist("../../../../dist/lib/actions/sandbox/process-recovery.js");
|
||||
const resolve = requireDist("../../../../dist/lib/adapters/openshell/resolve.js");
|
||||
const agentRuntime = requireDist("../../../../dist/lib/agent/runtime.js");
|
||||
const nim = requireDist("../../../../dist/lib/inference/nim.js");
|
||||
const sandboxVersion = requireDist("../../../../dist/lib/sandbox/version.js");
|
||||
const shields = requireDist("../../../../dist/lib/shields/index.js");
|
||||
const registry = requireDist("../../../../dist/lib/state/registry.js");
|
||||
const sandboxSession = requireDist("../../../../dist/lib/state/sandbox-session.js");
|
||||
const statusPreflight = requireDist("./status-preflight.js");
|
||||
const statusSnapshot = requireDist("./status-snapshot.js");
|
||||
const dockerHealth = requireDist("./docker-health.js");
|
||||
const processRecovery = requireDist("./process-recovery.js");
|
||||
const resolve = requireDist("../../adapters/openshell/resolve.js");
|
||||
const agentRuntime = requireDist("../../agent/runtime.js");
|
||||
const nim = requireDist("../../inference/nim.js");
|
||||
const sandboxVersion = requireDist("../../sandbox/version.js");
|
||||
const shields = requireDist("../../shields/index.js");
|
||||
const registry = requireDist("../../state/registry.js");
|
||||
const sandboxSession = requireDist("../../state/sandbox-session.js");
|
||||
|
||||
const lookup =
|
||||
options.lookupState === "missing"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ProviderHealthProbeOptions } from "../../inference/health";
|
||||
import {
|
||||
classifySandboxContainerFailureForStatus,
|
||||
classifySandboxStatusPreflightFailure,
|
||||
|
|
@ -10,8 +11,7 @@ import {
|
|||
maybeGetSandboxStatusInferenceHealth,
|
||||
sandboxGpuProofStatusSuffix,
|
||||
sandboxGpuProofUnverified,
|
||||
} from "../../../../dist/lib/actions/sandbox/status";
|
||||
import type { ProviderHealthProbeOptions } from "../../../../dist/lib/inference/health";
|
||||
} from "./status";
|
||||
|
||||
describe("sandbox status inference health", () => {
|
||||
it("passes the current model with the current provider", () => {
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
import {
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const {
|
||||
detectOpenShellStateRpcPreflightIssue,
|
||||
detectOpenShellStateRpcResultIssue,
|
||||
formatOpenShellStateRpcIssue,
|
||||
getGatewayClusterImageDrift,
|
||||
getGatewayHostProcessDrift,
|
||||
parseGatewayClusterImageVersion,
|
||||
} from "../../../../dist/lib/adapters/openshell/gateway-drift";
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
} = requireDist("./gateway-drift.ts") as typeof import("./gateway-drift");
|
||||
|
||||
describe("OpenShell gateway drift preflight", () => {
|
||||
let spies: MockInstance[] = [];
|
||||
|
|
@ -75,8 +74,8 @@ describe("OpenShell gateway drift preflight", () => {
|
|||
});
|
||||
|
||||
it("uses the shared gateway-health classifier when checking the active cluster gateway", () => {
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const docker = requireDist("../../../../dist/lib/adapters/docker/inspect.js");
|
||||
const openshellRuntime = requireDist("./runtime.js");
|
||||
const docker = requireDist("../docker/inspect.js");
|
||||
spies.push(
|
||||
vi.spyOn(openshellRuntime, "captureOpenshell").mockImplementation((rawArgs: unknown) => {
|
||||
const args = rawArgs as string[];
|
||||
|
|
@ -118,8 +117,8 @@ describe("OpenShell gateway drift preflight", () => {
|
|||
});
|
||||
|
||||
it("ignores stale cluster containers whose published port is not the active gateway endpoint", () => {
|
||||
const openshellRuntime = requireDist("../../../../dist/lib/adapters/openshell/runtime.js");
|
||||
const docker = requireDist("../../../../dist/lib/adapters/docker/inspect.js");
|
||||
const openshellRuntime = requireDist("./runtime.js");
|
||||
const docker = requireDist("../docker/inspect.js");
|
||||
spies.push(
|
||||
vi.spyOn(openshellRuntime, "captureOpenshell").mockImplementation((rawArgs: unknown) => {
|
||||
const args = rawArgs as string[];
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { resolveOpenshell } from "../../../../dist/lib/adapters/openshell/resolve";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveOpenshell } from "./resolve";
|
||||
|
||||
describe("lib/resolve-openshell", () => {
|
||||
it("returns command -v result when absolute path", () => {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { AgentDefinition } from "./defs";
|
||||
|
||||
type AgentOnboardModule = typeof import("../../../dist/lib/agent/onboard");
|
||||
type DockerImageModule = typeof import("../../../dist/lib/adapters/docker/image");
|
||||
type DockerInspectModule = typeof import("../../../dist/lib/adapters/docker/inspect");
|
||||
type SandboxBaseImageModule = typeof import("../../../dist/lib/sandbox-base-image");
|
||||
type AgentOnboardModule = typeof import("./onboard");
|
||||
type DockerImageModule = typeof import("../adapters/docker/image");
|
||||
type DockerInspectModule = typeof import("../adapters/docker/inspect");
|
||||
type SandboxBaseImageModule = typeof import("../sandbox-base-image");
|
||||
|
||||
/**
|
||||
* Build a minimal Hermes agent manifest for base-image provisioning tests.
|
||||
|
|
@ -66,19 +66,17 @@ function withMockedDocker<T>(
|
|||
}) => T,
|
||||
): T {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const dockerImageModule = require("../../../dist/lib/adapters/docker/image") as DockerImageModule;
|
||||
const dockerImageModule = require("../adapters/docker/image") as DockerImageModule;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const dockerInspectModule =
|
||||
require("../../../dist/lib/adapters/docker/inspect") as DockerInspectModule;
|
||||
const dockerInspectModule = require("../adapters/docker/inspect") as DockerInspectModule;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const sandboxBaseImageModule =
|
||||
require("../../../dist/lib/sandbox-base-image") as SandboxBaseImageModule;
|
||||
const sandboxBaseImageModule = require("../sandbox-base-image") as SandboxBaseImageModule;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const runnerModule = require("../../../dist/lib/runner") as { ROOT: string };
|
||||
const runnerModule = require("../runner") as { ROOT: string };
|
||||
const originalDockerBuild = dockerImageModule.dockerBuild;
|
||||
const originalDockerImageInspect = dockerInspectModule.dockerImageInspect;
|
||||
const originalResolveSandboxBaseImage = sandboxBaseImageModule.resolveSandboxBaseImage;
|
||||
const agentOnboardModulePath = require.resolve("../../../dist/lib/agent/onboard");
|
||||
const agentOnboardModulePath = require.resolve("./onboard");
|
||||
delete require.cache[agentOnboardModulePath];
|
||||
|
||||
const dockerBuildMock = vi.fn().mockReturnValue({ status: 0 });
|
||||
|
|
@ -97,7 +95,7 @@ function withMockedDocker<T>(
|
|||
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const agentOnboardModule = require("../../../dist/lib/agent/onboard") as AgentOnboardModule;
|
||||
const agentOnboardModule = require("./onboard") as AgentOnboardModule;
|
||||
return run({
|
||||
ensureAgentBaseImage: agentOnboardModule.ensureAgentBaseImage,
|
||||
dockerBuildMock,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
loadAgent,
|
||||
resolveAgentName,
|
||||
resolveAgentNameAlias,
|
||||
} from "../../../dist/lib/agent/defs";
|
||||
} from "./defs";
|
||||
|
||||
const tempAgentDirs: string[] = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { loadAgent } from "../../../dist/lib/agent/defs";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
import { handleAgentSetup } from "../../../dist/lib/agent/onboard";
|
||||
import type { AgentDefinition } from "./defs";
|
||||
import { loadAgent } from "./defs";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import { handleAgentSetup } from "./onboard";
|
||||
import {
|
||||
recordFailingDeepAgentsSmokeCall,
|
||||
recordSuccessfulDeepAgentsRuntimeCall,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
import { printDashboardUi } from "../../../dist/lib/agent/onboard";
|
||||
import type { AgentDefinition } from "./defs";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import { printDashboardUi } from "./onboard";
|
||||
|
||||
const buildUrlsLoopback = (_token: string | null, port: number): string[] => [
|
||||
`http://127.0.0.1:${port}/`,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
import type { AgentDefinition } from "./defs";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import {
|
||||
collectHermesStartupDiagnostics,
|
||||
handleAgentSetup,
|
||||
printDashboardUi,
|
||||
verifyAgentBinaryAvailable,
|
||||
} from "../../../dist/lib/agent/onboard";
|
||||
import type { AgentDefinition } from "./defs";
|
||||
} from "./onboard";
|
||||
|
||||
function makeAgent(overrides: Partial<AgentDefinition> = {}): AgentDefinition {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -13,17 +13,14 @@ import fs from "node:fs";
|
|||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
__testing,
|
||||
HERMES_SECRET_BOUNDARY_VALIDATOR_PATH,
|
||||
} from "../../../dist/lib/agent/hermes-recovery-boundary";
|
||||
import { buildRecoveryScript } from "../../../dist/lib/agent/runtime";
|
||||
import {
|
||||
createRecoveryPreloadHarnessPaths,
|
||||
type RecoveryPreloadHarnessPaths,
|
||||
rewriteRecoveryPreloadPaths,
|
||||
} from "../../../test/helpers/runtime-recovery-preload-test-helpers";
|
||||
import { __testing, HERMES_SECRET_BOUNDARY_VALIDATOR_PATH } from "./hermes-recovery-boundary";
|
||||
import { hermesAgent } from "./hermes-recovery-boundary-fixtures";
|
||||
import { buildRecoveryScript } from "./runtime";
|
||||
|
||||
function writeStub(dir: string, name: string, body: string) {
|
||||
const stub = path.join(dir, name);
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
// runtime-hermes-secret-boundary-behavioural.test.ts.
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { HERMES_SECRET_BOUNDARY_VALIDATOR_PATH } from "../../../dist/lib/agent/hermes-recovery-boundary";
|
||||
import { HERMES_SECRET_BOUNDARY_VALIDATOR_PATH } from "./hermes-recovery-boundary";
|
||||
import { hermesAgent, minimalAgent } from "./hermes-recovery-boundary-fixtures";
|
||||
import {
|
||||
buildHermesDashboardProcessRecoveryScript,
|
||||
buildManualRecoveryCommand,
|
||||
buildRecoveryScript,
|
||||
} from "../../../dist/lib/agent/runtime";
|
||||
import { hermesAgent, minimalAgent } from "./hermes-recovery-boundary-fixtures";
|
||||
} from "./runtime";
|
||||
|
||||
const VALIDATOR_PATH = HERMES_SECRET_BOUNDARY_VALIDATOR_PATH;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,9 @@ import fs from "node:fs";
|
|||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildOpenClawRecoveryScript, buildRecoveryScript } from "../../../dist/lib/agent/runtime";
|
||||
import {
|
||||
buildGatewayGuardRecoveryLines,
|
||||
GATEWAY_PRELOAD_GUARDS,
|
||||
} from "../../../dist/lib/agent/runtime-recovery-preload";
|
||||
import { minimalAgent } from "./hermes-recovery-boundary-fixtures";
|
||||
import { buildOpenClawRecoveryScript, buildRecoveryScript } from "./runtime";
|
||||
import { buildGatewayGuardRecoveryLines, GATEWAY_PRELOAD_GUARDS } from "./runtime-recovery-preload";
|
||||
|
||||
const [SAFETY_NET_GUARD, CIAO_GUARD] = GATEWAY_PRELOAD_GUARDS;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { loadAgent } from "../../../dist/lib/agent/defs";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
import type { AgentDefinition } from "./defs";
|
||||
import { loadAgent } from "./defs";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import {
|
||||
buildManualRecoveryCommand,
|
||||
buildRecoveryScript,
|
||||
getTerminalCommand,
|
||||
TERMINAL_AGENT_RECOVERY_SCRIPT,
|
||||
} from "../../../dist/lib/agent/runtime";
|
||||
import type { AgentDefinition } from "./defs";
|
||||
} from "./runtime";
|
||||
|
||||
const terminalAgent = {
|
||||
name: "terminal-agent",
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
import type { AgentDefinition } from "./defs";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import {
|
||||
buildHermesDashboardProcessRecoveryScript,
|
||||
buildManualRecoveryCommand,
|
||||
buildOpenClawRecoveryScript,
|
||||
buildRecoveryScript,
|
||||
} from "../../../dist/lib/agent/runtime";
|
||||
import type { AgentDefinition } from "./defs";
|
||||
} from "./runtime";
|
||||
|
||||
function makeAgent(overrides: Partial<AgentDefinition> = {}): AgentDefinition {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
printSandboxCreateRecoveryHints,
|
||||
reconstructImageRefCreateCommand,
|
||||
shouldIncludeBuildContextPath,
|
||||
} from "../../dist/lib/build-context";
|
||||
} from "./build-context";
|
||||
|
||||
type ConsoleErrorSpy = ReturnType<typeof vi.spyOn>;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
extractEnabledChannelsFromOpenclawConfig,
|
||||
parseGatewayLogScanOutput,
|
||||
probeChannelRuntimeStatus,
|
||||
} from "../../dist/lib/channel-runtime-status.js";
|
||||
} from "./channel-runtime-status.js";
|
||||
|
||||
// Build an executeSandboxCommand mock that returns the config file body
|
||||
// on the `cat` call and a synthesized gateway-log-scan output on the
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
computePatchedTag,
|
||||
ensurePatchedClusterImage,
|
||||
extractUpstreamVersion,
|
||||
} from "../../dist/lib/cluster-image-patch";
|
||||
} from "./cluster-image-patch";
|
||||
|
||||
const UPSTREAM = "ghcr.io/nvidia/openshell/cluster:0.0.36";
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
getGatewayHttpEndpoint,
|
||||
getGatewayHttpsEndpoint,
|
||||
parseGatewayBindAddress,
|
||||
} from "../../../dist/lib/core/gateway-address";
|
||||
} from "./gateway-address";
|
||||
|
||||
const ENV_KEY = "TEST_GATEWAY_BIND_ADDRESS";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
import { parseGatewayPort, parsePort } from "../../../dist/lib/core/ports";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import { parseGatewayPort, parsePort } from "./ports";
|
||||
|
||||
const GATEWAY_VALIDATION_OPTIONS = {
|
||||
dashboardPort: 18789,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
import { describe, expect, it } from "vitest";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import {
|
||||
compactText,
|
||||
stripEndpointSuffix,
|
||||
normalizeProviderBaseUrl,
|
||||
isLoopbackHostname,
|
||||
formatEnvAssignment,
|
||||
isLoopbackHostname,
|
||||
normalizeProviderBaseUrl,
|
||||
parsePolicyPresetEnv,
|
||||
} from "../../../dist/lib/core/url-utils";
|
||||
stripEndpointSuffix,
|
||||
} from "./url-utils";
|
||||
|
||||
describe("compactText", () => {
|
||||
it("collapses whitespace", () => {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { getVersion } from "../../../dist/lib/core/version";
|
||||
import { join } from "node:path";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import { getVersion } from "./version";
|
||||
|
||||
const repoRoot = join(import.meta.dirname, "..", "..", "..");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { buildChain, buildControlUiUrls } from "../../../dist/lib/dashboard/contract.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildChain, buildControlUiUrls } from "./contract.js";
|
||||
|
||||
describe("buildChain", () => {
|
||||
it("returns default loopback chain with no arguments", () => {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { validateName } from "../runner";
|
||||
import {
|
||||
buildDeployEnvLines,
|
||||
executeDeploy,
|
||||
|
|
@ -9,8 +10,7 @@ import {
|
|||
inferDeployProvider,
|
||||
isBrevInstanceFailed,
|
||||
isBrevInstanceReady,
|
||||
} from "../../../dist/lib/deploy";
|
||||
import { validateName } from "../../../dist/lib/runner";
|
||||
} from "./index";
|
||||
|
||||
describe("inferDeployProvider", () => {
|
||||
it("prefers an explicit provider override", () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { runDebugCommandWithOptions } from "../../../dist/lib/diagnostics/debug-command";
|
||||
import { runDebugCommandWithOptions } from "./debug-command";
|
||||
|
||||
describe("debug command", () => {
|
||||
it("runs parsed debug options and falls back to the default sandbox", () => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
// Import from compiled dist/ so coverage is attributed correctly.
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import {
|
||||
buildDmesgRerunCommand,
|
||||
createTarball,
|
||||
|
|
@ -14,7 +14,7 @@ import {
|
|||
isDmesgPermissionDeniedOutput,
|
||||
isDmesgRestrictedForCurrentUser,
|
||||
redact,
|
||||
} from "../../../dist/lib/diagnostics/debug";
|
||||
} from "./debug";
|
||||
|
||||
describe("redact", () => {
|
||||
it("redacts NVIDIA_INFERENCE_API_KEY=value patterns", () => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
parseVethGateway,
|
||||
selectSandboxNamespace,
|
||||
selectSandboxPod,
|
||||
} from "../../../../dist/lib/domain/dns/setup-proxy.js";
|
||||
} from "./setup-proxy.js";
|
||||
|
||||
describe("DNS setup proxy domain helpers", () => {
|
||||
it("selects a sandbox pod using fixed-string style matching", () => {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { createRequire } from "node:module";
|
|||
|
||||
import { afterEach, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest";
|
||||
|
||||
type GatewayRuntimeModule = typeof import("../../dist/lib/gateway-runtime-action");
|
||||
type GatewayRuntimeModule = typeof import("./gateway-runtime-action");
|
||||
|
||||
const requireDist = createRequire(import.meta.url);
|
||||
const gatewayRuntimeModulePath = "../../dist/lib/gateway-runtime-action.js";
|
||||
const gatewayRuntimeModulePath = "./gateway-runtime-action.js";
|
||||
|
||||
describe("gateway-runtime-action per-sandbox gateway routing", () => {
|
||||
let gatewayRuntime: GatewayRuntimeModule;
|
||||
|
|
@ -20,14 +20,14 @@ describe("gateway-runtime-action per-sandbox gateway routing", () => {
|
|||
beforeEach(() => {
|
||||
spies = [];
|
||||
delete require.cache[requireDist.resolve(gatewayRuntimeModulePath)];
|
||||
const openshellRuntime = requireDist("../../dist/lib/adapters/openshell/runtime.js");
|
||||
const openshellRuntime = requireDist("./adapters/openshell/runtime.js");
|
||||
captureSpy = vi.spyOn(openshellRuntime, "captureOpenshell");
|
||||
runSpy = vi.spyOn(openshellRuntime, "runOpenshell");
|
||||
spies.push(captureSpy, runSpy);
|
||||
|
||||
// The recovery path also pokes onboard.startGatewayForRecovery via lazy
|
||||
// require(); stub it so the tests do not pull onboard's runtime in.
|
||||
const onboard = requireDist("../../dist/lib/onboard.js");
|
||||
const onboard = requireDist("./onboard.js");
|
||||
startGatewaySpy = vi
|
||||
.spyOn(onboard, "startGatewayForRecovery")
|
||||
.mockResolvedValue(undefined as never);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
GatewayTokenCommandError,
|
||||
parseGatewayTokenArgs,
|
||||
runGatewayTokenCommand,
|
||||
} from "../../dist/lib/gateway-token-command";
|
||||
} from "./gateway-token-command";
|
||||
|
||||
function makeSinks() {
|
||||
const out: string[] = [];
|
||||
|
|
|
|||
|
|
@ -2,31 +2,17 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const DIST_AUTH = path.join(
|
||||
import.meta.dirname,
|
||||
"..",
|
||||
"..",
|
||||
"dist",
|
||||
"lib",
|
||||
"hermes-provider-auth.js",
|
||||
);
|
||||
const DIST_BROKER = path.join(
|
||||
import.meta.dirname,
|
||||
"..",
|
||||
"..",
|
||||
"dist",
|
||||
"lib",
|
||||
"hermes-tool-gateway-broker.js",
|
||||
);
|
||||
const SOURCE_AUTH = path.join(import.meta.dirname, "hermes-provider-auth.ts");
|
||||
const SOURCE_BROKER = path.join(import.meta.dirname, "hermes-tool-gateway-broker.ts");
|
||||
|
||||
function clearDistModule(modulePath: string): void {
|
||||
function clearSourceModule(modulePath: string): void {
|
||||
try {
|
||||
delete require.cache[require.resolve(modulePath)];
|
||||
} catch {
|
||||
|
|
@ -35,21 +21,21 @@ function clearDistModule(modulePath: string): void {
|
|||
}
|
||||
|
||||
function loadAuth(): Record<string, any> {
|
||||
clearDistModule(DIST_AUTH);
|
||||
return require(DIST_AUTH);
|
||||
clearSourceModule(SOURCE_AUTH);
|
||||
return require(SOURCE_AUTH);
|
||||
}
|
||||
|
||||
function loadAuthWithBrokerStub(brokerStub: Record<string, any>): Record<string, any> {
|
||||
clearDistModule(DIST_AUTH);
|
||||
clearDistModule(DIST_BROKER);
|
||||
const broker = require(DIST_BROKER);
|
||||
clearSourceModule(SOURCE_AUTH);
|
||||
clearSourceModule(SOURCE_BROKER);
|
||||
const broker = require(SOURCE_BROKER);
|
||||
Object.assign(broker, brokerStub);
|
||||
return require(DIST_AUTH);
|
||||
return require(SOURCE_AUTH);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
clearDistModule(DIST_AUTH);
|
||||
clearDistModule(DIST_BROKER);
|
||||
clearSourceModule(SOURCE_AUTH);
|
||||
clearSourceModule(SOURCE_BROKER);
|
||||
});
|
||||
|
||||
describe("Hermes provider OpenShell credential handoff", () => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
createBedrockRuntimeAdapterServer,
|
||||
createOpenAiChatCompletion,
|
||||
streamOpenAiChatCompletion,
|
||||
} from "../../../dist/lib/inference/bedrock-runtime-adapter";
|
||||
} from "./bedrock-runtime-adapter";
|
||||
|
||||
const servers: http.Server[] = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
classifyCustomAnthropicEndpoint,
|
||||
isBedrockRuntimeEndpoint,
|
||||
} from "../../../dist/lib/inference/bedrock-runtime";
|
||||
import { classifyCustomAnthropicEndpoint, isBedrockRuntimeEndpoint } from "./bedrock-runtime";
|
||||
|
||||
describe("Bedrock Runtime endpoint classification", () => {
|
||||
it("detects standard Bedrock Runtime hosts", () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// Import from compiled dist/ for correct coverage attribution.
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import {
|
||||
CLOUD_MODEL_OPTIONS,
|
||||
DEFAULT_HERMES_PROVIDER_MODEL,
|
||||
|
|
@ -21,7 +21,7 @@ import {
|
|||
planInferenceRouteReconcile,
|
||||
sanitizeRouteValueForDisplay,
|
||||
VLLM_LOCAL_CREDENTIAL_ENV,
|
||||
} from "../../../dist/lib/inference/config";
|
||||
} from "./config";
|
||||
|
||||
describe("inference selection config", () => {
|
||||
it("exposes the curated cloud model picker options", () => {
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
|
||||
import fs from "node:fs";
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// Import from compiled dist/ for correct coverage attribution.
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import {
|
||||
getRemoteProviderHealthEndpoint,
|
||||
probeRemoteProviderHealth,
|
||||
probeProviderHealth,
|
||||
} from "../../../dist/lib/inference/health";
|
||||
probeRemoteProviderHealth,
|
||||
} from "./health";
|
||||
|
||||
import { BUILD_ENDPOINT_URL } from "../../../dist/lib/inference/provider-models";
|
||||
import { BUILD_ENDPOINT_URL } from "./provider-models";
|
||||
|
||||
describe("inference health", () => {
|
||||
describe("getRemoteProviderHealthEndpoint", () => {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
waitForLocalAdapterHealth,
|
||||
writeLocalAdapterJsonFile,
|
||||
writeLocalAdapterSecretFile,
|
||||
} from "../../../dist/lib/inference/local-adapter-lifecycle";
|
||||
} from "./local-adapter-lifecycle";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
const servers: http.Server[] = [];
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { afterAll, afterEach, beforeAll, describe, it, expect } from "vitest";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
|
||||
|
||||
// Import from compiled dist/ for correct coverage attribution.
|
||||
import { OLLAMA_MODEL_REGISTRY } from "../../../dist/lib/inference/ollama-model-registry";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import { OLLAMA_MODEL_REGISTRY } from "./ollama-model-registry";
|
||||
|
||||
// Derive the "large enough to fit every registry entry" memory threshold
|
||||
// from the registry itself so adding or resizing a model in the registry
|
||||
|
|
@ -16,31 +15,32 @@ import { OLLAMA_MODEL_REGISTRY } from "../../../dist/lib/inference/ollama-model-
|
|||
const LARGE_OLLAMA_FIT_MEMORY_MB = Math.max(
|
||||
...OLLAMA_MODEL_REGISTRY.map((entry) => entry.requiredMemoryMB),
|
||||
);
|
||||
|
||||
import {
|
||||
CONTAINER_REACHABILITY_IMAGE,
|
||||
DEFAULT_OLLAMA_MODEL,
|
||||
LOCAL_INFERENCE_SANDBOX_HOST_URL_ENV,
|
||||
QWEN3_6_OLLAMA_MODEL,
|
||||
getOllamaContainerPort,
|
||||
resetOllamaContainerPortCache,
|
||||
getDefaultOllamaModel,
|
||||
getBootstrapOllamaModelOptions,
|
||||
getDefaultOllamaModel,
|
||||
getLocalProviderBaseUrl,
|
||||
getLocalProviderContainerReachabilityCheck,
|
||||
getLocalProviderHealthCheck,
|
||||
getLocalProviderHealthEndpoint,
|
||||
getLocalProviderLabel,
|
||||
getLocalProviderValidationBaseUrl,
|
||||
getOllamaContainerPort,
|
||||
getOllamaModelOptions,
|
||||
getOllamaProbeCommand,
|
||||
getOllamaWarmupCommand,
|
||||
isOllamaRunnerCrash,
|
||||
LOCAL_INFERENCE_SANDBOX_HOST_URL_ENV,
|
||||
parseOllamaList,
|
||||
parseOllamaTags,
|
||||
probeLocalProviderHealth,
|
||||
validateOllamaModel,
|
||||
QWEN3_6_OLLAMA_MODEL,
|
||||
resetOllamaContainerPortCache,
|
||||
validateLocalProvider,
|
||||
} from "../../../dist/lib/inference/local";
|
||||
validateOllamaModel,
|
||||
} from "./local";
|
||||
|
||||
describe("local inference helpers", () => {
|
||||
const originalSandboxHostUrl = process.env[LOCAL_INFERENCE_SANDBOX_HOST_URL_ENV];
|
||||
|
|
@ -185,37 +185,6 @@ describe("local inference helpers", () => {
|
|||
expect(callCount).toBe(2);
|
||||
});
|
||||
|
||||
it("rejects non-WSL Ollama when the backend and proxy ports collide", () => {
|
||||
const output = execFileSync(
|
||||
process.execPath,
|
||||
[
|
||||
"-e",
|
||||
[
|
||||
"const platform = require('./dist/lib/platform.js');",
|
||||
"platform.isWsl = () => false;",
|
||||
"const localInference = require('./dist/lib/inference/local.js');",
|
||||
"const result = localInference.validateLocalProvider('ollama-local', () => '{\"models\":[]}');",
|
||||
"process.stdout.write(JSON.stringify(result));",
|
||||
].join(""),
|
||||
],
|
||||
{
|
||||
cwd: path.resolve(__dirname, "../../.."),
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
NEMOCLAW_OLLAMA_PORT: "11435",
|
||||
NEMOCLAW_OLLAMA_PROXY_PORT: "11435",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const result = JSON.parse(output);
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.message).toContain("NEMOCLAW_OLLAMA_PORT");
|
||||
expect(result.message).toContain("NEMOCLAW_OLLAMA_PROXY_PORT");
|
||||
expect(result.message).toContain("11435");
|
||||
});
|
||||
|
||||
it("returns a clear error when ollama-local is unavailable", () => {
|
||||
const result = validateLocalProvider("ollama-local", () => "");
|
||||
expect(result.ok).toBe(false);
|
||||
|
|
@ -679,7 +648,7 @@ describe("local inference helpers", () => {
|
|||
});
|
||||
|
||||
it("filters installed-model selection by memory fit", async () => {
|
||||
const { getDefaultOllamaModel: gdom } = await import("../../../dist/lib/inference/local");
|
||||
const { getDefaultOllamaModel: gdom } = await import("./local");
|
||||
// Even though nemotron-3-nano:30b is installed, it does not fit a host
|
||||
// with only 12 GiB available — the selector must downgrade to a fitting
|
||||
// installed model rather than blindly returning DEFAULT_OLLAMA_MODEL.
|
||||
|
|
@ -690,7 +659,7 @@ describe("local inference helpers", () => {
|
|||
});
|
||||
|
||||
it("resolveNonInteractiveOllamaModel respects unknown tags and downgrades known oversize ones", async () => {
|
||||
const { resolveNonInteractiveOllamaModel } = await import("../../../dist/lib/inference/local");
|
||||
const { resolveNonInteractiveOllamaModel } = await import("./local");
|
||||
const messages: string[] = [];
|
||||
const log = (m: string) => messages.push(m);
|
||||
|
||||
|
|
@ -730,7 +699,7 @@ describe("local inference helpers", () => {
|
|||
});
|
||||
|
||||
it("resolveNonInteractiveOllamaModel surfaces the no-fit warning when even the smallest model exceeds available memory", async () => {
|
||||
const { resolveNonInteractiveOllamaModel } = await import("../../../dist/lib/inference/local");
|
||||
const { resolveNonInteractiveOllamaModel } = await import("./local");
|
||||
const messages: string[] = [];
|
||||
const log = (m: string) => messages.push(m);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import {
|
|||
promptManualModelId,
|
||||
promptRemoteModel,
|
||||
promptVllmModel,
|
||||
} from "../../../dist/lib/inference/model-prompts";
|
||||
import { VLLM_MODELS, modelsForPlatform } from "../../../dist/lib/inference/vllm-models";
|
||||
} from "./model-prompts";
|
||||
import { modelsForPlatform, VLLM_MODELS } from "./vllm-models";
|
||||
|
||||
function promptSequence(responses: string[]) {
|
||||
const queue = [...responses];
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import { createRequire } from "module";
|
|||
import type { Mock } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// Import from compiled dist/ for coverage attribution.
|
||||
import * as nim from "../../../dist/lib/inference/nim";
|
||||
// Import source directly so tests cannot pass against a stale build.
|
||||
import * as nim from "./nim";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const NIM_DIST_PATH = require.resolve("../../../dist/lib/inference/nim");
|
||||
const RUNNER_PATH = require.resolve("../../../dist/lib/runner");
|
||||
const NIM_DIST_PATH = require.resolve("./nim");
|
||||
const RUNNER_PATH = require.resolve("../runner");
|
||||
const fs = require("fs");
|
||||
const NIM_API_KEY_ENV_KEYS = ["NGC_API_KEY", "NVIDIA_INFERENCE_API_KEY", "NVIDIA_API_KEY"];
|
||||
function clearNimApiKeyEnv(): Array<[string, string | undefined]> {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
extractNousRecommendedModelOptions,
|
||||
getHermesProviderModelOptions,
|
||||
mergeModelOptions,
|
||||
} from "../../../dist/lib/inference/nous-models";
|
||||
} from "./nous-models";
|
||||
|
||||
describe("Nous recommended model helpers", () => {
|
||||
it("prepends paid portal recommendations and keeps fallback models for the full list", () => {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
OLLAMA_DOWNLOAD_SIZE_FALLBACK_BYTES,
|
||||
OLLAMA_MODEL_REGISTRY,
|
||||
SMALLEST_OLLAMA_MODEL_TAG,
|
||||
} from "../../../dist/lib/inference/ollama-model-registry";
|
||||
} from "./ollama-model-registry";
|
||||
|
||||
describe("OLLAMA_MODEL_REGISTRY", () => {
|
||||
it("is ordered largest-first by requiredMemoryMB", () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { validateOllamaModel } from "../../../dist/lib/inference/local";
|
||||
import { validateOllamaModel } from "./local";
|
||||
|
||||
describe("Ollama probe timeout retry", () => {
|
||||
it("retries with extended timeout on non-Spark hosts when first probe times out", () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
probeOllamaRuntimeModelStatus,
|
||||
resetOllamaRuntimeContextWindowAutoState,
|
||||
resolveOllamaRuntimeContextWindow,
|
||||
} from "../../../dist/lib/inference/ollama-runtime-context";
|
||||
} from "./ollama-runtime-context";
|
||||
|
||||
const getOllamaHost = () => "127.0.0.1";
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
getRunningOllamaDaemonVersion,
|
||||
isOllamaVersionAtLeast,
|
||||
MIN_OLLAMA_VERSION,
|
||||
} from "../../../dist/lib/inference/ollama-version";
|
||||
} from "./ollama-version";
|
||||
|
||||
describe("Ollama version detection", () => {
|
||||
it("parses 'ollama version is X.Y.Z' output", () => {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
formatModelSize,
|
||||
getOllamaModelSize,
|
||||
probeRegistrySize,
|
||||
} from "../../../../dist/lib/inference/ollama/model-size";
|
||||
} from "./model-size";
|
||||
|
||||
const MANIFEST = JSON.stringify({
|
||||
layers: [{ size: 1_000_000_000 }, { size: 200_000_000 }, { size: 25_000 }],
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import { createRequire } from "node:module";
|
|||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const PROXY_DIST = require.resolve("../../../../dist/lib/inference/ollama/proxy");
|
||||
const LOCAL_DIST = require.resolve("../../../../dist/lib/inference/local");
|
||||
const CREDS_DIST = require.resolve("../../../../dist/lib/credentials/store");
|
||||
const PROXY_DIST = require.resolve("./proxy");
|
||||
const LOCAL_DIST = require.resolve("../local");
|
||||
const CREDS_DIST = require.resolve("../../credentials/store");
|
||||
|
||||
interface MockSetup {
|
||||
installed: string[];
|
||||
|
|
@ -15,7 +15,7 @@ interface MockSetup {
|
|||
}
|
||||
|
||||
function loadProxyWithMocks(setup: MockSetup): {
|
||||
proxy: typeof import("../../../../dist/lib/inference/ollama/proxy");
|
||||
proxy: typeof import("./proxy");
|
||||
promptArgs: string[];
|
||||
restore: () => void;
|
||||
} {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { createRequire } from "node:module";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const WINDOWS_DIST_PATH = require.resolve("../../../../dist/lib/inference/ollama/windows");
|
||||
const RUNNER_PATH = require.resolve("../../../../dist/lib/runner");
|
||||
const WINDOWS_DIST_PATH = require.resolve("./windows");
|
||||
const RUNNER_PATH = require.resolve("../../runner");
|
||||
const childProcess = require("node:child_process");
|
||||
|
||||
function commandText(command: string | string[]): string {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const {
|
||||
isHijackedDockerInternalUrl,
|
||||
} = require("../../../dist/lib/inference/onboard-host-docker-internal");
|
||||
const {
|
||||
isSandboxInternalUrl,
|
||||
probeOpenAiLikeEndpoint,
|
||||
} = require("../../../dist/lib/inference/onboard-probes");
|
||||
const { isHijackedDockerInternalUrl } = require("./onboard-host-docker-internal");
|
||||
const { isSandboxInternalUrl, probeOpenAiLikeEndpoint } = require("./onboard-probes");
|
||||
|
||||
describe("host.docker.internal onboarding inference policy", () => {
|
||||
it("does not treat host.docker.internal as a usable sandbox URL", () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
|
@ -19,7 +18,7 @@ const {
|
|||
isSandboxInternalUrl,
|
||||
probeOpenAiLikeEndpoint,
|
||||
RETRIABLE_HTTP_PROBE_STATUSES,
|
||||
} = require("../../../dist/lib/inference/onboard-probes");
|
||||
} = require("./onboard-probes");
|
||||
|
||||
describe("OpenAI-compatible inference probe response parsing", () => {
|
||||
it("detects tool-calling responses payloads conservatively", () => {
|
||||
|
|
@ -739,80 +738,6 @@ exit 0
|
|||
}
|
||||
});
|
||||
|
||||
it("retries strict tool-call validation after the parent curl process times out", () => {
|
||||
const repoRoot = path.join(import.meta.dirname, "../../..");
|
||||
const onboardProbePath = JSON.stringify(
|
||||
path.join(repoRoot, "dist", "lib", "inference", "onboard-probes.js"),
|
||||
);
|
||||
const httpProbePath = JSON.stringify(
|
||||
path.join(repoRoot, "dist", "lib", "adapters", "http", "probe.js"),
|
||||
);
|
||||
const script = `
|
||||
const httpProbe = require(${httpProbePath});
|
||||
let calls = 0;
|
||||
const timeoutMs = [];
|
||||
httpProbe.runCurlProbe = (_args, opts = {}) => {
|
||||
calls += 1;
|
||||
timeoutMs.push(opts.timeoutMs ?? null);
|
||||
if (calls === 1) {
|
||||
return {
|
||||
ok: false,
|
||||
httpStatus: 0,
|
||||
curlStatus: -110,
|
||||
body: "",
|
||||
stderr: "spawnSync curl ETIMEDOUT",
|
||||
message: "curl failed (exit -110): spawnSync curl ETIMEDOUT",
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
httpStatus: 200,
|
||||
curlStatus: 0,
|
||||
body: JSON.stringify({
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
tool_calls: [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "sessions_send",
|
||||
arguments: { message: "hello" },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
stderr: "",
|
||||
message: "HTTP 200",
|
||||
};
|
||||
};
|
||||
const probes = require(${onboardProbePath});
|
||||
const result = probes.probeOpenAiLikeEndpoint(
|
||||
"https://api.example.com/v1",
|
||||
"test-model",
|
||||
null,
|
||||
{ skipResponsesProbe: true, requireChatCompletionsToolCalling: true },
|
||||
);
|
||||
process.stdout.write(JSON.stringify({ result, calls, timeoutMs }));
|
||||
`;
|
||||
|
||||
const run = spawnSync(process.execPath, ["-e", script], {
|
||||
cwd: repoRoot,
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
expect(run.status).toBe(0);
|
||||
const payload = JSON.parse(run.stdout);
|
||||
expect(payload.result).toMatchObject({ ok: true, api: "openai-completions" });
|
||||
expect(payload.calls).toBe(2);
|
||||
expect(payload.timeoutMs).toHaveLength(2);
|
||||
expect(payload.timeoutMs[0]).toBeGreaterThan(0);
|
||||
expect(payload.timeoutMs[1]).toBeGreaterThan(payload.timeoutMs[0]);
|
||||
});
|
||||
|
||||
it("keeps retrying when initial timeout is followed by a transient 502", () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-timeout-502-probe-"));
|
||||
const fakeBin = path.join(tmpDir, "bin");
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
validateAnthropicModel,
|
||||
validateNvidiaEndpointModel,
|
||||
validateOpenAiLikeModel,
|
||||
} from "../../../dist/lib/inference/provider-models";
|
||||
} from "./provider-models";
|
||||
|
||||
describe("provider model helpers", () => {
|
||||
it("fetches NVIDIA endpoint model ids", () => {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
DEFAULT_VLLM_MODEL,
|
||||
VLLM_EXTRA_ARGS_ENV,
|
||||
VLLM_MODELS,
|
||||
assertGatedModelAccess,
|
||||
buildVllmServeCommand,
|
||||
DEFAULT_VLLM_MODEL,
|
||||
modelsForPlatform,
|
||||
parseVllmExtraServeArgs,
|
||||
preflightVllmModelEnv,
|
||||
selectVllmModelFromEnv,
|
||||
} from "../../../dist/lib/inference/vllm-models";
|
||||
VLLM_EXTRA_ARGS_ENV,
|
||||
VLLM_MODELS,
|
||||
} from "./vllm-models";
|
||||
|
||||
describe("vllm model registry", () => {
|
||||
it("returns null when NEMOCLAW_VLLM_MODEL is unset so the caller can fall back to the profile default", () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { applyVllmRuntimeContextWindow } from "../../../dist/lib/inference/vllm-runtime-context";
|
||||
import { applyVllmRuntimeContextWindow } from "./vllm-runtime-context";
|
||||
|
||||
function applyContextWindow(
|
||||
modelsResponse: unknown,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
mintAgentKeyWithAccessToken,
|
||||
pollForToken,
|
||||
refreshAccessTokenWithRefreshToken,
|
||||
} from "../../dist/lib/oauth-device-code";
|
||||
} from "./oauth-device-code";
|
||||
|
||||
describe("pollForToken", () => {
|
||||
it("rejects successful token responses missing an access token", async () => {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
import assert from "node:assert/strict";
|
||||
import { afterEach, beforeEach, describe, it, vi } from "vitest";
|
||||
import { getAgentChoices, loadAgent } from "../../../dist/lib/agent/defs";
|
||||
import { resolveAgent } from "../../../dist/lib/agent/onboard";
|
||||
import { createSelectOnboardAgent } from "../../../dist/lib/onboard/agent-selection";
|
||||
import { selectFromNumberedMenuOrExit } from "../../../dist/lib/onboard/prompt-helpers";
|
||||
import { getAgentChoices, loadAgent } from "../agent/defs";
|
||||
import { resolveAgent } from "../agent/onboard";
|
||||
import { createSelectOnboardAgent } from "./agent-selection";
|
||||
import { selectFromNumberedMenuOrExit } from "./prompt-helpers";
|
||||
|
||||
// Exercises the real agent registry (agents/openclaw + agents/hermes) so the
|
||||
// red->green transition reflects genuine wizard behavior rather than a mock.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { selectBedrockRuntimeCustomAnthropic } from "../../../dist/lib/onboard/bedrock-runtime";
|
||||
import { BACK_TO_SELECTION } from "../../../dist/lib/onboard/credential-navigation";
|
||||
import { selectBedrockRuntimeCustomAnthropic } from "./bedrock-runtime";
|
||||
import { BACK_TO_SELECTION } from "./credential-navigation";
|
||||
|
||||
const BEDROCK_URL = "https://bedrock-runtime.us-east-1.amazonaws.com";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { setOnboardBrandingAgent } from "./branding";
|
||||
import {
|
||||
printContainerDnsRemediation,
|
||||
printDockerBridgeContainerStartFailure,
|
||||
} from "../../../dist/lib/onboard/bridge-dns-preflight";
|
||||
import { setOnboardBrandingAgent } from "../../../dist/lib/onboard/branding";
|
||||
} from "./bridge-dns-preflight";
|
||||
|
||||
describe("printDockerBridgeContainerStartFailure", () => {
|
||||
const savedInvokedAs = process.env.NEMOCLAW_INVOKED_AS;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ import os from "node:os";
|
|||
import path from "node:path";
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { CUSTOM_BUILD_CONTEXT_WARN_BYTES } from "../../../dist/lib/onboard/custom-build-context";
|
||||
import { stageCreateSandboxBuildContext } from "../../../dist/lib/onboard/build-context-stage";
|
||||
import { stageCreateSandboxBuildContext } from "./build-context-stage";
|
||||
import { CUSTOM_BUILD_CONTEXT_WARN_BYTES } from "./custom-build-context";
|
||||
|
||||
const tmpDirs: string[] = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { hydrateCredentialEnv } from "../../../dist/lib/onboard/credential-env";
|
||||
import { hydrateCredentialEnv } from "./credential-env";
|
||||
|
||||
const ORIGINAL_TELEGRAM_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
BACK_TO_SELECTION,
|
||||
returningToProviderSelection,
|
||||
shouldReturnToProviderSelection,
|
||||
} from "../../../dist/lib/onboard/credential-navigation";
|
||||
} from "./credential-navigation";
|
||||
|
||||
describe("credential prompt navigation helpers", () => {
|
||||
it("treats both the shared back sentinel and credential back intents as provider-selection navigation", () => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
getRegistryOccupiedDashboardPorts,
|
||||
preflightDashboardPortRangeAvailability,
|
||||
resolveCreateSandboxDashboardPort,
|
||||
} from "../../../dist/lib/onboard/dashboard-port";
|
||||
} from "./dashboard-port";
|
||||
|
||||
describe("findDashboardForwardOwner", () => {
|
||||
it("parses openshell forward list column format (#2169)", () => {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue