import * as assert from 'assert'; import * as fs from 'fs/promises'; import * as path from 'path'; import * as os from 'os'; import { existsSync } from 'fs'; import { LuaAddonsManager } from '../lua-addons-manager'; /** * Integration tests for LuaAddonsManager * Uses temporary directories to avoid polluting the file system */ suite('LuaAddonsManager', () => { let tempDir: string; suiteSetup(async () => { tempDir = path.join(os.tmpdir(), `dcs-test-${Date.now()}-${Math.random()}`); }); suiteTeardown(async () => { if (existsSync(tempDir)) { await fs.rm(tempDir, { recursive: true, force: true }); } }); suite('getExtensionVersion', () => { test('should return undefined if addon not found', async () => { const extensionDir = path.join(tempDir, 'getExtensionVersion-1'); const sourceDir = path.join(tempDir, 'getExtensionVersion-1-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); const version = await manager.getExtensionVersion('nonexistent'); assert.strictEqual(version, undefined); }); test('should return version string for installed addon', async () => { const extensionDir = path.join(tempDir, 'getExtensionVersion-2'); const sourceDir = path.join(tempDir, 'getExtensionVersion-2-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); await fs.mkdir(path.join(extensionDir, 'dcs-types.1.2.3'), { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); const version = await manager.getExtensionVersion('dcs-types'); assert.strictEqual(version, '1.2.3'); }); test('should return first matching version if multiple versions exist', async () => { const extensionDir = path.join(tempDir, 'getExtensionVersion-3'); const sourceDir = path.join(tempDir, 'getExtensionVersion-3-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon.1.0.0'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon.2.0.0'), { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); const version = await manager.getExtensionVersion('addon'); // Should return one of the versions (first found) assert.match(version!, /^[12]\.0\.0$/); }); }); suite('getPackageVersion', () => { test('should read version from package.json', async () => { const extensionDir = path.join(tempDir, 'getPackageVersion-1'); const sourceDir = path.join(tempDir, 'getPackageVersion-1-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const packageJson = path.join(extensionDir, 'package.json'); await fs.writeFile(packageJson, JSON.stringify({ version: '2.1.0' })); const manager = new LuaAddonsManager(extensionDir, sourceDir); const version = await manager.getPackageVersion(); assert.strictEqual(version, '2.1.0'); }); test('should throw error if package.json not found', async () => { const extensionDir = path.join(tempDir, 'getPackageVersion-2'); const sourceDir = path.join(tempDir, 'getPackageVersion-2-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); await assert.rejects( () => manager.getPackageVersion(), /Failed to read extension version/ ); }); test('should throw error if version field missing', async () => { const extensionDir = path.join(tempDir, 'getPackageVersion-3'); const sourceDir = path.join(tempDir, 'getPackageVersion-3-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const packageJson = path.join(extensionDir, 'package.json'); await fs.writeFile(packageJson, JSON.stringify({ name: 'test' })); const manager = new LuaAddonsManager(extensionDir, sourceDir); await assert.rejects( () => manager.getPackageVersion(), /Version field not found/ ); }); }); suite('removeExtension', () => { test('should remove addon by name', async () => { const extensionDir = path.join(tempDir, 'removeExtension-1'); const sourceDir = path.join(tempDir, 'removeExtension-1-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon1.1.0.0'), { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); await manager.removeExtension('addon1'); const remaining = await fs.readdir(extensionDir); assert.strictEqual(remaining.length, 0); }); test('should not affect other addons', async () => { const extensionDir = path.join(tempDir, 'removeExtension-2'); const sourceDir = path.join(tempDir, 'removeExtension-2-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon1.1.0.0'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon2.1.0.0'), { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); await manager.removeExtension('addon1'); const remaining = await fs.readdir(extensionDir); assert.strictEqual(remaining.length, 1); assert.strictEqual(remaining[0], 'addon2.1.0.0'); }); }); suite('removeAllExtensions', () => { test('should remove multiple addons by name', async () => { const extensionDir = path.join(tempDir, 'removeAllExtensions-1'); const sourceDir = path.join(tempDir, 'removeAllExtensions-1-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon1.1.0.0'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon2.1.0.0'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon3.1.0.0'), { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); await manager.removeAllExtensions(['addon1', 'addon2']); const remaining = await fs.readdir(extensionDir); assert.deepStrictEqual(remaining.sort(), ['addon3.1.0.0']); }); test('should delete all versions of matching addons', async () => { const extensionDir = path.join(tempDir, 'removeAllExtensions-2'); const sourceDir = path.join(tempDir, 'removeAllExtensions-2-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon1.0.0.1'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon1.0.0.2'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon1.0.0.3'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon2.1.0.0'), { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); await manager.removeAllExtensions(['addon1']); const remaining = await fs.readdir(extensionDir); assert.deepStrictEqual(remaining.sort(), ['addon2.1.0.0']); }); test('should ignore folders with invalid version format', async () => { const extensionDir = path.join(tempDir, 'removeAllExtensions-3'); const sourceDir = path.join(tempDir, 'removeAllExtensions-3-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); await fs.mkdir(path.join(extensionDir, 'addon1.1.0.0'), { recursive: true }); await fs.mkdir(path.join(extensionDir, 'invalid-folder'), { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); await manager.removeAllExtensions(['addon1']); const remaining = await fs.readdir(extensionDir); assert.deepStrictEqual(remaining.sort(), ['invalid-folder']); }); test('should not throw if addon does not exist', async () => { const extensionDir = path.join(tempDir, 'removeAllExtensions-4'); const sourceDir = path.join(tempDir, 'removeAllExtensions-4-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); // Should not throw await manager.removeAllExtensions(['nonexistent']); }); }); suite('installExtension', () => { test('should copy addon from source to target', async () => { const extensionDir = path.join(tempDir, 'installExtension-1'); const sourceDir = path.join(tempDir, 'installExtension-1-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const sourceAddon = path.join(sourceDir, 'addon1'); await fs.mkdir(sourceAddon, { recursive: true }); await fs.writeFile(path.join(sourceAddon, 'file.lua'), 'content'); const manager = new LuaAddonsManager(extensionDir, sourceDir); await manager.installExtension('addon1'); const targetAddon = path.join(extensionDir, 'addon1'); assert.strictEqual(existsSync(targetAddon), true); assert.strictEqual(existsSync(path.join(targetAddon, 'file.lua')), true); }); test('should throw error if source directory does not exist', async () => { const extensionDir = path.join(tempDir, 'installExtension-2'); const sourceDir = path.join(tempDir, 'installExtension-2-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const manager = new LuaAddonsManager(extensionDir, sourceDir); await assert.rejects( () => manager.installExtension('nonexistent'), /Source extension folder not found/ ); }); test('should copy nested directories recursively', async () => { const extensionDir = path.join(tempDir, 'installExtension-3'); const sourceDir = path.join(tempDir, 'installExtension-3-source'); await fs.mkdir(extensionDir, { recursive: true }); await fs.mkdir(sourceDir, { recursive: true }); const sourceAddon = path.join(sourceDir, 'addon1'); await fs.mkdir(path.join(sourceAddon, 'subdir'), { recursive: true }); await fs.writeFile(path.join(sourceAddon, 'file.lua'), 'root'); await fs.writeFile(path.join(sourceAddon, 'subdir', 'file.lua'), 'nested'); const manager = new LuaAddonsManager(extensionDir, sourceDir); await manager.installExtension('addon1'); const targetAddon = path.join(extensionDir, 'addon1'); assert.strictEqual(existsSync(path.join(targetAddon, 'file.lua')), true); assert.strictEqual(existsSync(path.join(targetAddon, 'subdir', 'file.lua')), true); }); }); });