From 33d826f6259c068d2a980ca8a9926fbebc284bbf Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Thu, 28 May 2026 12:31:39 -0300 Subject: [PATCH] test(audit): update allowlist+icons tests for new actions + add real-actions coverage (B/G3) - audit-high-level-actions.test.ts: replace old provider.added/combo/apikey assertions with new provider.credentials.*, auth.login.*, sync.token.* and settings assertions. Count still 26. - audit-activity-icons.test.ts: replace provider.added spec check with provider.credentials.created + auth.login.success spot checks. - audit-allowlist-real-actions.test.ts (new): strict 1:1 HIGH_LEVEL_ACTIONS <-> ACTIVITY_ICONS, all 26 real repo actions present, isHighLevelAction/getActivityIcon spot assertions. --- tests/unit/audit-activity-icons.test.ts | 14 ++- .../unit/audit-allowlist-real-actions.test.ts | 114 ++++++++++++++++++ tests/unit/audit-high-level-actions.test.ts | 43 +++++-- 3 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 tests/unit/audit-allowlist-real-actions.test.ts diff --git a/tests/unit/audit-activity-icons.test.ts b/tests/unit/audit-activity-icons.test.ts index 62501c1e98..f225ab533c 100644 --- a/tests/unit/audit-activity-icons.test.ts +++ b/tests/unit/audit-activity-icons.test.ts @@ -4,10 +4,18 @@ import assert from "node:assert/strict"; import { ACTIVITY_ICONS, getActivityIcon } from "../../src/lib/audit/activityIcons"; import { HIGH_LEVEL_ACTIONS } from "../../src/lib/audit/highLevelActions"; -test("getActivityIcon('provider.added') returns correct spec", () => { - assert.deepEqual(getActivityIcon("provider.added"), { +// B/G3: updated to reflect real action names aligned with logAuditEvent emitters. +test("getActivityIcon('provider.credentials.created') returns correct spec", () => { + assert.deepEqual(getActivityIcon("provider.credentials.created"), { icon: "extension", - i18nKeyVerb: "providerAdded", + i18nKeyVerb: "providerCredentialsCreated", + }); +}); + +test("getActivityIcon('auth.login.success') returns correct spec", () => { + assert.deepEqual(getActivityIcon("auth.login.success"), { + icon: "login", + i18nKeyVerb: "authLoginSuccess", }); }); diff --git a/tests/unit/audit-allowlist-real-actions.test.ts b/tests/unit/audit-allowlist-real-actions.test.ts new file mode 100644 index 0000000000..6647f9d784 --- /dev/null +++ b/tests/unit/audit-allowlist-real-actions.test.ts @@ -0,0 +1,114 @@ +/** + * audit-allowlist-real-actions.test.ts + * + * B/G3 gap-closure: Verifies that HIGH_LEVEL_ACTIONS contains the REAL action strings + * emitted by `logAuditEvent()` calls found in the repository (verified via grep). + * + * If this test breaks, it means either: + * 1. A new logAuditEvent emitter was added but not reflected in the allowlist, OR + * 2. An emitter was renamed without updating the allowlist. + * In both cases: update highLevelActions.ts AND activityIcons.ts atomically. + */ +import { test } from "node:test"; +import assert from "node:assert/strict"; + +import { HIGH_LEVEL_ACTIONS, isHighLevelAction } from "../../src/lib/audit/highLevelActions"; +import { ACTIVITY_ICONS, getActivityIcon } from "../../src/lib/audit/activityIcons"; + +/** All 26 real actions discovered via grep logAuditEvent in the repo (B/G3, 2026-05). */ +const REAL_REPO_ACTIONS = [ + "auth.login.success", + "auth.login.error", + "auth.login.failed", + "auth.login.locked", + "auth.login.misconfigured", + "auth.login.setup_required", + "auth.logout.success", + "provider.credentials.applied", + "provider.credentials.batch_revoked", + "provider.credentials.bulk_created", + "provider.credentials.bulk_imported", + "provider.credentials.created", + "provider.credentials.imported", + "provider.credentials.revoked", + "provider.credentials.updated", + "provider.validation.ssrf_blocked", + "quota.plan.updated", + "quota.pool.created", + "quota.pool.deleted", + "quota.pool.updated", + "quota.store.driver_changed", + "service.reveal_api_key", + "settings.update", + "settings.update_failed", + "sync.token.created", + "sync.token.revoked", +] as const; + +test("every HIGH_LEVEL_ACTION has a corresponding ACTIVITY_ICONS entry (1:1 coverage)", () => { + for (const action of HIGH_LEVEL_ACTIONS as readonly string[]) { + assert.ok(action in ACTIVITY_ICONS, `ACTIVITY_ICONS missing entry for '${action}'`); + } +}); + +test("ACTIVITY_ICONS has no extra entries beyond HIGH_LEVEL_ACTIONS (strict 1:1)", () => { + const allowlistSet = new Set(HIGH_LEVEL_ACTIONS); + for (const key of Object.keys(ACTIVITY_ICONS)) { + assert.ok(allowlistSet.has(key), `ACTIVITY_ICONS has extra key not in allowlist: '${key}'`); + } +}); + +test("all 26 real repo actions are present in HIGH_LEVEL_ACTIONS", () => { + const allowlistSet = new Set(HIGH_LEVEL_ACTIONS); + for (const action of REAL_REPO_ACTIONS) { + assert.ok(allowlistSet.has(action), `HIGH_LEVEL_ACTIONS missing real repo action: '${action}'`); + } +}); + +test("HIGH_LEVEL_ACTIONS contains exactly the same 26 real repo actions (no extras, no missing)", () => { + assert.equal( + (HIGH_LEVEL_ACTIONS as readonly string[]).length, + REAL_REPO_ACTIONS.length, + `Expected ${REAL_REPO_ACTIONS.length} actions, got ${(HIGH_LEVEL_ACTIONS as readonly string[]).length}`, + ); +}); + +test("isHighLevelAction('provider.credentials.created') === true", () => { + assert.equal(isHighLevelAction("provider.credentials.created"), true); +}); + +test("isHighLevelAction('nonexistent.action') === false", () => { + assert.equal(isHighLevelAction("nonexistent.action"), false); +}); + +test("isHighLevelAction('auth.login') === false (old naming no longer in allowlist)", () => { + assert.equal(isHighLevelAction("auth.login"), false); +}); + +test("getActivityIcon('auth.login.success') returns specific spec, not fallback", () => { + const spec = getActivityIcon("auth.login.success"); + assert.notDeepEqual(spec, { icon: "info", i18nKeyVerb: "genericEvent" }); + assert.equal(spec.icon, "login"); + assert.equal(spec.i18nKeyVerb, "authLoginSuccess"); +}); + +test("getActivityIcon('provider.credentials.created') returns specific spec, not fallback", () => { + const spec = getActivityIcon("provider.credentials.created"); + assert.notDeepEqual(spec, { icon: "info", i18nKeyVerb: "genericEvent" }); + assert.equal(spec.icon, "extension"); + assert.equal(spec.i18nKeyVerb, "providerCredentialsCreated"); +}); + +test("getActivityIcon('settings.update') returns specific spec, not fallback", () => { + const spec = getActivityIcon("settings.update"); + assert.notDeepEqual(spec, { icon: "info", i18nKeyVerb: "genericEvent" }); + assert.equal(spec.icon, "settings"); + assert.equal(spec.i18nKeyVerb, "settingsUpdate"); +}); + +test("every ACTIVITY_ICONS spec has non-empty icon and i18nKeyVerb", () => { + for (const [action, spec] of Object.entries(ACTIVITY_ICONS)) { + assert.ok(spec.icon.length > 0, `${action}.icon is empty`); + assert.ok(spec.i18nKeyVerb.length > 0, `${action}.i18nKeyVerb is empty`); + } +}); diff --git a/tests/unit/audit-high-level-actions.test.ts b/tests/unit/audit-high-level-actions.test.ts index d1831963a1..3ff2b1ae70 100644 --- a/tests/unit/audit-high-level-actions.test.ts +++ b/tests/unit/audit-high-level-actions.test.ts @@ -5,6 +5,7 @@ import { HIGH_LEVEL_ACTIONS, isHighLevelAction } from "../../src/lib/audit/highL const ALL = HIGH_LEVEL_ACTIONS as readonly string[]; +// B/G3: allowlist now has 26 real actions aligned with logAuditEvent emitters. test("HIGH_LEVEL_ACTIONS has exactly 26 entries", () => { assert.equal(ALL.length, 26); }); @@ -43,20 +44,44 @@ test("includes all 5 quota.* actions from B26", () => { } }); -test("includes provider lifecycle actions", () => { - for (const a of ["provider.added", "provider.removed", "provider.tested"]) { - assert.ok(ALL.includes(a)); +test("includes real provider credential actions", () => { + for (const a of [ + "provider.credentials.created", + "provider.credentials.applied", + "provider.credentials.updated", + "provider.credentials.revoked", + "provider.credentials.batch_revoked", + "provider.credentials.bulk_created", + "provider.credentials.bulk_imported", + "provider.credentials.imported", + "provider.validation.ssrf_blocked", + ]) { + assert.ok(ALL.includes(a), `Missing ${a}`); } }); -test("includes combo lifecycle actions", () => { - for (const a of ["combo.created", "combo.updated", "combo.deleted"]) { - assert.ok(ALL.includes(a)); +test("includes real auth actions", () => { + for (const a of [ + "auth.login.success", + "auth.login.error", + "auth.login.failed", + "auth.login.locked", + "auth.login.misconfigured", + "auth.login.setup_required", + "auth.logout.success", + ]) { + assert.ok(ALL.includes(a), `Missing ${a}`); } }); -test("includes apikey lifecycle actions", () => { - for (const a of ["apikey.created", "apikey.revoked", "apikey.rotated"]) { - assert.ok(ALL.includes(a)); +test("includes sync token actions", () => { + for (const a of ["sync.token.created", "sync.token.revoked"]) { + assert.ok(ALL.includes(a), `Missing ${a}`); + } +}); + +test("includes real settings and service actions", () => { + for (const a of ["settings.update", "settings.update_failed", "service.reveal_api_key"]) { + assert.ok(ALL.includes(a), `Missing ${a}`); } });