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"); }); });