authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-08-05 22:35:52-07:00
committergravatar for me@theo.liTheo Browne <me@theo.li> 2026-08-06 08:09:51-04:00
log8f341f20c91e708de1dab949dbd723e86f2ff328
treed32b2d7b40876f659e455cd65f76104a1fe6d53a
parent808d6853555c8b57f414a06e0a79eb1b9e192ef5

test(server): use the launcher state writer in release tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

1 files changed, 25 insertions(+), 14 deletions(-)

apps/server/src/cloud/http.test.ts+25-14
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1import * as NodeServices from "@effect/platform-node/NodeServices";1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { describe, expect, it } from "@effect/vitest";2import { describe, expect, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";3import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";4import * as Layer from "effect/Layer";
5import * as Option from "effect/Option";5import * as Option from "effect/Option";
6import * as Path from "effect/Path";6import * as Path from "effect/Path";
7import * as PlatformError from "effect/PlatformError";7import * as PlatformError from "effect/PlatformError";
...@@ -18,6 +18,12 @@ import { RelayClientTracer } from "@t3tools/shared/relayTracing";...@@ -18,6 +18,12 @@ import { RelayClientTracer } from "@t3tools/shared/relayTracing";
18import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts";18import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts";
19import * as ServerSecretStore from "../auth/ServerSecretStore.ts";19import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
20import * as ServerConfigModule from "../config.ts";20import * as ServerConfigModule from "../config.ts";
21import { writeServiceState } from "../serviceLauncher.ts";
22import {
23 SERVICE_LAUNCHER_PROTOCOL,
24 SERVICE_STATE_FILE,
25 type ServiceUpdateRecord,
26} from "./serviceProtocol.ts";
21import * as ServerEnvironment from "../environment/ServerEnvironment.ts";27import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
22import { CLOUD_CLI_DESIRED_LINK_SECRET } from "./CliState.ts";28import { CLOUD_CLI_DESIRED_LINK_SECRET } from "./CliState.ts";
23import * as CliTokenManager from "./CliTokenManager.ts";29import * as CliTokenManager from "./CliTokenManager.ts";
...@@ -259,18 +265,20 @@ describe("releaseManagedTunnelOnShutdown", () => {...@@ -259,18 +265,20 @@ describe("releaseManagedTunnelOnShutdown", () => {
259 readonly respond?: () => Response;265 readonly respond?: () => Response;
260 }266 }
261267
262 // Writes the launcher's durable state file into this test's baseDir; the268 // Writes the launcher's durable state file into this test's baseDir with
263 // release reads it to detect an in-flight update handoff.269 // the launcher's own writer; the release reads it to detect an in-flight
264 const writeServiceState = (update: Record<string, unknown>) =>270 // update handoff.
271 const writeLauncherState = (update: ServiceUpdateRecord) =>
265 Effect.gen(function* () {272 Effect.gen(function* () {
266 const fs = yield* FileSystem.FileSystem;
267 const path = yield* Path.Path;273 const path = yield* Path.Path;
268 const config = yield* ServerConfigModule.ServerConfig;274 const config = yield* ServerConfigModule.ServerConfig;
269 const statePath = path.join(config.baseDir, "runtime", "service-state.json");275 const statePath = path.join(config.baseDir, "runtime", SERVICE_STATE_FILE);
270 yield* fs.makeDirectory(path.dirname(statePath), { recursive: true });276 yield* Effect.promise(() =>
271 yield* fs.writeFileString(277 writeServiceState(statePath, {
272 statePath,278 protocol: SERVICE_LAUNCHER_PROTOCOL,
273 JSON.stringify({ version: 1, activeVersion: "0.0.30", update }),279 activeVersion: "0.0.30",
280 update,
281 }),
274 );282 );
275 });283 });
276284
...@@ -326,8 +334,11 @@ describe("releaseManagedTunnelOnShutdown", () => {...@@ -326,8 +334,11 @@ describe("releaseManagedTunnelOnShutdown", () => {
326 ),334 ),
327 // The release consults the launcher state file under the configured335 // The release consults the launcher state file under the configured
328 // baseDir, so every harness run gets a scoped temp baseDir.336 // baseDir, so every harness run gets a scoped temp baseDir.
329 Effect.provide(ServerConfigModule.layerTest("/", { prefix: "t3-http-release-test-" })),337 Effect.provide(
330 Effect.provide(NodeServices.layer),338 ServerConfigModule.layerTest("/", { prefix: "t3-http-release-test-" }).pipe(
339 Layer.provideMerge(NodeServices.layer),
340 ),
341 ),
331 Effect.scoped,342 Effect.scoped,
332 );343 );
333344
...@@ -419,7 +430,7 @@ describe("releaseManagedTunnelOnShutdown", () => {...@@ -419,7 +430,7 @@ describe("releaseManagedTunnelOnShutdown", () => {
419 const requests: Array<HttpClientRequest.HttpClientRequest> = [];430 const requests: Array<HttpClientRequest.HttpClientRequest> = [];
420431
421 return Effect.gen(function* () {432 return Effect.gen(function* () {
422 yield* writeServiceState({433 yield* writeLauncherState({
423 id: "update-1",434 id: "update-1",
424 fromVersion: "0.0.30",435 fromVersion: "0.0.30",
425 targetVersion: "0.0.31",436 targetVersion: "0.0.31",
...@@ -446,7 +457,7 @@ describe("releaseManagedTunnelOnShutdown", () => {...@@ -446,7 +457,7 @@ describe("releaseManagedTunnelOnShutdown", () => {
446 const requests: Array<HttpClientRequest.HttpClientRequest> = [];457 const requests: Array<HttpClientRequest.HttpClientRequest> = [];
447458
448 return Effect.gen(function* () {459 return Effect.gen(function* () {
449 yield* writeServiceState({460 yield* writeLauncherState({
450 id: "update-1",461 id: "update-1",
451 fromVersion: "0.0.30",462 fromVersion: "0.0.30",
452 targetVersion: "0.0.31",463 targetVersion: "0.0.31",