aps-agent/tests/node/packaging-smoke.test.mjs

140 lines
5.6 KiB
JavaScript
Raw Normal View History

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
parseScanOutput,
classifyVerdict,
resolveInstallerName,
discoverArtifacts,
} from '../../scripts/defender-scan.mjs';
import {
sha256File,
parseChecksumsFile,
checksumsContent,
expectedInstallDir,
dryRunInstallPath,
checkInstaller,
} from '../../scripts/offline-install-check.mjs';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
test('defender: clean output is parsed as clean', () => {
const parsed = parseScanOutput('Scan starting...\nScan finished.\nScanning X found no threats.\n');
assert.equal(parsed.status, 'clean');
assert.equal(parsed.threatCount, 0);
assert.equal(parsed.hasThreats, false);
});
test('defender: threat block exposes count and threat name', () => {
const text = 'Scanning X found 1 threats.\n\n<===========================LIST OF DETECTED THREATS==========================>\nThreat : Virus:DOS/EICAR_Test_File\nResources : 1 total\n file : X\n';
const parsed = parseScanOutput(text);
assert.equal(parsed.status, 'threat');
assert.equal(parsed.threatCount, 1);
assert.ok(parsed.threatNames.includes('Virus:DOS/EICAR_Test_File'));
});
test('defender: remediating-mode threat output is recognized', () => {
const parsed = parseScanOutput('Scanning X found 2 threats.\nCleaning started...\n');
assert.equal(parsed.status, 'threat');
assert.equal(parsed.threatCount, 2);
});
test('defender: skipped output is not treated as clean', () => {
const parsed = parseScanOutput('Scan starting...\nScan finished.\nScanning D:\\abc was skipped.\n');
assert.equal(parsed.status, 'skipped');
});
test('defender: verdict classification', () => {
assert.equal(classifyVerdict([{ status: 'clean' }]), 'PASS');
assert.equal(classifyVerdict([{ status: 'clean' }, { status: 'threat' }]), 'THREAT');
assert.equal(classifyVerdict([{ status: 'error' }]), 'FAIL');
assert.equal(classifyVerdict([{ status: 'timeout' }]), 'FAIL');
assert.equal(classifyVerdict([{ status: 'skipped' }]), 'PASS');
});
test('defender: installer name resolution from electron-builder template', () => {
const config = { artifactName: 'aps-agent-desktop-' + '$' + '{version}' + '.' + '$' + '{ext}', version: '0.1.0' };
assert.equal(resolveInstallerName(config), 'aps-agent-desktop-0.1.0.exe');
});
test('defender: artifact discovery is driven by apps/desktop/package.json', (t) => {
const artifacts = discoverArtifacts(repoRoot);
assert.deepEqual(
artifacts.map((a) => a.id),
['sidecar-exe', 'desktop-frozen-exe', 'bundled-sidecar-exe', 'nsis-installer'],
);
const installer = artifacts.find((a) => a.id === 'nsis-installer');
assert.ok(installer.file.endsWith('aps-agent-desktop-0.1.0.exe'));
if (!installer.exists) t.skip('NSIS installer not built on this machine');
});
test('offline: sha256File is deterministic lowercase hex', async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'aps-node-test-'));
try {
const file = path.join(dir, 'sample.bin');
fs.writeFileSync(file, Buffer.alloc(64 * 1024, 3));
const first = await sha256File(file);
const second = await sha256File(file);
assert.equal(first, second);
assert.match(first, /^[0-9a-f]{64}$/);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('offline: checksumsContent and parseChecksumsFile round trip', async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'aps-node-test-'));
try {
const file = path.join(dir, 'installer.exe');
fs.writeFileSync(file, Buffer.alloc(1024, 1));
const hash = await sha256File(file);
const content = checksumsContent([{ file, sha256: hash }], dir);
const parsed = parseChecksumsFile(content);
assert.equal(parsed.length, 1);
assert.equal(parsed[0].sha256, hash);
assert.equal(parsed[0].file, 'installer.exe');
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('offline: expected install dir is LOCALAPPDATA Programs productName', () => {
const dir = expectedInstallDir('C:\\Users\\tester\\AppData\\Local', '工业智核 APS');
assert.equal(dir, 'C:\\Users\\tester\\AppData\\Local\\Programs\\工业智核 APS');
});
test('offline: dry-run detects absent and complete install layouts', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'aps-node-test-'));
try {
const absent = dryRunInstallPath(path.join(dir, 'missing-local'), '工业智核 APS');
assert.equal(absent.exists, false);
const local = path.join(dir, 'LocalAppData');
const installDir = path.join(local, 'Programs', '工业智核 APS');
fs.mkdirSync(path.join(installDir, 'resources', 'sidecar'), { recursive: true });
fs.writeFileSync(path.join(installDir, '工业智核 APS.exe'), 'app');
fs.writeFileSync(path.join(installDir, 'resources', 'sidecar', 'aps-sidecar.exe'), 'sidecar');
const complete = dryRunInstallPath(local, '工业智核 APS');
assert.equal(complete.exists, true);
assert.equal(complete.layoutValid, true);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('offline: installer size gate', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'aps-node-test-'));
try {
const file = path.join(dir, 'big.exe');
fs.writeFileSync(file, Buffer.alloc(4096, 0));
assert.equal(checkInstaller(file, 1024).sizeOk, true);
assert.equal(checkInstaller(path.join(dir, 'nope.exe'), 1024).exists, false);
assert.equal(checkInstaller(file, 10 * 1024).sizeOk, false);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});