import { afterEach, describe, expect, test } from "bun:test"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { prepareDiagnosticFile } from "../src/commands/diagnostics"; import { runReadOnlyArgvStatus } from "../src/lib/process"; import { redactEnv } from "../src/lib/redaction"; const directories: string[] = []; afterEach(async () => { await Promise.all( directories .splice(0) .map((directory) => rm(directory, { recursive: true, force: true })) ); }); async function source(bytes: Uint8Array): Promise { const directory = await mkdtemp(join(tmpdir(), "hy2xs-diagnostics-")); directories.push(directory); const path = join(directory, "hy2xs.env"); await writeFile(path, bytes); return path; } describe("безопасная подготовка diagnostics", () => { test("секрет редактируется до выдачи текста на запись", async () => { const path = await source( new TextEncoder().encode("HY2XS_ADMIN_CON_PASS=top-secret\n") ); const prepared = await prepareDiagnosticFile(path, redactEnv); expect(prepared).toContain(""); expect(prepared).not.toContain("top-secret"); }); test("диагностический boolean виден, а соседний пароль скрыт", async () => { const path = await source( new TextEncoder().encode( "HY2XS_FORCE_PASSWORD_CHANGE=false\nHY2XS_ADMIN_INITIAL_PASSWORD=top-secret\n" ) ); const prepared = await prepareDiagnosticFile(path, redactEnv); expect(prepared).toContain("HY2XS_FORCE_PASSWORD_CHANGE=false"); expect(prepared).toContain("HY2XS_ADMIN_INITIAL_PASSWORD="); expect(prepared).not.toContain("top-secret"); }); test("повреждённый UTF-8 заменяется безопасной причиной, а не U+FFFD", async () => { const path = await source( new Uint8Array([0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x3d, 0xff]) ); const prepared = await prepareDiagnosticFile(path, redactEnv); expect(prepared).toContain("некорректный UTF-8"); expect(prepared).not.toContain("SECRET="); expect(prepared).not.toContain("�"); }); test("вывод внешней команды ограничен по памяти и помечен как обрезанный", async () => { const result = await runReadOnlyArgvStatus( [process.execPath, "-e", `process.stdout.write("x".repeat(128))`], 16 ); expect(result.exitCode).toBe(0); expect(result.stdout).toBe("x".repeat(16)); expect(result.stdoutTruncated).toBe(true); }); });