var test = require('node:test'); var assert = require('node:assert/strict'); var fs = require('node:fs'); var os = require('node:os'); var path = require('node:path'); var attach = require('../lib/issues-github').attachIssueGithub; var makeStore = require('../lib/issues-store').createIssuesStore; function fixture(t, run) { var dir = fs.mkdtempSync(path.join(os.tmpdir(), 'clay-github-issues-')); t.after(function () { fs.rmSync(dir, { recursive: true, force: true }); }); var store = makeStore({ projectKnowledgeId: 'pk_test', baseDir: dir }); var issue = store.create({ title: 'A concrete bug', body: 'Reproduction steps' }, { type: 'user', userId: 'u1' }); var allowed = true; var calls = []; var assessments = new Map(); var context = { assessments: assessments, analyze: async function (before, prompt) { return prompt.indexOf('queries') >= 0 ? JSON.stringify({ queries: ['concrete bug', 'reproduction problem'] }) : JSON.stringify({ decision: 'ambiguous', matches: [{ url: result().url, reason: 'Similar symptoms, needs confirmation.' }] }); }, locks: new Map(), begin: function () { if (!allowed) throw new Error('Access revoked'); return { path: '/repo', knowledgeId: 'pk_test', osIdentity: { uid: 12 } }; }, read: function (before, ref) { if (!allowed) throw new Error('Access revoked'); return store.read(ref); }, save: function (before, ref, data, revision) { if (!allowed) throw new Error('Access revoked'); return store.update(ref, data, revision, { type: 'user', userId: 'u1' }); }, run: async function (cwd, args, identity, input) { calls.push({ args: args, identity: identity, input: input }); return run(args, input); } }; assessments.set('reviewed', { owner: JSON.stringify(['pk_test', '/repo', undefined, { uid: 12 }]), ref: issue.ref, revision: 1, repository: 'owner/repo', ambiguous: false, expires: Date.now() + 300000 }); return { api: attach(context), store: store, issue: issue, calls: calls, context: context, dir: dir, revoke: function () { allowed = false; }, args: function () { return { ref: issue.ref, expectedRevision: store.read(issue.ref).revision, repository: 'owner/repo', assessment: 'reviewed', title: 'Reviewed title', body: 'Reviewed body', confirm: true }; } }; } function result() { return { url: 'https://github.com/owner/repo/issues/12', title: 'Existing issue', state: 'OPEN' }; } test('search is read-only, includes closed issues and preserves caller identity', async function (t) { var f = fixture(t, function (args) { return args[0] === 'repo' ? { nameWithOwner: 'owner/repo' } : [result()]; }); var data = await f.api.search(f.args()); assert.equal(data.candidates[0].number, 12); assert.equal(data.repository, 'owner/repo'); assert.ok(f.calls[1].args.includes('all')); assert.deepEqual(f.calls[1].identity, { uid: 12 }); assert.equal(f.store.read(f.issue.ref).revision, 1); }); test('verified link persists independently from sessions and across store reload', async function (t) { var f = fixture(t, function () { return result(); }); var linked = await f.api.link(Object.assign(f.args(), { url: result().url })); assert.equal(linked.github.number, 12); assert.equal(linked.linkedWorkSessions.length, 0); var reload = makeStore({ projectKnowledgeId: 'pk_test', baseDir: f.dir }); assert.equal(reload.read(f.issue.ref).github.url, result().url); assert.equal(reload.readRevision(f.issue.ref, 2).github.url, result().url); assert.equal(reload.readRevision(f.issue.ref, 1).github, undefined); await assert.rejects(f.api.link({ ref: f.issue.ref, expectedRevision: 1, url: result().url }), /changed/); await assert.rejects(f.api.link(Object.assign(f.args(), { url: 'https://github.com/owner/repo/pull/12' })), /issue URL/); }); test('creation requires review, checks repository, records attempt before POST, and links once', async function (t) { var f = fixture(t, function (args, input) { if (args[0] === 'repo') return { nameWithOwner: 'owner/repo' }; assert.ok(f.store.read(f.issue.ref).github.attempt); var payload = JSON.parse(input); assert.equal(payload.title, 'Reviewed title'); assert.match(payload.body, /Reviewed body\n\n