Files

117 lines
3.5 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import {
resetFailedUnit,
type ResetFailedDependencies,
} from "../src/lib/systemd";
import type { MutationResult } from "../src/lib/process";
import { runRollbackStages } from "../src/lib/rollback";
function result(exitCode: number, stdout = "", stderr = ""): MutationResult {
return { exitCode, stdout, stderr };
}
function dependencies(
reset: MutationResult,
inspect: MutationResult,
calls: string[] = [],
): ResetFailedDependencies {
return {
reset: async (unit) => {
calls.push(`reset:${unit}`);
return reset;
},
inspect: async (unit) => {
calls.push(`inspect:${unit}`);
return inspect;
},
};
}
describe("systemd rollback cleanup", () => {
test("успешный reset-failed подтверждается наблюдением", async () => {
const calls: string[] = [];
await expect(
resetFailedUnit(
"hy2xs-admin",
dependencies(
result(0),
result(0, "LoadState=loaded\nActiveState=inactive\n"),
calls,
),
),
).resolves.toBeUndefined();
expect(calls).toEqual(["reset:hy2xs-admin", "inspect:hy2xs-admin"]);
});
test("уже выгруженный юнит является успешным конечным состоянием", async () => {
await expect(
resetFailedUnit(
"hy2xs-admin",
dependencies(
result(5, "", "Unit hy2xs-admin.service not loaded."),
result(0, "LoadState=not-found\nActiveState=inactive\n"),
),
),
).resolves.toBeUndefined();
});
test("уже выгруженный юнит не создаёт manual-recovery failure", async () => {
const failures = await runRollbackStages([
{
name: "reset failed hy2xs-admin",
run: async () =>
resetFailedUnit(
"hy2xs-admin",
dependencies(
result(5, "", "Unit hy2xs-admin.service not loaded."),
result(0, "LoadState=not-found\nActiveState=inactive\n"),
),
),
},
]);
expect(failures).toEqual([]);
});
test("решение не зависит от английского текста stderr", async () => {
await expect(
resetFailedUnit(
"hysteria-server",
dependencies(
result(5, "", "модуль не загружен"),
result(0, "LoadState=not-found\nActiveState=inactive\n"),
),
),
).resolves.toBeUndefined();
});
test("сохранившееся failed-состояние остаётся настоящим отказом", async () => {
await expect(
resetFailedUnit(
"hysteria-server",
dependencies(
result(1, "", "access denied"),
result(0, "LoadState=loaded\nActiveState=failed\n"),
),
),
).rejects.toThrow("remains failed");
});
test("ошибка наблюдения не выдаётся за успешную уборку", async () => {
await expect(
resetFailedUnit(
"hysteria-server",
dependencies(result(1), result(1, "", "Failed to connect to bus")),
),
).rejects.toThrow("cannot verify systemd state");
});
test("неполный ответ systemctl show отклоняется fail-closed", async () => {
await expect(
resetFailedUnit(
"hysteria-server",
dependencies(result(0), result(0, "LoadState=loaded\n")),
),
).rejects.toThrow("did not return LoadState and ActiveState");
});
});