Repository navigation
test(onboard): centralize sandbox identity fixtures #10476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
cd74a8a
test(onboard): centralize sandbox identity fixtures
apurvvkumaria 5187132
test(onboard): align managed create identity
apurvvkumaria 94d6e78
test(onboard): bind fixture create attempts
apurvvkumaria 6e5014b
merge(onboard): refresh sandbox fixture changes
apurvvkumaria e49c4fc
merge: resolve conflicts with main
github-actions[bot] b0ccca4
merge(onboard): reconcile sandbox identity fixture
apurvvkumaria 156479b
merge(e2e): refresh Brave fixture changes
apurvvkumaria 4af7879
test(onboard): keep managed sandbox identity consistent
apurvvkumaria 2f59c15
merge(onboard): reconcile remote fixture updates
apurvvkumaria f8c36a0
test(onboard): keep fixture observations consistent
apurvvkumaria 4b594e2
test(onboard): report missing fixture sandbox
apurvvkumaria 00d386b
test(onboard): remove duplicate selector case
apurvvkumaria 30ed049
test(onboard): bound replacement fixture identity
apurvvkumaria 7e736b2
test(onboard): defer shared fixture identity import
apurvvkumaria ddb456b
test(onboard): reject mismatched fixture gateways
apurvvkumaria bf28ca1
merge(onboard): reconcile durable identity fixture
apurvvkumaria b860d15
merge: resolve conflicts with main
github-actions[bot] 783b622
merge(onboard): refresh identity fixture consumers
apurvvkumaria 858e4e9
merge(onboard): preserve fixture identity on current main
apurvvkumaria c6d2235
merge: resolve conflicts with main
github-actions[bot] 7eb34a2
merge(test): refresh retained runtime improvements
apurvvkumaria 1130ac1
merge(main): integrate host PATH and onboarding fixes
apurvvkumaria 4de2595
test(onboard): route runtime identity through fixture
apurvvkumaria 31b3e0d
merge(main): integrate optional SDK decision
apurvvkumaria 09cd396
test(onboard): complete fixture watch selection
apurvvkumaria ab01a40
merge: resolve conflicts with main
github-actions[bot] 1f0efbf
merge(main): reconcile sandbox fixture infrastructure
apurvvkumaria e720637
merge(pr): retain validated fixture resolution
apurvvkumaria d6a3a76
test(onboard): finish fixture identity cutover
apurvvkumaria File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import { describe, expect, it } from "vitest"; | ||
| import { createRequire } from "node:module"; | ||
|
|
||
| import { | ||
| NEMOCLAW_CREATE_ATTEMPT_LABEL, | ||
| NEMOCLAW_CREATE_ATTEMPT_NONCE_HEX_LENGTH, | ||
| isOpenShellSandboxId, | ||
| parseOpenShellSandboxId, | ||
| parseStrictOpenShellSandboxListJson, | ||
| } from "../../src/lib/adapters/openshell/sandbox-identity"; | ||
|
|
||
| type CreatedSandboxFixture = { | ||
| readonly capture: (command: string[]) => string | null; | ||
| readonly create: (command: string[]) => void; | ||
| readonly delete: () => void; | ||
| readonly recreate: (command: string[]) => void; | ||
| readonly setPhase: (phase: string) => void; | ||
| readonly run: (command: string[]) => { status: number; stdout: Buffer; stderr: Buffer } | null; | ||
| readonly state: Readonly<{ | ||
| sandboxName: string; | ||
| sandboxId: string; | ||
| gatewayName: string; | ||
| phase: string; | ||
| lifecycleState: string; | ||
| generation: number; | ||
| createAttemptNonce: string | null; | ||
| ownerScopedIdentityObserved: boolean; | ||
| }>; | ||
| }; | ||
|
|
||
| const requireCjs = createRequire(import.meta.url); | ||
| const { createCreatedSandboxFixture } = requireCjs("./onboard-script-mocks.cjs") as { | ||
| createCreatedSandboxFixture: (options?: Record<string, unknown>) => CreatedSandboxFixture; | ||
| }; | ||
|
|
||
| const CREATE_ATTEMPT_NONCE = "a".repeat(NEMOCLAW_CREATE_ATTEMPT_NONCE_HEX_LENGTH); | ||
|
|
||
| function selectorListCommand(gatewayName: string | null, nonce = CREATE_ATTEMPT_NONCE): string[] { | ||
| return [ | ||
| "openshell", | ||
| "sandbox", | ||
| "list", | ||
| ...(gatewayName === null ? [] : ["-g", gatewayName]), | ||
| "--selector", | ||
| `${NEMOCLAW_CREATE_ATTEMPT_LABEL}=${nonce}`, | ||
| "--output", | ||
| "json", | ||
| "--limit", | ||
| "2", | ||
| ]; | ||
| } | ||
|
|
||
| function createCommand(nonce = CREATE_ATTEMPT_NONCE): string[] { | ||
| return ["openshell", "sandbox", "create", "--label", `${NEMOCLAW_CREATE_ATTEMPT_LABEL}=${nonce}`]; | ||
| } | ||
|
|
||
| describe("created sandbox fixture", () => { | ||
| it("uses one ID for create, list, and get observations (#10463)", () => { | ||
| const fixture = createCreatedSandboxFixture({ | ||
| sandboxName: "alpha", | ||
| sandboxId: "sandbox-alpha", | ||
| gatewayName: "gateway-alpha", | ||
| }); | ||
|
|
||
| expect(fixture.capture(selectorListCommand("gateway-alpha"))).toBe("[]"); | ||
| fixture.create(createCommand()); | ||
| const createdSandboxId = fixture.state.sandboxId; | ||
|
|
||
| const selectorOutput = fixture.capture(selectorListCommand("gateway-alpha")); | ||
| const rows = parseStrictOpenShellSandboxListJson(selectorOutput ?? ""); | ||
| expect(rows).toHaveLength(1); | ||
| expect(rows?.[0]?.id).toBe(createdSandboxId); | ||
|
|
||
| const listOutput = fixture.capture(["openshell", "sandbox", "list", "-g", "gateway-alpha"]); | ||
| expect(listOutput).toBe("alpha Ready\n"); | ||
| expect(fixture.state.sandboxId).toBe(createdSandboxId); | ||
| expect(fixture.run(["openshell", "sandbox", "get", "alpha"])).toBeNull(); | ||
|
|
||
| const getOutput = fixture.capture([ | ||
| "openshell", | ||
| "sandbox", | ||
| "get", | ||
| "-g", | ||
| "gateway-alpha", | ||
| "alpha", | ||
| ]); | ||
| expect(parseOpenShellSandboxId(getOutput ?? "")).toBe(createdSandboxId); | ||
| expect( | ||
| parseOpenShellSandboxId( | ||
| fixture.run(["openshell", "sandbox", "get", "alpha"])?.stdout.toString() ?? "", | ||
| ), | ||
| ).toBe(createdSandboxId); | ||
| }); | ||
|
|
||
| it("invalidates the prior ID before recreation publishes a new ID (#10463)", () => { | ||
| const fixture = createCreatedSandboxFixture({ | ||
| sandboxName: "alpha", | ||
| sandboxId: "sandbox-alpha", | ||
| gatewayName: "gateway-alpha", | ||
| }); | ||
| fixture.create(createCommand()); | ||
| const priorSandboxId = fixture.state.sandboxId; | ||
|
|
||
| fixture.delete(); | ||
| expect(fixture.capture(selectorListCommand("gateway-alpha"))).toBe("[]"); | ||
| expect(fixture.capture(["openshell", "sandbox", "get", "-g", "gateway-alpha", "alpha"])).toBe( | ||
| "", | ||
| ); | ||
| expect( | ||
| fixture.run(["openshell", "sandbox", "get", "-g", "gateway-alpha", "alpha"]), | ||
| ).toMatchObject({ status: 1 }); | ||
|
|
||
| const replacementNonce = "b".repeat(NEMOCLAW_CREATE_ATTEMPT_NONCE_HEX_LENGTH); | ||
| fixture.recreate(createCommand(replacementNonce)); | ||
| const replacementSandboxId = fixture.state.sandboxId; | ||
| expect(replacementSandboxId).not.toBe(priorSandboxId); | ||
| const replacementRows = parseStrictOpenShellSandboxListJson( | ||
| fixture.capture(selectorListCommand("gateway-alpha", replacementNonce)) ?? "", | ||
| ); | ||
| expect(fixture.capture(selectorListCommand("gateway-alpha"))).toBe("[]"); | ||
| expect(replacementRows?.[0]?.id).toBe(replacementSandboxId); | ||
| expect(replacementRows?.[0]?.id).not.toBe(priorSandboxId); | ||
| expect( | ||
| parseOpenShellSandboxId( | ||
| fixture.capture(["openshell", "sandbox", "get", "-g", "gateway-alpha", "alpha"]) ?? "", | ||
| ), | ||
| ).toBe(replacementSandboxId); | ||
| }); | ||
|
|
||
| it("keeps a replacement ID valid for a maximum-length input (#10463)", () => { | ||
| const maximumSandboxId = "a".repeat(512); | ||
| const fixture = createCreatedSandboxFixture({ sandboxId: maximumSandboxId }); | ||
| fixture.create(createCommand()); | ||
| fixture.delete(); | ||
| fixture.recreate(createCommand("b".repeat(NEMOCLAW_CREATE_ATTEMPT_NONCE_HEX_LENGTH))); | ||
|
|
||
| expect(fixture.state.sandboxId).not.toBe(maximumSandboxId); | ||
| expect(isOpenShellSandboxId(fixture.state.sandboxId)).toBe(true); | ||
| }); | ||
|
|
||
| it.each([ | ||
| ["a missing", undefined], | ||
| ["an empty", ""], | ||
| ["a malformed", "invalid/id"], | ||
| ])("rejects %s durable sandbox ID (#10463)", (_case, sandboxId) => { | ||
| expect(() => createCreatedSandboxFixture({ sandboxId })).toThrow( | ||
| "Created sandbox fixture requires one durable sandbox ID.", | ||
| ); | ||
| }); | ||
|
|
||
| it("does not answer an identity observation for another gateway (#10463)", () => { | ||
| const fixture = createCreatedSandboxFixture({ | ||
| sandboxName: "alpha", | ||
| sandboxId: "sandbox-alpha", | ||
| gatewayName: "gateway-alpha", | ||
| }); | ||
| fixture.create(createCommand()); | ||
|
|
||
| expect(fixture.capture(selectorListCommand("gateway-bravo"))).toBeNull(); | ||
| expect( | ||
| fixture.capture(["openshell", "sandbox", "get", "-g", "gateway-bravo", "alpha"]), | ||
| ).toBeNull(); | ||
| expect(fixture.capture(selectorListCommand(null))).toBeNull(); | ||
| expect(fixture.capture(["openshell", "sandbox", "get", "alpha"])).toBeNull(); | ||
| }); | ||
|
|
||
| it("does not answer a selector for another create attempt (#10463)", () => { | ||
| const fixture = createCreatedSandboxFixture({ gatewayName: "gateway-alpha" }); | ||
| fixture.create(createCommand()); | ||
|
|
||
| expect( | ||
| fixture.capture( | ||
| selectorListCommand("gateway-alpha", "b".repeat(NEMOCLAW_CREATE_ATTEMPT_NONCE_HEX_LENGTH)), | ||
| ), | ||
| ).toBe("[]"); | ||
| }); | ||
|
|
||
| it("rejects a malformed create-attempt nonce (#10463)", () => { | ||
| const fixture = createCreatedSandboxFixture(); | ||
|
|
||
| expect(() => fixture.create(createCommand("invalid"))).toThrow( | ||
| "Created sandbox fixture requires one valid create-attempt label.", | ||
| ); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.