diff --git a/apps/api/src/tenders/geo/nuts-bundesland.spec.ts b/apps/api/src/tenders/geo/nuts-bundesland.spec.ts new file mode 100644 index 0000000..2fae885 --- /dev/null +++ b/apps/api/src/tenders/geo/nuts-bundesland.spec.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from 'vitest'; +import { bundeslandFromRegion, nutsPrefixFor } from './nuts-bundesland'; + +/** + * nuts-bundesland.spec — RED-first (TDD) proof for FILTER-02 (Pitfall 1, + * Assumption A1). `bundesland` is 100% NULL in the live Tender data + * (verified: 0/1671 rows) because the Phase-10 normalizer deferred + * NUTS->Bundesland mapping to this phase. This spec locks the derivation + * that makes the Bundesland filter return real hits instead of a + * constant-zero result. + * + * Real region samples cross-checked against the live DB (verified via + * `docker exec tessera-ctl-db-1 psql`): + * DE212 (60 rows) -> Oberbayern -> Bayern + * DE300 (56 rows) -> Berlin + * DE600 (38 rows) -> Hamburg + * DE712 (34 rows) -> Darmstadt -> Hessen + * DEA22 (24 rows) -> Köln -> Nordrhein-Westfalen + */ +describe('bundeslandFromRegion', () => { + it('maps all 16 NUTS-1 prefixes (DE1..DEG) to their Bundesland name', () => { + expect(bundeslandFromRegion('DE1')).toBe('Baden-Württemberg'); + expect(bundeslandFromRegion('DE2')).toBe('Bayern'); + expect(bundeslandFromRegion('DE3')).toBe('Berlin'); + expect(bundeslandFromRegion('DE4')).toBe('Brandenburg'); + expect(bundeslandFromRegion('DE5')).toBe('Bremen'); + expect(bundeslandFromRegion('DE6')).toBe('Hamburg'); + expect(bundeslandFromRegion('DE7')).toBe('Hessen'); + expect(bundeslandFromRegion('DE8')).toBe('Mecklenburg-Vorpommern'); + expect(bundeslandFromRegion('DE9')).toBe('Niedersachsen'); + expect(bundeslandFromRegion('DEA')).toBe('Nordrhein-Westfalen'); + expect(bundeslandFromRegion('DEB')).toBe('Rheinland-Pfalz'); + expect(bundeslandFromRegion('DEC')).toBe('Saarland'); + expect(bundeslandFromRegion('DED')).toBe('Sachsen'); + expect(bundeslandFromRegion('DEE')).toBe('Sachsen-Anhalt'); + expect(bundeslandFromRegion('DEF')).toBe('Schleswig-Holstein'); + expect(bundeslandFromRegion('DEG')).toBe('Thüringen'); + }); + + it('maps real NUTS-3-level region samples from the live DB (Assumption A1 validation)', () => { + expect(bundeslandFromRegion('DE212')).toBe('Bayern'); + expect(bundeslandFromRegion('DE300')).toBe('Berlin'); + expect(bundeslandFromRegion('DE600')).toBe('Hamburg'); + expect(bundeslandFromRegion('DE712')).toBe('Hessen'); + expect(bundeslandFromRegion('DEA22')).toBe('Nordrhein-Westfalen'); + }); + + it('is case-insensitive on the NUTS prefix', () => { + expect(bundeslandFromRegion('de212')).toBe('Bayern'); + }); + + it('returns null for null/undefined/empty region — 17%+ of live rows have region=null, must never crash', () => { + expect(bundeslandFromRegion(null)).toBeNull(); + expect(bundeslandFromRegion(undefined)).toBeNull(); + expect(bundeslandFromRegion('')).toBeNull(); + }); + + it('returns null for an unrecognized/malformed prefix instead of throwing', () => { + expect(bundeslandFromRegion('XX123')).toBeNull(); + expect(bundeslandFromRegion('FR')).toBeNull(); + }); +}); + +describe('nutsPrefixFor', () => { + it('reverses a Bundesland name to its NUTS-1 prefix (for region startsWith filtering)', () => { + expect(nutsPrefixFor('Bayern')).toBe('DE2'); + expect(nutsPrefixFor('Nordrhein-Westfalen')).toBe('DEA'); + expect(nutsPrefixFor('Thüringen')).toBe('DEG'); + }); + + it('returns null for an unrecognized Bundesland name', () => { + expect(nutsPrefixFor('Nicht-Existent')).toBeNull(); + }); +});