| import { afterEach, describe, expect, it, vi } from "vitest"; |
| import { |
| hasBlueBubblesSelfChatCopy, |
| rememberBlueBubblesSelfChatCopy, |
| resetBlueBubblesSelfChatCache, |
| } from "./monitor-self-chat-cache.js"; |
|
|
| describe("BlueBubbles self-chat cache", () => { |
| const directLookup = { |
| accountId: "default", |
| chatGuid: "iMessage;-;+15551234567", |
| senderId: "+15551234567", |
| } as const; |
|
|
| afterEach(() => { |
| resetBlueBubblesSelfChatCache(); |
| vi.useRealTimers(); |
| }); |
|
|
| it("matches repeated lookups for the same scope, timestamp, and text", () => { |
| vi.useFakeTimers(); |
| vi.setSystemTime(new Date("2026-03-07T00:00:00Z")); |
|
|
| rememberBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: " hello\r\nworld ", |
| timestamp: 123, |
| }); |
|
|
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: "hello\nworld", |
| timestamp: 123, |
| }), |
| ).toBe(true); |
| }); |
|
|
| it("canonicalizes DM scope across chatIdentifier and chatGuid", () => { |
| vi.useFakeTimers(); |
| vi.setSystemTime(new Date("2026-03-07T00:00:00Z")); |
|
|
| rememberBlueBubblesSelfChatCopy({ |
| accountId: "default", |
| chatIdentifier: "+15551234567", |
| senderId: "+15551234567", |
| body: "hello", |
| timestamp: 123, |
| }); |
|
|
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| accountId: "default", |
| chatGuid: "iMessage;-;+15551234567", |
| senderId: "+15551234567", |
| body: "hello", |
| timestamp: 123, |
| }), |
| ).toBe(true); |
|
|
| resetBlueBubblesSelfChatCache(); |
|
|
| rememberBlueBubblesSelfChatCopy({ |
| accountId: "default", |
| chatGuid: "iMessage;-;+15551234567", |
| senderId: "+15551234567", |
| body: "hello", |
| timestamp: 123, |
| }); |
|
|
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| accountId: "default", |
| chatIdentifier: "+15551234567", |
| senderId: "+15551234567", |
| body: "hello", |
| timestamp: 123, |
| }), |
| ).toBe(true); |
| }); |
|
|
| it("expires entries after the ttl window", () => { |
| vi.useFakeTimers(); |
| vi.setSystemTime(new Date("2026-03-07T00:00:00Z")); |
|
|
| rememberBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: "hello", |
| timestamp: 123, |
| }); |
|
|
| vi.advanceTimersByTime(11_001); |
|
|
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: "hello", |
| timestamp: 123, |
| }), |
| ).toBe(false); |
| }); |
|
|
| it("evicts older entries when the cache exceeds its cap", () => { |
| vi.useFakeTimers(); |
| vi.setSystemTime(new Date("2026-03-07T00:00:00Z")); |
|
|
| for (let i = 0; i < 513; i += 1) { |
| rememberBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: `message-${i}`, |
| timestamp: i, |
| }); |
| vi.advanceTimersByTime(1_001); |
| } |
|
|
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: "message-0", |
| timestamp: 0, |
| }), |
| ).toBe(false); |
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: "message-512", |
| timestamp: 512, |
| }), |
| ).toBe(true); |
| }); |
|
|
| it("enforces the cache cap even when cleanup is throttled", () => { |
| vi.useFakeTimers(); |
| vi.setSystemTime(new Date("2026-03-07T00:00:00Z")); |
|
|
| for (let i = 0; i < 513; i += 1) { |
| rememberBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: `burst-${i}`, |
| timestamp: i, |
| }); |
| } |
|
|
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: "burst-0", |
| timestamp: 0, |
| }), |
| ).toBe(false); |
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: "burst-512", |
| timestamp: 512, |
| }), |
| ).toBe(true); |
| }); |
|
|
| it("does not collide long texts that differ only in the middle", () => { |
| vi.useFakeTimers(); |
| vi.setSystemTime(new Date("2026-03-07T00:00:00Z")); |
|
|
| const prefix = "a".repeat(256); |
| const suffix = "b".repeat(256); |
| const longBodyA = `${prefix}${"x".repeat(300)}${suffix}`; |
| const longBodyB = `${prefix}${"y".repeat(300)}${suffix}`; |
|
|
| rememberBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: longBodyA, |
| timestamp: 123, |
| }); |
|
|
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: longBodyA, |
| timestamp: 123, |
| }), |
| ).toBe(true); |
| expect( |
| hasBlueBubblesSelfChatCopy({ |
| ...directLookup, |
| body: longBodyB, |
| timestamp: 123, |
| }), |
| ).toBe(false); |
| }); |
| }); |
|
|