返回 CodeWhale
github-adapters.test.mjs
根目录 / crates / tui / extension-host / test / github-adapters.test.mjs
1 import test from 'node:test'
2 import assert from 'node:assert/strict'
3 import { dirname, join } from 'node:path'
4 import { readFileSync } from 'node:fs'
5 import { createHash } from 'node:crypto'
6 import { transformStockSnapshot } from '../dist/stock-adapters.mjs'
7 import { createHarnessModule } from '../dist/builtin/harness.mjs'
8 import { startHost, BUNDLE } from './harness.mjs'
9 const transform = input => transformStockSnapshot({kind:'stock_adapter',operation:'github_result',input})
10 const fields = {title:'Lost tool result',expected:'Result reaches the agent',actual:'Result was missing',steps:['Request the result'],observed:['The result was absent'],inferred:[],impact:'Task needs a retry',reported_provider:null,reported_tool:'Run',reported_terminal:null,related_issues:[12,19]}
11 const report = {id:'cwreport_'+'a'.repeat(64),revises:null,fields,version:'0.10.1',platform:'linux x86_64',model:'test-model',redactions:['absolute_path','secret']}
12 const parse = input => JSON.parse(transform(input).result.content)
13 const owner = {plugin_id:'host:harness',generation:4,owner_token:'fixture-owned-job'}
14 const params = (id='one') => ({owner,execution_id:id,ticket:'opaque-core-execution-grant',deadline_ms:1000})
15
16 test('issue and PR snapshots preserve admitted source metadata and exact decimal caller number',()=>{
17 const raw={number:42,title:'Title',comments:[{body:'CJK 漢字'}],body:'short',body_artifact:'untrusted-source-field'}
18 for(const action of ['issue_context','pr_context']) {
19 const got=parse({action,number:'18446744073709551615',raw,large_body:false,diff:null})
20 assert.deepEqual(got[action==='issue_context'?'issue':'pr'],raw)
21 assert.equal(got.summary,`${action==='issue_context'?'Issue':'PR'} #18446744073709551615: Title`)
22 assert.equal(transform({action,number:'1',raw,large_body:false,diff:null}).result.metadata,null)
23 }
24 })
25 test('large bodies and optional diffs use scalar controls and supplied Core refs without making metadata',()=>{
26 const got=parse({action:'pr_context',number:'2',raw:{title:'T',body:'漢'.repeat(1197)+'END'},large_body:true,body_artifact:'artifacts/core-body',diff:'😀'.repeat(897)+'END',diff_artifact:'artifacts/core-diff'}).pr
27 assert.equal(got.body_summary,'漢'.repeat(897)+'...');assert.equal(got.body,'漢'.repeat(1197)+'...')
28 assert.equal(got.diff_summary,'😀'.repeat(897)+'...');assert.equal(got.body_artifact,'artifacts/core-body');assert.equal(got.diff_artifact,'artifacts/core-diff')
29 const control=parse({action:'issue_context',number:'1',raw:{body:'\u0000a\u0085b\n\tc'},large_body:true,body_artifact:null,diff:null}).issue
30 assert.equal(control.body_summary,'ab\n\tc')
31 })
32 test('every write and dry/dirty acknowledgement preserves prior target wording and success',()=>{
33 for(const target of ['issue','pr']) {
34 const subject=target==='issue'?'issue':'PR'
35 for(const dry_run of [false,true]) {
36 assert.equal(transform({action:'comment',target,number:'8',dry_run}).result.content,dry_run?`Dry run: would comment on ${target} #8.`:`Commented on ${target} #8.`)
37 const action=target==='issue'?'close_issue':'close_pr'
38 assert.equal(transform({action,target,number:'8',dry_run}).result.content,dry_run?`Dry run: would close ${subject} #8.`:`Closed ${subject} #8.`)
39 const dirty=transform({action,target,number:'8',dirty:true}).result;assert.equal(dirty.success,false);assert.equal(dirty.content,`Refusing to close ${subject}: worktree is dirty and allow_dirty was false.`)
40 }
41 }
42 })
43 test('draft/read/operator review share one complete Markdown renderer and unavailable publication status',()=>{
44 const draft=parse({action:'report_draft',report}),read=parse({action:'report_read',report}),review=transform({action:'report_review',report}).result.content
45 assert.deepEqual(draft,read);assert.equal(draft.review,review);assert.equal(draft.publication,'unavailable');assert.equal(draft.duplicate_search,'not_performed')
46 assert.equal(draft.artifact,`artifacts/issue-reports/${report.id}.json`)
47 assert.ok(review.startsWith(`# Lost tool result\n\nDraft: ${report.id}\nStatus: ready for review\nPublication: unavailable`))
48 assert.ok(review.includes('## Inferences (not verified)\n\nNone recorded.\n'));assert.ok(review.includes('- Provider (agent reported): unknown\n'))
49 assert.ok(review.includes('- [#19](https://github.com/Hmbown/CodeWhale/issues/19)\n'));assert.ok(review.includes('Redacted categories: absolute_path, secret\n'))
50 assert.ok(review.endsWith(`\nReview: \`/feedback review ${report.id}\`\nRevise: \`/feedback edit ${report.id} <change>\`\n`))
51 })
52 test('revision and inference fields remain attributed and distinct',()=>{
53 const revised={...report,revises:'cwreport_'+'b'.repeat(64),fields:{...fields,inferred:['Perhaps a renderer failed'],reported_provider:'test-provider',related_issues:[]},redactions:[]}
54 const content=transform({action:'report_review',report:revised}).result.content
55 assert.ok(content.includes(`Revises: ${revised.revises}\n`));assert.ok(content.includes('## Inferences (not verified)\n\n- Perhaps a renderer failed\n'))
56 assert.ok(!content.includes('Related issues'));assert.ok(!content.includes('Redacted categories'))
57 })
58 test('unknown action/target, malformed report and oversized snapshots refuse without truncation',()=>{
59 for(const input of [{action:'publish',number:'1',target:'issue'},{action:'comment',target:'pull',number:'1'},{action:'report_review',report:{...report,fields:{...fields,steps:4}}},{action:'issue_context',number:'1',raw:{body:'x'.repeat(1024*1024+1)}}]) assert.throws(()=>transform(input))
60 })
61 test('actual Node runner redeems only one opaque operation and presents the admitted report',async()=>{
62 const calls=[];const runner=createHarnessModule({async request(method,args){calls.push([method,args]);return {kind:'stock_adapter',operation:'github_result',input:{action:'report_read',report}}}},owner)
63 const got=await runner.run(params(),new AbortController().signal)
64 assert.equal(JSON.parse(got.result.content).report_id,report.id);assert.deepEqual(calls,[['exec/redeem',{owner,execution_id:'one',ticket:'opaque-core-execution-grant'}]])
65 await runner.dispose()
66 })
67 test('runner cancellation and owner disposal cannot present a late GitHub completion',async()=>{
68 let finish;const runner=createHarnessModule({request(){return new Promise(resolve=>{finish=resolve})}},owner)
69 const controller=new AbortController(),pending=runner.run(params(),controller.signal);controller.abort()
70 await assert.rejects(pending,/cancelled/);finish({kind:'stock_adapter',operation:'github_result',input:{action:'report_read',report}});await runner.dispose()
71 await assert.rejects(runner.run(params('two'),new AbortController().signal),/stale/)
72 })
73 test('real builtin Host process handles the adopted GitHub operation over the wire',async t=>{
74 const host=await startHost({tier:'builtin'});t.after(()=>host.stop())
75 const entry=join(dirname(BUNDLE),'builtin','harness.mjs'),sha256=createHash('sha256').update(readFileSync(entry)).digest('hex')
76 assert.equal((await host.call('ext/activate',{owner,plugin_name:'Pinned GitHub formatter',entry:{path:entry,sha256},config:{}})).status,'ok')
77 const redeemed=host.waitFor(message=>message.method==='exec/redeem')
78 const result=host.call('harness/run',params('wire'))
79 const message=await redeemed;assert.deepEqual(message.params,{owner,execution_id:'wire',ticket:'opaque-core-execution-grant'})
80 host.send({jsonrpc:'2.0',id:message.id,result:{kind:'stock_adapter',operation:'github_result',input:{action:'report_read',report}}})
81 assert.equal(JSON.parse((await result).result.content).review,transform({action:'report_review',report}).result.content)
82 assert.deepEqual(await host.call('ext/deactivate',{owner}),{disposed:true,leaked:[]})
83 })
84
85 test('frozen Rust source counterpart and adopted presenter produce byte-identical report reviews',()=>{
86 const fixture=JSON.parse(readFileSync(new URL('../../tests/fixtures/github-host-parity.json',import.meta.url),'utf8'))
87 for(const {report,review} of fixture.cases) assert.equal(transform({action:'report_review',report}).result.content,review)
88 })
89
89 lines Plain Text