History

test: stop path and platform tests depending on the host OS (#9564)

Julius Marminge committed 2 months ago

Commit cc607531 parent bfef97310 files · +65 −33Browse files

Unified · Split

10 files
  1. apps/desktop/src/app/DesktopAppIdentity.test.ts +2 −0
  2. apps/desktop/src/app/DesktopAssets.test.ts +7 −1
  3. apps/desktop/src/app/DesktopEnvironment.test.ts +6 −1
  4. apps/server/src/auth/ServerSecretStore.test.ts +2 −2
  5. apps/server/src/project/ProjectFaviconResolver.test.ts +3 −2
  6. apps/server/src/provider/Drivers/ClaudeSkills.test.ts +13 −7
  7. packages/ssh/src/config.test.ts +2 −1
  8. packages/tailscale/src/tailscale.test.ts +14 −12
  9. scripts/mobile-showcase.test.ts +14 −6
  10. scripts/mobile-showcase.ts +2 −1
apps/desktop/src/app/DesktopAppIdentity.test.ts+2−0
@@ -1,3 +1,4 @@
1+import * as NodePath from "@effect/platform-node/NodePath";
12 import * as NodeServices from "@effect/platform-node/NodeServices";
23 import { assert, describe, it } from "@effect/vitest";
34 import * as Effect from "effect/Effect";
@@ -88,6 +89,7 @@ const makeEnvironmentLayer = (overrides: TestEnvironmentInput = {}) => { · 84 unmodified lines
8889 Layer.provide(
8990 Layer.mergeAll(
9091 NodeServices.layer,
92+ NodePath.layerPosix,
9193 DesktopConfig.layerTest({
9294 ...env,
9395 }),
apps/desktop/src/app/DesktopAssets.test.ts+7−1
@@ -1,3 +1,4 @@
1+import * as NodePath from "@effect/platform-node/NodePath";
12 import * as NodeServices from "@effect/platform-node/NodeServices";
23 import { assert, describe, it } from "@effect/vitest";
34 import * as Effect from "effect/Effect";
@@ -20,7 +21,11 @@ const environmentLayer = DesktopEnvironment.layer({ · 16 unmodified lines
2021 isPackaged: true,
2122 resourcesPath: "/Applications/T3 Code.app/Contents/Resources",
2223 runningUnderArm64Translation: false,
23−}).pipe(Layer.provide(Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest({}))));
24+}).pipe(
25+ Layer.provide(
26+ Layer.mergeAll(NodeServices.layer, NodePath.layerPosix, DesktopConfig.layerTest({})),
27+ ),
28+);
2429
2530 describe("DesktopAssets", () => {
2631 it.effect("uses canonical source-tree icons for unpackaged development", () =>
@@ -39,6 +44,7 @@ describe("DesktopAssets", () => { · 12 unmodified lines
3944 Layer.provide(
4045 Layer.mergeAll(
4146 NodeServices.layer,
47+ NodePath.layerPosix,
4248 DesktopConfig.layerTest({ VITE_DEV_SERVER_URL: "http://localhost:5733" }),
4349 ),
4450 ),
apps/desktop/src/app/DesktopEnvironment.test.ts+6−1
@@ -1,3 +1,4 @@
1+import * as NodePath from "@effect/platform-node/NodePath";
12 import * as NodeServices from "@effect/platform-node/NodeServices";
23 import { assert, describe, it } from "@effect/vitest";
34 import * as Effect from "effect/Effect";
@@ -26,7 +27,11 @@ const makeEnvironmentLayer = ( · 22 unmodified lines
2627 DesktopEnvironment.layer({
2728 ...defaultInput,
2829 ...overrides,
29− }).pipe(Layer.provide(Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest(env))));
30+ }).pipe(
31+ Layer.provide(
32+ Layer.mergeAll(NodeServices.layer, NodePath.layerPosix, DesktopConfig.layerTest(env)),
33+ ),
34+ );
3035
3136 const makeEnvironment = (
3237 overrides: Partial<DesktopEnvironment.MakeDesktopEnvironmentInput> = {},
apps/server/src/auth/ServerSecretStore.test.ts+2−2
@@ -109,7 +109,7 @@ const ConcurrentReadMissFileSystemLayer = Layer.effect( · 108 unmodified lines
109109 return {
110110 ...fileSystem,
111111 readFile: (path) =>
112− String(path).endsWith("/session-signing-key.bin")
112+ /[\\/]session-signing-key\.bin$/.test(String(path))
113113 ? Ref.updateAndGet(readCountRef, (count) => count + 1).pipe(
114114 Effect.flatMap((count) => {
115115 if (count > 2) {
@@ -219,7 +219,7 @@ it.layer(NodeServices.layer)("ServerSecretStore.layer", (it) => { · 103 unmodified lines
219219 yield* secretStore.set("session-signing-key", Uint8Array.from([1, 2, 3]));
220220
221221 assert.isTrue(
222− chmodCalls.some((call) => call.mode === 0o700 && call.path.endsWith("/secrets")),
222+ chmodCalls.some((call) => call.mode === 0o700 && /[\\/]secrets$/.test(call.path)),
223223 );
224224 assert.isAtLeast(chmodCalls.filter((call) => call.mode === 0o600).length, 2);
225225 }).pipe(Effect.provide(NodeServices.layer)),
apps/server/src/project/ProjectFaviconResolver.test.ts+3−2
@@ -54,11 +54,12 @@ it.layer(TestLayer)("ProjectFaviconResolverLive", (it) => { · 53 unmodified lines
5454 it.effect("serves repeated resolves from cache instead of re-walking candidates", () =>
5555 Effect.gen(function* () {
5656 const resolver = yield* ProjectFaviconResolver.ProjectFaviconResolver;
57+ const path = yield* Path.Path;
5758 const cwd = yield* makeTempDir;
5859 yield* writeTextFile(cwd, "public/favicon.svg", "<svg>public</svg>");
5960
6061 const resolved = yield* resolver.resolvePath(cwd);
61− expect(resolved?.endsWith("public/favicon.svg")).toBe(true);
62+ expect(resolved?.endsWith(path.join("public", "favicon.svg"))).toBe(true);
6263
6364 // `favicon.svg` outranks `public/favicon.svg`, so a resolver that walked
6465 // the candidate list again would switch to it. Staying on the original
@@ -71,7 +72,7 @@ it.layer(TestLayer)("ProjectFaviconResolverLive", (it) => { · 6 unmodified lines
7172
7273 yield* TestClock.adjust(Duration.minutes(11));
7374
74− expect((yield* resolver.resolvePath(cwd))?.endsWith("/favicon.svg")).toBe(true);
75+ expect(yield* resolver.resolvePath(cwd)).toBe(path.join(cwd, "favicon.svg"));
7576 expect(yield* resolver.resolvePath(cwd)).not.toBe(resolved);
7677 }).pipe(Effect.provide(TestClock.layer())),
7778 );
apps/server/src/provider/Drivers/ClaudeSkills.test.ts+13−7
@@ -1,3 +1,4 @@
1+import * as NodePath from "@effect/platform-node/NodePath";
12 import * as NodeServices from "@effect/platform-node/NodeServices";
23 import { assert, it } from "@effect/vitest";
34 import * as Effect from "effect/Effect";
@@ -492,7 +493,11 @@ it.layer(NodeServices.layer)("discoverClaudeSkills", (it) => { · 488 unmodified lines
492493
493494 it.effect("lets the administrator's managed policy outrank every other settings file", () =>
494495 Effect.gen(function* () {
495− const path = yield* Path.Path;
496+ // The function is pure on its `path` argument, so hand it the
497+ // implementation matching each platform under test rather than the
498+ // host's.
499+ const path = yield* Path.Path.pipe(Effect.provide(NodePath.layerPosix));
500+ const win32Path = yield* Path.Path.pipe(Effect.provide(NodePath.layerWin32));
496501
497502 for (const [platform, expected] of [
498503 ["darwin", "/Library/Application Support/ClaudeCode/managed-settings.json"],
@@ -508,14 +513,15 @@ it.layer(NodeServices.layer)("discoverClaudeSkills", (it) => { · 9 unmodified lines
508513 }
509514
510515 assert.deepEqual(
511− skillOverrideSettingsPaths(path, "/home/.claude", undefined, "win32", {
512− PROGRAMDATA: "C:/ProgramData",
516+ skillOverrideSettingsPaths(win32Path, "C:\\Users\\me\\.claude", undefined, "win32", {
517+ PROGRAMDATA: "C:\\ProgramData",
513518 }).at(-1),
514− "C:/ProgramData/ClaudeCode/managed-settings.json",
519+ "C:\\ProgramData\\ClaudeCode\\managed-settings.json",
520+ );
521+ assert.deepEqual(
522+ skillOverrideSettingsPaths(win32Path, "C:\\Users\\me\\.claude", undefined, "win32", {}),
523+ ["C:\\Users\\me\\.claude\\settings.json"],
515524 );
516− assert.deepEqual(skillOverrideSettingsPaths(path, "/home/.claude", undefined, "win32", {}), [
517− "/home/.claude/settings.json",
518− ]);
519525
520526 // Only the repository root's local file joins in, after the
521527 // workspace's own local file so it wins.
packages/ssh/src/config.test.ts+2−1
@@ -1,3 +1,4 @@
1+import * as NodePath from "@effect/platform-node/NodePath";
12 import * as NodeServices from "@effect/platform-node/NodeServices";
23 import { assert, describe, it } from "@effect/vitest";
34 import * as Effect from "effect/Effect";
@@ -133,6 +134,6 @@ describe("ssh config", () => { · 129 unmodified lines
133134 "/tmp/home",
134135 );
135136 assert.equal(pattern, "/tmp/home/.ssh/config.d/*.conf");
136− }).pipe(Effect.provide(NodeServices.layer)),
137+ }).pipe(Effect.provide(NodePath.layerPosix)),
137138 );
138139 });
packages/tailscale/src/tailscale.test.ts+14−12
@@ -8,6 +8,7 @@ import * as Sink from "effect/Sink"; · 7 unmodified lines
88 import * as Stream from "effect/Stream";
99 import * as TestClock from "effect/testing/TestClock";
1010 import { ChildProcessSpawner } from "effect/unstable/process";
11+import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
1112
1213 import {
1314 buildTailscaleHttpsBaseUrl,
@@ -98,14 +99,22 @@ function neverFinishingMockHandle() { · 84 unmodified lines
9899 });
99100 }
100101
102+// The executable name depends on the host platform (`tailscale.exe` on
103+// Windows), so pin it: these tests assert the posix spelling.
104+function spawnerLayer(spawner: ChildProcessSpawner.ChildProcessSpawner["Service"]) {
105+ return Layer.merge(
106+ Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner),
107+ Layer.succeed(HostProcessPlatform, "linux"),
108+ );
109+}
110+
101111 function mockSpawnerLayer(
102112 handler: (
103113 command: string,
104114 args: ReadonlyArray<string>,
105115 ) => { stdout?: string; stderr?: string; code?: number },
106116 ) {
107− return Layer.succeed(
108− ChildProcessSpawner.ChildProcessSpawner,
117+ return spawnerLayer(
109118 ChildProcessSpawner.make((command) => {
110119 const childProcess = command as unknown as {
111120 readonly command: string;
@@ -194,10 +203,7 @@ describe("tailscale", () => { · 82 unmodified lines
194203 method: "spawn",
195204 cause: systemCause,
196205 });
197− const layer = Layer.succeed(
198− ChildProcessSpawner.ChildProcessSpawner,
199− ChildProcessSpawner.make(() => Effect.fail(cause)),
200− );
206+ const layer = spawnerLayer(ChildProcessSpawner.make(() => Effect.fail(cause)));
201207
202208 return Effect.gen(function* () {
203209 const error = yield* readTailscaleStatus.pipe(Effect.flip, Effect.provide(layer));
@@ -218,8 +224,7 @@ describe("tailscale", () => { · 14 unmodified lines
218224 // inside an `Effect.callback` registration, so that throw arrives as a
219225 // defect rather than a typed error - the shape reproduced here.
220226 const defect = Object.assign(new Error("spawn tailscale ENOTDIR"), { code: "ENOTDIR" });
221− const layer = Layer.succeed(
222− ChildProcessSpawner.ChildProcessSpawner,
227+ const layer = spawnerLayer(
223228 ChildProcessSpawner.make(() =>
224229 Effect.callback<never, never>(() => {
225230 throw defect;
@@ -297,10 +302,7 @@ describe("tailscale", () => { · 71 unmodified lines
297302 it.effect("times out tailscale status through TestClock", () => {
298303 const layer = Layer.merge(
299304 TestClock.layer(),
300− Layer.succeed(
301− ChildProcessSpawner.ChildProcessSpawner,
302− ChildProcessSpawner.make(() => Effect.succeed(neverFinishingMockHandle())),
303− ),
305+ spawnerLayer(ChildProcessSpawner.make(() => Effect.succeed(neverFinishingMockHandle()))),
304306 );
305307
306308 return Effect.gen(function* () {
scripts/mobile-showcase.test.ts+14−6
@@ -1,3 +1,5 @@
1+// @effect-diagnostics nodeBuiltinImport:off - expectations mirror the host path joins the script under test performs.
2+import * as NodePath from "node:path";
13 import { assert, it } from "@effect/vitest";
24 import { PNG } from "pngjs";
35
@@ -167,8 +169,14 @@ it("expands both appearances into independent upload-ready directories", () => { · 163 unmodified lines
167169 directory: showcaseCaptureDirectory("/captures", capture),
168170 })),
169171 [
170− { appearance: "light", directory: "/captures/apple/iphone-test/light/t3-code" },
171− { appearance: "dark", directory: "/captures/apple/iphone-test/dark/t3-code" },
172+ {
173+ appearance: "light",
174+ directory: NodePath.join("/captures", "apple", "iphone-test", "light", "t3-code"),
175+ },
176+ {
177+ appearance: "dark",
178+ directory: NodePath.join("/captures", "apple", "iphone-test", "dark", "t3-code"),
179+ },
172180 ],
173181 );
174182 });
@@ -192,10 +200,10 @@ it("expands themes into independent upload-ready directories per appearance", () · 17 unmodified lines
192200 showcaseCaptureDirectory("/captures", capture),
193201 ),
194202 [
195− "/captures/apple/iphone-test/light/ocean",
196− "/captures/apple/iphone-test/light/ember",
197− "/captures/apple/iphone-test/dark/ocean",
198− "/captures/apple/iphone-test/dark/ember",
203+ NodePath.join("/captures", "apple", "iphone-test", "light", "ocean"),
204+ NodePath.join("/captures", "apple", "iphone-test", "light", "ember"),
205+ NodePath.join("/captures", "apple", "iphone-test", "dark", "ocean"),
206+ NodePath.join("/captures", "apple", "iphone-test", "dark", "ember"),
199207 ],
200208 );
201209 });
scripts/mobile-showcase.ts+2−1
@@ -53,7 +53,8 @@ export function resolveAndroidSdkRoot( · 52 unmodified lines
5353 const configured = environment.ANDROID_HOME ?? environment.ANDROID_SDK_ROOT;
5454 if (configured) return configured;
5555 const home = environment.HOME ?? environment.USERPROFILE ?? "";
56− return NodePath.join(home, platform === "darwin" ? "Library/Android/sdk" : "Android/Sdk");
56+ const path = platform === "win32" ? NodePath.win32 : NodePath.posix;
57+ return path.join(home, platform === "darwin" ? "Library/Android/sdk" : "Android/Sdk");
5758 }
5859
5960 const ANDROID_SDK_ROOT = resolveAndroidSdkRoot(NodeProcess.env);