// Row 54 (#1543): the seeded fixture the reads and views tests share. A // business holds a Discord user id, token files, an environment variable // name and bot ids; a data root holds a notifier config and a Discord // binding with guild, channel and user ids and a token file; an agent's // model var holds a path. A queue row's note names an id and token paths in // the shapes real notes use (row 35: "~/.config/mosaic-dev/secrets/…"). // None may reach a response, the page or a log, nor may the temporary // directory. Rows 9 and 11 have After entries in the stored {id, when} shape. import assert from 'node:assert/strict'; import { cpSync, mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { scratchRepo, genesisCommitted, mapText, MAP_ROWS } from '../../queue/tests/helpers.mjs'; import { businessDoc, writeJson } from '../../business/tests/helpers.mjs'; export const SRC = join(import.meta.dirname, '..', 'src'); export const SNOWFLAKES = ['123456789012345678', '223456789012345678', '323456789012345678', '423456789012345678', '523456789012345678']; export const day = n => new Date(Date.now() + n * 86400000).toISOString().slice(0, 10); export const NOTE = `token at /srv/secret/x.token for ${SNOWFLAKES[4]}; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:/srv/secret/r.json)`; export const NOTE_SHOWN = 'token at for ; tokens 0600 under (receipt file:)'; // A canonical scratch checkout with a committed queue, the reader script // beside a copy of packages/queue/src, and a RELEASE file. export function queueRepo(t) { const rows = MAP_ROWS.map(r => r.id === 6 ? { ...r, note: NOTE } : r.id === 11 ? { ...r, after: [{ id: 9, when: 'done' }] } : r); const repo = scratchRepo(t, { map: mapText(rows) }); genesisCommitted(repo); mkdirSync(join(repo.root, 'packages/webui/src'), { recursive: true }); cpSync(join(SRC, 'queue-read.mjs'), join(repo.root, 'packages/webui/src/queue-read.mjs')); writeFileSync(join(repo.root, 'RELEASE'), '0.0.99\n'); return repo; } // The business, notifier and binding files under one temporary base. export function seeded(t, { business = true, notify = true } = {}) { const base = realpathSync(mkdtempSync(join(tmpdir(), 'mosaic-webui-reads-'))); t.after(() => rmSync(base, { recursive: true, force: true })); const configDir = join(base, 'config'), dataRoot = join(base, 'data'); const doc = businessDoc(base); doc.vars['human.discordUserId'] = SNOWFLAKES[0]; doc.roles.coder.credentials.gitea = { env: 'CODER_GITEA_SECRET_ENV', rotateBy: day(-1) }; doc.roles.coder.credentials.vikunja.expires = day(3); doc.roles.reviewer.credentials.vikunja.expires = day(-2); doc.roles.reviewer.vars = { model: '/srv/secret/models/local.gguf' }; if (business) writeJson(join(configDir, 'businesses', 'acme.json'), doc); if (notify) writeJson(join(dataRoot, 'notify', 'acme', 'notify.json'), { notifyVersion: 1, binding: 'jason-dm' }); writeJson(join(dataRoot, 'discord', 'jason-dm.json'), { bindingVersion: 1, name: 'jason-dm', seat: 'sage', guildId: SNOWFLAKES[1], guildName: 'g', botUserId: SNOWFLAKES[2], tokenFile: join(base, 'secrets', 'discord.token'), channels: [{ id: SNOWFLAKES[3], name: 'c', mode: 'open' }], users: [{ id: SNOWFLAKES[0], name: 'jason' }], }); const system = { configVersion: 1, environment: 'development', dataRoot, execution: { backend: 'docker', provider: 'zai', model: 'glm-5.3-flash', adapter: 'mock' } }; return { base, configDir, dataRoot, system, doc }; } // Nothing secret, no id, no temporary path, in any body. export function clean(text, ...bases) { for (const b of bases) assert.equal(text.includes(b), false, `response names ${b}`); for (const s of SNOWFLAKES) assert.equal(text.includes(s), false, `response carries id ${s}`); for (const s of ['placeholder-not-a-token', 'CODER_GITEA_SECRET_ENV', '"file"', '"env"', '"botId"', 'discordUserId', '"tokenFile"', '"guildId"', '"channels"', '/srv/secret', '~/', 'secrets/mosaic-stack']) assert.equal(text.includes(s), false, `response carries ${s}`); }