AkurAI Build
Menu

Public repository / 0d7f5f902b20084326479f36d4ccf2b55ee890c0

AkurAI-Mail / src/lib/webmail/threads.test.ts

Latest change 22202d1bde69be4ace8a299dffd91f8ff24378df - Recover deployed webmail frontend (frontend parity with EC2 a6afffc) by Ólafur Búi Ólafsson

import { describe, expect, test } from "bun:test";
import { groupThreads, normalizeSubject, sameParticipants } from "./threads";

describe("normalizeSubject", () => {
  test("strips one reply/forward prefix and lowercases", () => {
    expect(normalizeSubject("Re: Q3 Roadmap")).toBe("q3 roadmap");
    expect(normalizeSubject("FWD:  Hello ")).toBe("hello");
    expect(normalizeSubject("AW: Hallo")).toBe("hallo");
    expect(normalizeSubject()).toBe("");
  });
});

describe("sameParticipants", () => {
  test("matches the same sender or a reply pair", () => {
    expect(sameParticipants({ id: "a", from: "A@x.is" }, { id: "b", from: "a@x.is" })).toBe(true);
    expect(sameParticipants({ id: "a", from: "a@x.is", to: "b@x.is" }, { id: "b", from: "b@x.is", to: "a@x.is" })).toBe(true);
    expect(sameParticipants({ id: "a", from: "a@x.is", to: "me@x.is" }, { id: "b", from: "c@x.is", to: "me@x.is" })).toBe(false);
    expect(sameParticipants({ id: "a" }, { id: "b", from: "c@x.is" })).toBe(false);
  });
});

describe("groupThreads", () => {
  test("joins messages by In-Reply-To and References, oldest first, newest thread first", () => {
    const threads = groupThreads([
      { id: "1", messageId: "<1@x>", subject: "Plan", timestamp: 10 },
      { id: "2", messageId: "<2@x>", inReplyTo: "<1@x>", subject: "Re: Plan", timestamp: 30, unread: true },
      { id: "3", messageId: "<3@x>", references: ["<1@x>"], subject: "Re: Plan", timestamp: 20 },
      { id: "4", messageId: "<4@x>", subject: "Other", timestamp: 40 },
    ]);
    expect(threads.map((t) => t.messages.map((m) => m.id))).toEqual([["4"], ["1", "3", "2"]]);
    expect(threads[1]!.latest.id).toBe("2");
    expect(threads[1]!.unreadCount).toBe(1);
  });

  test("falls back to subject plus participants only for header-less messages", () => {
    const threads = groupThreads([
      { id: "u1", subject: "Update", from: "carol@corp.com", to: "me@x.is", timestamp: 2 },
      { id: "u2", subject: "Update", from: "dave@other.com", to: "me@x.is", timestamp: 1 },
      { id: "u3", subject: "Re: Update", from: "carol@corp.com", to: "me@x.is", timestamp: 3 },
    ]);
    expect(threads.map((t) => t.messages.map((m) => m.id))).toEqual([["u1", "u3"], ["u2"]]);
  });
});