Spaces:
Running
Running
| /** | |
| * Node built-in test runner for pure GenerationReadinessPanel helpers. | |
| * Mirrors pure exports in GenerationReadinessPanel.tsx (kept isolated — no React). | |
| * Run: node --test src/components/project/GenerationReadinessPanel.node-test.mjs | |
| */ | |
| import { describe, it } from 'node:test'; | |
| import assert from 'node:assert/strict'; | |
| const MSP_LABELS = { | |
| mikro: 'Mikro', | |
| mala: 'Mała', | |
| srednia: 'Średnia', | |
| duza: 'Duża', | |
| unknown: 'Nieustalony', | |
| }; | |
| function formatMspLabel(status) { | |
| const key = String(status || 'unknown').toLowerCase().trim(); | |
| return MSP_LABELS[key] || MSP_LABELS.unknown; | |
| } | |
| function formatEur(value) { | |
| if (value === null || value === undefined || Number.isNaN(Number(value))) { | |
| return 'brak danych'; | |
| } | |
| return `${Number(value).toLocaleString('pl-PL', { maximumFractionDigits: 0 })} EUR`; | |
| } | |
| function shouldShowEligibilitySection(readiness) { | |
| if (!readiness) return false; | |
| if (readiness.eligibility && typeof readiness.eligibility === 'object') return true; | |
| const missing = readiness.missing_fields || []; | |
| return missing.some((m) => m.category === 'eligibility'); | |
| } | |
| function isEligibilitySpineEnforced(readiness) { | |
| if (!readiness) return true; | |
| const elig = readiness.eligibility; | |
| if (readiness.eligibility_spine_enforced === false) return false; | |
| if (elig && typeof elig === 'object' && elig.spine_enabled === false) return false; | |
| return true; | |
| } | |
| function isEligibilityBlocking(readiness) { | |
| if (!readiness) return true; | |
| const elig = readiness.eligibility; | |
| if (!elig || typeof elig !== 'object') { | |
| return (readiness.missing_fields || []).some((m) => m.category === 'eligibility'); | |
| } | |
| if (Array.isArray(elig.blockers) && elig.blockers.length > 0) return true; | |
| if (!isEligibilitySpineEnforced(readiness)) { | |
| return false; | |
| } | |
| if (elig.ready_for_full_autopilot === true) return false; | |
| if (elig.ready_for_full_autopilot === false) return true; | |
| const mspStatus = String(elig.msp?.status || 'unknown').toLowerCase().trim(); | |
| if (mspStatus === 'unknown' || Boolean(elig.msp?.is_large)) return true; | |
| if (Boolean(elig.de_minimis?.exhausted)) return true; | |
| if (elig.de_minimis?.used_eur === null || elig.de_minimis?.used_eur === undefined) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| function eligibilityBannerMessage(readiness) { | |
| if (isEligibilityBlocking(readiness)) { | |
| return { | |
| tone: 'blocked', | |
| text: | |
| 'Full Autopilot zablokowany do czasu weryfikacji kwalifikowalności (fail-closed). ' + | |
| 'Brak wiarygodnego statusu MŚP lub de minimis nie jest traktowany jako „OK”.', | |
| }; | |
| } | |
| if (!isEligibilitySpineEnforced(readiness)) { | |
| return { | |
| tone: 'info', | |
| text: | |
| 'Spine kwalifikowalności w trybie informacyjnym (nie egzekwowany) — nie bramkuje Full Autopilot. ' + | |
| 'Status MŚP / de minimis poniżej tylko do wglądu.', | |
| }; | |
| } | |
| return { | |
| tone: 'ok', | |
| text: 'Spine kwalifikowalności OK — MŚP i de minimis nie blokują Full Autopilot.', | |
| }; | |
| } | |
| describe('formatMspLabel', () => { | |
| it('maps known statuses', () => { | |
| assert.equal(formatMspLabel('mikro'), 'Mikro'); | |
| assert.equal(formatMspLabel('duza'), 'Duża'); | |
| assert.equal(formatMspLabel('unknown'), 'Nieustalony'); | |
| }); | |
| it('fails closed on empty', () => { | |
| assert.equal(formatMspLabel(null), 'Nieustalony'); | |
| assert.equal(formatMspLabel(''), 'Nieustalony'); | |
| assert.equal(formatMspLabel('weird'), 'Nieustalony'); | |
| }); | |
| }); | |
| describe('formatEur', () => { | |
| it('formats numbers', () => { | |
| assert.match(formatEur(45000), /45\s?000/); | |
| assert.match(formatEur(0), /0/); | |
| }); | |
| it('fails closed on null/undefined/NaN', () => { | |
| assert.equal(formatEur(null), 'brak danych'); | |
| assert.equal(formatEur(undefined), 'brak danych'); | |
| assert.equal(formatEur(Number.NaN), 'brak danych'); | |
| }); | |
| }); | |
| describe('shouldShowEligibilitySection', () => { | |
| it('false when no readiness', () => { | |
| assert.equal(shouldShowEligibilitySection(null), false); | |
| assert.equal(shouldShowEligibilitySection({}), false); | |
| }); | |
| it('true when eligibility report present', () => { | |
| assert.equal( | |
| shouldShowEligibilitySection({ eligibility: { blockers: [] } }), | |
| true, | |
| ); | |
| }); | |
| it('true when missing_fields category eligibility', () => { | |
| assert.equal( | |
| shouldShowEligibilitySection({ | |
| missing_fields: [{ field: 'msp_status', label: 'MŚP', category: 'eligibility' }], | |
| }), | |
| true, | |
| ); | |
| }); | |
| it('false when only other missing categories', () => { | |
| assert.equal( | |
| shouldShowEligibilitySection({ | |
| missing_fields: [{ field: 'title', label: 'Tytuł', category: 'project' }], | |
| }), | |
| false, | |
| ); | |
| }); | |
| }); | |
| describe('isEligibilityBlocking (fail-closed)', () => { | |
| it('blocks when readiness null', () => { | |
| assert.equal(isEligibilityBlocking(null), true); | |
| }); | |
| it('blocks on eligibility missing_fields without report', () => { | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| missing_fields: [{ field: 'nip', label: 'NIP', category: 'eligibility' }], | |
| }), | |
| true, | |
| ); | |
| }); | |
| it('does not block on unrelated missing fields without report', () => { | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| missing_fields: [{ field: 'title', label: 'Tytuł', category: 'project' }], | |
| }), | |
| false, | |
| ); | |
| }); | |
| it('blocks on explicit blockers always', () => { | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility_spine_enforced: false, | |
| eligibility: { | |
| spine_enabled: false, | |
| ready_for_full_autopilot: true, | |
| blockers: ['Status duża firma'], | |
| msp: { status: 'duza', is_large: true }, | |
| de_minimis: { used_eur: 0 }, | |
| }, | |
| }), | |
| true, | |
| ); | |
| }); | |
| it('does not claim FA blocked when spine not enforced (no blockers)', () => { | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility_spine_enforced: false, | |
| eligibility: { | |
| spine_enabled: false, | |
| ready_for_full_autopilot: false, | |
| blockers: [], | |
| msp: { status: 'unknown' }, | |
| de_minimis: {}, | |
| }, | |
| }), | |
| false, | |
| ); | |
| }); | |
| it('honors ready_for_full_autopilot when enforced', () => { | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility_spine_enforced: true, | |
| eligibility: { | |
| ready_for_full_autopilot: true, | |
| blockers: [], | |
| msp: { status: 'mikro' }, | |
| de_minimis: { used_eur: 10_000, exhausted: false }, | |
| }, | |
| }), | |
| false, | |
| ); | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility_spine_enforced: true, | |
| eligibility: { | |
| ready_for_full_autopilot: false, | |
| blockers: [], | |
| msp: { status: 'mikro' }, | |
| de_minimis: { used_eur: 10_000 }, | |
| }, | |
| }), | |
| true, | |
| ); | |
| }); | |
| it('fail-closed heuristics when ready flag absent', () => { | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility: { | |
| blockers: [], | |
| msp: { status: 'unknown' }, | |
| de_minimis: { used_eur: 0 }, | |
| }, | |
| }), | |
| true, | |
| ); | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility: { | |
| blockers: [], | |
| msp: { status: 'mikro', is_large: true }, | |
| de_minimis: { used_eur: 0 }, | |
| }, | |
| }), | |
| true, | |
| ); | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility: { | |
| blockers: [], | |
| msp: { status: 'mikro' }, | |
| de_minimis: { used_eur: 300_000, exhausted: true }, | |
| }, | |
| }), | |
| true, | |
| ); | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility: { | |
| blockers: [], | |
| msp: { status: 'mikro' }, | |
| de_minimis: { used_eur: null }, | |
| }, | |
| }), | |
| true, | |
| ); | |
| }); | |
| it('treats used_eur 0 as known (not missing)', () => { | |
| assert.equal( | |
| isEligibilityBlocking({ | |
| eligibility: { | |
| blockers: [], | |
| msp: { status: 'srednia' }, | |
| de_minimis: { used_eur: 0, remaining_eur: 300_000, exhausted: false }, | |
| }, | |
| }), | |
| false, | |
| ); | |
| }); | |
| }); | |
| describe('eligibilityBannerMessage', () => { | |
| it('blocked tone on blockers', () => { | |
| const b = eligibilityBannerMessage({ | |
| eligibility: { blockers: ['x'], msp: { status: 'duza' } }, | |
| }); | |
| assert.equal(b.tone, 'blocked'); | |
| assert.match(b.text, /fail-closed/i); | |
| }); | |
| it('info tone when spine not enforced', () => { | |
| const b = eligibilityBannerMessage({ | |
| eligibility_spine_enforced: false, | |
| eligibility: { | |
| spine_enabled: false, | |
| blockers: [], | |
| msp: { status: 'unknown' }, | |
| }, | |
| }); | |
| assert.equal(b.tone, 'info'); | |
| assert.match(b.text, /informacyjnym/i); | |
| }); | |
| it('ok tone when ready', () => { | |
| const b = eligibilityBannerMessage({ | |
| eligibility_spine_enforced: true, | |
| eligibility: { | |
| ready_for_full_autopilot: true, | |
| blockers: [], | |
| msp: { status: 'mikro' }, | |
| de_minimis: { used_eur: 1 }, | |
| }, | |
| }); | |
| assert.equal(b.tone, 'ok'); | |
| }); | |
| }); | |