| import { afterEach, describe, expect, it, vi } from "vitest"; |
| import { importFreshModule } from "../../test/helpers/import-fresh.js"; |
| import { |
| clearSlackThreadParticipationCache, |
| hasSlackThreadParticipation, |
| recordSlackThreadParticipation, |
| } from "./sent-thread-cache.js"; |
|
|
| describe("slack sent-thread-cache", () => { |
| afterEach(() => { |
| clearSlackThreadParticipationCache(); |
| vi.restoreAllMocks(); |
| }); |
|
|
| it("records and checks thread participation", () => { |
| recordSlackThreadParticipation("A1", "C123", "1700000000.000001"); |
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(true); |
| }); |
|
|
| it("returns false for unrecorded threads", () => { |
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(false); |
| }); |
|
|
| it("distinguishes different channels and threads", () => { |
| recordSlackThreadParticipation("A1", "C123", "1700000000.000001"); |
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000002")).toBe(false); |
| expect(hasSlackThreadParticipation("A1", "C456", "1700000000.000001")).toBe(false); |
| }); |
|
|
| it("scopes participation by accountId", () => { |
| recordSlackThreadParticipation("A1", "C123", "1700000000.000001"); |
| expect(hasSlackThreadParticipation("A2", "C123", "1700000000.000001")).toBe(false); |
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(true); |
| }); |
|
|
| it("ignores empty accountId, channelId, or threadTs", () => { |
| recordSlackThreadParticipation("", "C123", "1700000000.000001"); |
| recordSlackThreadParticipation("A1", "", "1700000000.000001"); |
| recordSlackThreadParticipation("A1", "C123", ""); |
| expect(hasSlackThreadParticipation("", "C123", "1700000000.000001")).toBe(false); |
| expect(hasSlackThreadParticipation("A1", "", "1700000000.000001")).toBe(false); |
| expect(hasSlackThreadParticipation("A1", "C123", "")).toBe(false); |
| }); |
|
|
| it("clears all entries", () => { |
| recordSlackThreadParticipation("A1", "C123", "1700000000.000001"); |
| recordSlackThreadParticipation("A1", "C456", "1700000000.000002"); |
| clearSlackThreadParticipationCache(); |
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(false); |
| expect(hasSlackThreadParticipation("A1", "C456", "1700000000.000002")).toBe(false); |
| }); |
|
|
| it("shares thread participation across distinct module instances", async () => { |
| const cacheA = await importFreshModule<typeof import("./sent-thread-cache.js")>( |
| import.meta.url, |
| "./sent-thread-cache.js?scope=shared-a", |
| ); |
| const cacheB = await importFreshModule<typeof import("./sent-thread-cache.js")>( |
| import.meta.url, |
| "./sent-thread-cache.js?scope=shared-b", |
| ); |
|
|
| cacheA.clearSlackThreadParticipationCache(); |
|
|
| try { |
| cacheA.recordSlackThreadParticipation("A1", "C123", "1700000000.000001"); |
| expect(cacheB.hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(true); |
|
|
| cacheB.clearSlackThreadParticipationCache(); |
| expect(cacheA.hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(false); |
| } finally { |
| cacheA.clearSlackThreadParticipationCache(); |
| } |
| }); |
|
|
| it("expired entries return false and are cleaned up on read", () => { |
| recordSlackThreadParticipation("A1", "C123", "1700000000.000001"); |
| |
| vi.spyOn(Date, "now").mockReturnValue(Date.now() + 25 * 60 * 60 * 1000); |
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000001")).toBe(false); |
| }); |
|
|
| it("enforces maximum entries by evicting oldest fresh entries", () => { |
| for (let i = 0; i < 5001; i += 1) { |
| recordSlackThreadParticipation("A1", "C123", `1700000000.${String(i).padStart(6, "0")}`); |
| } |
|
|
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.000000")).toBe(false); |
| expect(hasSlackThreadParticipation("A1", "C123", "1700000000.005000")).toBe(true); |
| }); |
| }); |
|
|