Files
OmniRoute/tests/unit/log-export-secrets.test.ts
Damian Pozimski 385e90f444 feat(dashboard): continuous call-log export to pluggable destinations (BigQuery first) (#11945)
Feature grande e bem construída: exportação contínua de call logs para destinos plugáveis (BigQuery primeiro). Revisei especificamente o tratamento de segredos (`src/lib/logExport/secrets.ts`) e a migração — encryption gate real (`requiresEncryptionKey` recusa gravação em texto plano quando `STORAGE_ENCRYPTION_KEY` não está setada), redação antes de qualquer resposta de API, e a migração cria a tabela com `enabled=0`/`include_bodies=0` por padrão (opt-in, sem exportar nada até o operador configurar). 62/62 testes focados verdes, typecheck limpo.

Resolvido o conflito com o barrel `src/lib/localDb.ts` (removido nesta mesma sessão, #11795 fase 5 — todo consumidor já migrado para `src/lib/db/*`); a PR só adicionava um re-export nele, que não é mais necessário. Obrigado pela contribuição!
2026-08-30 09:16:52 -03:00

72 lines
2.9 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
process.env.STORAGE_ENCRYPTION_KEY =
process.env.STORAGE_ENCRYPTION_KEY || "log-export-secrets-test-key";
const secrets = await import("../../src/lib/logExport/secrets.ts");
const TYPE = "bigquery";
const SA_JSON = '{"type":"service_account","client_email":"a@b.iam.gserviceaccount.com"}';
test("encryptDestinationConfig_SecretField_IsCiphertextAtRest", () => {
const stored = secrets.encryptDestinationConfig(TYPE, {
projectId: "proj",
serviceAccountJson: SA_JSON,
});
assert.equal(stored.projectId, "proj");
assert.notEqual(stored.serviceAccountJson, SA_JSON);
assert.match(String(stored.serviceAccountJson), /^enc:v1:/);
});
test("decryptDestinationConfig_EncryptedConfig_RoundTripsBackToPlaintext", () => {
const stored = secrets.encryptDestinationConfig(TYPE, { serviceAccountJson: SA_JSON });
const runtime = secrets.decryptDestinationConfig(TYPE, stored);
assert.equal(runtime.serviceAccountJson, SA_JSON);
});
test("redactDestinationConfig_StoredSecret_NeverReturnsCiphertextOrPlaintext", () => {
const stored = secrets.encryptDestinationConfig(TYPE, {
projectId: "proj",
serviceAccountJson: SA_JSON,
});
const view = secrets.redactDestinationConfig(TYPE, stored);
assert.equal(view.projectId, "proj");
assert.equal(view.serviceAccountJson, secrets.SECRET_PLACEHOLDER);
assert.equal(JSON.stringify(view).includes("enc:v1:"), false);
assert.equal(JSON.stringify(view).includes("gserviceaccount"), false);
});
test("redactDestinationConfig_UnsetSecret_OmitsTheKeyEntirely", () => {
const view = secrets.redactDestinationConfig(TYPE, { projectId: "proj" });
assert.equal("serviceAccountJson" in view, false);
});
test("mergeDestinationConfig_PlaceholderSubmitted_KeepsStoredSecret", () => {
const stored = secrets.encryptDestinationConfig(TYPE, { serviceAccountJson: SA_JSON });
const merged = secrets.mergeDestinationConfig(TYPE, stored, {
projectId: "proj",
serviceAccountJson: secrets.SECRET_PLACEHOLDER,
});
assert.equal(merged.serviceAccountJson, stored.serviceAccountJson);
assert.equal(secrets.decryptDestinationConfig(TYPE, merged).serviceAccountJson, SA_JSON);
});
test("mergeDestinationConfig_NewSecretSubmitted_ReplacesStoredSecret", () => {
const stored = secrets.encryptDestinationConfig(TYPE, { serviceAccountJson: SA_JSON });
const merged = secrets.mergeDestinationConfig(TYPE, stored, {
serviceAccountJson: '{"type":"service_account","client_email":"new@x.iam.gserviceaccount.com"}',
});
assert.match(String(merged.serviceAccountJson), /new@x/);
});
test("mergeDestinationConfig_SecretOmitted_KeepsStoredSecret", () => {
const stored = secrets.encryptDestinationConfig(TYPE, { serviceAccountJson: SA_JSON });
const merged = secrets.mergeDestinationConfig(TYPE, stored, { projectId: "proj" });
assert.equal(merged.serviceAccountJson, stored.serviceAccountJson);
});