Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c971e8a
test(cli): restore source-shape guard
cv May 16, 2026
dde761a
test(onboard): start source-shape cleanup
cv May 16, 2026
8a5d528
test(onboard): finish source-shape purge
cv May 16, 2026
b359431
test(onboard): split GPU helper coverage
cv May 16, 2026
426e3ab
Merge branch 'refactor/delete-unused-code' into test/split-onboard-tests
cv May 16, 2026
8f5f716
test(onboard): split policy helper coverage
cv May 16, 2026
4be5d21
test(onboard): split dashboard helper coverage
cv May 16, 2026
e307e79
test(onboard): split sandbox naming coverage
cv May 16, 2026
205b253
test(onboard): split openshell version coverage
cv May 16, 2026
00ddc47
test(onboard): split dockerfile patch coverage
cv May 16, 2026
0f93679
test(onboard): split gateway runtime coverage
cv May 16, 2026
945dd46
test(onboard): avoid URL substring assertions
cv May 16, 2026
c4a52ad
test(onboard): split Model Router coverage
cv May 16, 2026
104e26d
test(onboard): split provider routing helpers
cv May 16, 2026
a5e4340
test(onboard): split messaging coverage
cv May 16, 2026
3fe62e2
test(onboard): split custom Dockerfile coverage
cv May 16, 2026
11df6f3
test(onboard): split provider CRUD helpers
cv May 16, 2026
604a23b
refactor(onboard): extract credential hydration helper
cv May 16, 2026
47ed6c2
test(onboard): move pure helper coverage to modules
cv May 16, 2026
aab80c2
test(onboard): split dashboard port helpers
cv May 16, 2026
fe101c8
refactor(onboard): extract summary formatting
cv May 16, 2026
a5567da
test(onboard): split web search helpers
cv May 16, 2026
79167d3
test(onboard): split VM DNS monkeypatch coverage
cv May 16, 2026
4c46670
fix(onboard): reuse live forward parser for dashboard owner
cv May 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/lib/adapters/http/probe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ describe("http-probe helpers", () => {
expect(summarizeCurlFailure(7, "", " connection refused ")).toBe(
"curl failed (exit 7): connection refused",
);
expect(summarizeCurlFailure(28, "", "")).toBe("curl failed (exit 28)");
expect(summarizeCurlFailure(0, "", "")).toBe("curl failed (exit 0)");
});

