const fs = require('fs'); const path = require('path'); const { execFileSync } = require('child_process'); const JSZip = require('jszip'); const base = __dirname; const pptxPath = path.join(base, 'prezentaciya-vampiricheskoe-tenevoe-pravitelstvo.pptx'); const pdfPath = path.join(base, 'prezentaciya-vampiricheskoe-tenevoe-pravitelstvo.pdf'); const reportPath = path.join(base, 'doklad.md'); const handoutPath = path.join(base, 'razdatochnyj-material.md'); const generatorPath = path.join(base, 'generate_presentation.js'); function assert(cond, msg) { if (!cond) throw new Error(msg); } async function main() { execFileSync('node', ['generate_presentation.js'], { cwd: base, stdio: 'inherit' }); assert(fs.existsSync(reportPath), 'Доклад не найден'); assert(fs.existsSync(handoutPath), 'Раздаточный материал не найден'); assert(fs.existsSync(pptxPath), 'PPTX не найден'); const report = fs.readFileSync(reportPath, 'utf8'); const handout = fs.readFileSync(handoutPath, 'utf8'); const generator = fs.readFileSync(generatorPath, 'utf8'); assert(report.includes('Sanguis Noctis Virus'), 'В докладе отсутствует ключевой термин'); assert(report.includes('Экономика качественной крови'), 'В докладе нет раздела о качественной крови'); assert(report.includes('Почему искусственная кровь им не подходит'), 'В докладе нет раздела про искусственную кровь'); assert(handout.includes('Тайный рынок доноров'), 'В раздатке нет блока про тайный рынок доноров'); assert(generator.includes('ai-01-snv12-agent.png') && generator.includes('ai-08-feeding-zones-map.png'), 'Генератор не использует новый набор scientific PNG'); const pptxBuf = fs.readFileSync(pptxPath); const zip = await JSZip.loadAsync(pptxBuf); const slideFiles = Object.keys(zip.files) .filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n)) .sort((a, b) => Number(a.match(/slide(\d+)/)[1]) - Number(b.match(/slide(\d+)/)[1])); assert(slideFiles.length === 15, `Ожидалось 15 слайдов, получено ${slideFiles.length}`); const firstSlide = await zip.files[slideFiles[0]].async('string'); const agentSlide = await zip.files[slideFiles[2]].async('string'); const marketSlide = await zip.files[slideFiles[11]].async('string'); const lastSlide = await zip.files[slideFiles[slideFiles.length - 1]].async('string'); assert(firstSlide.includes('Пищевая цепочка:'), 'На первом слайде нет заголовка'); assert(agentSlide.includes('SNV-12'), 'На биологическом слайде нет SNV-12'); assert(marketSlide.includes('Тайный рынок доноров'), 'На слайде рынка доноров нет заголовка'); assert(lastSlide.includes('Вывод'), 'На последнем слайде нет слайда с выводом'); try { execFileSync('soffice', ['--headless', '--convert-to', 'pdf', '--outdir', base, pptxPath], { cwd: base, stdio: 'pipe' }); } catch (err) { throw new Error(`Не удалось сконвертировать PPTX в PDF: ${err.message}`); } assert(fs.existsSync(pdfPath), 'PDF не был создан'); assert(fs.statSync(pdfPath).size > 20000, 'PDF подозрительно мал'); console.log('Verification OK'); console.log(`- report bytes: ${fs.statSync(reportPath).size}`); console.log(`- handout bytes: ${fs.statSync(handoutPath).size}`); console.log(`- pptx bytes: ${fs.statSync(pptxPath).size}`); console.log(`- pdf bytes: ${fs.statSync(pdfPath).size}`); console.log(`- slides: ${slideFiles.length}`); } main().catch((err) => { console.error(err.stack || err.message || String(err)); process.exit(1); });