const { applyTarget, isSafeOaOnlySmokeTarget, isSmokeLikeValue, normalizeRoleList, parseArgs, planForTarget, } = require("../../../src/scripts/repair-oa-only-smoke-accounts"); function target(overrides = {}) { return { key: overrides.key ?? "admin", identifier: overrides.identifier ?? "smoke-admin@example.com", maskedIdentifier: "sm***@***.com", roles: overrides.roles ?? ["admin", "employee"], }; } function row(overrides = {}) { return { id: "11111111-1111-4111-8111-111111111111", employeeNo: overrides.employeeNo ?? "T0001", name: overrides.name ?? "OA试运行测试管理员", email: overrides.email ?? "smoke-admin@example.com", phone: overrides.phone ?? "", status: overrides.status ?? "archived", employmentStatus: overrides.employmentStatus ?? "inactive", archived: overrides.archived ?? true, roles: overrides.roles ?? ["employee"], loginFailedCount: overrides.loginFailedCount ?? 3, hasLoginFailedFirstAt: overrides.hasLoginFailedFirstAt ?? true, loginLocked: overrides.loginLocked ?? true, activeAuthSessions: overrides.activeAuthSessions ?? 0, }; } describe("repair-oa-only-smoke-accounts", () => { afterEach(() => { delete process.env.OA_ONLY_SMOKE_REPAIR_APPLY; }); it("recognizes only smoke-like identifiers as safe targets", () => { expect(isSmokeLikeValue("smoke-admin@example.com")).toBe(true); expect(isSmokeLikeValue("SMOKE002")).toBe(true); expect(isSmokeLikeValue("OA试运行测试管理员")).toBe(true); expect(isSmokeLikeValue("real.employee@example.com")).toBe(false); }); it("blocks non-smoke employee matches before planning role repair", () => { const plan = planForTarget( target({ identifier: "real.employee@example.com", roles: ["admin", "employee"] }), row({ employeeNo: "HYD000100", name: "真实员工", email: "real.employee@example.com", status: "archived", employmentStatus: "inactive", archived: true, }), ); expect(isSafeOaOnlySmokeTarget(target({ identifier: "real.employee@example.com" }), plan.before)).toBe(false); expect(plan.status).toBe("BLOCKED"); expect(plan.reason).toBe("unsafe_non_smoke_target"); expect(plan.actions).toEqual([]); }); it("plans only state, lock, and missing-role repairs for safe OA-only smoke rows", () => { const plan = planForTarget(target(), row()); expect(plan.status).toBe("PLANNED"); expect(plan.actions).toEqual(["restore_active_employee", "clear_login_lock", "add_missing_roles"]); expect(plan.missingRoles).toEqual(["admin"]); expect(JSON.stringify(plan)).not.toContain("smoke-admin@example.com"); }); it("normalizes roles and parses apply mode without enabling apply by itself", () => { const options = parseArgs(["--apply", "--target=admin:smoke-admin@example.com:admin+employee", "--format=json"]); expect(options.mode).toBe("apply"); expect(options.targets[0]).toMatchObject({ key: "admin", identifier: "smoke-admin@example.com", roles: ["admin", "employee"], }); expect(normalizeRoleList(["admin", "admin", "invalid", "employee"])).toEqual(["admin", "employee"]); expect(process.env.OA_ONLY_SMOKE_REPAIR_APPLY).toBeUndefined(); }); it("applies only lifecycle, login-lock, token, missing-role, and audit repairs", async () => { const current = row({ status: "archived", employmentStatus: "inactive", archived: true, roles: ["employee"], }); const planned = planForTarget(target(), current); const client = { query: vi.fn(async (sql) => { const text = String(sql); if (text.includes("SELECT")) { return { rows: [ { id: current.id, employee_no: current.employeeNo, name: current.name, email: current.email, phone: current.phone, status: "active", employment_status: "active", archived: false, login_failed_count: 0, has_login_failed_first_at: false, login_locked_until: null, roles: ["admin", "employee"], active_auth_sessions: 0, }, ], }; } return { rows: [] }; }), }; await expect( applyTarget(client, target(), planned, new Date("2026-08-10T00:00:00.000Z")), ).resolves.toMatchObject({ result: "applied" }); const sql = client.query.mock.calls.map(([statement]) => String(statement)).join("\n"); expect(sql).toContain("token_version = token_version + 1"); expect(sql).toContain("INSERT INTO employee_roles"); expect(sql).toContain("ON CONFLICT (employee_id, role_code) DO NOTHING"); expect(sql).toContain("INSERT INTO employee_lifecycle_events"); expect(sql).not.toContain("DELETE FROM employee_roles"); expect(sql).not.toContain("password_hash"); expect(sql).not.toMatch(/(?:UPDATE|INSERT\s+INTO|DELETE\s+FROM)\s+auth_sessions/iu); expect(sql).not.toMatch(/(?:UPDATE|INSERT\s+INTO|DELETE\s+FROM)\s+password_reset_codes/iu); expect(sql).not.toMatch(/(?:UPDATE|INSERT\s+INTO|DELETE\s+FROM)\s+email_login_codes/iu); }); });