Skip to content
Source
Sign inSign up

mobile-native-client.test.ts

129 lines4.2 KB

1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";
5import * as Path from "effect/Path";
6​
7import {
8 buildRunTitle,
9 NativeClientError,
10 prebuildAndroid,
11 remoteBuildState,
12} from "./mobile-native-client.ts";
13​
14const caches = [".gradle/history", "app/.cxx/object"];
15​
16const fixture = Effect.gen(function* () {
17 const fs = yield* FileSystem.FileSystem;
18 const path = yield* Path.Path;
19 const mobile = yield* fs.makeTempDirectoryScoped({ prefix: "mobile-native-client-" });
20 const android = path.join(mobile, "android");
21 for (const cache of caches) {
22 yield* fs.makeDirectory(path.dirname(path.join(android, cache)), { recursive: true });
23 yield* fs.writeFileString(path.join(android, cache), cache);
24 }
25 const cleanPrebuild = Effect.gen(function* () {
26 yield* fs.remove(android, { recursive: true });
27 yield* fs.makeDirectory(path.join(android, "app"), { recursive: true });
28 yield* fs.writeFileString(path.join(android, "app/build.gradle"), "regenerated");
29 });
30 const assertCachesRestored = Effect.gen(function* () {
31 for (const cache of caches) {
32 assert.strictEqual(yield* fs.readFileString(path.join(android, cache)), cache);
33 }
34 assert.deepStrictEqual(yield* fs.readDirectory(mobile), ["android"]);
35 });
36 return { fs, path, mobile, android, cleanPrebuild, assertCachesRestored };
37});
38​
39it.layer(NodeServices.layer)("prebuildAndroid", (it) => {
40 it.effect("keeps Gradle and native caches through a clean prebuild", () =>
41 Effect.gen(function* () {
42 const { fs, path, mobile, android, cleanPrebuild, assertCachesRestored } = yield* fixture;
43​
44 yield* prebuildAndroid(mobile, cleanPrebuild);
45​
46 assert.strictEqual(
47 yield* fs.readFileString(path.join(android, "app/build.gradle")),
48 "regenerated",
49 );
50 yield* assertCachesRestored;
51 }),
52 );
53​
54 it.effect("restores the caches and returns the error when prebuild fails", () =>
55 Effect.gen(function* () {
56 const { mobile, cleanPrebuild, assertCachesRestored } = yield* fixture;
57 const failure = new NativeClientError({ message: "prebuild failed" });
58​
59 const error = yield* prebuildAndroid(
60 mobile,
61 cleanPrebuild.pipe(Effect.andThen(Effect.fail(failure))),
62 ).pipe(Effect.flip);
63​
64 assert.strictEqual(error, failure);
65 yield* assertCachesRestored;
66 }),
67 );
68});
69​
70it("finds a Blacksmith build by its run title and reads its state", () => {
71 const fingerprint = "a".repeat(40);
72 const title = buildRunTitle("android", "x86_64", fingerprint);
73 assert.strictEqual(title, `Dev client android x86_64 ${fingerprint}`);
74 assert.strictEqual(buildRunTitle("ios", null, fingerprint), `Dev client ios ${fingerprint}`);
75 const run = (
76 createdAt: string,
77 status: string,
78 conclusion: string | null,
79 displayTitle = title,
80 ) => ({
81 displayTitle,
82 status,
83 conclusion,
84 createdAt,
85 });
86 const since = Date.parse("2026-11-09T12:00:00Z");
87 assert.strictEqual(remoteBuildState([], title, since), "none");
88 // Another client's run, and a run from before this wait, say nothing about this build.
89 assert.strictEqual(
90 remoteBuildState(
91 [
92 run(
93 "2026-11-09T12:05:00Z",
94 "in_progress",
95 null,
96 buildRunTitle("android", "arm64-v8a", fingerprint),
97 ),
98 run("2026-11-09T11:00:00Z", "completed", "failure"),
99 ],
100 title,
101 since,
102 ),
103 "none",
104 );
105 assert.strictEqual(
106 remoteBuildState([run("2026-11-09T12:01:00Z", "queued", null)], title, since),
107 "running",
108 );
109 assert.strictEqual(
110 remoteBuildState([run("2026-11-09T12:01:00Z", "completed", "success")], title, since),
111 "succeeded",
112 );
113 // The newest run decides: a retry that is running outranks the failure before it.
114 assert.strictEqual(
115 remoteBuildState(
116 [
117 run("2026-11-09T12:01:00Z", "completed", "failure"),
118 run("2026-11-09T12:09:00Z", "in_progress", null),
119 ],
120 title,
121 since,
122 ),
123 "running",
124 );
125 assert.strictEqual(
126 remoteBuildState([run("2026-11-09T12:01:00Z", "completed", "cancelled")], title, since),
127 "failed",
128 );
129});