chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import { describe, it, afterEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
function mockFetchJson(data, status = 200) {
|
||||
globalThis.fetch = async () => ({ ok: status === 200, status, json: async () => data });
|
||||
}
|
||||
|
||||
function mockFetchText(text, status = 200) {
|
||||
globalThis.fetch = async () => ({ ok: status === 200, status, text: async () => text });
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
});
|
||||
|
||||
describe("releaseChecker", () => {
|
||||
let mod;
|
||||
|
||||
it("should load module", async () => {
|
||||
mod = await import("../../src/lib/versionManager/releaseChecker.ts");
|
||||
assert.ok(mod.getLatestRelease);
|
||||
assert.ok(mod.getReleaseByVersion);
|
||||
assert.ok(mod.getAvailableVersions);
|
||||
assert.ok(mod.getChecksums);
|
||||
assert.ok(mod.clearCache);
|
||||
});
|
||||
|
||||
describe("getLatestRelease", () => {
|
||||
it("should parse latest release from GitHub API", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({
|
||||
tag_name: "v6.9.7",
|
||||
published_at: "2025-01-01T00:00:00Z",
|
||||
body: "notes",
|
||||
assets: [{ name: "a.tar.gz", browser_download_url: "https://x.com/a.tar.gz", size: 100 }],
|
||||
});
|
||||
const r = await mod.getLatestRelease();
|
||||
assert.equal(r.tag, "v6.9.7");
|
||||
assert.equal(r.version, "6.9.7");
|
||||
assert.equal(r.publishedAt, "2025-01-01T00:00:00Z");
|
||||
assert.equal(r.body, "notes");
|
||||
assert.equal(r.assets.length, 1);
|
||||
assert.equal(r.assets[0].name, "a.tar.gz");
|
||||
assert.equal(r.assets[0].url, "https://x.com/a.tar.gz");
|
||||
assert.equal(r.assets[0].size, 100);
|
||||
});
|
||||
|
||||
it("should handle empty assets", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({ tag_name: "v1.0.0", published_at: "", body: "", assets: [] });
|
||||
const r = await mod.getLatestRelease();
|
||||
assert.equal(r.assets.length, 0);
|
||||
});
|
||||
|
||||
it("should handle null body", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({ tag_name: "v1.0.0", published_at: "", body: null, assets: [] });
|
||||
const r = await mod.getLatestRelease();
|
||||
assert.equal(r.body, "");
|
||||
});
|
||||
|
||||
it("should handle missing assets array", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({ tag_name: "v1.0.0", published_at: "", body: "" });
|
||||
const r = await mod.getLatestRelease();
|
||||
assert.equal(r.assets.length, 0);
|
||||
});
|
||||
|
||||
it("should throw on non-ok response", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({}, 404);
|
||||
await assert.rejects(() => mod.getLatestRelease(), { message: /GitHub API 404/ });
|
||||
});
|
||||
});
|
||||
|
||||
describe("getReleaseByVersion", () => {
|
||||
it("should fetch a specific version", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({ tag_name: "v6.9.0", published_at: "", body: "", assets: [] });
|
||||
const r = await mod.getReleaseByVersion("6.9.0");
|
||||
assert.ok(r);
|
||||
assert.equal(r.version, "6.9.0");
|
||||
});
|
||||
|
||||
it("should prepend v if missing", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({ tag_name: "v6.9.0", published_at: "", body: "", assets: [] });
|
||||
const r = await mod.getReleaseByVersion("6.9.0");
|
||||
assert.ok(r);
|
||||
});
|
||||
|
||||
it("should return null on 404", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({}, 404);
|
||||
const r = await mod.getReleaseByVersion("999.0.0");
|
||||
assert.equal(r, null);
|
||||
});
|
||||
|
||||
it("should return null on network error", async () => {
|
||||
mod.clearCache();
|
||||
globalThis.fetch = async () => {
|
||||
throw new Error("Network error");
|
||||
};
|
||||
const r = await mod.getReleaseByVersion("999.0.0");
|
||||
assert.equal(r, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAvailableVersions", () => {
|
||||
it("should return version tags", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson([{ tag_name: "v6.9.7" }, { tag_name: "v6.9.6" }]);
|
||||
const v = await mod.getAvailableVersions();
|
||||
assert.deepEqual(v, ["v6.9.7", "v6.9.6"]);
|
||||
});
|
||||
|
||||
it("should handle empty array", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson([]);
|
||||
const v = await mod.getAvailableVersions();
|
||||
assert.deepEqual(v, []);
|
||||
});
|
||||
|
||||
it("should handle non-array response", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({ message: "not array" });
|
||||
const v = await mod.getAvailableVersions();
|
||||
assert.deepEqual(v, []);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getChecksums", () => {
|
||||
it("should parse checksums.txt", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchText("abc123 file.tar.gz\n456def other.tar.gz\n");
|
||||
const c = await mod.getChecksums("6.9.7");
|
||||
assert.equal(c.size, 2);
|
||||
assert.equal(c.get("file.tar.gz"), "abc123");
|
||||
assert.equal(c.get("other.tar.gz"), "456def");
|
||||
});
|
||||
|
||||
it("should return empty map on 404", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchText("not found", 404);
|
||||
const c = await mod.getChecksums("999.0.0");
|
||||
assert.equal(c.size, 0);
|
||||
});
|
||||
|
||||
it("should return empty map on error", async () => {
|
||||
mod.clearCache();
|
||||
globalThis.fetch = async () => {
|
||||
throw new Error("fail");
|
||||
};
|
||||
const c = await mod.getChecksums("999.0.0");
|
||||
assert.equal(c.size, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("clearCache", () => {
|
||||
it("should clear cache so next call fetches fresh data", async () => {
|
||||
mod.clearCache();
|
||||
mockFetchJson({ tag_name: "v6.9.7", published_at: "", body: "", assets: [] });
|
||||
const r1 = await mod.getLatestRelease();
|
||||
assert.equal(r1.version, "6.9.7");
|
||||
|
||||
mod.clearCache();
|
||||
mockFetchJson({ tag_name: "v6.9.8", published_at: "", body: "", assets: [] });
|
||||
const r2 = await mod.getLatestRelease();
|
||||
assert.equal(r2.version, "6.9.8");
|
||||
});
|
||||
});
|
||||
|
||||
describe("cache behavior", () => {
|
||||
it("should cache responses within TTL", async () => {
|
||||
mod.clearCache();
|
||||
let callCount = 0;
|
||||
globalThis.fetch = async () => {
|
||||
callCount++;
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({ tag_name: "v6.9.7", published_at: "", body: "", assets: [] }),
|
||||
};
|
||||
};
|
||||
await mod.getLatestRelease();
|
||||
assert.equal(callCount, 1);
|
||||
await mod.getLatestRelease();
|
||||
assert.equal(callCount, 1); // cached
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user