test(11-03): add failing spec for CPV-Divisions-Katalog + normalizeCpv
RED-first (TDD): normalizeCpv/divisionOf/cpvMatchesDivisions across the
three inconsistent live-DB CPV formats ("45", "45000000", "45000000-7")
plus a CPV_DIVISIONS catalog shape check — module does not exist yet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
CPV_DIVISIONS,
|
||||
cpvMatchesDivisions,
|
||||
divisionOf,
|
||||
normalizeCpv,
|
||||
} from './cpv-catalog';
|
||||
|
||||
/**
|
||||
* cpv-catalog.spec — RED-first (TDD) proof for FILTER-03 (D-03, Pitfall 2).
|
||||
*
|
||||
* Core test: cpvCodes arrive in the live DB in mixed formats ("45",
|
||||
* "45000000", "45000000-7") — the normalizer/matcher must handle all three
|
||||
* via two-sided normalization down to the leading 2-digit CPV division,
|
||||
* never an exact-equality match against a single canonical shape.
|
||||
*/
|
||||
|
||||
describe('normalizeCpv', () => {
|
||||
it('strips the check-digit suffix: "45000000-7" -> "45000000"', () => {
|
||||
expect(normalizeCpv('45000000-7')).toBe('45000000');
|
||||
});
|
||||
|
||||
it('passes through an already-8-digit code unchanged', () => {
|
||||
expect(normalizeCpv('45000000')).toBe('45000000');
|
||||
});
|
||||
|
||||
it('passes through a bare 2-digit division code unchanged', () => {
|
||||
expect(normalizeCpv('45')).toBe('45');
|
||||
});
|
||||
|
||||
it('strips non-digit characters generally, not only a trailing dash-digit', () => {
|
||||
expect(normalizeCpv('45000000-7 ')).toBe('45000000');
|
||||
});
|
||||
|
||||
it('returns an empty string for null/empty input, never throws', () => {
|
||||
expect(normalizeCpv('')).toBe('');
|
||||
expect(normalizeCpv(null as unknown as string)).toBe('');
|
||||
expect(normalizeCpv(undefined as unknown as string)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('divisionOf', () => {
|
||||
it('extracts the leading 2 digits from an 8-digit code', () => {
|
||||
expect(divisionOf('45000000')).toBe('45');
|
||||
});
|
||||
|
||||
it('is a no-op for an already-2-digit division code', () => {
|
||||
expect(divisionOf('45')).toBe('45');
|
||||
});
|
||||
|
||||
it('extracts the leading 2 digits from a raw code with a check-digit suffix', () => {
|
||||
expect(divisionOf('45000000-7')).toBe('45');
|
||||
});
|
||||
|
||||
it('returns an empty string for empty/invalid input, never throws', () => {
|
||||
expect(divisionOf('')).toBe('');
|
||||
expect(divisionOf('-')).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cpvMatchesDivisions', () => {
|
||||
it('matches when any code in the array normalizes into a selected division', () => {
|
||||
expect(
|
||||
cpvMatchesDivisions(['45000000-7', '71000000'], ['45']),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('matches a bare 2-digit division code against itself', () => {
|
||||
expect(cpvMatchesDivisions(['45'], ['45'])).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when no code matches any selected division', () => {
|
||||
expect(cpvMatchesDivisions(['71000000', '90500000'], ['45'])).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for an empty codes array, never throws', () => {
|
||||
expect(cpvMatchesDivisions([], ['45'])).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when no divisions are selected', () => {
|
||||
expect(cpvMatchesDivisions(['45000000'], [])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CPV_DIVISIONS catalog', () => {
|
||||
it('contains the Bauarbeiten division (45) with a German label', () => {
|
||||
const entry = CPV_DIVISIONS.find((d) => d.code === '45');
|
||||
expect(entry).toBeDefined();
|
||||
expect(entry?.label).toBe('Bauarbeiten');
|
||||
});
|
||||
|
||||
it('every entry has a 2-digit numeric code and a non-empty label', () => {
|
||||
expect(CPV_DIVISIONS.length).toBeGreaterThan(30);
|
||||
for (const entry of CPV_DIVISIONS) {
|
||||
expect(entry.code).toMatch(/^\d{2}$/);
|
||||
expect(entry.label.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('has no duplicate codes', () => {
|
||||
const codes = CPV_DIVISIONS.map((d) => d.code);
|
||||
expect(new Set(codes).size).toBe(codes.length);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user