import assert from 'node:assert/strict' import crypto from 'node:crypto' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import { fileURLToPath } from 'node:url' import { afterEach, test } from 'vitest' import { ensureWindowsVcRuntime, loadVcRuntimeManifest, readPythonHome } from './windows-vc-runtime' const here = path.dirname(fileURLToPath(import.meta.url)) const roots: string[] = [] function tempRoot() { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'aiturk-vc-runtime-')) roots.push(root) return root } function sha256(data: Buffer) { return crypto.createHash('sha256').update(data).digest('hex') } // A fake install: venv/pyvenv.cfg -> python home, plus a bundled source dir // whose manifest describes the given files. function fixture(files: Record) { const root = tempRoot() const home = path.join(root, 'python home') const venv = path.join(root, 'venv') const source = path.join(root, 'bundle') for (const dir of [home, venv, source]) { fs.mkdirSync(dir, { recursive: true }) } fs.writeFileSync(path.join(home, 'python.exe'), 'exe') fs.writeFileSync(path.join(venv, 'pyvenv.cfg'), `home = ${home}\r\nversion_info = 3.11\r\n`) const entries = Object.entries(files).map(([name, { data, ownedByPython }]) => { const bytes = Buffer.from(data) fs.writeFileSync(path.join(source, name), bytes) return { name, size: bytes.length, sha256: sha256(bytes), ...(ownedByPython ? { ownedByPython } : {}) } }) fs.writeFileSync( path.join(source, 'manifest.json'), JSON.stringify({ schemaVersion: 1, version: 'test', architecture: 'x64', files: entries }) ) return { home, venv, source } } afterEach(() => { for (const root of roots.splice(0)) { fs.rmSync(root, { recursive: true, force: true }) } }) test('non-Windows platforms are left untouched', () => { const { home, venv, source } = fixture({ 'msvcp140.dll': { data: 'crt' } }) assert.deepEqual(ensureWindowsVcRuntime({ platform: 'linux', venvRoot: venv, sourceDir: source }), { status: 'skipped', reason: 'not windows' }) assert.equal(fs.existsSync(path.join(home, 'msvcp140.dll')), false) }) test('readPythonHome parses pyvenv.cfg home with spaces', () => { const { home, venv } = fixture({ 'msvcp140.dll': { data: 'crt' } }) assert.equal(readPythonHome(venv), home) assert.equal(readPythonHome(path.join(venv, 'missing')), null) }) test('missing CRT files are copied beside python.exe and verified', () => { const { home, venv, source } = fixture({ 'msvcp140.dll': { data: 'msvcp' }, 'msvcp140_1.dll': { data: 'msvcp1' } }) const result = ensureWindowsVcRuntime({ platform: 'win32', venvRoot: venv, sourceDir: source }) assert.deepEqual(result, { status: 'repaired', pythonHome: home, copied: ['msvcp140.dll', 'msvcp140_1.dll'] }) assert.equal(fs.readFileSync(path.join(home, 'msvcp140_1.dll'), 'utf8'), 'msvcp1') assert.deepEqual(fs.readdirSync(home).filter(name => name.endsWith('.tmp')), []) // Second launch: nothing to do. assert.deepEqual(ensureWindowsVcRuntime({ platform: 'win32', venvRoot: venv, sourceDir: source }), { status: 'ok', pythonHome: home, copied: [] }) }) test('a mismatching msvcp140 copy is replaced, a python-owned vcruntime is kept', () => { const { home, venv, source } = fixture({ 'msvcp140.dll': { data: 'good' }, 'vcruntime140.dll': { data: 'bundled', ownedByPython: true } }) fs.writeFileSync(path.join(home, 'msvcp140.dll'), 'stale') fs.writeFileSync(path.join(home, 'vcruntime140.dll'), 'interpreter-owned') const result = ensureWindowsVcRuntime({ platform: 'win32', venvRoot: venv, sourceDir: source }) assert.deepEqual(result, { status: 'repaired', pythonHome: home, copied: ['msvcp140.dll'] }) assert.equal(fs.readFileSync(path.join(home, 'msvcp140.dll'), 'utf8'), 'good') assert.equal(fs.readFileSync(path.join(home, 'vcruntime140.dll'), 'utf8'), 'interpreter-owned') }) test('a tampered bundled file is refused and nothing is written', () => { const { home, venv, source } = fixture({ 'msvcp140.dll': { data: 'genuine' } }) fs.writeFileSync(path.join(source, 'msvcp140.dll'), 'tampered') const result = ensureWindowsVcRuntime({ platform: 'win32', venvRoot: venv, sourceDir: source }) assert.equal(result.status, 'failed') assert.match((result as { error: string }).error, /does not match its manifest digest/) assert.equal(fs.existsSync(path.join(home, 'msvcp140.dll')), false) }) test('an unresolvable python home fails without throwing', () => { const { venv, source } = fixture({ 'msvcp140.dll': { data: 'crt' } }) fs.writeFileSync(path.join(venv, 'pyvenv.cfg'), 'home = C:\\does-not-exist\n') assert.equal(ensureWindowsVcRuntime({ platform: 'win32', venvRoot: venv, sourceDir: source }).status, 'failed') }) test('the shipped CRT set matches its manifest and covers the voice wheels', () => { const sourceDir = path.join(here, '..', 'assets', 'windows', 'vc-runtime', 'x64') const manifest = loadVcRuntimeManifest(sourceDir) const names = manifest.files.map(file => file.name) for (const required of ['msvcp140.dll', 'msvcp140_1.dll', 'vcruntime140.dll', 'vcruntime140_1.dll']) { assert.ok(names.includes(required), `${required} must be bundled`) } for (const file of manifest.files) { const bytes = fs.readFileSync(path.join(sourceDir, file.name)) assert.equal(bytes.length, file.size, file.name) assert.equal(sha256(bytes), file.sha256, file.name) } }) test('the Windows package ships the CRT set as an extra resource', () => { const pkg = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf8')) const resources = pkg.build.win.extraResources assert.ok( resources.some(entry => entry.from === 'assets/windows/vc-runtime/x64' && entry.to === 'vc-runtime/x64'), 'win.extraResources must carry vc-runtime/x64' ) }) test('ensureRuntime places the CRT before handing the venv python to the backend', () => { const source = fs.readFileSync(path.join(here, 'main.ts'), 'utf8') const start = source.indexOf('async function ensureRuntime(') const body = source.slice(start, source.indexOf('\nfunction ', start + 1)) const ensureIdx = body.indexOf('ensureWindowsVcRuntime(') const commandIdx = body.indexOf('backend.command = getVenvPython(VENV_ROOT)') assert.notEqual(ensureIdx, -1, 'ensureRuntime must call ensureWindowsVcRuntime') assert.ok(ensureIdx < commandIdx, 'CRT must be in place before the backend command is chosen') })