This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/surfaces/icd-test-runner/js/crud/notifications.js
2026-03-14 19:36:33 +00:00

129 lines
6.0 KiB
JavaScript

/**
* ICD Test Runner — CRUD: Notifications
* Full preference lifecycle + notification endpoint error paths.
*/
(function () {
'use strict';
var T = window.ICD;
if (!T) return;
if (!T.crud) T.crud = {};
T.crud.notifications = async function (testTag) {
// ── Preference: Set ──
await T.test('crud', 'notifications', 'PUT /notifications/preferences/:type (set)', async function () {
var d = await T.apiPut('/notifications/preferences/kb.ready', {
in_app: true, email: true
});
T.assert(typeof d === 'object', 'expected object');
T.assert(d.type === 'kb.ready', 'type should be kb.ready, got: ' + d.type);
T.assert(d.in_app === true, 'in_app should be true');
T.assert(d.email === true, 'email should be true');
});
// ── Preference: Read back via list ──
await T.test('crud', 'notifications', 'GET /notifications/preferences (round-trip)', async function () {
var d = await T.apiGet('/notifications/preferences');
T.assertHasKey(d, 'data', 'preferences envelope');
T.assert(Array.isArray(d.data), 'data should be array');
var found = d.data.find(function (p) { return p.type === 'kb.ready'; });
T.assert(found, 'kb.ready preference should be in list');
T.assert(found.in_app === true, 'in_app should be true');
T.assert(found.email === true, 'email should be true');
});
// ── Preference: Partial update ──
await T.test('crud', 'notifications', 'PUT /notifications/preferences/:type (partial update)', async function () {
var d = await T.apiPut('/notifications/preferences/kb.ready', {
email: false
});
T.assert(d.in_app === true, 'in_app should remain true after partial update');
T.assert(d.email === false, 'email should be false after partial update');
});
// ── Preference: Wildcard ──
await T.test('crud', 'notifications', 'PUT /notifications/preferences/* (wildcard)', async function () {
var d = await T.apiPut('/notifications/preferences/*', {
in_app: false, email: true
});
T.assert(d.type === '*', 'type should be *, got: ' + d.type);
T.assert(d.in_app === false, 'in_app should be false');
T.assert(d.email === true, 'email should be true');
});
// ── Preference: Delete + verify removed ──
await T.test('crud', 'notifications', 'DELETE /notifications/preferences/:type (reset)', async function () {
await T.safeDelete('/notifications/preferences/kb.ready');
var d = await T.apiGet('/notifications/preferences');
var found = (d.data || []).find(function (p) { return p.type === 'kb.ready'; });
T.assert(!found, 'kb.ready should be removed after delete');
});
// ── Preference: Idempotent delete ──
await T.test('crud', 'notifications', 'DELETE /notifications/preferences/:type (idempotent)', async function () {
await T.safeDelete('/notifications/preferences/nonexistent.type');
});
// ── Cleanup wildcard preference ──
await T.test('crud', 'notifications', 'DELETE /notifications/preferences/* (cleanup)', async function () {
await T.safeDelete('/notifications/preferences/*');
});
// ── Notification list: envelope shape ──
await T.test('crud', 'notifications', 'GET /notifications (envelope shape)', async function () {
var d = await T.apiGet('/notifications');
T.assertHasKey(d, 'data', 'notifications envelope');
T.assertHasKey(d, 'total', 'notifications total');
T.assertHasKey(d, 'limit', 'notifications limit');
T.assertHasKey(d, 'offset', 'notifications offset');
T.assert(Array.isArray(d.data), 'data should be array');
T.assert(typeof d.total === 'number', 'total should be number');
T.assert(typeof d.limit === 'number', 'limit should be number');
T.assert(typeof d.offset === 'number', 'offset should be number');
});
// ── Notification list: pagination params ──
await T.test('crud', 'notifications', 'GET /notifications?limit=2 (pagination)', async function () {
var d = await T.apiGet('/notifications?limit=2&offset=0');
T.assert(d.limit === 2, 'limit should be 2, got: ' + d.limit);
T.assert(d.offset === 0, 'offset should be 0, got: ' + d.offset);
});
// ── Notification list: unread_only filter ──
await T.test('crud', 'notifications', 'GET /notifications?unread_only=true', async function () {
var d = await T.apiGet('/notifications?unread_only=true');
T.assertHasKey(d, 'data', 'unread envelope');
T.assert(Array.isArray(d.data), 'unread data should be array');
});
// ── Unread count: shape ──
await T.test('crud', 'notifications', 'GET /notifications/unread-count (shape)', async function () {
var d = await T.apiGet('/notifications/unread-count');
T.assertHasKey(d, 'count', 'unread-count');
T.assert(typeof d.count === 'number', 'count should be number');
});
// ── Mark all read: no-op on empty ──
await T.test('crud', 'notifications', 'POST /notifications/mark-all-read (no-op)', async function () {
var d = await T.apiPost('/notifications/mark-all-read');
T.assertHasKey(d, 'ok', 'mark-all-read');
T.assert(d.ok === true, 'ok should be true');
});
// ── Mark read: non-existent → 404 ──
await T.test('crud', 'notifications', 'PATCH /notifications/:id/read (not found → 404)', async function () {
var token = await T.getAuthToken();
var d = await T.authFetch(token, 'PATCH', '/notifications/00000000-0000-0000-0000-000000000000/read');
T.assertStatus(d, 404, 'mark read non-existent');
});
// ── Delete: non-existent → 404 ──
await T.test('crud', 'notifications', 'DELETE /notifications/:id (not found → 404)', async function () {
var token = await T.getAuthToken();
var d = await T.authFetch(token, 'DELETE', '/notifications/00000000-0000-0000-0000-000000000000');
T.assertStatus(d, 404, 'delete non-existent');
});
};
})();