import test from 'node:test';import assert from 'node:assert/strict'; import {COUNTRIES,IDS,normalizeRows,validYear,finite,latest,paired,pearson,growth,change,coverage,status,formatValue,escapeHTML,csvCell,toCSV,validateSession,timeline,compareCountries,crossCountryGap} from './core.js'; import {makeWorldBankURL,parseWorldBank} from './worldbank.js';import {createDemo} from './demo-data.js'; const A='SE.SEC.ENRR',B='IT.NET.USER.ZS';const rows=normalizeRows([ {iso3:'ARG',year:2020,indicator:A,value:100},{iso3:'ARG',year:2021,indicator:A,value:101},{iso3:'BOL',year:2020,indicator:A,value:80}, {iso3:'BOL',year:2021,indicator:A,value:82},{iso3:'ARG',year:2020,indicator:B,value:77},{iso3:'ARG',year:2021,indicator:B,value:80}, {iso3:'BOL',year:2020,indicator:B,value:45},{iso3:'BOL',year:2021,indicator:B,value:49}]); const mk=(iso3,year,indicator,value)=>({iso3,year,indicator,value}); test('country registry is unique and contains Bolivia and Paraguay',()=>{assert.equal(COUNTRIES.BOL,'Bolivia');assert.equal(COUNTRIES.PRY,'Paraguay');assert.equal(new Set(Object.keys(COUNTRIES)).size,16);}); test('8 indicators supported',()=>assert.equal(IDS.length,8)); test('years are constrained',()=>{assert.equal(validYear(2025),true);assert.equal(validYear(1980),false);assert.equal(validYear(2030),false);}); test('finite excludes Infinity and NaN',()=>{assert.equal(finite(Infinity),false);assert.equal(finite(0),true)}); test('normalize filters unexpected countries',()=>assert.equal(normalizeRows([mk('FAK',2020,A,1)]).length,0)); test('normalize filters unknown indicators',()=>assert.equal(normalizeRows([mk('ARG',2020,'FAKE',1)]).length,0)); test('normalize filters out-of-range year',()=>assert.equal(normalizeRows([mk('ARG',1980,A,1)]).length,0)); test('normalize filters missing values',()=>assert.equal(normalizeRows([mk('ARG',2020,A,null)]).length,0)); test('normalize preserves valid zero',()=>assert.equal(normalizeRows([mk('ARG',2020,A,0)])[0].value,0)); test('normalize de-duplicates country-year-indicator',()=>assert.equal(normalizeRows([mk('ARG',2020,A,2),mk('ARG',2020,A,3)]).length,1)); test('normalize sorts years',()=>assert.equal(normalizeRows([mk('ARG',2024,A,2),mk('ARG',2014,A,3)])[0].year,2014)); test('latest returns newest observation',()=>assert.equal(latest(rows,'ARG',A,2020,2022).year,2021)); test('latest does not forward-fill',()=>assert.equal(latest(rows,'ARG',A,2022,2024),null)); test('pairing uses exactly matching year and country',()=>assert.equal(paired(rows,A,B,['ARG','BOL'],2020,2021).length,4)); test('pairing excludes unmatched years',()=>assert.equal(paired(rows,A,B,['ARG'],2022,2023).length,0)); test('pearson returns null for insufficient data',()=>assert.equal(pearson([{x:1,y:2}], 'x','y'),null)); test('pearson is +1 for perfect positive association',()=>assert.ok(Math.abs(pearson([{x:1,y:2},{x:2,y:4},{x:3,y:6}], 'x','y')-1)<1e-9)); test('pearson is -1 for negative association',()=>assert.ok(Math.abs(pearson([{x:1,y:3},{x:2,y:2},{x:3,y:1}], 'x','y')+1)<1e-9)); test('pearson returns null for constant variable',()=>assert.equal(pearson([{x:1,y:2},{x:1,y:3},{x:1,y:4}],'x','y'),null)); test('growth is percent change, not percentage point difference',()=>assert.ok(Math.abs(growth(50,55)-10)<1e-10)); test('growth avoids zero denominator',()=>assert.equal(growth(0,2),null)); test('change tracks first and last real years',()=>assert.deepEqual(change({year:2020,value:10},{year:2022,value:13}),{amount:3,from:2020,to:2022})); test('coverage includes expected missing cells',()=>assert.deepEqual(coverage(rows,['ARG','BOL'],[A,B],2020,2022),{count:8,eligible:12,ratio:8/12})); test('status computes average reporting lag',()=>assert.equal(status(rows,['ARG'],[A],2020,2023).meanLag,2)); test('format value attaches valid units',()=>assert.match(formatValue(10.1,A),/%/)); test('html is escaped',()=>assert.equal(escapeHTML('