/** * earth-now.co * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai * File: apps/ingest/test/noaa-co2.test.ts * Purpose: NOAA GML co2_mm_mlo parser/fetcher tests — fixture-backed incl. missing month + truncated file, no network */ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; import type { FetchLike } from "../src/fetch-like"; import { NOAA_CO2_URL, fetchNoaaCo2, parseNoaaMonthlyCo2 } from "../src/sources/noaa-co2"; const fixture = (name: string): string => readFileSync(new URL(`./fixtures/${name}`, import.meta.url), "utf8"); describe("parseNoaaMonthlyCo2", () => { const sample = fixture("co2_mm_mlo_sample.txt"); it("parses the sample: 16 data rows, one -9.99 missing month skipped → 15 observations", () => { const obs = parseNoaaMonthlyCo2(sample); expect(obs).toHaveLength(15); }); it("pins each observation to the 15th of the month UTC with the right value", () => { const obs = parseNoaaMonthlyCo2(sample); expect(obs[0]).toEqual({ time: "2024-01-15T00:00:00.000Z", value: 422.8 }); expect(obs[obs.length - 1]).toEqual({ time: "2025-04-15T00:00:00.000Z", value: 429.64 }); }); it("skips the missing month (2024-06, average -9.99) without inventing a value", () => { const obs = parseNoaaMonthlyCo2(sample); expect(obs.some((o) => o.time.startsWith("2024-06"))).toBe(false); // May and July around the gap are both present. expect(obs.some((o) => o.time.startsWith("2024-05"))).toBe(true); expect(obs.some((o) => o.time.startsWith("2024-07"))).toBe(true); }); it("THROWS on the truncated fixture (chosen degraded behavior — never feed partial files downstream)", () => { expect(() => parseNoaaMonthlyCo2(fixture("co2_mm_mlo_truncated.txt"))).toThrow( /expected 8 columns.*truncated/, ); }); it("throws when there are no data rows at all", () => { expect(() => parseNoaaMonthlyCo2("# only comments\n# nothing else\n")).toThrow( /no valid data rows/, ); }); it("throws on out-of-range year/month", () => { const bad = "1800 1 1800.04 350.00 350.00 31 0.4 0.1"; expect(() => parseNoaaMonthlyCo2(bad)).toThrow(/invalid year/); const badMonth = "2024 13 2024.99 420.00 420.00 31 0.4 0.1"; expect(() => parseNoaaMonthlyCo2(badMonth)).toThrow(/invalid month/); }); }); describe("fetchNoaaCo2", () => { it("fetches the NOAA URL and parses via an injected fetchImpl (no network)", async () => { const seen: string[] = []; const fetchImpl: FetchLike = async (url) => { seen.push(url); return { ok: true, status: 200, text: async () => fixture("co2_mm_mlo_sample.txt") }; }; const obs = await fetchNoaaCo2(fetchImpl); expect(obs).toHaveLength(15); expect(seen).toEqual([NOAA_CO2_URL]); }); it("throws on HTTP errors", async () => { const fetchImpl: FetchLike = async () => ({ ok: false, status: 500, text: async () => "" }); await expect(fetchNoaaCo2(fetchImpl)).rejects.toThrow(/HTTP 500/); }); });