ktg-plugin-marketplace/plugins/llm-security/tests/scanners/ide-extension-discovery.test.mjs

100 lines
3.8 KiB
JavaScript

// ide-extension-discovery.test.mjs — discoverJetBrainsExtensions integration tests.
// All fixtures built in-test via mkdtemp + mkdir — no committed filesystem state.
import { describe, it, after } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtemp, mkdir, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
discoverJetBrainsExtensions,
getAndroidStudioBaseDir,
} from '../../scanners/lib/ide-extension-discovery.mjs';
const TEST_PREFIX = 'llmsec-jbdisc-';
const created = [];
async function buildFixtureBase(layout) {
const base = await mkdtemp(join(tmpdir(), TEST_PREFIX));
created.push(base);
for (const [product, plugins] of Object.entries(layout)) {
for (const plug of plugins) {
await mkdir(join(base, product, 'plugins', plug), { recursive: true });
}
}
return base;
}
describe('discoverJetBrainsExtensions — basic walk', () => {
it('finds plugins under IntelliJIdea<version>/plugins/', async () => {
const base = await buildFixtureBase({
'IntelliJIdea2024.3': ['com.example.one', 'com.example.two'],
});
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
assert.equal(res.extensions.length, 2);
assert.ok(res.extensions.every(e => e.type === 'jetbrains'));
assert.ok(res.extensions.some(e => e.name === 'com.example.one'));
assert.equal(res.extensions[0].productDir, 'IntelliJIdea2024.3');
});
});
describe('discoverJetBrainsExtensions — Fleet excluded', () => {
it('ignores Fleet directory', async () => {
const base = await buildFixtureBase({
'IntelliJIdea2024.3': ['com.example.good'],
'Fleet': ['com.example.fleet'],
});
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
assert.equal(res.extensions.length, 1);
assert.equal(res.extensions[0].name, 'com.example.good');
assert.ok(res.extensions.every(e => !e.location.includes('Fleet')));
});
});
describe('discoverJetBrainsExtensions — Android Studio support', () => {
it('discovers from AndroidStudio product dir', async () => {
const base = await buildFixtureBase({
'AndroidStudio2024.3.1': ['com.google.example'],
});
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
assert.equal(res.extensions.length, 1);
assert.equal(res.extensions[0].productDir, 'AndroidStudio2024.3.1');
});
});
describe('discoverJetBrainsExtensions — multi-product', () => {
it('walks all matching product dirs', async () => {
const base = await buildFixtureBase({
'IntelliJIdea2024.3': ['a'],
'PyCharm2024.3': ['b'],
'GoLand2024.3': ['c'],
'Toolbox': ['d'], // excluded
});
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
assert.equal(res.extensions.length, 3);
assert.ok(!res.extensions.some(e => e.location.includes('Toolbox')));
});
});
describe('discoverJetBrainsExtensions — skip hidden + disabled_plugins.txt', () => {
it('ignores dotfile plugins and disabled_plugins.txt', async () => {
const base = await buildFixtureBase({
'IntelliJIdea2024.3': ['.hidden', 'com.real', 'disabled_plugins.txt'],
});
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
assert.equal(res.extensions.length, 1);
assert.equal(res.extensions[0].name, 'com.real');
});
});
describe('getAndroidStudioBaseDir — Linux path divergence', () => {
it('is a path-producing function (no exception)', () => {
// Actual return value is null or a path depending on host — just assert no throw.
const v = getAndroidStudioBaseDir();
assert.ok(v === null || typeof v === 'string');
});
});
after(async () => {
for (const r of created) await rm(r, { recursive: true, force: true }).catch(() => {});
});