Files
craftvia/scripts/test-garage-storage.ts
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

100 lines
4.2 KiB
TypeScript

// Smoke-Test des S3-Objektspeichers gegen einen echten Garage-Node (Phase C/D der
// MinIO→Garage-Migration, docs/KONZEPT-garage-migration.md §9).
//
// Prüft den realen S3-Pfad (AWS SDK v3, path-style) über die exportierte
// `resolveBackupStore()`:
// 1. Put → Get (Byte-Identität),
// 2. List (Prefix enthält den Key),
// 3. Delete (remove räumt den Prefix),
// 4. Negativfall: fehlender Bucket → sprechender Konfigurationsfehler
// (KEIN stiller CreateBucket-Versuch mehr).
//
// Ausführung:
// - OHNE S3_ENDPOINT → der Test ÜBERSPRINGT sich (Exit 0), damit das Standard-Gate
// (npm run test) ohne laufenden Objektspeicher grün bleibt.
// - MIT gesetzten S3_*-Variablen (z. B. gegen den Compose-Garage oder einen lokalen
// Container) → echter End-to-End-Durchstich.
//
// S3_ENDPOINT=http://127.0.0.1:3900 S3_ACCESS_KEY=… S3_SECRET_KEY=… \
// S3_BUCKET=isms-documents S3_REGION=us-east-1 npx tsx scripts/test-garage-storage.ts
import { resolveBackupStore, type BackupTargetConfig } from "../src/server/storage/backup-store";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
function envCfg(bucketOverride?: string): BackupTargetConfig {
return {
backupTarget: "s3",
backupLocalDir: null,
backupS3Endpoint: process.env.S3_ENDPOINT?.trim() ?? null,
backupS3Bucket: bucketOverride ?? process.env.S3_BUCKET?.trim() ?? "isms-documents",
backupS3Region: process.env.S3_REGION?.trim() || "us-east-1",
backupS3AccessKey: process.env.S3_ACCESS_KEY?.trim() ?? null,
backupS3SecretKey: process.env.S3_SECRET_KEY?.trim() ?? null,
};
}
async function main() {
const endpoint = process.env.S3_ENDPOINT?.trim();
if (!endpoint) {
console.log("↷ übersprungen: kein S3_ENDPOINT gesetzt (kein laufender Objektspeicher).");
console.log(" Für den echten Durchstich S3_ENDPOINT/S3_ACCESS_KEY/S3_SECRET_KEY/S3_BUCKET setzen.");
return;
}
if (!process.env.S3_ACCESS_KEY || !process.env.S3_SECRET_KEY) {
console.log("✗ FEHLER S3_ENDPOINT gesetzt, aber S3_ACCESS_KEY/S3_SECRET_KEY fehlen.");
failures++;
return;
}
// ── Positiv: Put → Get → List → Delete gegen den provisionierten Bucket ──────
const store = resolveBackupStore(envCfg());
const prefix = `__smoketest__/${process.pid}-${Date.now()}/`;
const key = `${prefix}garage-probe.bin`;
const payload = Buffer.from("certvia · garage smoke test · äöü · 🗄️", "utf8");
await store.put(key, payload);
ok(true, "Put gegen Garage erfolgreich");
const got = await store.get(key);
ok(got != null && got.equals(payload), "Get liefert identische Bytes zurück");
const missing = await store.get(`${prefix}gibtsnicht.bin`);
ok(missing === null, "Get eines fehlenden Keys → null (kein Wurf)");
const listed = await store.list(prefix);
ok(listed.includes(key), "List enthält den geschriebenen Key (Prefix-Scan)");
const removed = await store.remove(prefix);
ok(removed >= 1, `Delete räumt den Prefix (entfernt: ${removed})`);
const afterRemove = await store.list(prefix);
ok(afterRemove.length === 0, "Prefix ist nach remove leer");
// ── Negativ: fehlender Bucket → sprechender Konfigurationsfehler ─────────────
const badBucket = `does-not-exist-${process.pid}-${Date.now()}`;
const badStore = resolveBackupStore(envCfg(badBucket));
try {
await badStore.put(`${badBucket}/x.bin`, Buffer.from("x"));
ok(false, "fehlender Bucket hätte einen Fehler werfen müssen");
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
const speaks = /nicht provisioniert|nicht erreichbar/.test(msg) && msg.includes(badBucket);
ok(speaks, "fehlender Bucket → sprechender Konfigurationsfehler (kein stiller CreateBucket)");
if (!speaks) console.log(" erhaltene Meldung:", msg);
}
}
main()
.then(() => {
console.log(failures === 0 ? "\nGarage-Storage-Smoke-Test grün." : `\n${failures} Prüfung(en) fehlgeschlagen.`);
process.exit(failures === 0 ? 0 : 1);
})
.catch((err) => {
console.error("\n✗ Unerwarteter Fehler im Smoke-Test:", err);
process.exit(1);
});