authorgravatar for 269035359+t3-code[bot]@users.noreply.github.comt3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com> 2026-08-16 05:08:33+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-16 05:08:33+02:00
log27732293373fbb081a966b437ae022afe77db16b
tree0bfbd7a0e0e634aa9d817c3b3d4fadff78a5ddad
parentd484735c64ed98a0737b594818996660f72c1616
signature Signed by PGP key B5690EEEBB952194

test: remove redundant and stale tests (#6267)


11 files changed, 0 insertions(+), 875 deletions(-)

apps/desktop/src/backend/tailscaleEndpointProvider.test.ts-8
......@@ -5,7 +5,6 @@ import { HttpClient } from "effect/unstable/http";
55import { ChildProcessSpawner } from "effect/unstable/process";
66
77import {
8 isTailscaleIpv4Address,
98 parseTailscaleMagicDnsName,
109 resolveTailscaleAdvertisedEndpoints,
1110} from "./tailscaleEndpointProvider.ts";
......@@ -22,13 +21,6 @@ const unusedTailscaleExternalServicesLayer = Layer.mergeAll(
2221);
2322
2423describe("tailscale endpoint provider", () => {
25 it("detects Tailnet IPv4 addresses", () => {
26 assert.equal(isTailscaleIpv4Address("100.64.0.1"), true);
27 assert.equal(isTailscaleIpv4Address("100.127.255.254"), true);
28 assert.equal(isTailscaleIpv4Address("100.128.0.1"), false);
29 assert.equal(isTailscaleIpv4Address("192.168.1.44"), false);
30 });
31
3224 it.effect("parses MagicDNS names from tailscale status", () =>
3325 Effect.gen(function* () {
3426 const dnsName = yield* parseTailscaleMagicDnsName(
apps/desktop/src/preview/PickPreload.test.ts deleted-86
......@@ -1,86 +0,0 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import { computeLabelPosition } from "./PickLabelPosition.ts";
4
5const VIEWPORT = { viewportWidth: 1280, viewportHeight: 800 };
6
7describe("computeLabelPosition", () => {
8 it("anchors to the element's top-left when there's room above and to the right", () => {
9 const { x, y } = computeLabelPosition({
10 ...VIEWPORT,
11 targetLeft: 200,
12 targetTop: 200,
13 targetBottom: 240,
14 labelWidth: 120,
15 labelHeight: 18,
16 });
17 expect(x).toBe(200);
18 // 200 (top) - 18 (height) - 4 (gap)
19 expect(y).toBe(200 - 18 - 4);
20 });
21
22 it("clamps left edge so the label stays inside the viewport", () => {
23 const { x } = computeLabelPosition({
24 ...VIEWPORT,
25 targetLeft: -50,
26 targetTop: 200,
27 targetBottom: 240,
28 labelWidth: 120,
29 labelHeight: 18,
30 });
31 expect(x).toBe(4);
32 });
33
34 it("clamps right edge when the label would overflow the viewport (the bug we shipped)", () => {
35 const { x } = computeLabelPosition({
36 ...VIEWPORT,
37 targetLeft: 1240,
38 targetTop: 200,
39 targetBottom: 240,
40 labelWidth: 200,
41 labelHeight: 18,
42 });
43 // viewportWidth (1280) - labelWidth (200) - margin (4) = 1076
44 expect(x).toBe(1076);
45 });
46
47 it("flips the label below the element when there's no room above", () => {
48 const { y } = computeLabelPosition({
49 ...VIEWPORT,
50 targetLeft: 200,
51 targetTop: 4,
52 targetBottom: 44,
53 labelWidth: 120,
54 labelHeight: 18,
55 });
56 // labelY = 4 - 18 - 4 = -18 → flip → 44 + 4 = 48
57 expect(y).toBe(48);
58 });
59
60 it("pins to the bottom margin when the element fills the viewport (no room above OR below)", () => {
61 const { y } = computeLabelPosition({
62 ...VIEWPORT,
63 targetLeft: 200,
64 targetTop: 0,
65 targetBottom: 800,
66 labelWidth: 120,
67 labelHeight: 18,
68 });
69 // Above overflows top → flip below = 800 + 4 = 804 → also overflows
70 // bottom → pin to viewportHeight - labelHeight - margin = 778.
71 expect(y).toBe(800 - 18 - 4);
72 });
73
74 it("never returns a negative coordinate", () => {
75 const { x, y } = computeLabelPosition({
76 ...VIEWPORT,
77 targetLeft: -1000,
78 targetTop: -1000,
79 targetBottom: -900,
80 labelWidth: 5000,
81 labelHeight: 5000,
82 });
83 expect(x).toBeGreaterThanOrEqual(0);
84 expect(y).toBeGreaterThanOrEqual(0);
85 });
86});
apps/mobile/src/features/terminal/terminalMenu.test.ts-11
......@@ -8,7 +8,6 @@ import { getTerminalLabel } from "@t3tools/shared/terminalLabels";
88import {
99 buildTerminalMenuSessions,
1010 nextOpenTerminalId,
11 nextTerminalId,
1211 previousLiveTerminalId,
1312 resolveProjectScriptTerminalId,
1413 type TerminalMenuSession,
......@@ -125,16 +124,6 @@ describe("buildTerminalMenuSessions", () => {
125124 });
126125});
127126
128describe("nextTerminalId", () => {
129 it("uses the primary id when no terminals are listed yet", () => {
130 expect(nextTerminalId([])).toBe(DEFAULT_TERMINAL_ID);
131 });
132
133 it("allocates term-2 when only the primary shell exists", () => {
134 expect(nextTerminalId([DEFAULT_TERMINAL_ID])).toBe("term-2");
135 });
136});
137
138127describe("nextOpenTerminalId", () => {
139128 it("matches nextTerminalId when not on a terminal route", () => {
140129 expect(nextOpenTerminalId({ listedTerminalIds: [] })).toBe(DEFAULT_TERMINAL_ID);
apps/server/src/orchestration/commandInvariants.test.ts-21
......@@ -14,7 +14,6 @@ import * as Effect from "effect/Effect";
1414import {
1515 findThreadById,
1616 listThreadsByProjectId,
17 requireNonNegativeInteger,
1817 requireThread,
1918 requireThreadAbsent,
2019} from "./commandInvariants.ts";
......@@ -200,24 +199,4 @@ describe("commandInvariants", () => {
200199 ),
201200 ).rejects.toThrow("already exists");
202201 });
203
204 it("requires non-negative integers", async () => {
205 await Effect.runPromise(
206 requireNonNegativeInteger({
207 commandType: "thread.checkpoint.revert",
208 field: "turnCount",
209 value: 0,
210 }),
211 );
212
213 await expect(
214 Effect.runPromise(
215 requireNonNegativeInteger({
216 commandType: "thread.checkpoint.revert",
217 field: "turnCount",
218 value: -1,
219 }),
220 ),
221 ).rejects.toThrow("greater than or equal to 0");
222 });
223202});
apps/server/src/provider/Layers/ProviderRegistry.test.ts-66
......@@ -40,9 +40,7 @@ import { ProviderInstanceRegistryHydrationLive } from "./ProviderInstanceRegistr
4040import {
4141 haveProvidersChanged,
4242 mergeProviderSnapshot,
43 mergeProviderSnapshots,
4443 ProviderRegistryLive,
45 selectProvidersByKind,
4644} from "./ProviderRegistry.ts";
4745import * as ServerConfig from "../../config.ts";
4846import * as ServerSettingsModule from "../../serverSettings.ts";
......@@ -893,70 +891,6 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te
893891 }),
894892 );
895893
896 it("persists merged provider snapshots for the providers that were refreshed", () => {
897 const previousProviders = [
898 {
899 instanceId: ProviderInstanceId.make("cursor"),
900 driver: ProviderDriverKind.make("cursor"),
901 status: "ready",
902 enabled: true,
903 installed: true,
904 auth: { status: "authenticated" },
905 checkedAt: "2026-04-14T00:00:00.000Z",
906 version: "2026.04.09-f2b0fcd",
907 models: [
908 {
909 slug: "claude-opus-4-6",
910 name: "Opus 4.6",
911 isCustom: false,
912 capabilities: createModelCapabilities({
913 optionDescriptors: [
914 selectDescriptor("reasoning", "Reasoning", [
915 { id: "high", label: "High", isDefault: true },
916 ]),
917 booleanDescriptor("fastMode", "Fast Mode"),
918 booleanDescriptor("thinking", "Thinking"),
919 ],
920 }),
921 },
922 ],
923 slashCommands: [],
924 skills: [],
925 },
926 {
927 instanceId: ProviderInstanceId.make("codex"),
928 driver: ProviderDriverKind.make("codex"),
929 status: "ready",
930 enabled: true,
931 installed: true,
932 auth: { status: "authenticated" },
933 checkedAt: "2026-04-14T00:00:00.000Z",
934 version: "1.0.0",
935 models: [],
936 slashCommands: [],
937 skills: [],
938 },
939 ] as const satisfies ReadonlyArray<ServerProvider>;
940 const refreshedCursor = {
941 ...previousProviders[0],
942 checkedAt: "2026-04-14T00:01:00.000Z",
943 models: [],
944 } satisfies ServerProvider;
945
946 const mergedProviders = mergeProviderSnapshots(previousProviders, [refreshedCursor]);
947 const persistedProviders = selectProvidersByKind(
948 mergedProviders,
949 new Set([ProviderDriverKind.make("cursor")]),
950 );
951
952 assert.deepStrictEqual(persistedProviders, [
953 {
954 ...refreshedCursor,
955 models: [...previousProviders[0].models],
956 },
957 ]);
958 });
959
960894 it.effect("persists the merged snapshot when a live update has empty models", () =>
961895 Effect.gen(function* () {
962896 const cursorDriver = ProviderDriverKind.make("cursor");
apps/web/src/historyBootstrap.test.ts deleted-139
......@@ -1,139 +0,0 @@
1import { MessageId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import { buildBootstrapInput } from "./historyBootstrap";
5
6const messageId = (value: string) => MessageId.make(value);
7
8describe("buildBootstrapInput", () => {
9 it("includes full transcript when under budget", () => {
10 const result = buildBootstrapInput(
11 [
12 {
13 id: messageId("u-1"),
14 role: "user",
15 text: "hello",
16 createdAt: "2026-02-09T00:00:00.000Z",
17 turnId: null,
18 updatedAt: "2026-02-09T00:00:00.000Z",
19 streaming: false,
20 },
21 {
22 id: messageId("a-1"),
23 role: "assistant",
24 text: "world",
25 createdAt: "2026-02-09T00:00:01.000Z",
26 turnId: null,
27 updatedAt: "2026-02-09T00:00:01.000Z",
28 streaming: false,
29 },
30 ],
31 "what's next?",
32 1_500,
33 );
34
35 expect(result.includedCount).toBe(2);
36 expect(result.omittedCount).toBe(0);
37 expect(result.truncated).toBe(false);
38 expect(result.text).toContain("USER:\nhello");
39 expect(result.text).toContain("ASSISTANT:\nworld");
40 expect(result.text).toContain("Latest user request (answer this now):");
41 expect(result.text).toContain("what's next?");
42 });
43
44 it("truncates older transcript messages when over budget", () => {
45 const result = buildBootstrapInput(
46 [
47 {
48 id: messageId("u-1"),
49 role: "user",
50 text: "first question with details",
51 createdAt: "2026-02-09T00:00:00.000Z",
52 turnId: null,
53 updatedAt: "2026-02-09T00:00:00.000Z",
54 streaming: false,
55 },
56 {
57 id: messageId("a-1"),
58 role: "assistant",
59 text: "first answer with details",
60 createdAt: "2026-02-09T00:00:01.000Z",
61 turnId: null,
62 updatedAt: "2026-02-09T00:00:01.000Z",
63 streaming: false,
64 },
65 {
66 id: messageId("u-2"),
67 role: "user",
68 text: "second question with details",
69 createdAt: "2026-02-09T00:00:02.000Z",
70 turnId: null,
71 updatedAt: "2026-02-09T00:00:02.000Z",
72 streaming: false,
73 },
74 ],
75 "final request",
76 320,
77 );
78
79 expect(result.truncated).toBe(true);
80 expect(result.omittedCount).toBeGreaterThan(0);
81 expect(result.includedCount).toBeLessThan(3);
82 expect(result.text).toContain("omitted to stay within input limits");
83 expect(result.text.length).toBeLessThanOrEqual(320);
84 });
85
86 it("preserves the latest prompt when prompt-only fallback is required", () => {
87 const latestPrompt = "Please keep this exact latest prompt.";
88 const result = buildBootstrapInput(
89 [
90 {
91 id: messageId("u-1"),
92 role: "user",
93 text: "old context",
94 createdAt: "2026-02-09T00:00:00.000Z",
95 turnId: null,
96 updatedAt: "2026-02-09T00:00:00.000Z",
97 streaming: false,
98 },
99 ],
100 latestPrompt,
101 latestPrompt.length + 3,
102 );
103
104 expect(result.text).toBe(latestPrompt);
105 expect(result.includedCount).toBe(0);
106 expect(result.omittedCount).toBe(1);
107 expect(result.truncated).toBe(true);
108 });
109
110 it("captures user image attachment context in transcript blocks", () => {
111 const result = buildBootstrapInput(
112 [
113 {
114 id: messageId("u-image"),
115 role: "user",
116 text: "",
117 attachments: [
118 {
119 type: "image",
120 id: "img-1",
121 name: "screenshot.png",
122 mimeType: "image/png",
123 sizeBytes: 2_048,
124 },
125 ],
126 createdAt: "2026-02-09T00:00:00.000Z",
127 turnId: null,
128 updatedAt: "2026-02-09T00:00:00.000Z",
129 streaming: false,
130 },
131 ],
132 "What does this error mean?",
133 1_500,
134 );
135
136 expect(result.text).toContain("Attached image");
137 expect(result.text).toContain("screenshot.png");
138 });
139});
apps/web/src/lib/terminalUiStateCleanup.test.ts deleted-65
......@@ -1,65 +0,0 @@
1import { scopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime/environment";
2import { ThreadId } from "@t3tools/contracts";
3import { describe, expect, it } from "vite-plus/test";
4
5import { collectActiveTerminalUiThreadKeys } from "./terminalUiStateCleanup";
6
7const threadId = (id: string): ThreadId => ThreadId.make(id);
8const threadKey = (environmentId: string, id: string): string =>
9 scopedThreadKey(scopeThreadRef(environmentId as never, threadId(id)));
10
11describe("collectActiveTerminalUiThreadKeys", () => {
12 it("retains non-deleted server threads", () => {
13 const activeThreadKeys = collectActiveTerminalUiThreadKeys({
14 snapshotThreads: [
15 { key: threadKey("env-a", "server-1"), deletedAt: null, archivedAt: null },
16 { key: threadKey("env-b", "server-2"), deletedAt: null, archivedAt: null },
17 ],
18 draftThreadKeys: [],
19 });
20
21 expect(activeThreadKeys).toEqual(
22 new Set([threadKey("env-a", "server-1"), threadKey("env-b", "server-2")]),
23 );
24 });
25
26 it("ignores deleted and archived server threads and keeps local draft threads", () => {
27 const activeThreadKeys = collectActiveTerminalUiThreadKeys({
28 snapshotThreads: [
29 { key: threadKey("env-a", "server-active"), deletedAt: null, archivedAt: null },
30 {
31 key: threadKey("env-a", "server-deleted"),
32 deletedAt: "2026-03-05T08:00:00.000Z",
33 archivedAt: null,
34 },
35 {
36 key: threadKey("env-a", "server-archived"),
37 deletedAt: null,
38 archivedAt: "2026-03-05T09:00:00.000Z",
39 },
40 ],
41 draftThreadKeys: [threadKey("env-a", "local-draft")],
42 });
43
44 expect(activeThreadKeys).toEqual(
45 new Set([threadKey("env-a", "server-active"), threadKey("env-a", "local-draft")]),
46 );
47 });
48
49 it("does not keep draft-linked terminal UI state for archived server threads", () => {
50 const archivedThreadId = threadKey("env-a", "server-archived");
51
52 const activeThreadKeys = collectActiveTerminalUiThreadKeys({
53 snapshotThreads: [
54 {
55 key: archivedThreadId,
56 deletedAt: null,
57 archivedAt: "2026-03-05T09:00:00.000Z",
58 },
59 ],
60 draftThreadKeys: [archivedThreadId, threadKey("env-a", "local-draft")],
61 });
62
63 expect(activeThreadKeys).toEqual(new Set([threadKey("env-a", "local-draft")]));
64 });
65});
apps/web/src/orchestrationEventEffects.test.ts deleted-135
......@@ -1,135 +0,0 @@
1import {
2 CheckpointRef,
3 EventId,
4 MessageId,
5 ProjectId,
6 ProviderInstanceId,
7 ThreadId,
8 TurnId,
9 type OrchestrationEvent,
10} from "@t3tools/contracts";
11import { describe, expect, it } from "vite-plus/test";
12
13import { deriveOrchestrationBatchEffects } from "./orchestrationEventEffects";
14
15function makeEvent<T extends OrchestrationEvent["type"]>(
16 type: T,
17 payload: Extract<OrchestrationEvent, { type: T }>["payload"],
18 overrides: Partial<Extract<OrchestrationEvent, { type: T }>> = {},
19): Extract<OrchestrationEvent, { type: T }> {
20 const sequence = overrides.sequence ?? 1;
21 return {
22 sequence,
23 eventId: EventId.make(`event-${sequence}`),
24 aggregateKind: "thread",
25 aggregateId:
26 "threadId" in payload
27 ? payload.threadId
28 : "projectId" in payload
29 ? payload.projectId
30 : ProjectId.make("project-1"),
31 occurredAt: "2026-02-27T00:00:00.000Z",
32 commandId: null,
33 causationEventId: null,
34 correlationId: null,
35 metadata: {},
36 type,
37 payload,
38 ...overrides,
39 } as Extract<OrchestrationEvent, { type: T }>;
40}
41
42describe("deriveOrchestrationBatchEffects", () => {
43 it("targets draft promotion and terminal cleanup from thread lifecycle events", () => {
44 const createdThreadId = ThreadId.make("thread-created");
45 const deletedThreadId = ThreadId.make("thread-deleted");
46 const archivedThreadId = ThreadId.make("thread-archived");
47
48 const effects = deriveOrchestrationBatchEffects([
49 makeEvent("thread.created", {
50 threadId: createdThreadId,
51 projectId: ProjectId.make("project-1"),
52 title: "Created thread",
53 modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5-codex" },
54 runtimeMode: "full-access",
55 interactionMode: "default",
56 branch: null,
57 worktreePath: null,
58 createdAt: "2026-02-27T00:00:00.000Z",
59 updatedAt: "2026-02-27T00:00:00.000Z",
60 }),
61 makeEvent("thread.deleted", {
62 threadId: deletedThreadId,
63 deletedAt: "2026-02-27T00:00:01.000Z",
64 }),
65 makeEvent("thread.archived", {
66 threadId: archivedThreadId,
67 archivedAt: "2026-02-27T00:00:02.000Z",
68 updatedAt: "2026-02-27T00:00:02.000Z",
69 }),
70 ]);
71
72 expect(effects.promoteDraftThreadIds).toEqual([createdThreadId]);
73 expect(effects.clearDeletedThreadIds).toEqual([deletedThreadId]);
74 expect(effects.removeTerminalUiStateThreadIds).toEqual([deletedThreadId, archivedThreadId]);
75 expect(effects.needsProviderInvalidation).toBe(false);
76 });
77
78 it("keeps only the final lifecycle outcome for a thread within one batch", () => {
79 const threadId = ThreadId.make("thread-1");
80
81 const effects = deriveOrchestrationBatchEffects([
82 makeEvent("thread.deleted", {
83 threadId,
84 deletedAt: "2026-02-27T00:00:01.000Z",
85 }),
86 makeEvent("thread.created", {
87 threadId,
88 projectId: ProjectId.make("project-1"),
89 title: "Recreated thread",
90 modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5-codex" },
91 runtimeMode: "full-access",
92 interactionMode: "default",
93 branch: null,
94 worktreePath: null,
95 createdAt: "2026-02-27T00:00:02.000Z",
96 updatedAt: "2026-02-27T00:00:02.000Z",
97 }),
98 makeEvent("thread.turn-diff-completed", {
99 threadId,
100 turnId: TurnId.make("turn-1"),
101 checkpointTurnCount: 1,
102 checkpointRef: CheckpointRef.make("checkpoint-1"),
103 status: "ready",
104 files: [],
105 assistantMessageId: MessageId.make("assistant-1"),
106 completedAt: "2026-02-27T00:00:03.000Z",
107 }),
108 ]);
109
110 expect(effects.promoteDraftThreadIds).toEqual([threadId]);
111 expect(effects.clearDeletedThreadIds).toEqual([]);
112 expect(effects.removeTerminalUiStateThreadIds).toEqual([]);
113 expect(effects.needsProviderInvalidation).toBe(true);
114 });
115
116 it("does not retain archive cleanup when a thread is unarchived later in the same batch", () => {
117 const threadId = ThreadId.make("thread-1");
118
119 const effects = deriveOrchestrationBatchEffects([
120 makeEvent("thread.archived", {
121 threadId,
122 archivedAt: "2026-02-27T00:00:01.000Z",
123 updatedAt: "2026-02-27T00:00:01.000Z",
124 }),
125 makeEvent("thread.unarchived", {
126 threadId,
127 updatedAt: "2026-02-27T00:00:02.000Z",
128 }),
129 ]);
130
131 expect(effects.promoteDraftThreadIds).toEqual([]);
132 expect(effects.clearDeletedThreadIds).toEqual([]);
133 expect(effects.removeTerminalUiStateThreadIds).toEqual([]);
134 });
135});
apps/web/src/orchestrationRecovery.test.ts deleted-306
......@@ -1,306 +0,0 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 createOrchestrationRecoveryCoordinator,
5 deriveReplayRetryDecision,
6} from "./orchestrationRecovery";
7
8describe("createOrchestrationRecoveryCoordinator", () => {
9 it("defers live events until bootstrap completes and then requests replay", () => {
10 const coordinator = createOrchestrationRecoveryCoordinator();
11
12 expect(coordinator.beginSnapshotRecovery("bootstrap")).toBe(true);
13 expect(coordinator.classifyDomainEvent(4)).toBe("defer");
14
15 expect(coordinator.completeSnapshotRecovery(2)).toBe(true);
16 expect(coordinator.getState()).toMatchObject({
17 latestSequence: 2,
18 highestObservedSequence: 4,
19 bootstrapped: true,
20 pendingReplay: false,
21 inFlight: null,
22 });
23 });
24
25 it("classifies sequence gaps as recovery-only replay work", () => {
26 const coordinator = createOrchestrationRecoveryCoordinator();
27
28 coordinator.beginSnapshotRecovery("bootstrap");
29 coordinator.completeSnapshotRecovery(3);
30
31 expect(coordinator.classifyDomainEvent(5)).toBe("recover");
32 expect(coordinator.beginReplayRecovery("sequence-gap")).toBe(true);
33 expect(coordinator.getState().inFlight).toEqual({
34 kind: "replay",
35 reason: "sequence-gap",
36 });
37 });
38
39 it("tracks live event batches without entering recovery", () => {
40 const coordinator = createOrchestrationRecoveryCoordinator();
41
42 coordinator.beginSnapshotRecovery("bootstrap");
43 coordinator.completeSnapshotRecovery(3);
44
45 expect(coordinator.classifyDomainEvent(4)).toBe("apply");
46 expect(coordinator.markEventBatchApplied([{ sequence: 4 }])).toEqual([{ sequence: 4 }]);
47 expect(coordinator.getState()).toMatchObject({
48 latestSequence: 4,
49 highestObservedSequence: 4,
50 bootstrapped: true,
51 inFlight: null,
52 });
53 });
54
55 it("requests another replay when deferred events arrive during replay recovery", () => {
56 const coordinator = createOrchestrationRecoveryCoordinator();
57
58 coordinator.beginSnapshotRecovery("bootstrap");
59 coordinator.completeSnapshotRecovery(3);
60 coordinator.classifyDomainEvent(5);
61 coordinator.beginReplayRecovery("sequence-gap");
62 coordinator.classifyDomainEvent(7);
63 coordinator.markEventBatchApplied([{ sequence: 4 }, { sequence: 5 }, { sequence: 6 }]);
64
65 expect(coordinator.completeReplayRecovery()).toEqual({
66 replayMadeProgress: true,
67 shouldReplay: true,
68 });
69 });
70
71 it("retries replay when no progress was made but higher live sequences were observed", () => {
72 const coordinator = createOrchestrationRecoveryCoordinator();
73
74 coordinator.beginSnapshotRecovery("bootstrap");
75 coordinator.completeSnapshotRecovery(3);
76 coordinator.classifyDomainEvent(5);
77 coordinator.beginReplayRecovery("sequence-gap");
78
79 expect(coordinator.completeReplayRecovery()).toEqual({
80 replayMadeProgress: false,
81 shouldReplay: true,
82 });
83 expect(coordinator.getState()).toMatchObject({
84 latestSequence: 3,
85 highestObservedSequence: 5,
86 pendingReplay: false,
87 inFlight: null,
88 });
89 });
90
91 it("does not request another replay when a replay made no progress and nothing newer was observed", () => {
92 const coordinator = createOrchestrationRecoveryCoordinator();
93
94 coordinator.beginSnapshotRecovery("bootstrap");
95 coordinator.completeSnapshotRecovery(3);
96 coordinator.beginReplayRecovery("sequence-gap");
97
98 expect(coordinator.completeReplayRecovery()).toEqual({
99 replayMadeProgress: false,
100 shouldReplay: false,
101 });
102 });
103
104 it("marks replay failure as unbootstrapped so snapshot fallback is recovery-only", () => {
105 const coordinator = createOrchestrationRecoveryCoordinator();
106
107 coordinator.beginSnapshotRecovery("bootstrap");
108 coordinator.completeSnapshotRecovery(3);
109 coordinator.beginReplayRecovery("sequence-gap");
110 coordinator.failReplayRecovery();
111
112 expect(coordinator.getState()).toMatchObject({
113 bootstrapped: false,
114 inFlight: null,
115 });
116 expect(coordinator.beginSnapshotRecovery("replay-failed")).toBe(true);
117 expect(coordinator.getState().inFlight).toEqual({
118 kind: "snapshot",
119 reason: "replay-failed",
120 });
121 });
122
123 it("keeps enough state to explain why bootstrap snapshot recovery requests replay", () => {
124 const coordinator = createOrchestrationRecoveryCoordinator();
125
126 expect(coordinator.beginSnapshotRecovery("bootstrap")).toBe(true);
127 expect(coordinator.classifyDomainEvent(4)).toBe("defer");
128 expect(coordinator.completeSnapshotRecovery(2)).toBe(true);
129
130 expect(coordinator.getState()).toMatchObject({
131 latestSequence: 2,
132 highestObservedSequence: 4,
133 bootstrapped: true,
134 pendingReplay: false,
135 inFlight: null,
136 });
137 });
138
139 it("reports skip state when snapshot recovery is requested while replay is in flight", () => {
140 const coordinator = createOrchestrationRecoveryCoordinator();
141
142 coordinator.beginSnapshotRecovery("bootstrap");
143 coordinator.completeSnapshotRecovery(3);
144 expect(coordinator.beginReplayRecovery("sequence-gap")).toBe(true);
145
146 expect(coordinator.beginSnapshotRecovery("bootstrap")).toBe(false);
147 expect(coordinator.getState()).toMatchObject({
148 pendingReplay: true,
149 inFlight: {
150 kind: "replay",
151 reason: "sequence-gap",
152 },
153 });
154 });
155});
156
157describe("deriveReplayRetryDecision", () => {
158 it("retries immediately when replay made progress", () => {
159 expect(
160 deriveReplayRetryDecision({
161 previousTracker: {
162 attempts: 2,
163 latestSequence: 3,
164 highestObservedSequence: 5,
165 },
166 completion: {
167 replayMadeProgress: true,
168 shouldReplay: true,
169 },
170 recoveryState: {
171 latestSequence: 5,
172 highestObservedSequence: 5,
173 },
174 baseDelayMs: 100,
175 maxNoProgressRetries: 3,
176 }),
177 ).toEqual({
178 shouldRetry: true,
179 delayMs: 0,
180 tracker: null,
181 });
182 });
183
184 it("caps no-progress retries for the same frontier", () => {
185 const first = deriveReplayRetryDecision({
186 previousTracker: null,
187 completion: {
188 replayMadeProgress: false,
189 shouldReplay: true,
190 },
191 recoveryState: {
192 latestSequence: 3,
193 highestObservedSequence: 5,
194 },
195 baseDelayMs: 100,
196 maxNoProgressRetries: 3,
197 });
198
199 const second = deriveReplayRetryDecision({
200 previousTracker: first.tracker,
201 completion: {
202 replayMadeProgress: false,
203 shouldReplay: true,
204 },
205 recoveryState: {
206 latestSequence: 3,
207 highestObservedSequence: 5,
208 },
209 baseDelayMs: 100,
210 maxNoProgressRetries: 3,
211 });
212
213 const third = deriveReplayRetryDecision({
214 previousTracker: second.tracker,
215 completion: {
216 replayMadeProgress: false,
217 shouldReplay: true,
218 },
219 recoveryState: {
220 latestSequence: 3,
221 highestObservedSequence: 5,
222 },
223 baseDelayMs: 100,
224 maxNoProgressRetries: 3,
225 });
226
227 const fourth = deriveReplayRetryDecision({
228 previousTracker: third.tracker,
229 completion: {
230 replayMadeProgress: false,
231 shouldReplay: true,
232 },
233 recoveryState: {
234 latestSequence: 3,
235 highestObservedSequence: 5,
236 },
237 baseDelayMs: 100,
238 maxNoProgressRetries: 3,
239 });
240
241 expect(first).toEqual({
242 shouldRetry: true,
243 delayMs: 100,
244 tracker: {
245 attempts: 1,
246 latestSequence: 3,
247 highestObservedSequence: 5,
248 },
249 });
250 expect(second).toEqual({
251 shouldRetry: true,
252 delayMs: 200,
253 tracker: {
254 attempts: 2,
255 latestSequence: 3,
256 highestObservedSequence: 5,
257 },
258 });
259 expect(third).toEqual({
260 shouldRetry: true,
261 delayMs: 400,
262 tracker: {
263 attempts: 3,
264 latestSequence: 3,
265 highestObservedSequence: 5,
266 },
267 });
268 expect(fourth).toEqual({
269 shouldRetry: false,
270 delayMs: 0,
271 tracker: null,
272 });
273 });
274
275 it("resets the retry budget when the replay frontier changes", () => {
276 const exhausted = {
277 attempts: 3,
278 latestSequence: 3,
279 highestObservedSequence: 5,
280 };
281
282 expect(
283 deriveReplayRetryDecision({
284 previousTracker: exhausted,
285 completion: {
286 replayMadeProgress: false,
287 shouldReplay: true,
288 },
289 recoveryState: {
290 latestSequence: 3,
291 highestObservedSequence: 6,
292 },
293 baseDelayMs: 100,
294 maxNoProgressRetries: 3,
295 }),
296 ).toEqual({
297 shouldRetry: true,
298 delayMs: 100,
299 tracker: {
300 attempts: 1,
301 latestSequence: 3,
302 highestObservedSequence: 6,
303 },
304 });
305 });
306});
infra/relay/scripts/deploy.test.ts-20
......@@ -9,7 +9,6 @@ import {
99 missingRelayPublicConfigFields,
1010 publicConfigFromOutput,
1111 reconcileRootEnvPublicConfig,
12 reconcileRootEnvRelayUrl,
1312 RelayDeployError,
1413 RelayDeployPublicConfigUnavailableError,
1514 serializeGithubOutput,
......@@ -87,25 +86,6 @@ describe("hasDeployChanges", () => {
8786 });
8887});
8988
90describe("reconcileRootEnvRelayUrl", () => {
91 it("adds the relay URL to an empty root env file", () => {
92 expect(reconcileRootEnvRelayUrl("", "https://relay.example.test")).toBe(
93 "T3CODE_RELAY_URL=https://relay.example.test\n",
94 );
95 });
96
97 it("preserves unrelated root env entries while replacing a previous relay URL", () => {
98 expect(
99 reconcileRootEnvRelayUrl(
100 "T3CODE_CLERK_PUBLISHABLE_KEY=pk_test_example\nT3CODE_RELAY_URL=https://old.example.test\n",
101 "https://relay.example.test",
102 ),
103 ).toBe(
104 "T3CODE_CLERK_PUBLISHABLE_KEY=pk_test_example\nT3CODE_RELAY_URL=https://relay.example.test\n",
105 );
106 });
107});
108
10989describe("reconcileRootEnvPublicConfig", () => {
11090 const config = {
11191 relayUrl: "https://relay.example.test",
scripts/mobile-showcase.test.ts-18
......@@ -22,7 +22,6 @@ import {
2222 resolveAndroidSdkRoot,
2323 selectLanIpv4Address,
2424 showcaseCaptureDirectory,
25 showcaseSceneUrl,
2625 validateStoreAsset,
2726 validateStoreAssetCount,
2827} from "./mobile-showcase.ts";
......@@ -244,23 +243,6 @@ it("selects a reachable LAN IPv4 address", () => {
244243 );
245244});
246245
247it("maps capture scenes to the real application routes", () => {
248 assert.equal(showcaseSceneUrl("threads", "environment-1"), "t3code://");
249 assert.equal(showcaseSceneUrl("environments", "environment-1"), "t3code://settings/environments");
250 assert.equal(
251 showcaseSceneUrl("thread", "environment-1"),
252 "t3code://threads/environment-1/remote-command-center",
253 );
254 assert.equal(
255 showcaseSceneUrl("terminal", "environment-1"),
256 "t3code://threads/environment-1/remote-command-center/terminal?terminalId=term-1",
257 );
258 assert.equal(
259 showcaseSceneUrl("review", "environment-1"),
260 "t3code://threads/environment-1/remote-command-center/review",
261 );
262});
263
264246it("seeds a playful multi-environment project spectrum", () => {
265247 assert.deepStrictEqual(
266248 SHOWCASE_PROJECTS.map((project) => project.title),