| ... | ... | @@ -45,6 +45,16 @@ const makeSuccessfulProcess = (stdout: string) => { |
| 45 | 45 | }); |
| 46 | 46 | }; |
| 47 | 47 | |
| 48 | const makeDelayedSuccessfulProcess = (stdout: string, delayMs: number) => { |
| 49 | const process = makeSuccessfulProcess(stdout); |
| 50 | return { |
| 51 | ...process, |
| 52 | exitCode: Effect.sleep(Duration.millis(delayMs)).pipe( |
| 53 | Effect.as(ChildProcessSpawner.ExitCode(0)), |
| 54 | ), |
| 55 | }; |
| 56 | }; |
| 57 | |
| 48 | 58 | const makeRunningProcess = (onKill: () => void) => { |
| 49 | 59 | let finish: ((exitCode: ChildProcessSpawner.ExitCode) => void) | null = null; |
| 50 | 60 | return ChildProcessSpawner.makeHandle({ |
| ... | ... | @@ -174,6 +184,7 @@ describe("ssh tunnel scripts", () => { |
| 174 | 184 | assert.include(buildRemoteLaunchScript(), '--base-dir "$DEFAULT_SERVER_HOME"'); |
| 175 | 185 | assert.notInclude(buildRemoteLaunchScript(), "server-home"); |
| 176 | 186 | assert.include(buildRemoteLaunchScript(), "Remote T3 server did not become ready"); |
| 187 | assert.include(buildRemoteLaunchScript(), 'wait_ready "60000"'); |
| 177 | 188 | assert.include(buildRemoteLaunchScript({ packageSpec: "t3@nightly" }), "t3@nightly"); |
| 178 | 189 | assert.include( |
| 179 | 190 | buildRemotePairingScript(target), |
| ... | ... | @@ -235,6 +246,29 @@ describe("ssh tunnel scripts", () => { |
| 235 | 246 | }).pipe(Effect.provide(processLayer)); |
| 236 | 247 | }); |
| 237 | 248 | |
| 249 | it.effect("allows cold remote launches to exceed the default SSH command timeout", () => { |
| 250 | const target = { |
| 251 | alias: "devbox", |
| 252 | hostname: "devbox.example.com", |
| 253 | username: "julius", |
| 254 | port: 2222, |
| 255 | } as const; |
| 256 | const spawner = ChildProcessSpawner.make(() => |
| 257 | Effect.succeed(makeDelayedSuccessfulProcess('{"remotePort":3774}\n', 75_000)), |
| 258 | ); |
| 259 | const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner); |
| 260 | const processLayer = Layer.mergeAll(NodeServices.layer, spawnerLayer, TestClock.layer()); |
| 261 | |
| 262 | return Effect.gen(function* () { |
| 263 | const fiber = yield* Effect.forkChild(launchOrReuseRemoteServer(target)); |
| 264 | yield* Effect.yieldNow; |
| 265 | yield* TestClock.adjust(Duration.seconds(75)); |
| 266 | |
| 267 | const result = yield* Fiber.join(fiber); |
| 268 | assert.equal(result.remotePort, 3774); |
| 269 | }).pipe(Effect.provide(processLayer)); |
| 270 | }); |
| 271 | |
| 238 | 272 | it("allows the remote port picker to run without a state file path", () => { |
| 239 | 273 | assert.include(REMOTE_PICK_PORT_SCRIPT, 'const filePath = process.argv[2] ?? "";'); |
| 240 | 274 | }); |