Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
93 lines
3.3 KiB
TypeScript
93 lines
3.3 KiB
TypeScript
import { mkdtemp, readFile, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import { clearSetup, readSetup, setupFilePath, writeSetup } from "./setup-store.js";
|
|
|
|
let userData: string;
|
|
|
|
beforeEach(async () => {
|
|
userData = await mkdtemp(path.join(tmpdir(), "rakazo-setup-"));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await rm(userData, { recursive: true, force: true });
|
|
});
|
|
|
|
describe("setup store", () => {
|
|
it("reports no setup before the first run", async () => {
|
|
await expect(readSetup(userData)).resolves.toBeNull();
|
|
});
|
|
|
|
it("keeps the chosen instance across launches", async () => {
|
|
await writeSetup(userData, { mode: "existing", serverUrl: "https://rakazo.example.com" });
|
|
await expect(readSetup(userData)).resolves.toEqual({
|
|
mode: "existing",
|
|
serverUrl: "https://rakazo.example.com",
|
|
});
|
|
});
|
|
|
|
it("clears saved setup so first-run runs again", async () => {
|
|
await writeSetup(userData, { mode: "existing", serverUrl: "https://rakazo.example.com" });
|
|
await clearSetup(userData);
|
|
await expect(readSetup(userData)).resolves.toBeNull();
|
|
});
|
|
|
|
it("creates the user data directory when it does not exist yet", async () => {
|
|
const nested = path.join(userData, "nested", "profile");
|
|
await writeSetup(nested, { mode: "new", serverUrl: "http://127.0.0.1:5173" });
|
|
await expect(readSetup(nested)).resolves.toEqual({
|
|
mode: "new",
|
|
serverUrl: "http://127.0.0.1:5173",
|
|
});
|
|
});
|
|
|
|
it.runIf(process.platform !== "win32")("keeps the saved address private", async () => {
|
|
await writeSetup(userData, { mode: "existing", serverUrl: "https://rakazo.example.com" });
|
|
const info = await stat(setupFilePath(userData));
|
|
expect(info.mode & 0o777).toBe(0o600);
|
|
});
|
|
|
|
it.runIf(process.platform !== "win32")(
|
|
"does not load setup through a final symlink",
|
|
async () => {
|
|
const target = path.join(userData, "linked-setup.json");
|
|
await writeFile(
|
|
target,
|
|
JSON.stringify({ mode: "existing", serverUrl: "https://rakazo.example.com" }),
|
|
"utf8",
|
|
);
|
|
await symlink(target, setupFilePath(userData));
|
|
|
|
await expect(readSetup(userData)).resolves.toBeNull();
|
|
},
|
|
);
|
|
|
|
it.runIf(process.platform !== "win32")(
|
|
"replaces a final symlink instead of overwriting its target",
|
|
async () => {
|
|
const victim = path.join(userData, "victim.txt");
|
|
await writeFile(victim, "untouched", "utf8");
|
|
await symlink(victim, setupFilePath(userData));
|
|
|
|
await writeSetup(userData, { mode: "existing", serverUrl: "https://rakazo.example.com" });
|
|
|
|
await expect(readSetup(userData)).resolves.toEqual({
|
|
mode: "existing",
|
|
serverUrl: "https://rakazo.example.com",
|
|
});
|
|
await expect(readFile(victim, "utf8")).resolves.toBe("untouched");
|
|
},
|
|
);
|
|
|
|
it("falls back to setup when the saved file is corrupt", async () => {
|
|
await writeFile(setupFilePath(userData), "{ not json", "utf8");
|
|
await expect(readSetup(userData)).resolves.toBeNull();
|
|
});
|
|
|
|
it("does not buffer an oversized saved setup", async () => {
|
|
await writeFile(setupFilePath(userData), " ".repeat(64 * 1024 + 1), "utf8");
|
|
await expect(readSetup(userData)).resolves.toBeNull();
|
|
});
|
|
});
|