Skip to content

Commit 607ed6b

Browse files
test(workspace): answer awaited skill syncs offline in the swept tests
Syncs these tests now await could hit the real network: launch-resolve always (`localhost:5001`), state-account-scope and the plugin cache tests when `ALTIMATE_WORKSPACE` is exported. One shared stub, restored per test. Also asserts the unlink step makes no second request. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0174HUYiaceApqHP7EKMNgnb
1 parent 3bff76d commit 607ed6b

6 files changed

Lines changed: 51 additions & 23 deletions

File tree

‎packages/opencode/test/altimate/plugin/workspace.test.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const { syncInternals } = await import("../../../src/altimate/workspace/engine-s
5252
// and recordApprovedBinding for tenant/apiUrl scoping. Re-import allows
5353
// per-test override of the module state.
5454
import { AltimateApi } from "../../../src/altimate/api/client"
55+
import { stubEmptySkillList } from "../workspace/skill-list-fixture"
5556
const originalIsConfigured = AltimateApi.isConfigured
5657
const originalGetCreds = AltimateApi.getCredentials
5758
type Creds = Awaited<ReturnType<typeof AltimateApi.getCredentials>>
@@ -145,9 +146,13 @@ describe("detectProjectRemote", () => {
145146
// ─────────────────────────────────────────────────────────────────────────────
146147

147148
describe("workspace binding cache", () => {
149+
let restoreFetch = () => {}
148150
beforeEach(() => {
149151
stubCreds("acme", "https://api.acme.example.com")
152+
// Recording a link here awaits its skill sync; answered offline, whatever the workspace flag is.
153+
restoreFetch = stubEmptySkillList("api.acme.example.com")
150154
})
155+
afterEach(() => restoreFetch())
151156

152157
test("records and reads back a binding for the same directory + tenant", async () => {
153158
await recordApprovedBinding("/work/proj-a", {

‎packages/opencode/test/altimate/workspace/identity-section.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ describe("systemSection", () => {
517517
// step 2 needs no request of its own; joined to step 1's resolve, it could only render what
518518
// step 1 knew.
519519
const next = await inProject(systemSection)
520+
expect(calls).toBe(1)
520521
release()
521522
await first
522523
expect(next).not.toContain('is "old"')

‎packages/opencode/test/altimate/workspace/launch-resolve.test.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const { recordApprovedBinding, cachePath } = await import(
4141

4242
// Stub credentials so state.ts's tenant/apiUrl scoping is deterministic.
4343
import { AltimateApi } from "../../../src/altimate/api/client"
44+
import { stubEmptySkillList } from "./skill-list-fixture"
4445
type Creds = Awaited<ReturnType<typeof AltimateApi.getCredentials>>
4546
const originalIsConfigured = AltimateApi.isConfigured
4647
const originalGetCreds = AltimateApi.getCredentials
@@ -62,8 +63,12 @@ function unstubCreds() {
6263
originalGetCreds
6364
}
6465

66+
let restoreFetch = () => {}
67+
6568
beforeEach(() => {
6669
stubCreds()
70+
// The resolver fixture awaits the skill sync that recording a link starts; answered offline.
71+
restoreFetch = stubEmptySkillList("localhost:5001")
6772
setResolvedWorkspaceId(null)
6873
// Clean cache file between tests so state doesn't leak across cases.
6974
try {
@@ -77,6 +82,7 @@ beforeEach(() => {
7782
})
7883
afterEach(() => {
7984
unstubCreds()
85+
restoreFetch()
8086
setResolvedWorkspaceId(null)
8187
delete process.env.ALTIMATE_WORKSPACE
8288
})

‎packages/opencode/test/altimate/workspace/routing-pin.test.ts‎

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const { resolvePinnedBindingForRouting, recordApprovedBinding, __resetPinValidat
2222
const { refresh, precedenceInternals } = await import("../../../src/altimate/workspace/precedence")
2323
const { Instance } = await import("../../../src/project/instance")
2424
const { SNOWFLAKE_TOOLS } = await import("./precedence-fixture")
25+
const { stubEmptySkillList } = await import("./skill-list-fixture")
2526
const { AltimateApi } = await import("../../../src/altimate/api/client")
2627
const { WorkspaceApi } = await import("../../../src/altimate/workspace/api-client")
2728

@@ -39,27 +40,6 @@ function stubCreds() {
3940
({ altimateInstanceName: "acme", altimateUrl: "https://api.test", altimateApiKey: "k" }) as Creds
4041
}
4142

42-
const originalFetch = globalThis.fetch
43-
44-
/** Seeding a link runs a skill sync (awaited, see `seedLocalLink`) that lists the workspace's
45-
* skills from `api.test`. Answered here with an empty page, so that sync finishes offline instead
46-
* of waiting on however long a resolver takes to fail a name that cannot exist. */
47-
function stubFetch() {
48-
globalThis.fetch = (async (input: string | URL, init?: RequestInit) => {
49-
const url = new URL(String(input))
50-
if (url.host !== "api.test") return originalFetch(input, init)
51-
const skills = url.pathname.endsWith("/skills")
52-
// The page is echoed back: the skill-list parser rejects a page that does not match its request.
53-
const body = skills
54-
? { items: [], page: Number(url.searchParams.get("page") ?? "1"), pages: 1, total: 0 }
55-
: { detail: "not found" }
56-
return new Response(JSON.stringify(body), {
57-
status: skills ? 200 : 404,
58-
headers: { "content-type": "application/json" },
59-
})
60-
}) as typeof fetch
61-
}
62-
6343
let listCalls = 0
6444

6545
function stubList(rows: { id: number; name: string }[]) {
@@ -118,6 +98,7 @@ async function seedLocalLink(datamateId = 7, datamateName = "project-link") {
11898
}
11999

120100
const ORIGINAL_PILOT = process.env.ALTIMATE_WORKSPACE
101+
let restoreFetch = () => {}
121102

122103
beforeEach(() => {
123104
// `derive` short-circuits on `pilot-off` before it ever reads a binding.
@@ -126,7 +107,8 @@ beforeEach(() => {
126107
listCalls = 0
127108
__resetPinValidation()
128109
stubCreds()
129-
stubFetch()
110+
// Seeding a link awaits its skill sync (`seedLocalLink`); answered offline, per test.
111+
restoreFetch = stubEmptySkillList("api.test")
130112
stubList([
131113
{ id: 42, name: "pinned-workspace" },
132114
{ id: 7, name: "project-link" },
@@ -135,6 +117,7 @@ beforeEach(() => {
135117
})
136118

137119
afterEach(() => {
120+
restoreFetch()
138121
clearPin()
139122
delete process.env.ALTIMATE_INTEGRATIONS
140123
// Restored per test, not only in `afterAll`: `beforeEach` sets it unconditionally, so leaving
@@ -155,7 +138,6 @@ afterAll(() => {
155138
;(AltimateApi as unknown as { isConfigured: unknown }).isConfigured = originalIsConfigured
156139
;(AltimateApi as unknown as { getCredentials: unknown }).getCredentials = originalGetCreds
157140
;(WorkspaceApi as unknown as { listDatamates: unknown }).listDatamates = originalList
158-
globalThis.fetch = originalFetch
159141
if (ORIGINAL_XDG_STATE_HOME === undefined) delete process.env.XDG_STATE_HOME
160142
else process.env.XDG_STATE_HOME = ORIGINAL_XDG_STATE_HOME
161143
rmSync(SANDBOX, { recursive: true, force: true })
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// altimate_change - new file
2+
//
3+
// Recording a link starts a skill sync, and a test that awaits it (`awaitBackfill`) waits on that
4+
// sync's skill-list request. Against a stubbed Altimate host with no fetch stub, that request goes
5+
// to the real network and holds the test for as long as the host takes to fail — up to the API
6+
// client's 15s timeout. This answers it offline instead.
7+
8+
/** Answer `host`'s skill-list request with an empty page, and anything else on it with a 404.
9+
* Other hosts pass through to the fetch that was installed before. Returns the restore. */
10+
export function stubEmptySkillList(host: string): () => void {
11+
const original = globalThis.fetch
12+
globalThis.fetch = (async (input: string | URL, init?: RequestInit) => {
13+
const url = new URL(String(input))
14+
if (url.host !== host) return original(input, init)
15+
const skills = url.pathname.endsWith("/skills")
16+
// The page is echoed back: the skill-list parser rejects a page that does not match its request.
17+
const body = skills
18+
? { items: [], page: Number(url.searchParams.get("page") ?? "1"), pages: 1, total: 0 }
19+
: { detail: "not found" }
20+
return new Response(JSON.stringify(body), {
21+
status: skills ? 200 : 404,
22+
headers: { "content-type": "application/json" },
23+
})
24+
}) as typeof fetch
25+
return () => {
26+
globalThis.fetch = original
27+
}
28+
}

‎packages/opencode/test/altimate/workspace/state-account-scope.test.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ process.env.OPENCODE_TEST_STATE_HOME = path.join(SANDBOX, "state")
2424
const { recordApprovedBinding, readLocalBinding, clearLocalBinding, cachePath, credentialDigest } =
2525
await import("../../../src/altimate/workspace/state")
2626
const { AltimateApi } = await import("../../../src/altimate/api/client")
27+
const { stubEmptySkillList } = await import("./skill-list-fixture")
2728

2829
const ROOT = path.join(SANDBOX, "project")
2930
mkdirSync(ROOT, { recursive: true })
@@ -51,11 +52,16 @@ const binding = (datamateId: number, datamateName: string) => ({
5152
linkedAt: Date.now(),
5253
})
5354

55+
let restoreFetch = () => {}
56+
5457
beforeEach(() => {
5558
rmSync(cachePath(), { force: true })
59+
// Recording a link here awaits its skill sync; answered offline, whatever the workspace flag is.
60+
restoreFetch = stubEmptySkillList(new URL(API_URL).host)
5661
})
5762

5863
afterEach(() => {
64+
restoreFetch()
5965
;(AltimateApi as unknown as { isConfigured: unknown }).isConfigured = originalIsConfigured
6066
;(AltimateApi as unknown as { getCredentials: unknown }).getCredentials = originalGetCreds
6167
})

0 commit comments

Comments
 (0)