Files
SRCmail/lib/__tests__/subject-prefix.test.ts
T
Stefan HildebrandtandLinus Rath 2fac6ebfb8 test: add characterisation tests for untested integration seams
Golden-master tests pinning the CURRENT behavior of high-value modules
that had no coverage — integration seams, security helpers, two API
route handlers, and complex pure utils. 111 tests across 12 files.

New tests:
- auth-crypto / session-cookie: AES-256-GCM session encryption roundtrip,
  tamper/version/missing-secret handling; cookie-slot naming.
- unified-mailbox: multi-account fan-out, sort, totals, per-account error
  isolation, personal-vs-shared JMAP target resolution, counts/roles.
- account-state-manager: snapshot/restore across the six real Zustand
  stores; clearAllStores reset shape; evict.
- mdn: RFC 5322 MDN assembly (CRLF, RFC2047, base64 wrap, headers).
- tnef: winmail.dat binary parsing from hand-built fixtures.
- download-filename / subject-prefix / birthday-calendar / eml-import:
  filename templating, multilingual prefix stripping, birthday event
  generation, .eml/.zip import.
- webdav / caldav-discover route handlers: auth guards, path validation,
  upstream URL construction, candidate probing.
- helpers/factories.ts: shared makeEmail/makeMailbox/makeFakeJmapClient.

Tests follow the repo's existing patterns (route-import, fake IJMAPClient,
fetch spy, real store singletons). Where current behavior looks buggy it
is pinned and flagged with a // CHARACTERISATION: comment (see PR for the
suspected-bugs list); no production code is changed.
2026-06-19 23:52:42 +02:00

68 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest';
import {
stripSubjectPrefixes,
buildReplySubject,
buildForwardSubject,
} from '@/lib/subject-prefix';
describe('stripSubjectPrefixes', () => {
it('strips a chain of mixed-language prefixes', () => {
expect(stripSubjectPrefixes('Re: AW: WG: foo')).toBe('foo');
});
it('strips the Outlook [N] counter and Eudora *N counter', () => {
expect(stripSubjectPrefixes('Re[2]: foo')).toBe('foo');
expect(stripSubjectPrefixes('Re*3: foo')).toBe('foo');
expect(stripSubjectPrefixes('Re*: foo')).toBe('foo');
});
it('is case-insensitive and idempotent', () => {
expect(stripSubjectPrefixes('RE: Re: foo')).toBe('foo');
expect(stripSubjectPrefixes(stripSubjectPrefixes('RE: Re: foo'))).toBe('foo');
});
it('strips a Cyrillic reply token', () => {
expect(stripSubjectPrefixes('Ответ: foo')).toBe('foo');
});
it('strips a Chinese token followed by an ASCII colon', () => {
expect(stripSubjectPrefixes('回复: foo')).toBe('foo');
});
it('CHARACTERISATION: does NOT strip a token followed by a full-width colon', () => {
// The colon in the regex is ASCII ":"; a full-width "" (U+FF1A), as some
// CJK mail clients emit, is left untouched. Likely a bug — see follow-ups.
expect(stripSubjectPrefixes('回复:foo')).toBe('回复:foo');
});
it('does NOT strip a bare single-letter "R:" (would eat real subjects)', () => {
expect(stripSubjectPrefixes('R: budget 2024')).toBe('R: budget 2024');
});
it('returns "" for empty / null / undefined', () => {
expect(stripSubjectPrefixes('')).toBe('');
expect(stripSubjectPrefixes(null)).toBe('');
expect(stripSubjectPrefixes(undefined)).toBe('');
});
it('leaves a prefix-free subject untouched', () => {
expect(stripSubjectPrefixes('foo')).toBe('foo');
});
});
describe('buildReplySubject / buildForwardSubject', () => {
it('replaces an existing prefix chain with the given prefix', () => {
expect(buildReplySubject('AW: WG: foo', 'Re:')).toBe('Re: foo');
expect(buildForwardSubject('Re: foo', 'Fwd:')).toBe('Fwd: foo');
});
it('prepends the prefix to a prefix-free subject', () => {
expect(buildReplySubject('foo', 'AW:')).toBe('AW: foo');
});
it('returns just the bare prefix for an empty subject', () => {
expect(buildReplySubject('', 'AW:')).toBe('AW:');
expect(buildForwardSubject(null, 'Fwd:')).toBe('Fwd:');
});
});