Files
hub/tools/search-test.js
T
nmemmertandClaude Sonnet 5.5 0383da87f5
build-container / build (push) Successful in 4m6s
Chores, Family page, genre search, child accounts, wider Search and Notifications center
- Chores tab with repeats, points and a week/month board
- Family page: calendar, chores, shopping, notes, chat and tasks on one start page
- Genre search in the Store, Free Books and LibriVox
- Child accounts: safe content, daily limit and bedtime, ask a parent
- Search now covers notes, chat, contacts, chores, recipes and your books
- Notifications center: bell with a per-person list of chat, reminders and announcements
- Roadmap, README and tests updated

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 11:48:55 -04:00

19 lines
3.3 KiB
JavaScript

// Search across the hub: notes, chat, contacts, chores and recipes (and what each person is allowed to see).
const http = require('http'), assert = require('assert'), fs = require('fs'), os = require('os'), path = require('path');
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'nehub-search-')); process.env.MEALIE_URL = 'http://127.0.0.1:9'; delete process.env.MEALIE_TOKEN;
const users = require('../users'), search = require('../search'), notes = require('../notes'), chat = require('../chat'), contacts = require('../contacts'), chores = require('../chores');
const P = { a: users.create({ name: 'Ann', role: 'admin', handle: 'YQ' }), b: users.create({ name: 'Bob', role: 'user', tabs: ['search', 'notes', 'chat'], handle: 'Yg' }) };
const wrap = (mod, base) => (req, res) => users.runAs(P[req.headers['x-user'] || 'a'], () => mod.handle(req, res));
const hub = http.createServer((req, res) => users.runAs(P[req.headers['x-user'] || 'a'], () => req.url.startsWith('/api/notes') ? notes.handle(req, res) : req.url.startsWith('/api/chat') ? chat.handle(req, res) : req.url.startsWith('/api/contacts') ? contacts.handle(req, res) : req.url.startsWith('/api/chores') ? chores.handle(req, res) : search.handle(req, res))).listen(0, async () => {
const B = 'http://127.0.0.1:' + hub.address().port, call = (p, o = {}, who) => fetch(B + p, { ...o, headers: { ...(o.headers || {}), ...(who ? { 'x-user': who } : {}) } }), post = (p, b, who) => call(p, { method: 'POST', body: JSON.stringify(b) }, who), j = r => r.json(), find = async (q, who) => j(await call('/api/search?q=' + encodeURIComponent(q), {}, who));
try {
await post('/api/notes', { type: 'list', title: 'Groceries', items: ['Sourdough starter', 'Eggs'], share: 'family' }, 'a'); await post('/api/notes', { type: 'note', title: 'Private', body: 'my sourdough secret', share: 'me' }, 'a'); await post('/api/contacts', { name: 'Sourdough Bakery', emails: [{ value: 'hi@bakery.test' }] }, 'a');
await post('/api/chores', { title: 'Feed the sourdough', repeat: 'daily', assign: 'anyone', points: 2 }, 'a'); const dm = (await j(await post('/api/chat/new', { type: 'direct', with: P.b.id }, 'a'))).conv; await post('/api/chat/send', { conv: dm.id, text: 'Who has the sourdough recipe?' }, 'a');
let r = await find('sourdough', 'a'); assert.deepEqual(r.notes.map(x => x.title).sort(), ['Groceries', 'Private'], 'the admin sees both notes (their own, and the family one)'); assert.equal(r.notes.find(x => x.title === 'Groceries').snippet.includes('Sourdough starter'), true, 'with where it matched');
assert.equal(r.chat.length, 1); assert.equal(r.chat[0].convName, 'Bob'); assert.equal(r.contacts[0].name, 'Sourdough Bakery'); assert.equal(r.chores[0].title, 'Feed the sourdough'); assert.deepEqual(r.recipes, [], 'no Mealie: no recipes, and no error');
r = await find('sourdough', 'b'); assert.deepEqual(r.notes.map(x => x.title), ['Groceries'], 'Bob does not see the private note'); assert.equal(r.chat.length, 1); assert.deepEqual(r.contacts, [], 'no Contacts tab: nothing from there'); assert.deepEqual(r.chores, [], 'and none from Chores');
r = await find('s', 'a'); assert.deepEqual(r.notes, [], 'one letter is not a search'); console.log('search ok');
} catch (e) { console.error(e); process.exitCode = 1; }
hub.close(); setTimeout(() => process.exit(process.exitCode || 0), 50);
});