136 lines
3.9 KiB
JavaScript
136 lines
3.9 KiB
JavaScript
|
|
// Unit tests for src/main/plugin-heuristics.js — A3 effect heuristics.
|
||
|
|
// The module is pure: fixtures are hand-built entry lists.
|
||
|
|
|
||
|
|
'use strict'
|
||
|
|
|
||
|
|
const test = require('node:test')
|
||
|
|
const assert = require('node:assert/strict')
|
||
|
|
|
||
|
|
const H = require('../src/main/plugin-heuristics.js')
|
||
|
|
|
||
|
|
test('summarize: empty entries → zero counts, no conflicts, no warning', () => {
|
||
|
|
const s = H.summarize({ entries: [] })
|
||
|
|
assert.equal(s.enabledCount, 0)
|
||
|
|
assert.equal(s.disabledCount, 0)
|
||
|
|
assert.equal(s.totalCount, 0)
|
||
|
|
assert.deepEqual(s.conflicts, [])
|
||
|
|
assert.equal(s.toolWarning, null)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: enabled/disabled counts split correctly', () => {
|
||
|
|
const s = H.summarize({
|
||
|
|
entries: [
|
||
|
|
{ id: 'bash', disabled: false },
|
||
|
|
{ id: 'fs' },
|
||
|
|
{ id: 'net', disabled: true },
|
||
|
|
],
|
||
|
|
})
|
||
|
|
assert.equal(s.enabledCount, 2)
|
||
|
|
assert.equal(s.disabledCount, 1)
|
||
|
|
assert.equal(s.totalCount, 3)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: near-name conflict (edit distance 1) flagged', () => {
|
||
|
|
const s = H.summarize({
|
||
|
|
entries: [
|
||
|
|
{ id: 'bash-local' },
|
||
|
|
{ id: 'bash-locel' }, // typo
|
||
|
|
],
|
||
|
|
})
|
||
|
|
assert.equal(s.conflicts.length, 1)
|
||
|
|
assert.equal(s.conflicts[0].kind, 'edit-distance')
|
||
|
|
assert.deepEqual(
|
||
|
|
[s.conflicts[0].a, s.conflicts[0].b].sort(),
|
||
|
|
['bash-local', 'bash-locel'],
|
||
|
|
)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: prefix-overlap conflict flagged', () => {
|
||
|
|
const s = H.summarize({
|
||
|
|
entries: [
|
||
|
|
{ id: 'bash' },
|
||
|
|
{ id: 'bash-local' },
|
||
|
|
],
|
||
|
|
})
|
||
|
|
assert.equal(s.conflicts.length, 1)
|
||
|
|
assert.equal(s.conflicts[0].kind, 'prefix')
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: disabled entries do not participate in conflict detection', () => {
|
||
|
|
const s = H.summarize({
|
||
|
|
entries: [
|
||
|
|
{ id: 'bash-local' },
|
||
|
|
{ id: 'bash-locel', disabled: true },
|
||
|
|
],
|
||
|
|
})
|
||
|
|
assert.equal(s.conflicts.length, 0)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: short ids (<4 chars) skipped in conflict detection', () => {
|
||
|
|
const s = H.summarize({
|
||
|
|
entries: [
|
||
|
|
{ id: 'fs' },
|
||
|
|
{ id: 'os' },
|
||
|
|
{ id: 'ls' },
|
||
|
|
],
|
||
|
|
})
|
||
|
|
assert.equal(s.conflicts.length, 0)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: distance 2 is NOT flagged by A3 (A1 already covers dist≤2)', () => {
|
||
|
|
// "abcd" vs "abef" differ by 2 chars — A1 flags this, A3 uses a tighter
|
||
|
|
// threshold to avoid noise in the summary bar.
|
||
|
|
const s = H.summarize({
|
||
|
|
entries: [
|
||
|
|
{ id: 'abcd' },
|
||
|
|
{ id: 'abef' },
|
||
|
|
],
|
||
|
|
})
|
||
|
|
assert.equal(s.conflicts.length, 0)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: tool warning fires at custom threshold', () => {
|
||
|
|
const many = []
|
||
|
|
for (let i = 0; i < 5; i++) many.push({ id: `plugin${i}` })
|
||
|
|
const s = H.summarize({ entries: many, toolWarnAt: 3 })
|
||
|
|
assert.ok(s.toolWarning)
|
||
|
|
assert.equal(s.toolWarning.count, 5)
|
||
|
|
assert.equal(s.toolWarning.threshold, 3)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: tool warning does not fire when at or below threshold', () => {
|
||
|
|
const s = H.summarize({
|
||
|
|
entries: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
|
||
|
|
toolWarnAt: 3,
|
||
|
|
})
|
||
|
|
assert.equal(s.toolWarning, null)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('summarize: default toolWarnAt is 30', () => {
|
||
|
|
const many = []
|
||
|
|
for (let i = 0; i < 31; i++) many.push({ id: `p${i}` })
|
||
|
|
const s = H.summarize({ entries: many })
|
||
|
|
assert.ok(s.toolWarning)
|
||
|
|
assert.equal(s.toolWarning.threshold, 30)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('findConflicts: prefix takes precedence over edit-distance for same pair', () => {
|
||
|
|
// 'test' vs 'test1' — prefix overlap AND edit distance 1. The prefix
|
||
|
|
// branch fires first and marks the pair, so we only see one entry.
|
||
|
|
const pairs = H.findConflicts(['test', 'test1'])
|
||
|
|
assert.equal(pairs.length, 1)
|
||
|
|
assert.equal(pairs[0].kind, 'prefix')
|
||
|
|
})
|
||
|
|
|
||
|
|
test('findConflicts: each pair emitted at most once even in noisy input', () => {
|
||
|
|
const pairs = H.findConflicts(['abcd', 'abce', 'abce', 'abcd'])
|
||
|
|
// Duplicates on the input list are de-duped in output via the pair-key set.
|
||
|
|
assert.equal(pairs.length, 1)
|
||
|
|
})
|
||
|
|
|
||
|
|
test('editDistance: sanity — matches known cases', () => {
|
||
|
|
assert.equal(H.editDistance('kitten', 'sitting'), 3)
|
||
|
|
assert.equal(H.editDistance('bash', 'bash'), 0)
|
||
|
|
assert.equal(H.editDistance('', 'abc'), 3)
|
||
|
|
})
|