it("summarizes JSON and text HTTP probe failures", () => {
Expand All @@ -36,6 +38,14 @@ describe("http-probe helpers", () => {
).toBe('HTTP 401: {"reason":"bad key","retry":false}');
expect(summarizeProbeError(" plain text body ", 500)).toBe("HTTP 500: plain text body");
expect(summarizeProbeFailure("", 0, 28, "timeout")).toBe("curl failed (exit 28): timeout");
expect(summarizeProbeFailure("body", 500, 7, "Connection refused")).toBe(
"curl failed (exit 7): Connection refused",
);
expect(summarizeProbeFailure("Not Found", 404, 0, "")).toBe("HTTP 404: Not Found");
expect(summarizeProbeFailure("", 0, 0, "")).toBe("HTTP 0 with no response body");
expect(summarizeProbeFailure(" Service Unavailable ", 503, 0, "")).toBe(
"HTTP 503: Service Unavailable",
);
});

it("captures successful curl output and cleans up the temp file", () => {
Expand Down
79 changes: 79 additions & 0 deletions src/lib/build-context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

import { printSandboxCreateRecoveryHints, shouldIncludeBuildContextPath } from "../../dist/lib/build-context";

type ConsoleErrorSpy = ReturnType<typeof vi.spyOn>;

describe("build context filtering", () => {
it("filters local-only artifacts out of the sandbox build context", () => {
expect(
shouldIncludeBuildContextPath(
"/repo/nemoclaw-blueprint",
"/repo/nemoclaw-blueprint/orchestrator/main.py",
),
).toBe(true);
expect(
shouldIncludeBuildContextPath(
"/repo/nemoclaw-blueprint",
"/repo/nemoclaw-blueprint/.venv/bin/python",
),
).toBe(false);
expect(
shouldIncludeBuildContextPath(
"/repo/nemoclaw-blueprint",
"/repo/nemoclaw-blueprint/.ruff_cache/cache",
),
).toBe(false);
});
});

describe("printSandboxCreateRecoveryHints", () => {
let errorSpy: ConsoleErrorSpy;

beforeEach(() => {
errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
});

afterEach(() => {
errorSpy.mockRestore();
});

function stderr(): string {
return errorSpy.mock.calls.map((call: unknown[]) => String(call[0])).join("\n");
}

it("prints resume guidance when sandbox image upload times out", () => {
printSandboxCreateRecoveryHints("failed to read image export stream");

expect(stderr()).toContain("image upload into the OpenShell gateway timed out");
expect(stderr()).toContain("onboard --resume");
expect(stderr()).toContain("Docker memory");
});

it("prints progress-specific resume guidance when upload reached the gateway", () => {
printSandboxCreateRecoveryHints(
[
"[progress] Uploaded to gateway",
"failed to read image export stream",
].join("\n"),
);

expect(stderr()).toContain("reuse existing gateway state");
});

it("prints resume guidance when sandbox image upload resets after transfer progress", () => {
printSandboxCreateRecoveryHints(
[
"Image openshell/sandbox-from:123 is available in the gateway.",
"Connection reset by peer",
].join("\n"),
);

expect(stderr()).toContain("image push/import stream was interrupted");
expect(stderr()).toContain("onboard --resume");
expect(stderr()).toContain("reached the gateway");
});
});
6 changes: 6 additions & 0 deletions src/lib/core/url-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ describe("normalizeProviderBaseUrl", () => {
expect(normalizeProviderBaseUrl("https://api.anthropic.com/v1/messages", "anthropic")).toBe(
"https://api.anthropic.com",
);
expect(normalizeProviderBaseUrl("https://proxy.example.com/v1", "anthropic")).toBe(
"https://proxy.example.com",
);
expect(normalizeProviderBaseUrl("https://proxy.example.com/v1/messages", "anthropic")).toBe(
"https://proxy.example.com",
);
});

it("strips trailing slashes", () => {
Expand Down
72 changes: 72 additions & 0 deletions src/lib/inference/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
VLLM_LOCAL_CREDENTIAL_ENV,
getOpenClawPrimaryModel,
getProviderSelectionConfig,
getSandboxInferenceConfig,
parseGatewayInference,
} from "../../../dist/lib/inference/config";

Expand Down Expand Up @@ -221,6 +222,77 @@ describe("inference selection config", () => {
});
});

describe("getSandboxInferenceConfig", () => {
it("maps NVIDIA Endpoints to the routed inference provider", () => {
expect(
getSandboxInferenceConfig("qwen/qwen3.5-397b-a17b", "nvidia-prod", "openai-completions"),
).toEqual({
providerKey: MANAGED_PROVIDER_ID,
primaryModelRef: `${MANAGED_PROVIDER_ID}/qwen/qwen3.5-397b-a17b`,
inferenceBaseUrl: INFERENCE_ROUTE_URL,
inferenceApi: "openai-completions",
inferenceCompat: null,
});
});

it("maps Model Router sandboxes through managed inference.local", () => {
expect(getSandboxInferenceConfig("nvidia-routed", "nvidia-router")).toEqual({
providerKey: MANAGED_PROVIDER_ID,
primaryModelRef: `${MANAGED_PROVIDER_ID}/nvidia-routed`,
inferenceBaseUrl: INFERENCE_ROUTE_URL,
inferenceApi: "openai-completions",
inferenceCompat: null,
});
});

it("leaves Kimi K2.6 compat to the model-specific setup registry", () => {
expect(
getSandboxInferenceConfig("moonshotai/kimi-k2.6", "nvidia-prod", "openai-completions"),
).toEqual({
providerKey: MANAGED_PROVIDER_ID,
primaryModelRef: `${MANAGED_PROVIDER_ID}/moonshotai/kimi-k2.6`,
inferenceBaseUrl: INFERENCE_ROUTE_URL,
inferenceApi: "openai-completions",
inferenceCompat: null,
});
});

it("maps OpenAI-compatible endpoints to the managed inference provider", () => {
expect(getSandboxInferenceConfig("deepseek-ai/DeepSeek-V4-Flash", "compatible-endpoint"))
.toEqual({
providerKey: MANAGED_PROVIDER_ID,
primaryModelRef: `${MANAGED_PROVIDER_ID}/deepseek-ai/DeepSeek-V4-Flash`,
inferenceBaseUrl: INFERENCE_ROUTE_URL,
inferenceApi: "openai-completions",
inferenceCompat: {
supportsStore: false,
},
});
});

it("maps Gemini to the routed inference provider with supportsStore disabled", () => {
expect(getSandboxInferenceConfig("gemini-2.5-flash", "gemini-api")).toEqual({
providerKey: MANAGED_PROVIDER_ID,
primaryModelRef: `${MANAGED_PROVIDER_ID}/gemini-2.5-flash`,
inferenceBaseUrl: INFERENCE_ROUTE_URL,
inferenceApi: "openai-completions",
inferenceCompat: {
supportsStore: false,
},
});
});

it("uses a probed Responses API override when one is available", () => {
expect(getSandboxInferenceConfig("gpt-5.4", "openai-api", "openai-responses")).toEqual({
providerKey: "openai",
primaryModelRef: "openai/gpt-5.4",
inferenceBaseUrl: INFERENCE_ROUTE_URL,
inferenceApi: "openai-responses",
inferenceCompat: null,
});
});
});

describe("parseGatewayInference", () => {
it("parses provider and model from openshell inference get output", () => {
const output = [
Expand Down
Loading
Loading