Files
OwnCord/Client/tauri-client/tests/unit/profiles.test.ts
T
J3vbandClaude c3837fa32c fix(client): batch of 15 client correctness fixes (#1367)
* fix(client): 1 defect(s) (OC-0078)

* fix(client): 1 defect(s) (OC-0147)

* fix(client): 1 defect(s) (OC-0141)

* fix(voice): 1 defect(s) (OC-0125)

* fix(client): 1 defect(s) (OC-0138)

* fix(client): 1 defect(s) (OC-0136)

* fix(client): 1 defect(s) (OC-0121)

* fix(voice): 1 defect(s) (OC-0132)

* fix(client): 1 defect(s) (OC-0057)

* fix(client): 1 defect(s) (OC-0122)

* fix(client): 1 defect(s) (OC-0130)

* fix(client): 1 defect(s) (OC-0060)

* fix(client): 1 defect(s) (OC-0123)

* fix(client): 1 defect(s) (OC-0124)

* fix(ws): 1 defect(s) (OC-0056)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-14 08:11:16 +02:00

896 lines
28 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, type Mock } from "vitest";
const mockInvoke = vi.fn();
vi.mock("@tauri-apps/api/core", () => ({
invoke: (...args: unknown[]) => mockInvoke(...args),
}));
import {
createProfileManager,
createTauriBackend,
type PersistenceBackend,
type CreateProfileData,
type ServerProfile,
type FetchFn,
type HealthStatus,
type ProfilesState,
} from "@lib/profiles";
// ---------------------------------------------------------------------------
// Deterministic UUID stub
// ---------------------------------------------------------------------------
let uuidCounter = 0;
function nextUuid(): string {
uuidCounter++;
return `00000000-0000-0000-0000-${String(uuidCounter).padStart(12, "0")}`;
}
// ---------------------------------------------------------------------------
// Mock persistence backend
// ---------------------------------------------------------------------------
function createMockBackend(): PersistenceBackend & {
saved: Array<{ schemaVersion: number; profiles: readonly ServerProfile[] }>;
} {
let stored: { schemaVersion: number; profiles: readonly ServerProfile[] } | null = null;
const saved: Array<{ schemaVersion: number; profiles: readonly ServerProfile[] }> = [];
return {
saved,
async load() {
return stored;
},
async save(data) {
stored = data;
saved.push(data);
},
};
}
// ---------------------------------------------------------------------------
// Mock fetch
// ---------------------------------------------------------------------------
function createMockFetch(handler: (url: string, init?: RequestInit) => Promise<Response>): FetchFn {
return handler as unknown as FetchFn;
}
function jsonResponse(body: unknown, status = 200): Response {
return new Response(JSON.stringify(body), {
status,
headers: { "Content-Type": "application/json" },
});
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
const sampleData: CreateProfileData = {
name: "Dev Server",
host: "localhost:8443",
username: "alice",
color: "#ff5500",
autoConnect: false,
rememberPassword: false,
};
const sampleData2: CreateProfileData = {
name: "Prod Server",
host: "prod.example.com:443",
username: "bob",
color: "#00aaff",
autoConnect: true,
rememberPassword: false,
};
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
describe("ProfileManager", () => {
let backend: ReturnType<typeof createMockBackend>;
let mockFetch: Mock;
beforeEach(() => {
backend = createMockBackend();
uuidCounter = 0;
vi.stubGlobal("crypto", {
randomUUID: vi.fn(() => nextUuid()),
});
mockFetch = vi.fn();
});
function mgr(fetchFn?: FetchFn) {
return createProfileManager(backend, fetchFn ?? (mockFetch as unknown as FetchFn));
}
// ── CRUD ─────────────────────────────────────────────────
describe("CRUD operations", () => {
it("starts with an empty profile list", () => {
const m = mgr();
expect(m.getAll()).toEqual([]);
});
it("adds a profile with a generated UUID", () => {
const m = mgr();
const profile = m.addProfile(sampleData);
expect(profile.id).toBe("00000000-0000-0000-0000-000000000001");
expect(profile.name).toBe("Dev Server");
expect(profile.host).toBe("localhost:8443");
expect(profile.username).toBe("alice");
expect(profile.color).toBe("#ff5500");
expect(profile.autoConnect).toBe(false);
expect(profile.lastConnected).toBeNull();
expect(m.getAll()).toHaveLength(1);
});
it("retrieves a profile by id", () => {
const m = mgr();
const created = m.addProfile(sampleData);
expect(m.getById(created.id)).toEqual(created);
expect(m.getById("nonexistent")).toBeNull();
});
it("updates a profile immutably", () => {
const m = mgr();
const original = m.addProfile(sampleData);
const updated = m.updateProfile(original.id, { name: "Renamed" });
expect(updated).not.toBeNull();
expect(updated!.name).toBe("Renamed");
expect(updated!.host).toBe(original.host);
// Original object not mutated
expect(original.name).toBe("Dev Server");
// Store has the updated version
expect(m.getById(original.id)!.name).toBe("Renamed");
});
it("returns null when updating a nonexistent profile", () => {
const m = mgr();
expect(m.updateProfile("missing", { name: "X" })).toBeNull();
});
it("removes an existing profile", () => {
const m = mgr();
const profile = m.addProfile(sampleData);
expect(m.removeProfile(profile.id)).toBe(true);
expect(m.getAll()).toHaveLength(0);
expect(m.getById(profile.id)).toBeNull();
});
it("returns false when removing a nonexistent profile", () => {
const m = mgr();
expect(m.removeProfile("missing")).toBe(false);
});
it("sets lastConnected to current ISO timestamp", () => {
const m = mgr();
const profile = m.addProfile(sampleData);
const before = new Date().toISOString();
m.setLastConnected(profile.id);
const after = new Date().toISOString();
const updated = m.getById(profile.id)!;
expect(updated.lastConnected).not.toBeNull();
expect(updated.lastConnected! >= before).toBe(true);
expect(updated.lastConnected! <= after).toBe(true);
// Original not mutated
expect(profile.lastConnected).toBeNull();
});
it("does nothing when setting lastConnected on nonexistent profile", () => {
const m = mgr();
// Should not throw
m.setLastConnected("missing");
expect(m.getAll()).toHaveLength(0);
expect(m.getById("missing")).toBeNull();
});
it("only updates lastConnected on the matching profile, not others", () => {
const m = mgr();
const p1 = m.addProfile(sampleData);
const p2 = m.addProfile(sampleData2);
m.setLastConnected(p1.id);
expect(m.getById(p1.id)!.lastConnected).not.toBeNull();
expect(m.getById(p2.id)!.lastConnected).toBeNull();
});
});
// ── Auto-connect ─────────────────────────────────────────
describe("auto-connect", () => {
it("returns the first auto-connect profile", () => {
const m = mgr();
m.addProfile(sampleData); // autoConnect: false
const autoProfile = m.addProfile(sampleData2); // autoConnect: true
expect(m.getAutoConnectProfile()).toEqual(autoProfile);
});
it("returns null when no profiles have autoConnect", () => {
const m = mgr();
m.addProfile(sampleData);
expect(m.getAutoConnectProfile()).toBeNull();
});
it("returns null when no profiles exist", () => {
const m = mgr();
expect(m.getAutoConnectProfile()).toBeNull();
});
});
// ── Health check ─────────────────────────────────────────
describe("health checks", () => {
it("returns online status for a healthy server", async () => {
const fetchFn = createMockFetch(async () => jsonResponse({ version: "1.2.3" }));
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
const result = await m.checkHealth(profile.id);
expect(result.status).toBe("online");
expect(result.version).toBe("1.2.3");
expect(typeof result.latencyMs).toBe("number");
});
it("sets status to checking before resolving", async () => {
const states: Array<HealthStatus | undefined> = [];
let resolveReq!: () => void;
const pending = new Promise<void>((r) => {
resolveReq = r;
});
const fetchFn = createMockFetch(async () => {
await pending;
return jsonResponse({ version: "1.0.0" });
});
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
// Subscribe to capture the "checking" state
m.store.subscribe((state: ProfilesState) => {
states.push(state.healthStatuses.get(profile.id));
});
const healthPromise = m.checkHealth(profile.id);
// At this point, state should have been set to "checking"
const checkingState = m.store.getState().healthStatuses.get(profile.id);
expect(checkingState?.status).toBe("checking");
resolveReq();
await healthPromise;
const finalState = m.store.getState().healthStatuses.get(profile.id);
expect(finalState?.status).toBe("online");
});
it("returns offline when fetch throws", async () => {
const fetchFn = createMockFetch(async () => {
throw new Error("network error");
});
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
const result = await m.checkHealth(profile.id);
expect(result.status).toBe("offline");
expect(result.latencyMs).toBeNull();
expect(result.version).toBeNull();
});
it("returns offline for non-OK response", async () => {
const fetchFn = createMockFetch(async () => jsonResponse({ error: "bad" }, 500));
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
const result = await m.checkHealth(profile.id);
expect(result.status).toBe("offline");
expect(typeof result.latencyMs).toBe("number");
});
it("returns offline for nonexistent profile", async () => {
const m = mgr();
const result = await m.checkHealth("nonexistent");
expect(result.status).toBe("offline");
});
it("pings the correct URL with /api/v1/health", async () => {
let capturedUrl = "";
const fetchFn = createMockFetch(async (url) => {
capturedUrl = url;
return jsonResponse({ version: "1.0.0" });
});
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
await m.checkHealth(profile.id);
expect(capturedUrl).toBe("https://localhost:8443/api/v1/health");
});
it("uses AbortController signal in fetch call", async () => {
let capturedSignal: AbortSignal | undefined;
const fetchFn = createMockFetch(async (_url, init) => {
capturedSignal = init?.signal ?? undefined;
return jsonResponse({ version: "1.0.0" });
});
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
await m.checkHealth(profile.id);
expect(capturedSignal).toBeInstanceOf(AbortSignal);
});
it("checkAllHealth pings all profiles in parallel", async () => {
const pingedHosts: string[] = [];
const fetchFn = createMockFetch(async (url) => {
pingedHosts.push(url);
return jsonResponse({ version: "2.0.0" });
});
const m = mgr(fetchFn);
const p1 = m.addProfile(sampleData);
const p2 = m.addProfile(sampleData2);
const results = await m.checkAllHealth();
expect(results.size).toBe(2);
expect(results.get(p1.id)?.status).toBe("online");
expect(results.get(p2.id)?.status).toBe("online");
expect(pingedHosts).toHaveLength(2);
expect(pingedHosts).toContain("https://localhost:8443/api/v1/health");
expect(pingedHosts).toContain("https://prod.example.com:443/api/v1/health");
});
it("checkAllHealth returns empty map when no profiles", async () => {
const m = mgr();
const results = await m.checkAllHealth();
expect(results.size).toBe(0);
});
});
// ── Export / Import ──────────────────────────────────────
describe("export and import", () => {
it("round-trips profiles through export and import", () => {
const m1 = mgr();
m1.addProfile(sampleData);
m1.addProfile(sampleData2);
const exported = m1.exportProfiles();
const backend2 = createMockBackend();
const m2 = createProfileManager(backend2, mockFetch as unknown as FetchFn);
const result = m2.importProfiles(exported);
expect(result.imported).toBe(2);
expect(result.skipped).toBe(0);
expect(m2.getAll()).toHaveLength(2);
const hosts = m2.getAll().map((p) => p.host);
expect(hosts).toContain("localhost:8443");
expect(hosts).toContain("prod.example.com:443");
});
it("skips duplicate hosts during import", () => {
const m = mgr();
m.addProfile(sampleData);
const incoming: ServerProfile[] = [
{
id: "ext-1",
name: "Duplicate",
host: "localhost:8443",
username: "charlie",
color: "#000000",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
{
id: "ext-2",
name: "New Server",
host: "new.example.com:443",
username: "dave",
color: "#ffffff",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
];
const result = m.importProfiles(JSON.stringify(incoming));
expect(result.imported).toBe(1);
expect(result.skipped).toBe(1);
expect(m.getAll()).toHaveLength(2);
});
it("handles invalid JSON gracefully", () => {
const m = mgr();
const result = m.importProfiles("not json");
expect(result).toEqual({ imported: 0, skipped: 0 });
});
it("handles non-array, non-envelope JSON gracefully", () => {
const m = mgr();
const result = m.importProfiles(JSON.stringify({ foo: "bar" }));
expect(result).toEqual({ imported: 0, skipped: 0 });
});
it("rejects import entries with invalid shape", () => {
const m = mgr();
const badEntries = [
{
id: "x",
name: "",
host: "a",
username: "b",
color: "#000",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
{
id: "y",
name: "Valid",
host: "valid.com:443",
username: "u",
color: "#fff",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
];
const result = m.importProfiles(JSON.stringify(badEntries));
expect(result.imported).toBe(1);
expect(result.skipped).toBe(1);
});
it("exported data includes schema version", () => {
const m = mgr();
m.addProfile(sampleData);
const exported = JSON.parse(m.exportProfiles());
expect(exported.schemaVersion).toBe(1);
expect(Array.isArray(exported.profiles)).toBe(true);
});
it("defaults rememberPassword to false when undefined in import", () => {
const m = mgr();
const incoming = [
{
id: "ext-1",
name: "No Remember",
host: "no-remember.com:443",
username: "user",
color: "#000",
autoConnect: false,
// rememberPassword omitted (undefined)
lastConnected: null,
},
];
const result = m.importProfiles(JSON.stringify(incoming));
expect(result.imported).toBe(1);
const imported = m.getAll().find((p) => p.host === "no-remember.com:443");
expect(imported).not.toBeUndefined();
expect(imported!.rememberPassword).toBe(false);
});
it("imports new UUIDs rather than keeping originals", () => {
const m1 = mgr();
const created = m1.addProfile(sampleData);
const exported = m1.exportProfiles();
const backend2 = createMockBackend();
const m2 = createProfileManager(backend2, mockFetch as unknown as FetchFn);
m2.importProfiles(exported);
const imported = m2.getAll();
expect(imported).toHaveLength(1);
// The imported profile should have a NEW UUID
expect(imported[0]!.id).not.toBe(created.id);
});
});
// ── Persistence ──────────────────────────────────────────
describe("persistence", () => {
it("loadProfiles populates store from backend", async () => {
// Pre-seed the backend
await backend.save({
schemaVersion: 1,
profiles: [
{
id: "persisted-1",
name: "Saved Server",
host: "saved.example.com:443",
username: "eve",
color: "#112233",
autoConnect: false,
rememberPassword: false,
lastConnected: "2026-01-01T00:00:00.000Z",
},
],
});
const m = mgr();
await m.loadProfiles();
expect(m.getAll()).toHaveLength(1);
expect(m.getAll()[0]!.name).toBe("Saved Server");
});
it("saveProfiles writes current state with schema version to backend", async () => {
const m = mgr();
m.addProfile(sampleData);
await m.saveProfiles();
expect(backend.saved).toHaveLength(1);
expect(backend.saved[0]!.schemaVersion).toBe(1);
expect(backend.saved[0]!.profiles).toHaveLength(1);
expect(backend.saved[0]!.profiles[0]!.name).toBe("Dev Server");
});
it("loadProfiles handles empty backend gracefully", async () => {
const m = mgr();
await m.loadProfiles();
expect(m.getAll()).toEqual([]);
});
});
// ── Auto-login ──────────────────────────────────────────
describe("setAutoLogin", () => {
it("sets auto-login on target profile and forces rememberPassword", () => {
const m = mgr();
const p1 = m.addProfile(sampleData); // autoConnect: false
m.setAutoLogin(p1.id);
const updated = m.getById(p1.id)!;
expect(updated.autoConnect).toBe(true);
expect(updated.rememberPassword).toBe(true);
});
it("clears auto-login from all other profiles", () => {
const m = mgr();
const p1 = m.addProfile(sampleData);
const p2 = m.addProfile(sampleData2); // autoConnect: true
m.setAutoLogin(p1.id);
expect(m.getById(p1.id)!.autoConnect).toBe(true);
expect(m.getById(p2.id)!.autoConnect).toBe(false);
});
it("clears auto-login on all profiles when passed null", () => {
const m = mgr();
m.addProfile(sampleData2); // autoConnect: true
m.setAutoLogin(null);
const all = m.getAll();
expect(all.every((p) => !p.autoConnect)).toBe(true);
});
it("does not modify profiles that already have autoConnect=false when clearing", () => {
const m = mgr();
const p1 = m.addProfile(sampleData); // autoConnect: false
const before = m.getById(p1.id)!;
m.setAutoLogin(null);
const after = m.getById(p1.id)!;
// Profile object reference should be the same (no unnecessary spread)
expect(after.autoConnect).toBe(false);
expect(after.name).toBe(before.name);
});
it("only one profile can be auto-login at a time", () => {
const m = mgr();
const p1 = m.addProfile(sampleData);
const p2 = m.addProfile(sampleData2);
m.setAutoLogin(p1.id);
m.setAutoLogin(p2.id);
expect(m.getById(p1.id)!.autoConnect).toBe(false);
expect(m.getById(p2.id)!.autoConnect).toBe(true);
});
it("does not spread non-autoConnect profiles when setting auto-login on a different profile", () => {
const m = mgr();
const p1 = m.addProfile(sampleData); // autoConnect: false
const p2 = m.addProfile(sampleData2); // autoConnect: true
const p3 = m.addProfile({ ...sampleData, name: "Third", host: "third.com:443" }); // autoConnect: false
m.setAutoLogin(p1.id);
// p3 was never auto-connect, so it should be returned as-is
expect(m.getById(p3.id)!.autoConnect).toBe(false);
// p2 was auto-connect, so it should be cleared
expect(m.getById(p2.id)!.autoConnect).toBe(false);
// p1 should now be auto-connect
expect(m.getById(p1.id)!.autoConnect).toBe(true);
});
});
// ── Health check — online_users parsing ────────────────
describe("health check — response parsing", () => {
it("parses online_users from health response", async () => {
const fetchFn = createMockFetch(async () =>
jsonResponse({ version: "1.2.3", online_users: 5 }),
);
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
const result = await m.checkHealth(profile.id);
expect(result.onlineUsers).toBe(5);
});
it("returns null onlineUsers when field is missing", async () => {
const fetchFn = createMockFetch(async () => jsonResponse({ version: "1.2.3" }));
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
const result = await m.checkHealth(profile.id);
expect(result.onlineUsers).toBeNull();
});
it("returns null version when field is not a string", async () => {
const fetchFn = createMockFetch(async () => jsonResponse({ version: 123 }));
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
const result = await m.checkHealth(profile.id);
expect(result.version).toBeNull();
});
});
// ── Reactive store ───────────────────────────────────────
describe("reactive store", () => {
it("notifies subscribers on profile add", () => {
const m = mgr();
const states: ProfilesState[] = [];
m.store.subscribe((s) => states.push(s));
m.addProfile(sampleData);
m.store.flush();
expect(states).toHaveLength(1);
expect(states[0]!.profiles).toHaveLength(1);
});
it("notifies subscribers on profile remove", () => {
const m = mgr();
const profile = m.addProfile(sampleData);
const states: ProfilesState[] = [];
m.store.subscribe((s) => states.push(s));
m.removeProfile(profile.id);
m.store.flush();
expect(states).toHaveLength(1);
expect(states[0]!.profiles).toHaveLength(0);
});
it("healthStatuses updates are visible via store", async () => {
const fetchFn = createMockFetch(async () => jsonResponse({ version: "3.0.0" }));
const m = mgr(fetchFn);
const profile = m.addProfile(sampleData);
await m.checkHealth(profile.id);
const statuses = m.store.getState().healthStatuses;
expect(statuses.get(profile.id)?.status).toBe("online");
expect(statuses.get(profile.id)?.version).toBe("3.0.0");
});
});
// ── createTauriBackend ──────────────────────────────────
describe("createTauriBackend", () => {
beforeEach(() => {
mockInvoke.mockReset();
});
it("load returns null when no data is stored", async () => {
mockInvoke.mockResolvedValueOnce({});
const backend = createTauriBackend();
const result = await backend.load();
expect(result).toBeNull();
expect(mockInvoke).toHaveBeenCalledWith("get_settings");
});
it("load returns profiles when valid data is stored", async () => {
const storedData = {
schemaVersion: 1,
profiles: [
{
id: "test-1",
name: "Test",
host: "test.com:443",
username: "user",
color: "#000",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
],
};
mockInvoke.mockResolvedValueOnce({ "owncord:profiles": storedData });
const backend = createTauriBackend();
const result = await backend.load();
expect(result).not.toBeNull();
expect(result!.profiles).toHaveLength(1);
expect(result!.profiles[0]!.name).toBe("Test");
});
it("load returns null for invalid data shape", async () => {
mockInvoke.mockResolvedValueOnce({
"owncord:profiles": { invalid: true },
});
const backend = createTauriBackend();
const result = await backend.load();
expect(result).toBeNull();
});
it("save calls invoke with correct key and data", async () => {
mockInvoke.mockResolvedValueOnce(undefined);
const backend = createTauriBackend();
const data = {
schemaVersion: 1,
profiles: [] as readonly ServerProfile[],
};
await backend.save(data);
expect(mockInvoke).toHaveBeenCalledWith("save_settings", {
key: "owncord:profiles",
value: data,
});
});
it("load returns null when stored value is null", async () => {
mockInvoke.mockResolvedValueOnce({ "owncord:profiles": null });
const backend = createTauriBackend();
const result = await backend.load();
expect(result).toBeNull();
});
it("load salvages the valid profiles when one stored entry is malformed (OC-0060)", async () => {
const storedData = {
schemaVersion: 1,
profiles: [
{
id: "good-1",
name: "Good One",
host: "good1.example.com:443",
username: "user1",
color: "#111111",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
{
// Malformed: written by a build predating the color field / partial write.
id: "bad-1",
name: "",
host: "bad1.example.com:443",
username: "user2",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
{
id: "good-2",
name: "Good Two",
host: "good2.example.com:443",
username: "user3",
color: "#222222",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
},
],
};
mockInvoke.mockResolvedValueOnce({ "owncord:profiles": storedData });
const backend = createTauriBackend();
const result = await backend.load();
// A single corrupt entry must not null out the whole store — only the
// bad entry should be dropped, salvaging the other two valid profiles.
expect(result).not.toBeNull();
expect(result!.profiles.map((p) => p.id)).toEqual(["good-1", "good-2"]);
});
});
// ── OC-0060: one bad stored profile must not evict the good ones ──
describe("OC-0060 malformed profile salvage (end-to-end)", () => {
beforeEach(() => {
mockInvoke.mockReset();
});
it("does not let a single malformed stored profile wipe out the rest on the next save", async () => {
const validProfiles = Array.from({ length: 5 }, (_, i) => ({
id: `orig-${i}`,
name: `Original ${i}`,
host: `orig${i}.example.com:443`,
username: `user${i}`,
color: "#abcdef",
autoConnect: false,
rememberPassword: false,
lastConnected: null,
}));
// Corrupt one entry the way a hand-edit / partial write would: empty name.
const corrupted = [...validProfiles.slice(0, 4), { ...validProfiles[4], name: "" }];
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === "get_settings") {
return Promise.resolve({
"owncord:profiles": { schemaVersion: 1, profiles: corrupted },
});
}
if (cmd === "save_settings") {
return Promise.resolve(undefined);
}
return Promise.resolve(undefined);
});
const backend = createTauriBackend();
const m = createProfileManager(backend, mockFetch as unknown as FetchFn);
await m.loadProfiles();
// The four well-formed originals must have survived the load.
expect(m.getAll()).toHaveLength(4);
m.addProfile(sampleData);
await m.saveProfiles();
const saveCall = mockInvoke.mock.calls.find(([cmd]) => cmd === "save_settings");
expect(saveCall).toBeDefined();
const savedValue = saveCall![1] as { value: { profiles: ServerProfile[] } };
// The persisted set must still contain the four originals plus the new
// profile — not just the newly added one.
expect(savedValue.value.profiles).toHaveLength(5);
expect(savedValue.value.profiles.map((p) => p.id)).toEqual(
expect.arrayContaining(["orig-0", "orig-1", "orig-2", "orig-3"]),
);
});
});
});