import fs from 'fs/promises'; import os from 'os'; import path from 'path'; import { execFile } from 'child_process'; import { promisify } from 'util'; import { createZipFromDir, extractZipToDir } from './src/utils/zip-extract.js'; const execFileAsync = promisify(execFile); let passed = 0; let failed = 0; function test(name, fn) { return Promise.resolve() .then(fn) .then(() => { console.log(` ✓ ${name}`); passed++; }) .catch((err) => { console.log(` ✗ ${name}: ${err.message}`); failed++; }); } function assert(cond, msg) { if (!cond) throw new Error(msg || 'Assertion failed'); } function assertEqual(actual, expected, msg) { if (actual !== expected) { throw new Error(msg || `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); } } console.log('\n── ZIP Import/Export Regression ──'); const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'vlcode-zip-')); const wrappedRoot = path.join(tmpRoot, 'wrapped'); const targetDir = path.join(tmpRoot, 'imported'); const archivePath = path.join(tmpRoot, 'wrapped.zip'); await fs.mkdir(path.join(wrappedRoot, 'EduSystem', 'Apps'), { recursive: true }); await fs.mkdir(path.join(wrappedRoot, 'EduSystem', 'Sections'), { recursive: true }); await fs.mkdir(path.join(wrappedRoot, 'EduSystem', '.vl-code'), { recursive: true }); await fs.writeFile(path.join(wrappedRoot, 'EduSystem', 'Apps', 'StudentApp.vx'), '// VL_VERSION:3.5\n\n', 'utf8'); await fs.writeFile(path.join(wrappedRoot, 'EduSystem', 'Sections', 'Home.sc'), '// VL_VERSION:3.5\n\n', 'utf8'); await fs.writeFile(path.join(wrappedRoot, 'EduSystem', '.vl-code', 'project.json'), JSON.stringify({ projectName: 'EduSystem' }, null, 2), 'utf8'); await test('createZipFromDir packages project files including .vl-code metadata', async () => { const buf = await createZipFromDir(path.join(wrappedRoot, 'EduSystem')); await fs.writeFile(archivePath, buf); const { stdout } = await execFileAsync('unzip', ['-Z1', archivePath]); assert(stdout.includes('Apps/StudentApp.vx'), 'archive should include app files'); assert(stdout.includes('.vl-code/project.json'), 'archive should include .vl-code metadata'); }); await test('extractZipToDir strips a single root wrapper directory on import', async () => { const wrappedArchive = await createZipFromDir(wrappedRoot); await fs.writeFile(archivePath, wrappedArchive); await fs.mkdir(targetDir, { recursive: true }); await extractZipToDir(archivePath, targetDir); const importedApp = await fs.readFile(path.join(targetDir, 'Apps', 'StudentApp.vx'), 'utf8'); assert(importedApp.includes('App_StudentApp'), 'expected app file at stripped path'); let wrappedExists = true; try { await fs.access(path.join(targetDir, 'EduSystem', 'Apps', 'StudentApp.vx')); } catch { wrappedExists = false; } assertEqual(wrappedExists, false, 'wrapper directory should not be preserved'); }); console.log(`\n── Results ──\n\n ${passed} passed, ${failed} failed\n`); process.exit(failed > 0 ? 1 : 0);