feat(docker): scan/inspect passifs des stacks Compose (tâche 2 SJ-4)
- 4 tables Docker (settings/compose_roots/compose_stacks/stack_services)
+ migration 0004 (timestamps journal monotones)
- templates docker/scan-compose + inspect-compose ; renderTemplate bascule
sur délimiteurs <% %> pour les templates docker/ afin de préserver les
Go-templates {{.ID}} intacts
- dockerScan: parseDockerScan (TDD) + scanDockerStacks (persiste stacks
candidats, complète la détection par labels)
- action docker_scan branchée dans execute (route dédiée, archivage report/log)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
// server/services/dockerScan.test.ts
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { parseDockerScan } from "./dockerScan.js";
|
||||
|
||||
const raw = [
|
||||
"===SU:DOCKER_SCAN===",
|
||||
"STACK_OK\tdir=/opt/stacks/media\tfile=/opt/stacks/media/compose.yaml",
|
||||
"STACK_INVALID\tdir=/opt/stacks/broken\tfile=/opt/stacks/broken/compose.yml",
|
||||
"===SU:DOCKER_LABELS===",
|
||||
"ACTIVE\tproject=media\tworking_dir=/opt/stacks/media",
|
||||
"===SU:EXIT=0===",
|
||||
].join("\n");
|
||||
|
||||
describe("parseDockerScan", () => {
|
||||
it("extrait stacks valides/invalides et actifs", () => {
|
||||
const r = parseDockerScan(raw);
|
||||
expect(r.stacks).toEqual([
|
||||
{ workingDir: "/opt/stacks/media", composeFile: "/opt/stacks/media/compose.yaml", valid: true },
|
||||
{ workingDir: "/opt/stacks/broken", composeFile: "/opt/stacks/broken/compose.yml", valid: false },
|
||||
]);
|
||||
expect(r.active).toEqual([{ project: "media", workingDir: "/opt/stacks/media" }]);
|
||||
});
|
||||
|
||||
it("retourne des listes vides si rien n'est trouvé", () => {
|
||||
const r = parseDockerScan("===SU:DOCKER_SCAN===\n===SU:DOCKER_LABELS===\n===SU:EXIT=0===");
|
||||
expect(r.stacks).toHaveLength(0);
|
||||
expect(r.active).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user