1import { Query } from "@distilled.cloud/core/query";
2import { Railway as RailwaySdk } from "@distilled.cloud/railway";
3import * as Railway from "@/Railway";
4import * as Test from "@/Test/Alchemy";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8
9const { test } = Test.make({ providers: Railway.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16const verifyLoginSession = Query.fn((code: string) =>
17 RailwaySdk.loginSessionVerify({ code }),
18);
19
20const consumeLoginSession = Query.fn((code: string) =>
21 RailwaySdk.loginSessionConsume({ code }),
22);
23
24test.provider(
25 "create, verify, and cancel a login session",
26 (stack) =>
27 Effect.gen(function* () {
28 yield* stack.destroy();
29
30 const code = yield* Railway.provideAnonymousRailway(
31 Railway.createLoginSession(),
32 );
33 expect(code).toEqual(expect.any(String));
34 expect(code.length).toBeGreaterThan(0);
35
36 const url = Railway.loginSessionUrl(code, { hostname: "alchemy-test" });
37 expect(url.startsWith("https://railway.com/cli-login?d=")).toBe(true);
38 const encoded = url.slice("https://railway.com/cli-login?d=".length);
39 const payload = Buffer.from(
40 encoded.replace(/-/g, "+").replace(/_/g, "/"),
41 "base64",
42 ).toString("utf8");
43 expect(payload).toContain(`wordCode=${code}`);
44 expect(payload).toContain("hostname=alchemy-test");
45
46 const verified = yield* Railway.provideAnonymousRailway(
47 verifyLoginSession(code).pipe(
48 Effect.catchTag("RailwayNotFound", () => Effect.succeed(false)),
49 ),
50 );
51 // Verify is a liveness check: true while the pairing session exists,
52 // even before the user authorizes in the browser.
53 expect(verified).toBe(true);
54
55 const beforeAuth = yield* Railway.provideAnonymousRailway(
56 consumeLoginSession(code).pipe(
57 Effect.catchTag("RailwayNotFound", () => Effect.succeed(null)),
58 ),
59 );
60 expect(beforeAuth).toBeNull();
61
62 const cancelled = yield* Railway.provideAnonymousRailway(
63 Railway.cancelLoginSession(code),
64 );
65 expect(cancelled).toBe(true);
66
67 const cancelledAgain = yield* Railway.provideAnonymousRailway(
68 Railway.cancelLoginSession(code).pipe(
69 Effect.catchTag("RailwayNotFound", () => Effect.succeed(false)),
70 ),
71 );
72 expect(typeof cancelledAgain).toBe("boolean");
73
74 const verifiedAfter = yield* Railway.provideAnonymousRailway(
75 verifyLoginSession(code).pipe(
76 Effect.catchTag("RailwayNotFound", () => Effect.succeed(false)),
77 ),
78 );
79 expect(verifiedAfter).toBe(false);
80
81 const consumedAfter = yield* Railway.provideAnonymousRailway(
82 consumeLoginSession(code).pipe(
83 Effect.catchTag("RailwayNotFound", () => Effect.succeed(null)),
84 ),
85 );
86 expect(consumedAfter).toBeNull();
87
88 yield* stack.destroy();
89 }).pipe(logLevel),
90 { tags: ["provider:railway", "live"], timeout: 120_000 },
91);