1import { describe, expect, it } from "vite-plus/test";
2import { AuthFilesystemReadScope, AuthOrchestrationReadScope } from "@t3tools/contracts";
3
4import {
5 createBrowseNavigationCoordinator,
6 getFilesystemBrowsePath,
7 getFilesystemBrowseTabCompletion,
8 resolveFilesystemReadAccess,
9} from "./filesystem.ts";
10
11const SPLIT_SCOPES_SERVER = { serverUpdateScope: "environment:maintain" } as const;
12
13describe("filesystem read access", () => {
14 it("waits for the initial catalog before declaring a missing environment disconnected", () => {
15 expect(
16 resolveFilesystemReadAccess({
17 isCatalogReady: false,
18 connection: null,
19 session: null,
20 sessionError: null,
21 }),
22 ).toEqual({ canReadFiles: false, isPending: true, error: null });
23 });
24
25 it.each([false, true])(
26 "preserves a cached file grant offline with catalog ready=%s",
27 (isCatalogReady) => {
28 const input = {
29 isCatalogReady,
30 connection: { phase: "offline", error: null },
31 session: { authenticated: true, scopes: [AuthFilesystemReadScope] },
32 sessionError: null,
33 } as const;
34 expect(resolveFilesystemReadAccess(input)).toEqual({
35 canReadFiles: true,
36 isPending: false,
37 error: null,
38 });
39 expect(
40 resolveFilesystemReadAccess({ ...input, sessionError: "The session has expired." }),
41 ).toEqual({ canReadFiles: false, isPending: false, error: "The session has expired." });
42 },
43 );
44
45 it.each([
46 { authenticated: true, scopes: [AuthOrchestrationReadScope], auth: SPLIT_SCOPES_SERVER },
47 { authenticated: false, scopes: [AuthFilesystemReadScope], auth: SPLIT_SCOPES_SERVER },
48 ] as const)("does not infer file access from an ungranted session", (session) => {
49 expect(
50 resolveFilesystemReadAccess({
51 isCatalogReady: true,
52 connection: { phase: "connected", error: null },
53 session,
54 sessionError: null,
55 }),
56 ).toEqual({ canReadFiles: false, isPending: false, error: null });
57 });
58
59 it("falls back to the orchestration grant on a server that predates filesystem:read", () => {
60 expect(
61 resolveFilesystemReadAccess({
62 isCatalogReady: true,
63 connection: { phase: "connected", error: null },
64 session: { authenticated: true, scopes: [AuthOrchestrationReadScope], auth: {} },
65 sessionError: null,
66 }),
67 ).toEqual({ canReadFiles: true, isPending: false, error: null });
68 });
69});
70
71describe("filesystem browse model", () => {
72 it("derives the browse target and navigation state", () => {
73 expect(getFilesystemBrowsePath("~/projects/t3")).toEqual({
74 isBrowsing: true,
75 directoryPath: "~/projects/",
76 filterQuery: "t3",
77 parentPath: "~/",
78 canBrowseUp: true,
79 });
80 expect(getFilesystemBrowsePath("C:\\Users\\test", "MacIntel").isBrowsing).toBe(false);
81 expect(getFilesystemBrowsePath("~/projects/", "", false).isBrowsing).toBe(false);
82 });
83
84 it("completes Tab like a shell", () => {
85 const entry = (name: string) => ({ name, fullPath: `/Users/me/${name}` });
86 expect(getFilesystemBrowseTabCompletion([entry("Creations")], "Creation")).toEqual({
87 name: "Creations",
88 isComplete: true,
89 });
90 expect(getFilesystemBrowseTabCompletion([entry("OpenSourced"), entry("OpenAI")], "op")).toEqual(
91 { name: "Open", isComplete: false },
92 );
93 expect(getFilesystemBrowseTabCompletion([entry("Open"), entry("Opal")], "Op")).toBeNull();
94 expect(getFilesystemBrowseTabCompletion([entry("openAI"), entry("OpenSource")], "o")).toEqual({
95 name: "open",
96 isComplete: false,
97 });
98 expect(getFilesystemBrowseTabCompletion([entry("😀one"), entry("😁two")], "")).toBeNull();
99 expect(getFilesystemBrowseTabCompletion([entry("ΟΣα"), entry("οσβ")], "ο")).toBeNull();
100 expect(getFilesystemBrowseTabCompletion([entry("İfoo"), entry("İfar")], "i\u0307")).toEqual({
101 name: "İf",
102 isComplete: false,
103 });
104 expect(getFilesystemBrowseTabCompletion([entry("😀one"), entry("😀other")], "😀")).toEqual({
105 name: "😀o",
106 isComplete: false,
107 });
108 expect(getFilesystemBrowseTabCompletion([], "x")).toBeNull();
109 });
110});
111
112describe("browse navigation", () => {
113 it("only commits the latest valid navigation", async () => {
114 const navigation = createBrowseNavigationCoordinator();
115 const first = Promise.withResolvers<void>();
116 const second = Promise.withResolvers<void>();
117 const commits: string[] = [];
118 const commit = (name: string) => () => commits.push(name);
119 const firstRun = navigation.run(() => first.promise, commit("first"));
120 const secondRun = navigation.run(() => second.promise, commit("second"));
121
122 second.resolve();
123 await expect(secondRun).resolves.toBe(true);
124 first.resolve();
125 await expect(firstRun).resolves.toBe(false);
126
127 const invalidated = Promise.withResolvers<void>();
128 const invalidatedRun = navigation.run(() => invalidated.promise, commit("stale"));
129 navigation.invalidate();
130 invalidated.resolve();
131
132 await expect(invalidatedRun).resolves.toBe(false);
133 expect(commits).toEqual(["second"]);
134 });
135});