| author | |
| committer | |
| log | 1108be0fc862d5a95374bf0b96518a20e6351025 |
| tree | 1a98c02ba50c9931887b810004f85b73fffe0a09 |
| parent | 12b6d026bdfba0cc8438fa5e323a8587d65bd2af |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Code <noreply@anthropic.com>5 files changed, 180 insertions(+), 153 deletions(-)
apps/server/scripts/t3-sqlite-state.test.ts+3-1| ... | @@ -7,6 +7,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; | ... | @@ -7,6 +7,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; |
| 7 | 7 | ||
| 8 | import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; | 8 | import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; |
| 9 | import { runSqliteState } from "./t3-sqlite-state.ts"; | 9 | import { runSqliteState } from "./t3-sqlite-state.ts"; |
| 10 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; | ||
| 10 | import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; | 11 | import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; |
| 11 | 12 | ||
| 12 | const createFixtureDatabase = Effect.fn("createSqliteStateFixtureDatabase")(function* ( | 13 | const createFixtureDatabase = Effect.fn("createSqliteStateFixtureDatabase")(function* ( |
| ... | @@ -89,7 +90,8 @@ it.layer(NodeServices.layer)("t3-sqlite-state", (it) => { | ... | @@ -89,7 +90,8 @@ it.layer(NodeServices.layer)("t3-sqlite-state", (it) => { |
| 89 | sql: "INSERT INTO fixtures (id, label) VALUES (2, 'seeded')", | 90 | sql: "INSERT INTO fixtures (id, label) VALUES (2, 'seeded')", |
| 90 | }); | 91 | }); |
| 91 | assert.equal(mutation.operation, "exec"); | 92 | assert.equal(mutation.operation, "exec"); |
| 92 | if (mutation.operation === "exec") { | 93 | if (mutation.operation === "exec" && (yield* HostProcessPlatform) !== "win32") { |
| 94 | // NTFS has no POSIX mode bits to report. | ||
| 93 | assert.equal((yield* fs.stat(mutation.backup)).mode & 0o777, 0o600); | 95 | assert.equal((yield* fs.stat(mutation.backup)).mode & 0o777, 0o600); |
| 94 | } | 96 | } |
| 95 | 97 |
apps/server/src/cli/theme.test.ts+9-4| ... | @@ -14,6 +14,11 @@ import { Command } from "effect/unstable/cli"; | ... | @@ -14,6 +14,11 @@ import { Command } from "effect/unstable/cli"; |
| 14 | 14 | ||
| 15 | import { cli } from "../bin.ts"; | 15 | import { cli } from "../bin.ts"; |
| 16 | import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; | 16 | import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; |
| 17 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; | ||
| 18 | |||
| 19 | // These force a failure with chmod, which Windows ignores for directories and | ||
| 20 | // cannot use to make a file unreadable, so the failure never happens there. | ||
| 21 | const windowsHost = HostProcessPlatform.defaultValue() === "win32"; | ||
| 17 | 22 | ||
| 18 | const runCli = (args: ReadonlyArray<string>) => | 23 | const runCli = (args: ReadonlyArray<string>) => |
| 19 | Command.runWith(cli, { version: "0.0.0" })(args).pipe( | 24 | Command.runWith(cli, { version: "0.0.0" })(args).pipe( |
| ... | @@ -161,7 +166,7 @@ describe("t3 theme", () => { | ... | @@ -161,7 +166,7 @@ describe("t3 theme", () => { |
| 161 | // rolled back rather than left mutating the environment's theme set. The | 166 | // rolled back rather than left mutating the environment's theme set. The |
| 162 | // userdata directory is made read-only while themes stays writable, so the | 167 | // userdata directory is made read-only while themes stays writable, so the |
| 163 | // failure lands after the publish -- the case the rollback exists for. | 168 | // failure lands after the publish -- the case the rollback exists for. |
| 164 | it.effect("rolls back a publish when the default cannot be written", () => | 169 | it.effect.skipIf(windowsHost)("rolls back a publish when the default cannot be written", () => |
| 165 | Effect.gen(function* () { | 170 | Effect.gen(function* () { |
| 166 | const baseDir = makeBaseDir(); | 171 | const baseDir = makeBaseDir(); |
| 167 | writeSettings(baseDir, {}); | 172 | writeSettings(baseDir, {}); |
| ... | @@ -229,7 +234,7 @@ describe("t3 theme", () => { | ... | @@ -229,7 +234,7 @@ describe("t3 theme", () => { |
| 229 | // Rollback moves the previous directory entry aside and back, so even an | 234 | // Rollback moves the previous directory entry aside and back, so even an |
| 230 | // entry the watcher would never publish -- here a symlink -- comes back | 235 | // entry the watcher would never publish -- here a symlink -- comes back |
| 231 | // exactly as it was when the set fails. | 236 | // exactly as it was when the set fails. |
| 232 | it.effect.skipIf(!symlinksSupported)( | 237 | it.effect.skipIf(!symlinksSupported || windowsHost)( |
| 233 | "restores a non-theme destination entry when the set fails", | 238 | "restores a non-theme destination entry when the set fails", |
| 234 | () => | 239 | () => |
| 235 | Effect.gen(function* () { | 240 | Effect.gen(function* () { |
| ... | @@ -255,7 +260,7 @@ describe("t3 theme", () => { | ... | @@ -255,7 +260,7 @@ describe("t3 theme", () => { |
| 255 | }), | 260 | }), |
| 256 | ); | 261 | ); |
| 257 | 262 | ||
| 258 | it.effect("restores the previous theme when a re-publish fails to set", () => | 263 | it.effect.skipIf(windowsHost)("restores the previous theme when a re-publish fails to set", () => |
| 259 | Effect.gen(function* () { | 264 | Effect.gen(function* () { |
| 260 | const baseDir = makeBaseDir(); | 265 | const baseDir = makeBaseDir(); |
| 261 | writeSettings(baseDir, {}); | 266 | writeSettings(baseDir, {}); |
| ... | @@ -352,7 +357,7 @@ describe("t3 theme", () => { | ... | @@ -352,7 +357,7 @@ describe("t3 theme", () => { |
| 352 | 357 | ||
| 353 | // An unreadable settings file must never read as "no settings": writing a | 358 | // An unreadable settings file must never read as "no settings": writing a |
| 354 | // fresh sparse file over it would discard every key the user had. | 359 | // fresh sparse file over it would discard every key the user had. |
| 355 | it.effect("refuses to write when the settings file cannot be read", () => | 360 | it.effect.skipIf(windowsHost)("refuses to write when the settings file cannot be read", () => |
| 356 | Effect.gen(function* () { | 361 | Effect.gen(function* () { |
| 357 | const baseDir = makeBaseDir(); | 362 | const baseDir = makeBaseDir(); |
| 358 | writeSettings(baseDir, { enableProviderUpdateChecks: false }); | 363 | writeSettings(baseDir, { enableProviderUpdateChecks: false }); |
apps/server/src/keybindings.test.ts+26-22| ... | @@ -12,6 +12,7 @@ import * as Schema from "effect/Schema"; | ... | @@ -12,6 +12,7 @@ import * as Schema from "effect/Schema"; |
| 12 | import * as ServerConfig from "./config.ts"; | 12 | import * as ServerConfig from "./config.ts"; |
| 13 | import * as Keybindings from "./keybindings.ts"; | 13 | import * as Keybindings from "./keybindings.ts"; |
| 14 | import { KeybindingsConfigError } from "@t3tools/contracts"; | 14 | import { KeybindingsConfigError } from "@t3tools/contracts"; |
| 15 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; | ||
| 15 | 16 | ||
| 16 | const KeybindingsConfigJson = Schema.fromJsonString(KeybindingsConfig); | 17 | const KeybindingsConfigJson = Schema.fromJsonString(KeybindingsConfig); |
| 17 | const encodeKeybindingsConfigJson = Schema.encodeEffect(KeybindingsConfigJson); | 18 | const encodeKeybindingsConfigJson = Schema.encodeEffect(KeybindingsConfigJson); |
| ... | @@ -511,31 +512,34 @@ it.layer(NodeServices.layer)("keybindings", (it) => { | ... | @@ -511,31 +512,34 @@ it.layer(NodeServices.layer)("keybindings", (it) => { |
| 511 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 512 | }).pipe(Effect.provide(makeKeybindingsLayer())), |
| 512 | ); | 513 | ); |
| 513 | 514 | ||
| 514 | it.effect("fails when config directory is not writable", () => | 515 | // chmod cannot make a directory unwritable on Windows, so the write succeeds. |
| 515 | Effect.gen(function* () { | 516 | it.effect.skipIf(HostProcessPlatform.defaultValue() === "win32")( |
| 516 | const fs = yield* FileSystem.FileSystem; | 517 | "fails when config directory is not writable", |
| 517 | const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; | 518 | () => |
| 518 | const { dirname } = yield* Path.Path; | 519 | Effect.gen(function* () { |
| 519 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ | 520 | const fs = yield* FileSystem.FileSystem; |
| 520 | { key: "mod+j", command: "terminal.toggle" }, | 521 | const { keybindingsConfigPath } = yield* ServerConfig.ServerConfig; |
| 521 | ]); | 522 | const { dirname } = yield* Path.Path; |
| 522 | yield* fs.chmod(dirname(keybindingsConfigPath), 0o500); | 523 | yield* writeKeybindingsConfig(keybindingsConfigPath, [ |
| 524 | { key: "mod+j", command: "terminal.toggle" }, | ||
| 525 | ]); | ||
| 526 | yield* fs.chmod(dirname(keybindingsConfigPath), 0o500); | ||
| 523 | 527 | ||
| 524 | const result = yield* Effect.gen(function* () { | 528 | const result = yield* Effect.gen(function* () { |
| 525 | const keybindings = yield* Keybindings.Keybindings; | 529 | const keybindings = yield* Keybindings.Keybindings; |
| 526 | return yield* keybindings.upsertKeybindingRule({ | 530 | return yield* keybindings.upsertKeybindingRule({ |
| 527 | key: "mod+shift+r", | 531 | key: "mod+shift+r", |
| 528 | command: "script.run-tests.run", | 532 | command: "script.run-tests.run", |
| 529 | }); | 533 | }); |
| 530 | }).pipe(toDetailResult); | 534 | }).pipe(toDetailResult); |
| 531 | assertFailure(result, "failed to write keybindings config"); | 535 | assertFailure(result, "failed to write keybindings config"); |
| 532 | 536 | ||
| 533 | yield* fs.chmod(dirname(keybindingsConfigPath), 0o700); | 537 | yield* fs.chmod(dirname(keybindingsConfigPath), 0o700); |
| 534 | 538 | ||
| 535 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); | 539 | const persisted = yield* readKeybindingsConfig(keybindingsConfigPath); |
| 536 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); | 540 | const persistedView = persisted.map(({ key, command }) => ({ key, command })); |
| 537 | assert.deepEqual(persistedView, [{ key: "mod+j", command: "terminal.toggle" }]); | 541 | assert.deepEqual(persistedView, [{ key: "mod+j", command: "terminal.toggle" }]); |
| 538 | }).pipe(Effect.provide(makeKeybindingsLayer())), | 542 | }).pipe(Effect.provide(makeKeybindingsLayer())), |
| 539 | ); | 543 | ); |
| 540 | 544 | ||
| 541 | it.effect("caches loaded resolved config across repeated reads", () => | 545 | it.effect("caches loaded resolved config across repeated reads", () => |
packages/shared/src/logging.test.ts+6-1| ... | @@ -3,6 +3,7 @@ import * as NodeFS from "node:fs"; | ... | @@ -3,6 +3,7 @@ import * as NodeFS from "node:fs"; |
| 3 | import * as NodeOS from "node:os"; | 3 | import * as NodeOS from "node:os"; |
| 4 | import * as NodePath from "node:path"; | 4 | import * as NodePath from "node:path"; |
| 5 | import { afterEach, describe, expect, it } from "vite-plus/test"; | 5 | import { afterEach, describe, expect, it } from "vite-plus/test"; |
| 6 | import { HostProcessPlatform } from "./hostProcess.ts"; | ||
| 6 | 7 | ||
| 7 | import { | 8 | import { |
| 8 | RotatingFileSink, | 9 | RotatingFileSink, |
| ... | @@ -10,6 +11,7 @@ import { | ... | @@ -10,6 +11,7 @@ import { |
| 10 | RotatingFileSinkError, | 11 | RotatingFileSinkError, |
| 11 | } from "./logging.ts"; | 12 | } from "./logging.ts"; |
| 12 | 13 | ||
| 14 | const windowsHost = HostProcessPlatform.defaultValue() === "win32"; | ||
| 13 | const tempDirectories: string[] = []; | 15 | const tempDirectories: string[] = []; |
| 14 | 16 | ||
| 15 | const makeTempDirectory = (): string => { | 17 | const makeTempDirectory = (): string => { |
| ... | @@ -69,7 +71,10 @@ describe("RotatingFileSink", () => { | ... | @@ -69,7 +71,10 @@ describe("RotatingFileSink", () => { |
| 69 | expect((thrown as RotatingFileSinkError).cause).toBeInstanceOf(Error); | 71 | expect((thrown as RotatingFileSinkError).cause).toBeInstanceOf(Error); |
| 70 | }); | 72 | }); |
| 71 | 73 | ||
| 72 | it("only treats a missing log file as an empty current size", () => { | 74 | // An over-long name is the one stat failure that is neither ENOENT nor a |
| 75 | // permission problem on posix. Windows reports it as ENOENT, so the sink | ||
| 76 | // correctly treats it as an absent file and there is nothing to assert. | ||
| 77 | it.skipIf(windowsHost)("only treats a missing log file as an empty current size", () => { | ||
| 73 | const directory = makeTempDirectory(); | 78 | const directory = makeTempDirectory(); |
| 74 | const filePath = NodePath.join(directory, "a".repeat(300)); | 79 | const filePath = NodePath.join(directory, "a".repeat(300)); |
| 75 | 80 |
packages/shared/src/relayClient.test.ts+136-125| ... | @@ -18,6 +18,10 @@ import { | ... | @@ -18,6 +18,10 @@ import { |
| 18 | makeCloudflaredRelayClient, | 18 | makeCloudflaredRelayClient, |
| 19 | } from "./relayClient.ts"; | 19 | } from "./relayClient.ts"; |
| 20 | 20 | ||
| 21 | // The suite runs the linux code path against the real filesystem, checking | ||
| 22 | // POSIX exec bits that NTFS never reports; the win32 branch skips that check. | ||
| 23 | const windowsHost = HostProcessPlatform.defaultValue() === "win32"; | ||
| 24 | |||
| 21 | const hostRuntimeLayer = (env: Record<string, string> = {}) => | 25 | const hostRuntimeLayer = (env: Record<string, string> = {}) => |
| 22 | Layer.mergeAll( | 26 | Layer.mergeAll( |
| 23 | Layer.succeed(HostProcessPlatform, "linux"), | 27 | Layer.succeed(HostProcessPlatform, "linux"), |
| ... | @@ -63,102 +67,106 @@ const makeSpawnerLayer = (commands: Array<string>) => | ... | @@ -63,102 +67,106 @@ const makeSpawnerLayer = (commands: Array<string>) => |
| 63 | ); | 67 | ); |
| 64 | 68 | ||
| 65 | describe("RelayClient", () => { | 69 | describe("RelayClient", () => { |
| 66 | it.effect("resolves explicit overrides before managed and PATH executables", () => | 70 | it.effect.skipIf(windowsHost)( |
| 67 | Effect.gen(function* () { | 71 | "resolves explicit overrides before managed and PATH executables", |
| 68 | const fileSystem = yield* FileSystem.FileSystem; | 72 | () => |
| 69 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 73 | Effect.gen(function* () { |
| 70 | prefix: "t3-cloudflared-test-", | 74 | const fileSystem = yield* FileSystem.FileSystem; |
| 71 | }); | 75 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 72 | const overridePath = `${baseDir}/override-cloudflared`; | 76 | prefix: "t3-cloudflared-test-", |
| 73 | yield* fileSystem.writeFileString(overridePath, "override"); | 77 | }); |
| 74 | yield* fileSystem.chmod(overridePath, 0o755); | 78 | const overridePath = `${baseDir}/override-cloudflared`; |
| 75 | const manager = yield* makeCloudflaredRelayClient({ | 79 | yield* fileSystem.writeFileString(overridePath, "override"); |
| 76 | baseDir, | 80 | yield* fileSystem.chmod(overridePath, 0o755); |
| 77 | }); | 81 | const manager = yield* makeCloudflaredRelayClient({ |
| 82 | baseDir, | ||
| 83 | }); | ||
| 78 | 84 | ||
| 79 | expect( | 85 | expect( |
| 80 | yield* manager.resolve.pipe( | 86 | yield* manager.resolve.pipe( |
| 81 | Effect.provideService( | 87 | Effect.provideService( |
| 82 | ConfigProvider.ConfigProvider, | 88 | ConfigProvider.ConfigProvider, |
| 83 | ConfigProvider.fromEnv({ | 89 | ConfigProvider.fromEnv({ |
| 84 | env: { PATH: "", T3CODE_CLOUDFLARED_PATH: overridePath }, | 90 | env: { PATH: "", T3CODE_CLOUDFLARED_PATH: overridePath }, |
| 85 | }), | 91 | }), |
| 92 | ), | ||
| 93 | ), | ||
| 94 | ).toEqual({ | ||
| 95 | status: "available", | ||
| 96 | executablePath: overridePath, | ||
| 97 | source: "override", | ||
| 98 | version: CLOUDFLARED_VERSION, | ||
| 99 | }); | ||
| 100 | }).pipe( | ||
| 101 | Effect.scoped, | ||
| 102 | Effect.provide( | ||
| 103 | Layer.mergeAll( | ||
| 104 | NodeServices.layer, | ||
| 105 | makeHttpClientLayer(new Uint8Array()), | ||
| 106 | makeSpawnerLayer([]), | ||
| 107 | hostRuntimeLayer(), | ||
| 86 | ), | 108 | ), |
| 87 | ), | ||
| 88 | ).toEqual({ | ||
| 89 | status: "available", | ||
| 90 | executablePath: overridePath, | ||
| 91 | source: "override", | ||
| 92 | version: CLOUDFLARED_VERSION, | ||
| 93 | }); | ||
| 94 | }).pipe( | ||
| 95 | Effect.scoped, | ||
| 96 | Effect.provide( | ||
| 97 | Layer.mergeAll( | ||
| 98 | NodeServices.layer, | ||
| 99 | makeHttpClientLayer(new Uint8Array()), | ||
| 100 | makeSpawnerLayer([]), | ||
| 101 | hostRuntimeLayer(), | ||
| 102 | ), | 109 | ), |
| 103 | ), | 110 | ), |
| 104 | ), | ||
| 105 | ); | 111 | ); |
| 106 | 112 | ||
| 107 | it.effect("downloads, verifies, validates, and atomically installs the managed executable", () => | 113 | it.effect.skipIf(windowsHost)( |
| 108 | Effect.gen(function* () { | 114 | "downloads, verifies, validates, and atomically installs the managed executable", |
| 109 | const fileSystem = yield* FileSystem.FileSystem; | 115 | () => |
| 110 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 116 | Effect.gen(function* () { |
| 111 | prefix: "t3-cloudflared-test-", | 117 | const fileSystem = yield* FileSystem.FileSystem; |
| 112 | }); | 118 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 113 | const bytes = new TextEncoder().encode("test-cloudflared-binary"); | 119 | prefix: "t3-cloudflared-test-", |
| 114 | const manager = yield* makeCloudflaredRelayClient({ | 120 | }); |
| 115 | baseDir, | 121 | const bytes = new TextEncoder().encode("test-cloudflared-binary"); |
| 116 | releaseAsset: { | 122 | const manager = yield* makeCloudflaredRelayClient({ |
| 117 | url: "https://example.test/cloudflared", | 123 | baseDir, |
| 118 | sha256: Encoding.encodeHex(sha256(bytes)), | 124 | releaseAsset: { |
| 119 | archive: "binary", | 125 | url: "https://example.test/cloudflared", |
| 120 | }, | 126 | sha256: Encoding.encodeHex(sha256(bytes)), |
| 121 | }); | 127 | archive: "binary", |
| 128 | }, | ||
| 129 | }); | ||
| 122 | 130 | ||
| 123 | const progress: Array<string> = []; | 131 | const progress: Array<string> = []; |
| 124 | const installed = yield* manager.installWithProgress((event) => | 132 | const installed = yield* manager.installWithProgress((event) => |
| 125 | Effect.sync(() => { | 133 | Effect.sync(() => { |
| 126 | if (event.type === "progress") { | 134 | if (event.type === "progress") { |
| 127 | progress.push(event.stage); | 135 | progress.push(event.stage); |
| 128 | } | 136 | } |
| 129 | }), | 137 | }), |
| 130 | ); | 138 | ); |
| 131 | const managedPath = `${baseDir}/tools/cloudflared/${CLOUDFLARED_VERSION}/linux-x64/cloudflared`; | 139 | const managedPath = `${baseDir}/tools/cloudflared/${CLOUDFLARED_VERSION}/linux-x64/cloudflared`; |
| 132 | expect(installed).toEqual({ | 140 | expect(installed).toEqual({ |
| 133 | status: "available", | 141 | status: "available", |
| 134 | executablePath: managedPath, | 142 | executablePath: managedPath, |
| 135 | source: "managed", | 143 | source: "managed", |
| 136 | version: CLOUDFLARED_VERSION, | 144 | version: CLOUDFLARED_VERSION, |
| 137 | }); | 145 | }); |
| 138 | expect(new TextDecoder().decode(yield* fileSystem.readFile(managedPath))).toBe( | 146 | expect(new TextDecoder().decode(yield* fileSystem.readFile(managedPath))).toBe( |
| 139 | "test-cloudflared-binary", | 147 | "test-cloudflared-binary", |
| 140 | ); | 148 | ); |
| 141 | expect(progress).toEqual([ | 149 | expect(progress).toEqual([ |
| 142 | "checking", | 150 | "checking", |
| 143 | "waiting_for_lock", | 151 | "waiting_for_lock", |
| 144 | "downloading", | 152 | "downloading", |
| 145 | "verifying", | 153 | "verifying", |
| 146 | "installing", | 154 | "installing", |
| 147 | "validating", | 155 | "validating", |
| 148 | "activating", | 156 | "activating", |
| 149 | ]); | 157 | ]); |
| 150 | expect(yield* manager.resolve).toEqual(installed); | 158 | expect(yield* manager.resolve).toEqual(installed); |
| 151 | }).pipe( | 159 | }).pipe( |
| 152 | Effect.scoped, | 160 | Effect.scoped, |
| 153 | Effect.provide( | 161 | Effect.provide( |
| 154 | Layer.mergeAll( | 162 | Layer.mergeAll( |
| 155 | NodeServices.layer, | 163 | NodeServices.layer, |
| 156 | makeHttpClientLayer(new TextEncoder().encode("test-cloudflared-binary")), | 164 | makeHttpClientLayer(new TextEncoder().encode("test-cloudflared-binary")), |
| 157 | makeSpawnerLayer([]), | 165 | makeSpawnerLayer([]), |
| 158 | hostRuntimeLayer(), | 166 | hostRuntimeLayer(), |
| 167 | ), | ||
| 159 | ), | 168 | ), |
| 160 | ), | 169 | ), |
| 161 | ), | ||
| 162 | ); | 170 | ); |
| 163 | 171 | ||
| 164 | it.effect("rejects downloads whose checksum does not match the pinned manifest", () => | 172 | it.effect("rejects downloads whose checksum does not match the pinned manifest", () => |
| ... | @@ -192,7 +200,7 @@ describe("RelayClient", () => { | ... | @@ -192,7 +200,7 @@ describe("RelayClient", () => { |
| 192 | ), | 200 | ), |
| 193 | ); | 201 | ); |
| 194 | 202 | ||
| 195 | it.effect("serializes concurrent installs within one runtime", () => { | 203 | it.effect.skipIf(windowsHost)("serializes concurrent installs within one runtime", () => { |
| 196 | const commands: Array<string> = []; | 204 | const commands: Array<string> = []; |
| 197 | const bytes = new TextEncoder().encode("test-cloudflared-binary"); | 205 | const bytes = new TextEncoder().encode("test-cloudflared-binary"); |
| 198 | return Effect.gen(function* () { | 206 | return Effect.gen(function* () { |
| ... | @@ -227,45 +235,48 @@ describe("RelayClient", () => { | ... | @@ -227,45 +235,48 @@ describe("RelayClient", () => { |
| 227 | ); | 235 | ); |
| 228 | }); | 236 | }); |
| 229 | 237 | ||
| 230 | it.effect("observes PATH changes after the manager has been constructed", () => { | 238 | it.effect.skipIf(windowsHost)( |
| 231 | const env = { PATH: "" }; | 239 | "observes PATH changes after the manager has been constructed", |
| 232 | return Effect.gen(function* () { | 240 | () => { |
| 233 | const fileSystem = yield* FileSystem.FileSystem; | 241 | const env = { PATH: "" }; |
| 234 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ | 242 | return Effect.gen(function* () { |
| 235 | prefix: "t3-cloudflared-test-", | 243 | const fileSystem = yield* FileSystem.FileSystem; |
| 236 | }); | 244 | const baseDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 237 | const binDir = `${baseDir}/bin`; | 245 | prefix: "t3-cloudflared-test-", |
| 238 | const executablePath = `${binDir}/cloudflared`; | 246 | }); |
| 239 | const manager = yield* makeCloudflaredRelayClient({ | 247 | const binDir = `${baseDir}/bin`; |
| 240 | baseDir, | 248 | const executablePath = `${binDir}/cloudflared`; |
| 241 | }); | 249 | const manager = yield* makeCloudflaredRelayClient({ |
| 250 | baseDir, | ||
| 251 | }); | ||
| 242 | 252 | ||
| 243 | expect(yield* manager.resolve).toEqual({ | 253 | expect(yield* manager.resolve).toEqual({ |
| 244 | status: "missing", | 254 | status: "missing", |
| 245 | version: CLOUDFLARED_VERSION, | 255 | version: CLOUDFLARED_VERSION, |
| 246 | }); | 256 | }); |
| 247 | 257 | ||
| 248 | yield* fileSystem.makeDirectory(binDir); | 258 | yield* fileSystem.makeDirectory(binDir); |
| 249 | yield* fileSystem.writeFileString(executablePath, "cloudflared"); | 259 | yield* fileSystem.writeFileString(executablePath, "cloudflared"); |
| 250 | yield* fileSystem.chmod(executablePath, 0o755); | 260 | yield* fileSystem.chmod(executablePath, 0o755); |
| 251 | env.PATH = binDir; | 261 | env.PATH = binDir; |
| 252 | 262 | ||
| 253 | expect(yield* manager.resolve).toEqual({ | 263 | expect(yield* manager.resolve).toEqual({ |
| 254 | status: "available", | 264 | status: "available", |
| 255 | executablePath, | 265 | executablePath, |
| 256 | source: "path", | 266 | source: "path", |
| 257 | version: CLOUDFLARED_VERSION, | 267 | version: CLOUDFLARED_VERSION, |
| 258 | }); | 268 | }); |
| 259 | }).pipe( | 269 | }).pipe( |
| 260 | Effect.scoped, | 270 | Effect.scoped, |
| 261 | Effect.provide( | 271 | Effect.provide( |
| 262 | Layer.mergeAll( | 272 | Layer.mergeAll( |
| 263 | NodeServices.layer, | 273 | NodeServices.layer, |
| 264 | makeHttpClientLayer(new Uint8Array()), | 274 | makeHttpClientLayer(new Uint8Array()), |
| 265 | makeSpawnerLayer([]), | 275 | makeSpawnerLayer([]), |
| 266 | hostRuntimeLayer(env), | 276 | hostRuntimeLayer(env), |
| 277 | ), | ||
| 267 | ), | 278 | ), |
| 268 | ), | 279 | ); |
| 269 | ); | 280 | }, |
| 270 | }); | 281 | ); |
| 271 | }); | 282 | }); |