Files
gitmost/apps/server/src/core/notification/notification.constants.spec.ts
T
claude code agent 227 7c48bab1f2 test: add unit tests for 10 candidates from issue #139
Adds co-located unit tests for ten targets (client → vitest *.test.ts(x),
server → jest *.spec.ts), plus minimal behavior-preserving extractions/exports
where the issue required a pure function to test:

- encode-wav: WAV header + PCM16 clamping
- editor-ext embed-provider / utils (sanitizeUrl, isInternalFileUrl) / indent
  (export clampIndent)
- label.dto @Matches regex
- move-page.dto vs generateJitteredKeyBetween parity (bug locked via test.failing)
- new-note-button canCreatePage (extracted to can-create-page.ts)
- history-editor diff (extracted pure computeHistoryDiff into history-diff.ts)
- notification getTypesForTab + repo contract (direct-tab divergence locked via
  test.failing)
- search buildTsQuery (extracted + sanitizes operator inputs so adversarial
  queries no longer risk a to_tsquery 500)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 04:13:44 +03:00

78 lines
3.1 KiB
TypeScript

import {
NotificationType,
DIRECT_NOTIFICATION_TYPES,
UPDATES_NOTIFICATION_TYPES,
getTypesForTab,
} from './notification.constants';
// Contract tests for `getTypesForTab` (notification.constants.ts), which maps a
// notification tab to the set of notification types it should contain.
// - 'direct' -> a 5-type whitelist (mentions / comments / permission grants)
// - 'updates' -> exactly [PAGE_UPDATED]
// - 'all' -> undefined (no type filter)
describe('getTypesForTab', () => {
it("returns exactly the 5 whitelisted types for 'direct'", () => {
expect(getTypesForTab('direct')).toEqual([
NotificationType.COMMENT_USER_MENTION,
NotificationType.COMMENT_CREATED,
NotificationType.COMMENT_RESOLVED,
NotificationType.PAGE_USER_MENTION,
NotificationType.PAGE_PERMISSION_GRANTED,
]);
expect(getTypesForTab('direct')).toHaveLength(5);
expect(getTypesForTab('direct')).toBe(DIRECT_NOTIFICATION_TYPES);
});
it("returns [PAGE_UPDATED] for 'updates'", () => {
expect(getTypesForTab('updates')).toEqual([NotificationType.PAGE_UPDATED]);
expect(getTypesForTab('updates')).toBe(UPDATES_NOTIFICATION_TYPES);
});
it("returns undefined (no filter) for 'all'", () => {
expect(getTypesForTab('all')).toBeUndefined();
});
});
// CONTRACT vs the repository query (notification.repo.ts ~line 57):
// direct -> WHERE type != PAGE_UPDATED
// updates -> WHERE type = PAGE_UPDATED
//
// For 'updates' the whitelist and the SQL agree exactly. For 'direct' they
// DIVERGE: the whitelist is a positive 5-type allow-list, but `type != PAGE_UPDATED`
// returns EVERY non-PAGE_UPDATED type — including verification/approval types that
// are NOT in the whitelist. So the repo would surface notifications the 'direct'
// tab is not supposed to contain. We model the repo predicate and assert it should
// match the whitelist; the 'direct' case genuinely fails today, so it is locked with
// `test.failing` (suite stays green, flips red once repo + whitelist are reconciled).
// What the repo's WHERE clause would actually return, given all known types.
const ALL_TYPES = Object.values(NotificationType);
function repoTypesForTab(tab: 'direct' | 'updates'): string[] {
if (tab === 'direct') {
return ALL_TYPES.filter((t) => t !== NotificationType.PAGE_UPDATED);
}
return ALL_TYPES.filter((t) => t === NotificationType.PAGE_UPDATED);
}
describe('getTypesForTab vs notification.repo query', () => {
it("'updates' whitelist matches the repo's `type = PAGE_UPDATED` filter", () => {
expect(new Set(repoTypesForTab('updates'))).toEqual(
new Set(getTypesForTab('updates')),
);
});
// BUG LOCK: the 'direct' whitelist (5 types) does not match what the repo's
// `type != PAGE_UPDATED` filter returns (all non-PAGE_UPDATED types). This SHOULD
// match; it currently does not. Flips green once the repo filters by the whitelist
// (e.g. `type IN (DIRECT_NOTIFICATION_TYPES)`).
test.failing(
"'direct' whitelist matches the repo's `type != PAGE_UPDATED` filter",
() => {
expect(new Set(repoTypesForTab('direct'))).toEqual(
new Set(getTypesForTab('direct')),
);
},
);
});