Small gaps: pantry flags in the meal plan, birthday and anniversary notifications, download notes and highlights

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-27 10:16:26 -04:00
1 parent 342c8af747
commit b2b772941b
9 files changed
+46 -4

No files matched your search

+8
View File
@@ -58,6 +58,14 @@ const users = require('../users'), push = require('../push');
ok('and only once for that stage', sent.length === n1, sent.length);
ok('an ordinary person is not told about it', !sent.some(s => /Mail sign-in/.test(s.title) && !s.sub.endpoint.endsWith('/admin')), sent);
// birthdays and anniversaries from Contacts come with the morning summary, once a day
{ const k = users.create({ name: 'Kim', role: 'user', tabs: ['contacts'] }), kd = users.dirOf(k.id); sent.length = 0;
fs.writeFileSync(path.join(kd, 'contacts.json'), JSON.stringify([{ id: 'c1', name: 'Jo', birthday: '--10-22', anniversary: '2010-10-22', emails: [], phones: [] }, { id: 'c2', name: 'Al', birthday: '--03-01', emails: [], phones: [] }]));
fs.writeFileSync(path.join(kd, 'push.json'), JSON.stringify({ tz: TZ, tasksAt: '08:00', tasks: true, events: false, subs: [{ sub: { endpoint: 'https://push.example/kim', keys: { p256dh: 'x', auth: 'y' } } }] }));
await push.tick(now); const mine = sent.filter(x => x.sub.endpoint.endsWith('/kim'));
ok('a birthday and an anniversary today make one notification', mine.length === 1 && /2 birthdays and anniversaries/.test(mine[0].title) && /Jo's birthday/.test(mine[0].body) && /anniversary \(16\)/.test(mine[0].body), mine);
await push.tick(now); ok('and not again the same day', sent.filter(x => x.sub.endpoint.endsWith('/kim')).length === 1); }
// a subscription the push service says is gone is dropped
dead = true; writePush({ tasks: true, tasksAt: '00:00', tasksDay: '' }); fs.writeFileSync(path.join(d, 'tasks.json'), JSON.stringify({ lists: [{ id: 'inbox', name: 'Tasks' }], tasks: [{ id: 't1', list: 'inbox', title: 'X', due: '2000-01-01', done: false }] }));
await push.tick(new Date());
+5
View File
@@ -62,6 +62,11 @@ const fake = http.createServer((req, res) => {
j = await (await post('/plan/shop', { from: '2027-03-02', to: '2027-03-08' })).json(); assert.equal(j.ok, true); assert.equal(j.recipes, 1, 'a recipe planned twice is added once, twice over');
assert.equal((await post('/plan/shop', { from: '2027-04-01', to: '2027-04-07' })).status, 400, 'nothing planned, nothing to add');
await post('/plan/remove', { id: (await (await get('/plan?from=2027-03-02&to=2027-03-02')).json())['2027-03-02'][0].id }); assert.deepEqual(await (await get('/plan?from=2027-03-02&to=2027-03-02')).json(), {});
// the pantry flags planned ingredients you already have ("3 cups flour" is covered by "flour"; butter is not in the pantry)
await post('/plan/add', { date: '2027-05-03', slug: 'bread', name: 'Bread' });
j = await (await post('/pantry', { items: 'Flour\n \nflour, Salt' })).json(); assert.deepEqual(j.items, ['Flour', 'flour', 'Salt'].filter((x, i, a) => a.indexOf(x) === i), 'trimmed and de-duplicated by exact text');
j = await (await get('/plan/pantry?from=2027-05-03&to=2027-05-09')).json(); assert.deepEqual(j.items.map(i => i.text), ['3 cups flour']); assert.equal(j.items[0].have.toLowerCase(), 'flour');
await post('/pantry', { items: [] }); assert.deepEqual((await (await get('/plan/pantry?from=2027-05-03&to=2027-05-09')).json()).items, [], 'an empty pantry flags nothing');
// a wrong token is explained
process.env.MEALIE_TOKEN = 'bad'; r = await get('/'); assert.equal(r.status, 502); assert.match((await r.json()).error, /token/);
process.env.MEALIE_TOKEN = ''; assert.equal((await get('/')).status, 503);