Skip to content

Commit 2746c3b

Browse files
committed
test: parameterize remaining suite generators
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
1 parent 588bb6d commit 2746c3b

27 files changed

Lines changed: 332 additions & 232 deletions

nemoclaw/src/register.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ describe("before_tool_call secret scanner hook (#1233)", () => {
392392
expect(result).toMatchObject({ block: true });
393393
});
394394

395-
it("blocks normalized relative memory paths when the host resolver is unavailable", () => {
395+
function verifyRelativeMemoryPathRejection() {
396396
const api = createMockApi();
397397
(api.resolvePath as unknown as ReturnType<typeof vi.fn>).mockReturnValue(
398398
undefined as unknown as string,
@@ -414,7 +414,12 @@ describe("before_tool_call secret scanner hook (#1233)", () => {
414414
});
415415
expect(result).toMatchObject({ block: true });
416416
}
417-
});
417+
}
418+
419+
it(
420+
"blocks normalized relative memory paths when the host resolver is unavailable",
421+
verifyRelativeMemoryPathRejection,
422+
);
418423
});
419424

420425
describe("getPluginConfig", () => {

src/lib/actions/inference-set-compatible-provider.test.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,17 +1062,29 @@ describe("runInferenceSet compatible providers", () => {
10621062
expect(deps.calls.writeSandboxConfig).not.toHaveBeenCalled();
10631063
});
10641064

1065-
for (const provider of ["compatible-endpoint", "compatible-anthropic-endpoint"]) {
1066-
it.each([
1067-
["loopback", "http://127.0.0.1:8000/v1", "93.184.216.34"],
1068-
["localhost", "http://localhost:8000/v1", "93.184.216.34"],
1069-
["link-local", "http://169.254.169.254/latest", "93.184.216.34"],
1070-
["RFC1918", "http://10.0.0.1:8000/v1", "93.184.216.34"],
1071-
["non-allowlisted internal", "http://evil.host.openshell.internal:18767/v1", "93.184.216.34"],
1072-
["HTTPS bridge", "https://host.openshell.internal:18767/v1", "93.184.216.34"],
1073-
["privileged-port bridge", "http://host.openshell.internal:80/v1", "93.184.216.34"],
1074-
["DNS-private", "https://private-resolution.example/v1", "10.0.0.8"],
1075-
])(`rejects %s endpoint metadata for ${provider}`, async (_kind, endpointUrl, resolvedAddress) => {
1065+
it.each(
1066+
(["compatible-endpoint", "compatible-anthropic-endpoint"] as const).flatMap((provider) =>
1067+
[
1068+
["loopback", "http://127.0.0.1:8000/v1", "93.184.216.34"],
1069+
["localhost", "http://localhost:8000/v1", "93.184.216.34"],
1070+
["link-local", "http://169.254.169.254/latest", "93.184.216.34"],
1071+
["RFC1918", "http://10.0.0.1:8000/v1", "93.184.216.34"],
1072+
[
1073+
"non-allowlisted internal",
1074+
"http://evil.host.openshell.internal:18767/v1",
1075+
"93.184.216.34",
1076+
],
1077+
["HTTPS bridge", "https://host.openshell.internal:18767/v1", "93.184.216.34"],
1078+
["privileged-port bridge", "http://host.openshell.internal:80/v1", "93.184.216.34"],
1079+
["DNS-private", "https://private-resolution.example/v1", "10.0.0.8"],
1080+
].map(
1081+
([kind, endpointUrl, resolvedAddress]) =>
1082+
[kind, provider, endpointUrl, resolvedAddress] as const,
1083+
),
1084+
),
1085+
)(
1086+
"rejects %s endpoint metadata for %s",
1087+
async (_kind, provider, endpointUrl, resolvedAddress) => {
10761088
const actualConfig =
10771089
await vi.importActual<typeof import("../sandbox/config")>("../sandbox/config");
10781090
const lookup = vi.fn(async () => [{ address: resolvedAddress, family: 4 }]);
@@ -1114,6 +1126,6 @@ describe("runInferenceSet compatible providers", () => {
11141126

11151127
expect(deps.calls.captureOpenshell).not.toHaveBeenCalled();
11161128
expect(deps.calls.updateSandbox).not.toHaveBeenCalled();
1117-
});
1118-
}
1129+
},
1130+
);
11191131
});

src/lib/actions/sandbox/connect-route-containment.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ describe("connect route containment", () => {
413413
expect(exitSpy).not.toHaveBeenCalled();
414414
});
415415

416-
it("scopes every inference read and repair write to the target non-default gateway", async () => {
416+
async function verifyInferenceRouteGatewayContainment() {
417417
const alpha = {
418418
name: "alpha",
419419
agent: "openclaw",
@@ -469,5 +469,10 @@ describe("connect route containment", () => {
469469
expect.arrayContaining(["-g", "nemoclaw"]),
470470
);
471471
expect(harness.runSetupDnsProxySpy).not.toHaveBeenCalled();
472-
});
472+
}
473+
474+
it(
475+
"scopes every inference read and repair write to the target non-default gateway",
476+
verifyInferenceRouteGatewayContainment,
477+
);
473478
});

src/lib/actions/sandbox/dcode-activity-probe.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,13 @@ describe("dcode activity probe", () => {
121121
);
122122
});
123123

124-
it("parses every declared probe state", () => {
124+
function verifyDeclaredActivityProbeStates() {
125125
for (const state of Object.values(DCODE_PROBE_STATE)) {
126126
expect(parseDcodeProbeState(`${DCODE_PROBE_PREFIX}${state}\n`)).toBe(state);
127127
}
128-
});
128+
}
129+
130+
it("parses every declared probe state", verifyDeclaredActivityProbeStates);
129131

130132
it("parses exactly one probe sentinel from sandbox exec output", () => {
131133
expect(parseDcodeProbeState(`${DCODE_PROBE_PREFIX}idle\n`)).toBe(

src/lib/actions/sandbox/gateway-restart.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ afterEach(() => {
1313
});
1414

1515
describe("gateway restart failure markers", () => {
16-
it("keeps supervisor failure markers aligned with the classifier", () => {
16+
function verifySupervisorFailureMarkers() {
1717
const expectedMarkers: Array<
1818
[string, ReturnType<typeof classifyGatewayRestartFailure>["layer"]]
1919
> = [
@@ -49,7 +49,9 @@ describe("gateway restart failure markers", () => {
4949
}),
5050
).toMatchObject({ layer });
5151
}
52-
});
52+
}
53+
54+
it("keeps supervisor failure markers aligned with the classifier", verifySupervisorFailureMarkers);
5355
});
5456

5557
describe("gateway restart failure classification precedence", () => {

src/lib/actions/sandbox/mcp-bridge-input-validation.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ describe("MCP CLI input validation", () => {
213213
expect(() => parseMcpAddArgs(["github", "--url", "stdio://github"])).toThrow(/https/);
214214
});
215215

216-
it("normalizes URLs without persisting credentials", () => {
216+
function verifyCredentialFreeUrlNormalization() {
217217
expect(normalizeMcpServerUrl("https://mcp.example.test")).toBe("https://mcp.example.test/");
218218
expect(() => normalizeMcpServerUrl("https://user:pass@mcp.example.test/mcp")).toThrow(
219219
/must not embed credentials/,
@@ -288,7 +288,9 @@ describe("MCP CLI input validation", () => {
288288
/literal and canonical/,
289289
);
290290
}
291-
});
291+
}
292+
293+
it("normalizes URLs without persisting credentials", verifyCredentialFreeUrlNormalization);
292294

293295
it("rejects a malformed credential-bearing URL without echoing it (#8698)", () => {
294296
const user = "qa-user";

src/lib/domain/installer/provider.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ describe("installer provider helpers", () => {
2525
expect(normalizeInstallerProvider("unsupported")).toBeNull();
2626
});
2727

28-
it("keeps provider values and aliases aligned with normalization", () => {
28+
function verifyProviderAliasNormalization() {
2929
for (const provider of INSTALLER_PROVIDER_VALUES) {
3030
expect(normalizeInstallerProvider(provider)).toBe(provider);
3131
}
3232
for (const [alias, provider] of Object.entries(INSTALLER_PROVIDER_ALIASES)) {
3333
expect(normalizeInstallerProvider(alias)).toBe(provider);
3434
}
35-
});
35+
}
36+
37+
it("keeps provider values and aliases aligned with normalization", verifyProviderAliasNormalization);
3638

3739
it("keeps help text values aligned with install.sh usage", () => {
3840
expect(installerProviderHelpValues()).toBe(

src/lib/domain/lifecycle/options.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ describe("lifecycle option normalization", () => {
116116
});
117117
});
118118

119-
it("recognises common truthy/falsy spellings of NEMOCLAW_CLEANUP_GATEWAY", () => {
119+
function verifyCleanupGatewayBooleanSpellings() {
120120
for (const truthy of ["1", "true", "TRUE", "Yes"]) {
121121
process.env[ENV_KEY] = truthy;
122122
expect(normalizeDestroySandboxOptions({}).cleanupGateway).toBe(true);
@@ -129,7 +129,9 @@ describe("lifecycle option normalization", () => {
129129
process.env[ENV_KEY] = noise;
130130
expect(normalizeDestroySandboxOptions({}).cleanupGateway).toBeUndefined();
131131
}
132-
});
132+
}
133+
134+
it("recognises common truthy/falsy spellings of NEMOCLAW_CLEANUP_GATEWAY", verifyCleanupGatewayBooleanSpellings);
133135
});
134136

135137
it("preserves typed rebuild options and still accepts compatibility argv", () => {

src/lib/onboard/initial-policy-real-policy.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ describe("initial sandbox policy real preset merge", () => {
332332
expect(effective.network_policies?.["observability-otlp-local"]).toBeDefined();
333333
});
334334

335-
it("keeps effective shipping policy methods explicit and avoids deprecated REST TLS mode", () => {
335+
function verifyShippingPolicyMethods() {
336336
const policyCases = [
337337
{ path: ["nemoclaw-blueprint", "policies", "openclaw-sandbox.yaml"], agent: "openclaw" },
338338
{
@@ -363,7 +363,12 @@ describe("initial sandbox policy real preset merge", () => {
363363
}
364364
}
365365
}
366-
});
366+
}
367+
368+
it(
369+
"keeps effective shipping policy methods explicit and avoids deprecated REST TLS mode",
370+
verifyShippingPolicyMethods,
371+
);
367372

368373
it("keeps the Restricted OpenClaw npm baseline inspected and GET-only (#8497)", () => {
369374
const baselinePath = repoPath("nemoclaw-blueprint", "policies", "openclaw-sandbox.yaml");

src/lib/onboard/sandbox-lifecycle.test.ts

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -94,50 +94,54 @@ describe("sandbox lifecycle MCP destroy boundaries", () => {
9494
onboardSessionState.recreate = null;
9595
});
9696

97-
for (const marker of ["destroyPreparedAt", "destroyPendingAt"] as const) {
98-
for (const withBridge of [false, true]) {
99-
it(`preserves ${marker} and blocks absent-sandbox recreation${withBridge ? " with bridges" : " without bridges"}`, () => {
100-
const runCaptureOpenshell = vi.fn(() => null);
101-
registryState.sandbox = {
102-
name: "alpha",
103-
agent: "openclaw",
104-
mcp: {
105-
bridges: withBridge
106-
? {
107-
github: {
108-
server: "github",
109-
agent: "openclaw",
110-
adapter: "mcporter",
111-
url: "https://mcp.example.test/mcp",
112-
env: ["GITHUB_TOKEN"],
113-
providerName: "alpha-mcp-github",
114-
providerId: "provider-123",
115-
policyName: "mcp-github",
116-
addedAt: "2026-07-02T22:49:42.000Z",
117-
},
118-
}
119-
: {},
120-
[marker]: "2026-07-02T22:49:42.000Z",
121-
},
122-
};
123-
const before = JSON.stringify(registryState.sandbox);
124-
const helpers = createSandboxLifecycleHelpers({
125-
runCaptureOpenshell,
126-
fetchGatewayAuthTokenFromSandbox: () => null,
127-
agentProductName: () => "OpenClaw",
128-
prompt: async () => "no",
129-
isAffirmativeAnswer: () => false,
130-
});
131-
132-
expect(() => helpers.inspectSandboxForCreate("alpha")).toThrow(
133-
/incomplete MCP destroy transaction.*finish cleanup before recreating/i,
134-
);
135-
expect(runCaptureOpenshell).not.toHaveBeenCalled();
136-
expect(registryState.removeSandbox).not.toHaveBeenCalled();
137-
expect(JSON.stringify(registryState.sandbox)).toBe(before);
97+
it.each([
98+
["destroyPreparedAt", "without bridges", false],
99+
["destroyPreparedAt", "with bridges", true],
100+
["destroyPendingAt", "without bridges", false],
101+
["destroyPendingAt", "with bridges", true],
102+
] as const)(
103+
"preserves %s and blocks absent-sandbox recreation %s",
104+
(marker, _bridgeState, withBridge) => {
105+
const runCaptureOpenshell = vi.fn(() => null);
106+
registryState.sandbox = {
107+
name: "alpha",
108+
agent: "openclaw",
109+
mcp: {
110+
bridges: withBridge
111+
? {
112+
github: {
113+
server: "github",
114+
agent: "openclaw",
115+
adapter: "mcporter",
116+
url: "https://mcp.example.test/mcp",
117+
env: ["GITHUB_TOKEN"],
118+
providerName: "alpha-mcp-github",
119+
providerId: "provider-123",
120+
policyName: "mcp-github",
121+
addedAt: "2026-07-02T22:49:42.000Z",
122+
},
123+
}
124+
: {},
125+
[marker]: "2026-07-02T22:49:42.000Z",
126+
},
127+
};
128+
const before = JSON.stringify(registryState.sandbox);
129+
const helpers = createSandboxLifecycleHelpers({
130+
runCaptureOpenshell,
131+
fetchGatewayAuthTokenFromSandbox: () => null,
132+
agentProductName: () => "OpenClaw",
133+
prompt: async () => "no",
134+
isAffirmativeAnswer: () => false,
138135
});
139-
}
140-
}
136+
137+
expect(() => helpers.inspectSandboxForCreate("alpha")).toThrow(
138+
/incomplete MCP destroy transaction.*finish cleanup before recreating/i,
139+
);
140+
expect(runCaptureOpenshell).not.toHaveBeenCalled();
141+
expect(registryState.removeSandbox).not.toHaveBeenCalled();
142+
expect(JSON.stringify(registryState.sandbox)).toBe(before);
143+
},
144+
);
141145

142146
it("keeps the source registry row when OpenShell reports no sandbox (#7736)", () => {
143147
const rows = new Map<string, SandboxEntry>([

0 commit comments

Comments
 (0)