Skip to content

Commit 0e9a408

Browse files
author
pavelhov
authored
Merge pull request #42 from pavelhov/codex/macos-update-windows
test(macos): keep POSIX updater fixtures off Windows
2 parents 39eee7d + 442ca26 commit 0e9a408

4 files changed

Lines changed: 33 additions & 27 deletions

‎tests/macos-update-helper.test.ts‎

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { join, resolve } from "node:path";
2020
import { performMacOSUpdateCommand, trustedMacOSUpdateSource, type MacOSUpdateHelperIo } from "../src/cli/macos-update";
2121
import { MacosUpdateTransactionStore, assertMacosUpdateAllowsMutation, type MacosUpdateSemanticSnapshot } from "../src/server/macos-update-transaction";
2222
import { acquireProxyLifecycleAuthority } from "../src/server/proxy-lifecycle-authority";
23+
// Store-backed macOS fixtures require POSIX permissions and durable directory fsync.
2324
const temporary: string[] = [];
2425
afterEach(() => { for (const root of temporary.splice(0)) rmSync(root,{recursive:true,force:true}); });
2526
const id = "c94b8309-5c96-41f1-8dd1-fd57aeab8913";
@@ -37,14 +38,14 @@ function fixture(snapshot: Partial<MacosUpdateSemanticSnapshot> = {}) {
3738
return {io,store,resumed,get stopped(){return stopped;},replace(){source={...source,build:"101",fingerprint:"replacement"};},setIntent(){fingerprint="external-edit";},setActive(n:number){active=n;},failVerify(){verified=false;},failResume(){resumeSucceeds=false;},allowResume(){resumeSucceeds=true;}};
3839
}
3940
const prepare = {action:"prepare" as const,transactionId:id,targetBuild:"101"};
40-
test("active prepare immediately returns confirmation; cancel preserves the live runtime",async()=>{
41+
test.skipIf(process.platform === "win32")("active prepare immediately returns confirmation; cancel preserves the live runtime",async()=>{
4142
const f=fixture();f.setActive(2);
4243
expect((await performMacOSUpdateCommand(prepare,f.io)).status).toBe("confirmation-required");
4344
expect(f.stopped).toBe(0);
4445
expect((await performMacOSUpdateCommand({action:"cancel",transactionId:id},f.io)).status).toBe("recovered");
4546
expect(f.stopped).toBe(0);expect(f.resumed).toEqual([]);expect(f.store.read()).toBeNull();
4647
});
47-
test("arming reverifies exit and uncertain/armed cancellation cannot clear exclusion",async()=>{
48+
test.skipIf(process.platform === "win32")("arming reverifies exit and uncertain/armed cancellation cannot clear exclusion",async()=>{
4849
const f=fixture();await performMacOSUpdateCommand(prepare,f.io);f.failVerify();
4950
expect((await performMacOSUpdateCommand({action:"arm",transactionId:id},f.io)).status).toBe("blocked");
5051
expect(f.store.read()).not.toBeNull();
@@ -53,7 +54,7 @@ test("arming reverifies exit and uncertain/armed cancellation cannot clear exclu
5354
expect((await performMacOSUpdateCommand({action:"reconcile"},g.io)).status).toBe("finish-required");
5455
expect(g.resumed).toEqual([]);
5556
});
56-
test("successful replacement restores owned intent unless newer file edits or native intent superseded it",async()=>{
57+
test.skipIf(process.platform === "win32")("successful replacement restores owned intent unless newer file edits or native intent superseded it",async()=>{
5758
for (const changed of [false,true]) {
5859
const f=fixture();await performMacOSUpdateCommand(prepare,f.io);await performMacOSUpdateCommand({action:"arm",transactionId:id},f.io);f.replace();if(changed)f.setIntent();
5960
expect((await performMacOSUpdateCommand({action:"reconcile"},f.io)).status).toBe("recovered");expect(f.resumed).toEqual([!changed]);expect(f.store.read()).toBeNull();
@@ -62,13 +63,13 @@ test("successful replacement restores owned intent unless newer file edits or na
6263
const a=await f.io.authority!({includeStart:true});f.store.recordNative(a);a.releaseAll();f.replace();
6364
await performMacOSUpdateCommand({action:"reconcile"},f.io);expect(f.resumed).toEqual([false]);
6465
});
65-
test("running-native never enables routing, and failed resume retains a retryable recovery record",async()=>{
66+
test.skipIf(process.platform === "win32")("running-native never enables routing, and failed resume retains a retryable recovery record",async()=>{
6667
const f=fixture({routing:"native"});await performMacOSUpdateCommand(prepare,f.io);f.replace();f.failResume();
6768
expect((await performMacOSUpdateCommand({action:"reconcile"},f.io)).status).toBe("blocked");expect(f.store.read()?.phase).toBe("recovering");
6869
f.allowResume();expect((await performMacOSUpdateCommand({action:"reconcile"},f.io)).status).toBe("recovered");expect(f.resumed).toEqual([false,false]);
6970
expect((await performMacOSUpdateCommand({action:"reconcile"},f.io)).status).toBe("idle");
7071
});
71-
test("prearm cancellation recovery survives helper failure and cold-launch retry",async()=>{
72+
test.skipIf(process.platform === "win32")("prearm cancellation recovery survives helper failure and cold-launch retry",async()=>{
7273
const f=fixture();await performMacOSUpdateCommand(prepare,f.io);f.failResume();
7374
expect((await performMacOSUpdateCommand({action:"cancel",transactionId:id},f.io)).status).toBe("blocked");f.allowResume();
7475
expect((await performMacOSUpdateCommand({action:"reconcile"},f.io)).status).toBe("recovered");
@@ -89,15 +90,15 @@ test.skipIf(process.platform !== "darwin")("real bundled CLI helper derives sour
8990
},15000);
9091

9192
import { macOSUpdateResumeIntent } from "../src/cli/macos-update";
92-
test("semantic matrix preserves stopped/native/supervised and newer OFF without copying settings",async()=>{
93+
test.skipIf(process.platform === "win32")("semantic matrix preserves stopped/native/supervised and newer OFF without copying settings",async()=>{
9394
for (const running of [true,false]) for (const routing of ["owned","native","external"] as const) for (const supervision of ["none","launchd"] as const) {
9495
const f=fixture({running,routing,supervision});await performMacOSUpdateCommand(prepare,f.io);
9596
expect(macOSUpdateResumeIntent(f.store.read()!,"after")).toEqual({running,restoreOwned:routing==="owned",supervised:supervision==="launchd"});
9697
await performMacOSUpdateCommand({action:"record-off"},f.io);
9798
expect(macOSUpdateResumeIntent(f.store.read()!,"after")).toEqual({running:false,restoreOwned:false,supervised:supervision==="launchd"});
9899
}
99100
});
100-
test("partial preparation stays excluded and Finish Update retries the same captured target",async()=>{
101+
test.skipIf(process.platform === "win32")("partial preparation stays excluded and Finish Update retries the same captured target",async()=>{
101102
const f=fixture();let stopAttempt=0;
102103
const base=f.io.preparation!;f.io.preparation=(source,authority)=>{const original=base(source,authority);return {...original,stop:async(t,a)=>{if(++stopAttempt===1)throw Error("partial stop");await original.stop(t,a);}};};
103104
expect((await performMacOSUpdateCommand(prepare,f.io)).status).toBe("blocked");expect(f.store.read()?.phase).toBe("uncertain");
@@ -108,7 +109,7 @@ test("partial preparation stays excluded and Finish Update retries the same capt
108109
});
109110

110111
import { resumeProduction } from "../src/cli/macos-update";
111-
test("newer OFF stops a recovery child or active bundle supervisor before clearing exclusion",async()=>{
112+
test.skipIf(process.platform === "win32")("newer OFF stops a recovery child or active bundle supervisor before clearing exclusion",async()=>{
112113
const f=fixture();await performMacOSUpdateCommand(prepare,f.io);await performMacOSUpdateCommand({action:"record-off"},f.io);
113114
const authority=await f.io.authority!({includeStart:true});let stops=0;
114115
const stop=async()=>{stops++;return {schemaVersion:1 as const,action:"stop" as const,ok:true,state:"stopped" as const,changed:true,pid:null,port:null,message:"stopped"};};
@@ -129,7 +130,7 @@ test("permission hardening reads do not supersede routing intent but user writes
129130
expect(macOSUpdateIntentFingerprint([config])).not.toBe(before);
130131
});
131132

132-
test("deferred native routing applies before independent service/runtime returns and retains exclusion on failure",async()=>{
133+
test.skipIf(process.platform === "win32")("deferred native routing applies before independent service/runtime returns and retains exclusion on failure",async()=>{
133134
for (const independentService of [false,true]) for (const success of [false,true]) {
134135
const f=fixture({running:false});await performMacOSUpdateCommand(prepare,f.io);
135136
const a=await f.io.authority!({includeStart:true});f.store.recordNative(a);a.releaseAll();f.replace();
@@ -148,7 +149,7 @@ test("deferred native routing applies before independent service/runtime returns
148149
}
149150
});
150151

151-
test("deferred native recovery publishes OFF and preserves a newer external route in an isolated home",async()=>{
152+
test.skipIf(process.platform === "win32")("deferred native recovery publishes OFF and preserves a newer external route in an isolated home",async()=>{
152153
for (const external of [false,true]) for (const confirmation of [false,true]) {
153154
const root=mkdtempSync(join(tmpdir(),"ccx-update-native-recovery-"));temporary.push(root);
154155
for(const directory of ["home","codex","state","tmp"])mkdirSync(join(root,directory));
@@ -178,7 +179,7 @@ test("deferred native recovery publishes OFF and preserves a newer external rout
178179
}
179180
},60000);
180181

181-
test("Later publishes deferred native intent under authority and retries without stopping or resuming",async()=>{
182+
test.skipIf(process.platform === "win32")("Later publishes deferred native intent under authority and retries without stopping or resuming",async()=>{
182183
for (const failure of ["none","false","throw"] as const) for (const retry of ["cancel","reconcile"] as const) {
183184
const f=fixture();f.setActive(1);
184185
expect((await performMacOSUpdateCommand(prepare,f.io)).status).toBe("confirmation-required");
@@ -206,7 +207,7 @@ test("Later publishes deferred native intent under authority and retries without
206207
}
207208
});
208209

209-
test("settings changed before the first stop supersede owned restoration on initial and confirmation preparation",async()=>{
210+
test.skipIf(process.platform === "win32")("settings changed before the first stop supersede owned restoration on initial and confirmation preparation",async()=>{
210211
for(const confirmation of [false,true]) for(const changed of [false,true]) {
211212
const f=fixture();
212213
if(confirmation) {
@@ -229,7 +230,7 @@ test("settings changed before the first stop supersede owned restoration on init
229230
}
230231
});
231232

232-
test("confirmation reached after a partial stop does not become a routing-only cancellation",async()=>{
233+
test.skipIf(process.platform === "win32")("confirmation reached after a partial stop does not become a routing-only cancellation",async()=>{
233234
const f=fixture();const preparation=f.io.preparation!;let first=true;
234235
f.io.preparation=(source,authority)=>{
235236
const io=preparation(source,authority);

‎tests/macos-update-independent-runtime.test.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { createServer } from "node:net";
66
import { acquireProxyLifecycleAuthority } from "../src/server/proxy-lifecycle-authority";
77
import { assertMacosUpdateAllowsRuntimeStart, MacosUpdateTransactionStore } from "../src/server/macos-update-transaction";
88
import { assertMacosUpdateAllowsServiceStart, type ServiceDiagnostic, type ServiceInstallState } from "../src/service";
9+
// These macOS fixtures require POSIX permissions, directory fsync, and bundle paths.
10+
// Keep Linux coverage; Windows cannot represent the durability/provenance contract.
911
const roots: string[] = [];
1012
afterEach(() => { for (const root of roots.splice(0)) rmSync(root,{recursive:true,force:true}); });
1113
async function fixture() {
@@ -20,7 +22,7 @@ async function fixture() {
2022
store.transition(authority,"independent","prepared"); store.transition(authority,"independent","armed"); authority.releaseAll();
2123
return {root,config,codex,home,store,bundlePath};
2224
}
23-
test("physical start uses actual paths and refuses bundle, mixed, missing provenance", async () => {
25+
test.skipIf(process.platform === "win32")("physical start uses actual paths and refuses bundle, mixed, missing provenance", async () => {
2426
const f = await fixture();
2527
const external = join(f.root,"external.ts"); writeFileSync(external,"");
2628
const bundled = join(f.bundlePath,"Contents","Resources","runtime.ts"); mkdirSync(join(f.bundlePath,"Contents","Resources"),{recursive:true}); writeFileSync(bundled,"");
@@ -29,7 +31,7 @@ test("physical start uses actual paths and refuses bundle, mixed, missing proven
2931
expect(()=>assertMacosUpdateAllowsRuntimeStart(f.store,{modulePath:external,executablePath:bundled})).toThrow();
3032
expect(()=>assertMacosUpdateAllowsRuntimeStart(f.store,{modulePath:join(f.root,"missing"),executablePath:external})).toThrow();
3133
});
32-
test("registered service target is checked independently of the caller", async () => {
34+
test.skipIf(process.platform === "win32")("registered service target is checked independently of the caller", async () => {
3335
const f = await fixture();
3436
const diagnostic: ServiceDiagnostic = {supported:true,registrationState:"present",supervisorState:"inactive",installed:true,enabled:true,running:false,viable:false,startable:true,stale:false,conflict:false,backend:"launchd",summary:""};
3537
const state: ServiceInstallState = {version:3,codexHome:f.codex,codexCommanderHome:f.config,bunPath:"/outside/bun",cliPath:"/outside/ccx",backend:"scheduler"};
@@ -38,7 +40,7 @@ test("registered service target is checked independently of the caller", async (
3840
expect(()=>check({...state,bunPath:`${f.bundlePath}/Contents/Resources/bun`,cliPath:`${f.bundlePath}/Contents/Resources/cli`})).toThrow();
3941
expect(()=>check({...state,bunPath:`${f.bundlePath}/Contents/Resources/bun`})).toThrow();
4042
});
41-
test("independent autonomous service child serves during armed update without rewriting routing", async () => {
43+
test.skipIf(process.platform === "win32")("independent autonomous service child serves during armed update without rewriting routing", async () => {
4244
const f = await fixture();
4345
const port = await new Promise<number>((resolve,reject)=>{const server=createServer(); server.once("error",reject); server.listen(0,"127.0.0.1",()=>{const value=server.address();const port=typeof value === "object" && value ? value.port : 0;server.close(error=>error?reject(error):resolve(port));});});
4446
const settings = JSON.stringify({port,hostname:"127.0.0.1",codexAutoStart:true,multiAgentGuidanceEnabled:true,clientIntegrations:{codex:true},defaultProvider:"mock",providers:{mock:{adapter:"openai-chat",baseUrl:"http://127.0.0.1:9/v1",allowPrivateNetwork:true,models:["test"],defaultModel:"test"}}});
@@ -87,7 +89,7 @@ test("independent service command starts without routing preparation or converge
8789
expect(starts).toBe(1); expect(writes).toBe(0);
8890
});
8991

90-
test("independent Ensure preserves routing and cannot start a registered bundled service", async () => {
92+
test.skipIf(process.platform === "win32")("independent Ensure preserves routing and cannot start a registered bundled service", async () => {
9193
const { ensureProxyLifecycleUnderLock } = await import("../src/cli/proxy-lifecycle");
9294
const { getDefaultConfig } = await import("../src/config");
9395
const f = await fixture(); const previous = process.env.CODEXCOMMANDER_HOME;

‎tests/macos-update-transaction.test.ts‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { handleMacosUpdateAdmission, inspectMacosRuntimeBundleProvenance } from
88
import { proxyLifecycleLockLeaseHeaders } from "../src/server/proxy-lifecycle-protocol";
99
import { getDefaultConfig } from "../src/config";
1010
import { acquireTemporaryDrain, getActiveTurnCount, tryAdmitTurn, resetLifecycleDrainStateForTests } from "../src/server/lifecycle";
11+
// These macOS fixtures require POSIX permissions, directory fsync, and bundle paths.
12+
// Keep Linux coverage; Windows cannot represent the durability/provenance contract.
1113
const roots: string[] = [];
1214
afterEach(() => {
1315
resetLifecycleDrainStateForTests();
@@ -41,7 +43,7 @@ async function fixture() {
4143
store, authority, snapshot, request
4244
};
4345
}
44-
describe("macOS durable update exclusion", () => {
46+
describe.skipIf(process.platform === "win32")("macOS durable update exclusion", () => {
4547
test("persists before stopping and retains original snapshot across retries and helper exit", async () => {
4648
const f = await fixture();
4749
let stopped = 0;
@@ -114,7 +116,7 @@ describe("macOS durable update exclusion", () => {
114116
expect(() => f.store.read()).toThrow();
115117
});
116118
});
117-
test("admission seal cannot interrupt work without durable Update Anyway authorization", async () => {
119+
test.skipIf(process.platform === "win32")("admission seal cannot interrupt work without durable Update Anyway authorization", async () => {
118120
const f = await fixture();
119121
f.store.begin(f.authority, f.request, {
120122
...f.snapshot, process: {
@@ -149,7 +151,7 @@ test("admission seal cannot interrupt work without durable Update Anyway authori
149151
expect(getActiveTurnCount()).toBe(0);
150152
expect(tryAdmitTurn()).toBeNull();
151153
});
152-
test("recovery scope is explicit and retry never forgets prior installer arm", async () => {
154+
test.skipIf(process.platform === "win32")("recovery scope is explicit and retry never forgets prior installer arm", async () => {
153155
const f = await fixture();
154156
f.store.begin(f.authority, f.request, f.snapshot);
155157
f.store.transition(f.authority, f.request.transactionId, "prepared");
@@ -164,15 +166,15 @@ test("recovery scope is explicit and retry never forgets prior installer arm", a
164166
expect(f.store.read()?.latestIntent?.running).toBeNull();
165167
expect(f.store.read()?.original.running).toBe(true);
166168
});
167-
test("shared mutations remain excluded after helper exit", async () => {
169+
test.skipIf(process.platform === "win32")("shared mutations remain excluded after helper exit", async () => {
168170
const f = await fixture();
169171
f.store.begin(f.authority, f.request, f.snapshot);
170172
f.authority.releaseAll();
171173
expect(() => assertMacosUpdateAllowsMutation(f.store)).toThrow("Finish Update");
172174
expect(f.store.read()?.phase).toBe("preparing");
173175
});
174176

175-
test("seal success followed by failed stop preserves durable recovery and closed admission", async () => {
177+
test.skipIf(process.platform === "win32")("seal success followed by failed stop preserves durable recovery and closed admission", async () => {
176178
const f = await fixture();
177179
const result = await prepareMacosUpdate(f.store, f.authority, f.request, {
178180
capture: async () => ({
@@ -205,7 +207,7 @@ test("seal success followed by failed stop preserves durable recovery and closed
205207
expect(() => assertMacosUpdateAllowsMutation(f.store)).toThrow();
206208
});
207209

208-
test("a runtime with only one executable dependency inside the bundle is never independent", async () => {
210+
test.skipIf(process.platform === "win32")("a runtime with only one executable dependency inside the bundle is never independent", async () => {
209211
await fixture();
210212
const root = roots.at(-1)!;
211213
const resources = join(root, "Test.app", "Contents", "Resources");
@@ -218,7 +220,7 @@ test("a runtime with only one executable dependency inside the bundle is never i
218220
expect(inspectMacosRuntimeBundleProvenance(external, external).kind).toBe("independent");
219221
});
220222

221-
test("pre-stop supersession is durable and does not mistake partial stop writes for newer intent",async()=>{
223+
test.skipIf(process.platform === "win32")("pre-stop supersession is durable and does not mistake partial stop writes for newer intent",async()=>{
222224
for(const changed of [false,true]) for(const retryEdit of [false,true]) {
223225
const f=await fixture();let fingerprint=changed?"user-edit":"original";let attempts=0;
224226
const io={
@@ -241,7 +243,7 @@ test("pre-stop supersession is durable and does not mistake partial stop writes
241243
}
242244
});
243245

244-
test("schema v1 accepts legacy records and strictly validates optional preparation and cancellation markers",async()=>{
246+
test.skipIf(process.platform === "win32")("schema v1 accepts legacy records and strictly validates optional preparation and cancellation markers",async()=>{
245247
const f=await fixture();
246248
const legacy=f.store.begin(f.authority,f.request,f.snapshot);
247249
expect(f.store.read()).toEqual(legacy);
@@ -256,7 +258,7 @@ test("schema v1 accepts legacy records and strictly validates optional preparati
256258
f.authority.releaseAll();
257259
});
258260

259-
test("a crash without a post-stop baseline conservatively supersedes restoration on retry",async()=>{
261+
test.skipIf(process.platform === "win32")("a crash without a post-stop baseline conservatively supersedes restoration on retry",async()=>{
260262
const f=await fixture();
261263
f.store.begin(f.authority,f.request,{...f.snapshot,intentFingerprint:"original"});
262264
f.store.recordPreparationIntent(f.authority,f.request.transactionId,"original");

0 commit comments

Comments
 (0)