const assert = require ( "assert" ) ;
const fs = require ( "fs" ) ;
const path = require ( "path" ) ;
const root = path . resolve ( _ _dirname , ".." ) ;
const api = fs . readFileSync ( path . join ( root , "src/api/aiTrainingEvaluation.js" ) , "utf8" ) ;
const component = fs . readFileSync ( path . join ( root , "src/views/components/TrainingAiEvaluation.vue" ) , "utf8" ) ;
const sidebar = fs . readFileSync ( path . join ( root , "src/views/components/TrainingAiSidebar.vue" ) , "utf8" ) ;
const page = fs . readFileSync ( path . join ( root , "src/views/training/GenericTrainingPage.vue" ) , "utf8" ) ;
const { normalizeEvaluationReports } = require ( path . join ( root , "src/views/components/trainingAiEvaluationReport.cjs" ) ) ;
assert ( /`\/api\/student\/training-tasks\/\$\{taskKey\}\/ai-evaluation`/ . test ( api ) , "evaluation API should use the student task endpoint" ) ;
assert ( /\/help/ . test ( api ) && /\/assessment/ . test ( api ) , "evaluation API should expose help and assessment operations" ) ;
assert ( /TrainingAiSidebar/ . test ( page ) && /:has-saved-answer="hasSavedAnswer"/ . test ( page ) , "generic training page should pass persisted answer state to the AI sidebar" ) ;
assert ( /form\.value/ . test ( page ) && /workRows\.value/ . test ( page ) && /processRows\.value/ . test ( page ) , "hasAnyAnswer should inspect all supported answer shapes" ) ;
assert ( /requestAiTrainingHelp\(taskKey, question\)/ . test ( api ) , "AI help API should accept the student's question" ) ;
assert ( /data:\s*\{\s*question\s*\}/ . test ( api ) , "AI help API should submit the student's question as JSON" ) ;
assert ( /const question = assistQuestion\.value\.trim\(\)/ . test ( sidebar ) , "AI sidebar should trim the student's question before sending it" ) ;
assert ( /if \(!question\)/ . test ( sidebar ) , "AI sidebar should block an empty AI help question" ) ;
assert ( /requestAiTrainingHelp\(currentTaskKey\.value, question\)/ . test ( sidebar ) , "AI sidebar should send the trimmed question to AI help" ) ;
assert ( /onMounted\(/ . test ( component ) && /watch\(\s*\(\)\s*=>\s*props\.taskKey/ . test ( component ) , "AI evaluation should load on mount and task changes" ) ;
assert ( /AI助学/ . test ( component ) && /AI助评/ . test ( component ) , "AI evaluation should offer separate help and assessment controls" ) ;
assert ( /PROCESSING/ . test ( component ) && /SUCCEEDED/ . test ( component ) , "AI evaluation should surface backend statuses" ) ;
assert ( /!props\.hasAnyAnswer/ . test ( component ) , "AI actions should disable when no answer is available" ) ;
assert ( /proxy\?\.\$modal\?\.confirm/ . test ( component ) , "assessment should request confirmation through the modal proxy" ) ;
assert ( ! /v-html/ . test ( component ) , "AI reports must be rendered as interpolated text, never v-html" ) ;
assert ( /scoreCriteria/ . test ( component ) , "assessment should display score criteria" ) ;
assert ( /normalizeEvaluationReports/ . test ( component ) , "the component should use the executable report normalizer" ) ;
const normalizedFlatPayload = normalizeEvaluationReports ( {
helpStatus : "succeeded" ,
helpReportJson : JSON . stringify ( { summary : "Add supporting data" , suggestions : [ "Cite the source" ] } ) ,
assessmentStatus : "succeeded" ,
assessmentScore : 92 ,
assessmentReportJson : JSON . stringify ( {
summary : "Clear analysis" ,
criteria : [
{ criterion : "Evidence" , weight : 40 , detail : "Uses relevant data" } ,
"Structure" ,
] ,
} ) ,
} ) ;
assert . strictEqual ( normalizedFlatPayload . help . status , "SUCCEEDED" , "flat help status should be normalized" ) ;
assert . match ( normalizedFlatPayload . helpReport , /Add supporting data/ , "flat help JSON should be formatted for text display" ) ;
assert . strictEqual ( normalizedFlatPayload . assessment . status , "SUCCEEDED" , "flat assessment status should be normalized" ) ;
assert . strictEqual ( normalizedFlatPayload . score , 92 , "assessmentScore should take precedence for the displayed score" ) ;
assert . deepStrictEqual ( normalizedFlatPayload . scoreCriteria , [
{ name : "Evidence" , score : 40 , description : "Uses relevant data" } ,
{ name : "Structure" , score : "" , description : "" } ,
] , "assessment criteria should be mapped from the flat assessment JSON" ) ;
const malformedPayload = normalizeEvaluationReports ( {
helpReportJson : "{not valid JSON" ,
assessmentReportJson : "{also not valid JSON" ,
} ) ;
assert . strictEqual ( malformedPayload . helpReport , "{not valid JSON" , "malformed help JSON should remain safe text" ) ;
assert . strictEqual ( malformedPayload . assessmentReport , "{also not valid JSON" , "malformed assessment JSON should remain safe text" ) ;
assert . strictEqual ( malformedPayload . score , null , "malformed assessment JSON should not produce a score" ) ;
assert . deepStrictEqual ( malformedPayload . scoreCriteria , [ ] , "malformed assessment JSON should not produce criteria" ) ;
assert ( /retry/ . test ( component ) || /重试/ . test ( component ) , "failed AI operations should expose a retry action" ) ;
assert ( /hasSavedAnswer/ . test ( page ) && /:has-saved-answer="hasSavedAnswer"/ . test ( page ) , "generic training page should distinguish persisted answers before enabling AI assessment" ) ;
assert ( /hasSavedAnswer/ . test ( sidebar ) , "AI sidebar should accept persisted-answer state" ) ;
assert ( /!props\.hasSavedAnswer/ . test ( sidebar ) , "AI assessment should be disabled until a saved answer exists" ) ;
assert ( /catch \(error\)/ . test ( sidebar ) , "AI sidebar should handle assessment failures without an unhandled rejection" ) ;