| 1 | import type { EnvironmentShellSummary } from "@t3tools/client-runtime/state/shell"; |
| 2 | import { |
| 3 | BearerConnectionProfile, |
| 4 | BearerConnectionTarget, |
| 5 | } from "@t3tools/client-runtime/connection"; |
| 6 | import { EnvironmentId } from "@t3tools/contracts"; |
| 7 | import { describe, expect, it } from "@effect/vitest"; |
| 8 | import * as Option from "effect/Option"; |
| 9 | |
| 10 | import { projectWorkspaceEnvironment, projectWorkspaceState } from "./workspaceModel"; |
| 11 | import type { EnvironmentPresentation } from "./environments"; |
| 12 | |
| 13 | const ENVIRONMENT_ID = EnvironmentId.make("environment-1"); |
| 14 | |
| 15 | function environment( |
| 16 | phase: EnvironmentPresentation["connection"]["phase"], |
| 17 | ): EnvironmentPresentation { |
| 18 | const connectionId = `bearer:${ENVIRONMENT_ID}`; |
| 19 | return { |
| 20 | environmentId: ENVIRONMENT_ID, |
| 21 | label: "Julius's MacBook Pro", |
| 22 | displayUrl: "https://environment.example.test", |
| 23 | relayManaged: false, |
| 24 | entry: { |
| 25 | target: new BearerConnectionTarget({ |
| 26 | environmentId: ENVIRONMENT_ID, |
| 27 | label: "Julius's MacBook Pro", |
| 28 | connectionId, |
| 29 | }), |
| 30 | profile: Option.some( |
| 31 | new BearerConnectionProfile({ |
| 32 | connectionId, |
| 33 | environmentId: ENVIRONMENT_ID, |
| 34 | label: "Julius's MacBook Pro", |
| 35 | httpBaseUrl: "https://environment.example.test", |
| 36 | wsBaseUrl: "wss://environment.example.test", |
| 37 | }), |
| 38 | ), |
| 39 | enabled: true, |
| 40 | }, |
| 41 | connection: { |
| 42 | phase, |
| 43 | error: phase === "error" ? "Connection failed." : null, |
| 44 | traceId: phase === "error" ? "trace-1" : null, |
| 45 | }, |
| 46 | serverConfig: null, |
| 47 | }; |
| 48 | } |
| 49 | |
| 50 | const EMPTY_SHELL_SUMMARY: EnvironmentShellSummary = { |
| 51 | hasSnapshot: false, |
| 52 | hasSynchronizingShell: false, |
| 53 | hasCachedShell: false, |
| 54 | hasLiveShell: false, |
| 55 | firstError: null, |
| 56 | }; |
| 57 | |
| 58 | const CACHED_SHELL_SUMMARY: EnvironmentShellSummary = { |
| 59 | ...EMPTY_SHELL_SUMMARY, |
| 60 | hasSnapshot: true, |
| 61 | hasSynchronizingShell: true, |
| 62 | hasCachedShell: true, |
| 63 | }; |
| 64 | |
| 65 | describe("mobile workspace projection", () => { |
| 66 | it("preserves explicit offline state without presenting it as a connection error", () => { |
| 67 | const projected = projectWorkspaceEnvironment(ENVIRONMENT_ID, environment("offline")); |
| 68 | |
| 69 | expect(projected.connectionState).toBe("offline"); |
| 70 | expect(projected.connectionError).toBeNull(); |
| 71 | }); |
| 72 | |
| 73 | it("reports offline before stale connected presentations", () => { |
| 74 | const environments = [projectWorkspaceEnvironment(ENVIRONMENT_ID, environment("connected"))]; |
| 75 | const state = projectWorkspaceState({ |
| 76 | isReady: true, |
| 77 | networkStatus: "offline", |
| 78 | environments, |
| 79 | shellSummary: EMPTY_SHELL_SUMMARY, |
| 80 | }); |
| 81 | |
| 82 | expect(state.connectionState).toBe("offline"); |
| 83 | expect(state.networkStatus).toBe("offline"); |
| 84 | expect(state.hasReadyEnvironment).toBe(false); |
| 85 | }); |
| 86 | |
| 87 | it("projects reconnecting environments dynamically from active phases", () => { |
| 88 | const environments = [ |
| 89 | projectWorkspaceEnvironment(ENVIRONMENT_ID, environment("reconnecting")), |
| 90 | projectWorkspaceEnvironment(EnvironmentId.make("environment-2"), environment("connected")), |
| 91 | ]; |
| 92 | const state = projectWorkspaceState({ |
| 93 | isReady: true, |
| 94 | networkStatus: "online", |
| 95 | environments, |
| 96 | shellSummary: EMPTY_SHELL_SUMMARY, |
| 97 | }); |
| 98 | |
| 99 | expect(state.connectingEnvironments).toHaveLength(1); |
| 100 | expect(state.connectingEnvironments[0]?.connectionState).toBe("reconnecting"); |
| 101 | expect(state.hasConnectingEnvironment).toBe(true); |
| 102 | expect(state.hasReadyEnvironment).toBe(true); |
| 103 | }); |
| 104 | |
| 105 | it("keeps retained snapshots visible while reconnecting without claiming readiness", () => { |
| 106 | const environments = [projectWorkspaceEnvironment(ENVIRONMENT_ID, environment("reconnecting"))]; |
| 107 | const state = projectWorkspaceState({ |
| 108 | isReady: true, |
| 109 | networkStatus: "online", |
| 110 | environments, |
| 111 | shellSummary: CACHED_SHELL_SUMMARY, |
| 112 | }); |
| 113 | |
| 114 | expect(state.hasLoadedShellSnapshot).toBe(true); |
| 115 | expect(state.hasPendingShellSnapshot).toBe(true); |
| 116 | expect(state.hasReadyEnvironment).toBe(false); |
| 117 | expect(state.connectionState).toBe("reconnecting"); |
| 118 | }); |
| 119 | }); |