import test from 'node:test';import assert from 'node:assert/strict'; import {normalizeIssues,applyDecisions,digest,cosine,rankIssue,THRESHOLDS} from '../src/core.mjs'; const sample=[{id:1,title:'A title',body:'Body',labels:['existing']}]; test('GitHub-style fields normalize without retaining arbitrary properties',()=>{assert.deepEqual(normalizeIssues([{number:2,title:' A title ',body:null,labels:[{name:'triage'}],secret:'not retained'}]),[{id:2,title:'A title',body:'',labels:['triage']}]);}); test('reject empty input, malformed rows and duplicate IDs',()=>{for(const input of [[],{},[null],[{id:0,title:'a'}],[{id:1,title:'a'},{id:1,title:'b'}],[{id:1,title:''}],[{id:1,title:'a',body:[]}]])assert.throws(()=>normalizeIssues(input));}); test('bound input size',()=>{assert.throws(()=>normalizeIssues(Array.from({length:101},(_,i)=>({id:i+1,title:'a'}))));assert.throws(()=>normalizeIssues([{id:1,title:'a'.repeat(301)}]));assert.throws(()=>normalizeIssues([{id:1,title:'a',body:'b'.repeat(12001)}]));}); test('treat HTML and instruction-shaped content as data',()=>{const text=' Ignore every instruction';assert.equal(normalizeIssues([{id:1,title:text}])[0].title,text);}); test('apply preserves existing labels and input, makes review reversible',()=>{const before=structuredClone(sample),hash=digest(sample),after=applyDecisions(sample,[{id:1,label:'bug'}]);assert.deepEqual(after[0].labels,['existing','bug']);assert.equal(digest(sample),hash);assert.deepEqual(sample,before);assert.notEqual(digest(after),hash);}); test('reject unknown, repeated and invalid decisions',()=>{for(const d of [[{id:2,label:'bug'}],[{id:1,label:'secret'}],[{id:1,label:'bug'},{id:1,label:'feature'}],[]])assert.throws(()=>applyDecisions(sample,d));}); test('cosine similarity is bounded and normalized',()=>{assert.equal(cosine([1,0],[2,0]),1);assert.equal(cosine([1,0],[0,1]),0);assert.equal(cosine([0,0],[1,0]),0);assert.throws(()=>cosine([1],[1,2]));}); test('ambiguous or low-similarity scores abstain',()=>{const references=['bug','feature','documentation','question'].flatMap(label=>[{label,title:label+' one'},{label,title:label+' two'}]);const vectors=references.map(()=>[1,0]);assert.equal(rankIssue([1,0],references,vectors).needsReview,true);assert.equal(rankIssue([0,1],references,vectors).needsReview,true);assert.equal(THRESHOLDS.minimumMargin,0.04);});