Spaces:
Paused
Paused
File size: 3,595 Bytes
50720ce | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | /**
* Tests that update-checker writes version state to data/ (gitignored),
* NOT to config/default.yaml (git-tracked).
*/
import { describe, it, expect, beforeEach, vi } from "vitest";
const mockWriteFileSync = vi.fn();
const mockExistsSync = vi.fn(() => false);
const mockMkdirSync = vi.fn();
const mockReadFileSync = vi.fn();
const mockMutateClientConfig = vi.fn();
vi.mock("../config.js", () => ({
mutateClientConfig: mockMutateClientConfig,
reloadAllConfigs: vi.fn(),
}));
vi.mock("../paths.js", () => ({
getConfigDir: vi.fn(() => "/fake/config"),
getDataDir: vi.fn(() => "/fake/data"),
isEmbedded: vi.fn(() => false),
}));
vi.mock("../utils/jitter.js", () => ({
jitterInt: vi.fn((ms: number) => ms),
}));
vi.mock("../tls/curl-fetch.js", () => ({
curlFetchGet: vi.fn(),
}));
vi.mock("fs", async (importOriginal) => {
const actual = await importOriginal<typeof import("fs")>();
return {
...actual,
readFileSync: mockReadFileSync,
writeFileSync: mockWriteFileSync,
existsSync: mockExistsSync,
mkdirSync: mockMkdirSync,
};
});
vi.mock("js-yaml", () => ({
default: {
load: vi.fn(() => ({
client: { app_version: "1.0.0", build_number: "100" },
})),
},
}));
import { curlFetchGet } from "../tls/curl-fetch.js";
const APPCAST_XML = `<?xml version="1.0"?>
<rss><channel><item>
<enclosure sparkle:shortVersionString="2.0.0" sparkle:version="200" url="https://example.com/download"/>
</item></channel></rss>`;
describe("update-checker writes to data/, not config/", () => {
beforeEach(() => {
vi.clearAllMocks();
mockExistsSync.mockReturnValue(false);
mockReadFileSync.mockReturnValue("");
});
it("applyVersionUpdate writes to data/version-state.json, not config/default.yaml", async () => {
vi.mocked(curlFetchGet).mockResolvedValue({
ok: true,
status: 200,
body: APPCAST_XML,
});
// Dynamic import to get fresh module state
const { checkForUpdate } = await import("../update-checker.js");
await checkForUpdate();
// Should write version-state.json to data/
const versionWrites = mockWriteFileSync.mock.calls.filter(
(call) => (call[0] as string).includes("version-state.json"),
);
expect(versionWrites.length).toBeGreaterThanOrEqual(1);
const writePath = versionWrites[0][0] as string;
expect(writePath).toBe("/fake/data/version-state.json");
// Parse the written content
const written = JSON.parse(versionWrites[0][1] as string) as {
app_version: string;
build_number: string;
};
expect(written.app_version).toBe("2.0.0");
expect(written.build_number).toBe("200");
});
it("never calls mutateYaml on config/default.yaml", async () => {
vi.mocked(curlFetchGet).mockResolvedValue({
ok: true,
status: 200,
body: APPCAST_XML,
});
const { checkForUpdate } = await import("../update-checker.js");
await checkForUpdate();
// No writes should target config/default.yaml
const configWrites = mockWriteFileSync.mock.calls.filter(
(call) => (call[0] as string).includes("/fake/config/"),
);
expect(configWrites).toHaveLength(0);
});
it("updates runtime config via mutateClientConfig", async () => {
vi.mocked(curlFetchGet).mockResolvedValue({
ok: true,
status: 200,
body: APPCAST_XML,
});
const { checkForUpdate } = await import("../update-checker.js");
await checkForUpdate();
expect(mockMutateClientConfig).toHaveBeenCalledWith({
app_version: "2.0.0",
build_number: "200",
});
});
});
|