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("12-food-chain-chart.png") && generator.includes("assets', 'generated'"), 'Генератор не использует пакет generated 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 chainSlide = await zip.files[slideFiles[13]].async('string'); const lastSlide = await zip.files[slideFiles[slideFiles.length - 1]].async('string'); assert(firstSlide.includes('Пищевая цепочка:'), 'На первом слайде нет заголовка'); assert(chainSlide.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); });