edb22a59c7
- migration 0005 : tables docker_image_events + action_requests - templates apply-compose (up -d --remove-orphans), prune-images (safe/agressif), down-compose (sans volumes/rmi) - dockerApply: parsers TDD (apply recreated/running/exited, prune images+bytes, down removed, parseHumanBytes) + orchestration applyStack/pruneImages/downStack réservée aux stacks enabled, insère docker_image_events - actionRequests: create/approve/reject/list — actions destructives validées explicitement (Hermes propose, opérateur approuve, run en arrière-plan) ; hors API directe (POST /:id/actions reste passif uniquement) - routes /machines/:id/action-requests + /action-requests/:id[/approve|/reject] - execute: RunActionOpts.aggressive, branches apply/prune/down, helper archiveExecution mutualisant le boilerplate d'archivage tsc 0 erreur · 91 tests · build OK · boot OK (migrations 0000→0005). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
parseDockerApply,
|
|
parseDockerPrune,
|
|
parseDockerDown,
|
|
parseHumanBytes,
|
|
} from "./dockerApply.js";
|
|
|
|
describe("parseDockerApply", () => {
|
|
const RAW = [
|
|
"===SU:DOCKER_APPLY===",
|
|
" Container media-app-1 Recreate",
|
|
" Container media-app-1 Recreated",
|
|
" Container media-worker-1 Created",
|
|
" Container media-db-1 Running",
|
|
" Container media-app-1 Started",
|
|
"===SU:DOCKER_PS_AFTER===",
|
|
'{"Name":"media-app-1","Service":"app","State":"running","Health":""}',
|
|
'{"Name":"media-db-1","Service":"db","State":"running","Health":"healthy"}',
|
|
'{"Name":"media-worker-1","Service":"worker","State":"exited","Health":""}',
|
|
"===SU:DOCKER_INSPECT_AFTER===",
|
|
"IMG\tsha256:newapp\tapp@sha256:dapp",
|
|
"IMG\tsha256:db\tdb@sha256:ddb",
|
|
"===SU:EXIT=0===",
|
|
].join("\n");
|
|
|
|
it("liste les conteneurs recréés/créés et l'état running/exited", () => {
|
|
const r = parseDockerApply(RAW);
|
|
expect(r.recreated.sort()).toEqual(["media-app-1", "media-worker-1"]);
|
|
expect(r.running.sort()).toEqual(["media-app-1", "media-db-1"]);
|
|
expect(r.exited).toEqual(["media-worker-1"]);
|
|
expect(r.errors).toHaveLength(0);
|
|
expect(r.exitCode).toBe(0);
|
|
});
|
|
|
|
it("remonte une erreur d'application nettoyée", () => {
|
|
const bad = [
|
|
"===SU:DOCKER_APPLY===",
|
|
' Container app-1 Error pulling image from https://reg.example/v2 token=SECRET123',
|
|
"===SU:DOCKER_PS_AFTER===",
|
|
"===SU:DOCKER_INSPECT_AFTER===",
|
|
"===SU:EXIT=1===",
|
|
].join("\n");
|
|
const r = parseDockerApply(bad);
|
|
expect(r.errors.length).toBeGreaterThan(0);
|
|
expect(r.errors[0]!.message).not.toContain("reg.example");
|
|
expect(r.errors[0]!.message).not.toContain("SECRET123");
|
|
expect(r.exitCode).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("parseHumanBytes", () => {
|
|
it("convertit les unités décimales Docker", () => {
|
|
expect(parseHumanBytes("0B")).toBe(0);
|
|
expect(parseHumanBytes("512MB")).toBe(512_000_000);
|
|
expect(parseHumanBytes("1.234GB")).toBe(Math.round(1.234 * 1e9));
|
|
expect(parseHumanBytes("1.5kB")).toBe(1500);
|
|
});
|
|
it("renvoie 0 pour une entrée illisible", () => {
|
|
expect(parseHumanBytes("n/a")).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("parseDockerPrune", () => {
|
|
const RAW = [
|
|
"===SU:DOCKER_PRUNE===",
|
|
"Deleted Images:",
|
|
"untagged: redis:6",
|
|
"deleted: sha256:aaa",
|
|
"deleted: sha256:bbb",
|
|
"",
|
|
"Total reclaimed space: 1.234GB",
|
|
"===SU:EXIT=0===",
|
|
].join("\n");
|
|
|
|
it("liste les images supprimées et l'espace récupéré", () => {
|
|
const r = parseDockerPrune(RAW);
|
|
expect(r.imagesDeleted).toEqual(["sha256:aaa", "sha256:bbb"]);
|
|
expect(r.bytesReclaimed).toBe(Math.round(1.234 * 1e9));
|
|
expect(r.errors).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe("parseDockerDown", () => {
|
|
it("liste les conteneurs retirés", () => {
|
|
const RAW = [
|
|
"===SU:DOCKER_DOWN===",
|
|
" Container media-app-1 Stopping",
|
|
" Container media-app-1 Stopped",
|
|
" Container media-app-1 Removing",
|
|
" Container media-app-1 Removed",
|
|
" Network media_default Removed",
|
|
"===SU:EXIT=0===",
|
|
].join("\n");
|
|
const r = parseDockerDown(RAW);
|
|
expect(r.removed).toEqual(["media-app-1"]);
|
|
expect(r.errors).toHaveLength(0);
|
|
});
|
|
});
|