fix: validate nested analysis fields before merging and preparation

This commit is contained in:
2026-09-06 09:10:18 +03:00
parent 144724484c
commit 2aaf56ce8b
6 changed files with 96 additions and 5 deletions
+11 -1
View File
@@ -1,6 +1,16 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {splitAnalysisText,analyzeInParts} from '../server/analysis-parts.mjs';
test('malformed part is isolated before merge and retained as a failed source range',async()=>{
const out=await analyzeInParts({text:'a'.repeat(30000)},async p=>({result:p.index
? {itemsAndSpecs:[{name:'Geçerli kit',quantity:'2 adet'}]}
: {itemsAndSpecs:[null],citations:[null]}}));
assert.equal(out.result.itemsAndSpecs.length,1);
assert.equal(out.result.sourceReview.status,'partial');
assert.equal(out.result.sourceReview.parts[0].status,'failed');
assert.equal(out.result.sourceReview.analyzedCharacters,6000);
await assert.rejects(()=>analyzeInParts({text:'kaynak'},async()=>({result:{citations:[null]}})),/hiçbir parçası/);
});
test('long Turkish source is covered once without loss, including final paragraph',()=>{
const text=('İğdır laboratuvar 🧪\n').repeat(7000)+'SON MADDE';
const parts=splitAnalysisText(text);assert.ok(parts.length>1);
@@ -9,7 +19,7 @@ test('long Turkish source is covered once without loss, including final paragrap
});
test('all source parts contribute requirements, failures remain explicit partial analysis',async()=>{
const text='a'.repeat(60000);
const complete=await analyzeInParts({text,fileName:'test'},async p=>({result:{documentChecklist:[{document:`Belge ${p.index}`}],sourceReview:{issues:[]}},attempts:[]}));
const complete=await analyzeInParts({text,fileName:'test'},async p=>({result:{documentChecklist:[{document:`Belge ${p.index}`,mandatory:true}],sourceReview:{issues:[]}},attempts:[]}));
assert.equal(complete.result.documentChecklist.length,3);assert.equal(complete.result.sourceReview.analyzedCharacters,60000);
assert.equal(complete.result.sourceReview.status,'review_required');assert.equal(complete.result.bidAnalysis,null);
const partial=await analyzeInParts({text},async p=>{if(p.index===1)throw new Error('test failure');return {result:{itemsAndSpecs:[{name:'Kit',quantity:p.index?'3 adet':'2 adet'}]}};});
+18 -1
View File
@@ -1,8 +1,25 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {qualifyAnalysis} from '../server/analysis-quality.mjs';
test('malformed nested model fields cannot reach preparation screens',()=>{
for(const result of [
{itemsAndSpecs:[null]}, {itemsAndSpecs:[{name:{text:'Kit'}}]},
{itemsAndSpecs:[{name:'Kit',quantity:'2 adet',keySpecs:'steril'}]},
{documentChecklist:[{document:'ÜTS',mandatory:'false'}]},
{criticalRequirements:[{text:'Numune'}]}, {deadlines:'120 gün'},
{roadmap:[{action:'Hazırla',step:'bir'}]},
{bidAnalysis:{recommendedBid:'1000'}}, {bidAnalysis:{winProbability:101}},
{citations:[null]}, {summary:{text:'Analiz'}}
]) assert.throws(()=>qualifyAnalysis(result), /biçim/);
});
test('missing item quantity or document obligation keeps analysis partial',()=>{
const out=qualifyAnalysis({itemsAndSpecs:[{name:'Kit'}],documentChecklist:[{document:'ÜTS'}]});
assert.equal(out.sourceReview.status,'partial');
assert.ok(out.sourceReview.issues.some(v=>v.includes('miktar/birim')));
assert.ok(out.sourceReview.issues.some(v=>v.includes('zorunluluk')));
});
test('truncated input and repaired output are partial, never complete analysis',()=>{
const r=qualifyAnalysis({itemsAndSpecs:[{name:'Kit'}]},{text:'a'.repeat(100),limit:50,expectedItems:100,repaired:true});
const r=qualifyAnalysis({itemsAndSpecs:[{name:'Kit',quantity:'2 adet'}]},{text:'a'.repeat(100),limit:50,expectedItems:100,repaired:true});
assert.equal(r.sourceReview.status,'partial');assert.equal(r.sourceReview.issues.length,3);
assert.equal(r.sourceReview.analyzedCharacters,50);
assert.equal(r.sourceReview.sections.documentChecklist,'not_extracted');