Files
nmemmertandClaude Sonnet 5.5 1cf157be64
Build and Push Docker Image / build-and-push (push) Successful in 5m14s
Prayer journal: pinning, waiting/partial status, import, dashboard card
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 16:27:04 -04:00

45 lines
3.1 KiB
JavaScript

// @vitest-environment node
import { test, expect } from 'vitest';
import { cleanPrayer, cleanPrayers, mergePrayers, openPrayers } from './prayers.js';
const NOW = 1_800_000_000_000;
const p = (id, extra = {}) => ({ id, text: `Pray ${id}`, forWhom: '', verses: [], prayedOn: [], createdAt: NOW - 5000, updatedAt: NOW - 1000, answeredAt: null, answerNote: '', ...extra });
test('only well-formed prayers are kept, rebuilt field by field', () => {
const out = cleanPrayer({ id: 'a-1', text: ' For my family ', forWhom: 'Home', evil: '<script>', verses: [{ book: 'PHP', chapter: 4, verse: 6, label: 'Philippians 4:6' }, { book: 'xx', chapter: 1 }, { book: 'JHN', chapter: 99999 }], prayedOn: ['2026-10-01', 'nope', '2026-10-01'], updatedAt: NOW - 1 }, NOW);
expect(out).toMatchObject({ id: 'a-1', text: 'For my family', forWhom: 'Home', prayedOn: ['2026-10-01'], answeredAt: null });
expect(out.verses).toEqual([{ book: 'PHP', chapter: 4, verse: 6, label: 'Philippians 4:6' }]);
expect('evil' in out).toBe(false);
expect(cleanPrayer({ id: 'bad id!', text: 'x' }, NOW)).toBeNull();
expect(cleanPrayer({ id: 'a', text: ' ' }, NOW)).toBeNull();
expect(cleanPrayer(null)).toBeNull();
expect(cleanPrayer({ id: 'g', deleted: true, updatedAt: NOW }, NOW)).toEqual({ id: 'g', deleted: true, updatedAt: NOW });
expect(cleanPrayers([p('a'), p('a'), 5, p('b')], NOW).map((x) => x.id)).toEqual(['a', 'b']);
});
test('the newer version of a prayer wins and the days prayed are combined', () => {
const mine = [p('a', { text: 'old wording', prayedOn: ['2026-10-01'], updatedAt: NOW - 5000 })];
const theirs = [p('a', { text: 'new wording', prayedOn: ['2026-10-02'], updatedAt: NOW - 1000 }), p('b')];
const merged = mergePrayers(mine, theirs, NOW);
expect(merged.find((x) => x.id === 'a')).toMatchObject({ text: 'new wording', prayedOn: ['2026-10-01', '2026-10-02'] });
expect(merged.map((x) => x.id).sort()).toEqual(['a', 'b']);
expect(mergePrayers(merged, merged, NOW)).toEqual(merged);
});
test('a deleted prayer is not brought back by an older copy, and old markers are forgotten', () => {
const gone = [{ id: 'a', deleted: true, updatedAt: NOW - 100 }];
const stale = [p('a', { updatedAt: NOW - 9000 })];
expect(mergePrayers(gone, stale, NOW)).toEqual([{ id: 'a', deleted: true, updatedAt: NOW - 100 }]);
expect(mergePrayers([{ id: 'z', deleted: true, updatedAt: NOW - 100 * 86_400_000 }], [], NOW)).toEqual([]);
expect(mergePrayers([p('a', { updatedAt: NOW })], [{ id: 'a', deleted: true, updatedAt: NOW - 5 }], NOW)[0].deleted).toBeUndefined(); // written again after the delete
});
test('open prayers are the ones not answered or deleted', () => {
expect(openPrayers([p('a'), p('b', { answeredAt: NOW }), { id: 'c', deleted: true, updatedAt: 1 }]).map((x) => x.id)).toEqual(['a']);
});
test('pin, rank and status are kept when valid and dropped when not', () => {
expect(cleanPrayer(p('a', { pinned: true, rank: 2, status: 'partial' }), NOW)).toMatchObject({ pinned: true, rank: 2, status: 'partial' });
expect(cleanPrayer(p('a', { pinned: 'yes', rank: -4, status: 'bogus' }), NOW)).toMatchObject({ pinned: false, rank: null, status: '' });
});