ms-ai-architect/tests/kb-update/test-taxonomy.test.mjs
Kjell Tore Guttormsen 8bea3a224a feat(ms-ai-architect): Sesjon 2 lag 0 - konsolider taksonomi til en sannhetskilde
Ny data/domain-taxonomy.json (tracket via gitignore-negasjon; generert
registry/rapporter forblir ignorert) konsoliderer de 4 divergerende
taksonomiene: (a) sitemap-prefikser, (b) discover INCLUDE/EXCLUDE,
(c) category->skill, (d) getFilePriority. lib/taxonomy.mjs laster + kompilerer.

Refaktor: poll-sitemaps, discover-new-urls, report-changes LESER taksonomien
(ingen embeddede kopier). Kriterium grep TARGET_PREFIXES=[ == 0 oppfylt.

Konsolideringsfunn:
- category-skill-map.json stale i 4 entries (copilot-extensibility,
  monitoring-observability, performance-scalability, prompt-engineering ->
  sa engineering; disk sier advisor/governance/security/advisor). DISK er
  kanon. Map-en er ukonsumert av kode (kun README) -> flagget, ikke rort.
- discover fikk poll-paritet (12 -> 18 sitemap-prefikser).
- skill utledes na fra kanonisk category_skill -> ingen divergens (invariant-test).

Adferdsbevarende: 0 mismatch old vs ny prioritetslogikk pa 51 endrede filer;
discover sine hardkodede skill-verdier matchet allerede disk.
Tester: validate 239, kb-update 49 (+7 taksonomi), kb-eval 13 - alle gronne.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REiKFhP4w6xGXXqWKpPCJJ
2026-06-19 21:02:59 +02:00

125 lines
6.2 KiB
JavaScript

// tests/kb-update/test-taxonomy.test.mjs
// Unit tests for scripts/kb-update/lib/taxonomy.mjs + data/domain-taxonomy.json.
// The taxonomy is the single source of truth (lag 0) consolidating the four
// previously-divergent taxonomies: poll TARGET_PREFIXES, discover INCLUDE/EXCLUDE,
// category-skill-map.json, and report-changes getFilePriority. These tests pin the
// behaviour the refactored scripts must reproduce, plus the no-divergence invariant.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
loadTaxonomy,
getSitemapPrefixes,
getCategorySkill,
makeClassifier,
makePriorityFn,
} from '../../scripts/kb-update/lib/taxonomy.mjs';
const tax = loadTaxonomy();
// --- (a) sitemap prefixes — poll/discover parity ---
test('sitemap_prefixes — 18 entries with poll/discover parity', () => {
const prefixes = getSitemapPrefixes(tax);
assert.equal(prefixes.length, 18);
// poll superset includes the 6 that discover previously lacked
for (const p of ['microsoftteams_en-us_', 'sharepoint_en-us_', 'microsoft-365_en-us_',
'training_en-us_', 'cloud-computing_en-us_', 'privacy_en-us_']) {
assert.ok(prefixes.includes(p), `missing prefix ${p}`);
}
assert.ok(prefixes.includes('azure_en-us_'));
// dotnet is deliberately excluded (75 sitemaps, 12 matches)
assert.ok(!prefixes.includes('dotnet_en-us_'));
});
// --- (c) category → skill — PHYSICAL DISK is canonical ---
test('category_skill — disk-truth canonical (the 4 formerly stale entries)', () => {
// category-skill-map.json had these as ms-ai-engineering; disk says otherwise.
assert.equal(getCategorySkill(tax, 'monitoring-observability'), 'ms-ai-governance');
assert.equal(getCategorySkill(tax, 'copilot-extensibility'), 'ms-ai-advisor');
assert.equal(getCategorySkill(tax, 'prompt-engineering'), 'ms-ai-advisor');
assert.equal(getCategorySkill(tax, 'performance-scalability'), 'ms-ai-security');
// unchanged ones
assert.equal(getCategorySkill(tax, 'rag-architecture'), 'ms-ai-engineering');
assert.equal(getCategorySkill(tax, 'responsible-ai'), 'ms-ai-governance');
assert.equal(getCategorySkill(tax, 'cost-optimization'), 'ms-ai-security');
// unknown category → null
assert.equal(getCategorySkill(tax, 'does-not-exist'), null);
});
// --- (b) classifyUrl — reproduces discover's classification exactly ---
test('classifyUrl — include rules map to correct {skill, category}', () => {
const classify = makeClassifier(tax);
assert.deepEqual(
classify('https://learn.microsoft.com/azure/ai-foundry/openai/how-to/manage-costs'),
{ skill: 'ms-ai-engineering', category: 'azure-ai-services' }
);
// monitoring → governance (was a divergence; derivation reproduces discover's original)
assert.deepEqual(
classify('https://learn.microsoft.com/azure/azure-monitor/logs/foo'),
{ skill: 'ms-ai-governance', category: 'monitoring-observability' }
);
// copilot-studio → advisor (was a divergence; derivation reproduces discover's original)
assert.deepEqual(
classify('https://learn.microsoft.com/microsoft-copilot-studio/fundamentals-what-is'),
{ skill: 'ms-ai-advisor', category: 'copilot-extensibility' }
);
// alternation pattern
assert.deepEqual(
classify('https://learn.microsoft.com/security/benchmark/azure/foo'),
{ skill: 'ms-ai-security', category: 'ai-security-engineering' }
);
});
test('classifyUrl — exclude wins over include and non-matches return null', () => {
const classify = makeClassifier(tax);
// /samples/ excluded even though /azure/ai-foundry/ would include
assert.equal(classify('https://learn.microsoft.com/azure/ai-foundry/samples/quickstart'), null);
// /training/ excluded
assert.equal(classify('https://learn.microsoft.com/training/modules/intro'), null);
// unrelated path
assert.equal(classify('https://learn.microsoft.com/azure/virtual-machines/overview'), null);
});
test('INVARIANT — every include category resolves and skill === category_skill[category]', () => {
const classify = makeClassifier(tax);
for (const rule of tax.relevance.include) {
const skill = getCategorySkill(tax, rule.category);
assert.ok(skill, `include category ${rule.category} absent from category_skill`);
}
// probe each rule via a synthetic URL and assert no skill/category divergence
const probes = [
['https://learn.microsoft.com/azure/ai-foundry/x', 'azure-ai-services'],
['https://learn.microsoft.com/azure/machine-learning/x', 'mlops-genaiops'],
['https://learn.microsoft.com/azure/search/x', 'rag-architecture'],
['https://learn.microsoft.com/azure/azure-monitor/x', 'monitoring-observability'],
['https://learn.microsoft.com/azure/well-architected/x', 'architecture'],
['https://learn.microsoft.com/microsoft-copilot-studio/x', 'copilot-extensibility'],
['https://learn.microsoft.com/purview/x', 'responsible-ai'],
['https://learn.microsoft.com/agent-framework/x', 'agent-orchestration'],
['https://learn.microsoft.com/azure/cosmos-db/x', 'data-engineering'],
];
for (const [url, expectedCat] of probes) {
const r = classify(url);
assert.ok(r, `no classification for ${url}`);
assert.equal(r.category, expectedCat);
assert.equal(r.skill, getCategorySkill(tax, r.category), `divergence at ${url}`);
}
});
// --- (d) getFilePriority — reproduces report-changes ordering exactly ---
test('getFilePriority — pattern → priority, first match wins', () => {
const prio = makePriorityFn(tax);
assert.equal(prio('skills/ms-ai-security/references/cost-optimization/budget.md'), 'critical');
assert.equal(prio('skills/ms-ai-governance/references/responsible-ai/fairness.md'), 'high');
// 'governance' substring → high
assert.equal(prio('skills/ms-ai-governance/references/norwegian-public-sector-governance/x.md'), 'high');
assert.equal(prio('skills/ms-ai-advisor/references/platforms/copilot-studio.md'), 'medium');
// no pattern → low
assert.equal(prio('skills/ms-ai-engineering/references/data-engineering/cosmos.md'), 'low');
});
test('getFilePriority — order: cost (critical) outranks governance (high)', () => {
const prio = makePriorityFn(tax);
// contains both 'cost' and 'governance' — critical rule is evaluated first
assert.equal(prio('skills/x/references/cost-governance-tradeoffs.md'), 'critical');
});