Files
spark-store/src/__tests__/unit/update-center/query.test.ts

299 lines
7.6 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
buildInstalledSourceMap,
mergeUpdateSources,
parseApmUpgradableOutput,
parseAptssUpgradableOutput,
parsePrintUrisOutput,
} from "../../../../electron/main/backend/update-center/query";
describe("update-center query", () => {
it("parses aptss upgradable output into normalized aptss items", () => {
const output = [
"Listing...",
"spark-weather/stable 2.0.0 amd64 [upgradable from: 1.9.0]",
"spark-same/stable 1.0.0 amd64 [upgradable from: 1.0.0]",
"",
].join("\n");
expect(parseAptssUpgradableOutput(output)).toEqual([
{
pkgname: "spark-weather",
source: "aptss",
currentVersion: "1.9.0",
nextVersion: "2.0.0",
arch: "amd64",
},
]);
});
it("parses the legacy from variant in upgradable output", () => {
const aptssOutput = "spark-clock/stable 1.2.0 amd64 [from: 1.1.0]";
const apmOutput = "spark-player/main 2.0.0 amd64 [from: 1.5.0]";
expect(parseAptssUpgradableOutput(aptssOutput)).toEqual([
{
pkgname: "spark-clock",
source: "aptss",
currentVersion: "1.1.0",
nextVersion: "1.2.0",
arch: "amd64",
},
]);
expect(parseApmUpgradableOutput(apmOutput)).toEqual([
{
pkgname: "spark-player",
source: "apm",
currentVersion: "1.5.0",
nextVersion: "2.0.0",
arch: "amd64",
},
]);
});
it("parses apt print-uris output into download metadata", () => {
const output =
"'https://example.invalid/pool/main/s/spark-weather_2.0.0_amd64.deb' spark-weather_2.0.0_amd64.deb 123456 SHA512:deadbeef";
expect(parsePrintUrisOutput(output)).toEqual({
downloadUrl:
"https://example.invalid/pool/main/s/spark-weather_2.0.0_amd64.deb",
fileName: "spark-weather_2.0.0_amd64.deb",
size: 123456,
sha512: "deadbeef",
});
});
it("marks an apm item as migration when the same package is only installed in aptss", () => {
const merged = mergeUpdateSources(
[
{
pkgname: "spark-weather",
source: "aptss",
currentVersion: "1.9.0",
nextVersion: "2.0.0",
},
],
[
{
pkgname: "spark-weather",
source: "apm",
currentVersion: "1.8.0",
nextVersion: "3.0.0",
},
],
new Map([["spark-weather", { aptss: true, apm: false }]]),
);
expect(merged).toEqual([
{
pkgname: "spark-weather",
source: "apm",
currentVersion: "1.8.0",
nextVersion: "3.0.0",
isMigration: true,
migrationSource: "aptss",
migrationTarget: "apm",
aptssVersion: "2.0.0",
},
{
pkgname: "spark-weather",
source: "aptss",
currentVersion: "1.9.0",
nextVersion: "2.0.0",
},
]);
});
it("uses Debian-style version ordering for migration decisions", () => {
const merged = mergeUpdateSources(
[
{
pkgname: "spark-browser",
source: "aptss",
currentVersion: "9.0",
nextVersion: "10.0",
},
],
[
{
pkgname: "spark-browser",
source: "apm",
currentVersion: "1:0.9",
nextVersion: "2:1.0",
},
],
new Map([["spark-browser", { aptss: true, apm: false }]]),
);
expect(merged[0]).toMatchObject({
pkgname: "spark-browser",
source: "apm",
isMigration: true,
aptssVersion: "10.0",
});
expect(merged[1]).toMatchObject({
pkgname: "spark-browser",
source: "aptss",
nextVersion: "10.0",
});
});
it("uses Debian epoch ordering in the fallback when dpkg is unavailable", async () => {
vi.resetModules();
vi.doMock("node:child_process", async (importOriginal) => {
const actual =
await importOriginal<typeof import("node:child_process")>();
return {
...actual,
default: actual,
spawnSync: vi.fn(() => ({
status: null,
error: new Error("dpkg unavailable"),
output: null,
pid: 0,
signal: null,
stdout: Buffer.alloc(0),
stderr: Buffer.alloc(0),
})),
};
});
const { mergeUpdateSources: mergeWithFallback } =
await import("../../../../electron/main/backend/update-center/query");
const merged = mergeWithFallback(
[
{
pkgname: "spark-reader",
source: "aptss",
currentVersion: "2.5.0",
nextVersion: "2.9.0",
},
],
[
{
pkgname: "spark-reader",
source: "apm",
currentVersion: "2.0.0",
nextVersion: "3.0.0",
},
],
new Map([["spark-reader", { aptss: true, apm: false }]]),
);
expect(merged[0]).toMatchObject({
pkgname: "spark-reader",
source: "apm",
isMigration: true,
aptssVersion: "2.9.0",
});
vi.doUnmock("node:child_process");
vi.resetModules();
});
it("uses Debian tilde ordering in the fallback when dpkg is unavailable", async () => {
vi.resetModules();
vi.doMock("node:child_process", async (importOriginal) => {
const actual =
await importOriginal<typeof import("node:child_process")>();
return {
...actual,
default: actual,
spawnSync: vi.fn(() => ({
status: null,
error: new Error("dpkg unavailable"),
output: null,
pid: 0,
signal: null,
stdout: Buffer.alloc(0),
stderr: Buffer.alloc(0),
})),
};
});
const { mergeUpdateSources: mergeWithFallback } =
await import("../../../../electron/main/backend/update-center/query");
const merged = mergeWithFallback(
[
{
pkgname: "spark-tilde",
source: "aptss",
currentVersion: "0.9",
nextVersion: "1.0~rc1",
},
],
[
{
pkgname: "spark-tilde",
source: "apm",
currentVersion: "0.9",
nextVersion: "1.0",
},
],
new Map([["spark-tilde", { aptss: true, apm: false }]]),
);
expect(merged[0]).toMatchObject({
pkgname: "spark-tilde",
source: "apm",
isMigration: true,
aptssVersion: "1.0~rc1",
});
vi.doUnmock("node:child_process");
vi.resetModules();
});
it("parses apm list output into normalized apm items", () => {
const output = [
"Listing...",
"spark-music/main 5.0.0 arm64 [upgradable from: 4.5.0]",
"spark-same/main 1.0.0 arm64 [upgradable from: 1.0.0]",
"",
].join("\n");
expect(parseApmUpgradableOutput(output)).toEqual([
{
pkgname: "spark-music",
source: "apm",
currentVersion: "4.5.0",
nextVersion: "5.0.0",
arch: "arm64",
},
]);
});
it("builds installed-source map from dpkg-query and apm list output", () => {
const dpkgOutput = [
"spark-weather\tinstall ok installed",
"spark-weather-data\tdeinstall ok config-files",
"spark-notes\tinstall ok installed",
"",
].join("\n");
const apmInstalledOutput = [
"Listing...",
"spark-weather/main,stable 3.0.0 amd64 [installed]",
"spark-player/main 1.0.0 amd64 [installed,automatic]",
"",
].join("\n");
expect(
Array.from(
buildInstalledSourceMap(dpkgOutput, apmInstalledOutput).entries(),
),
).toEqual([
["spark-weather", { aptss: true, apm: true }],
["spark-notes", { aptss: true, apm: false }],
["spark-player", { aptss: false, apm: true }],
]);
});
});