SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
6 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
40.1 KB · 490 lines typescript
Raw Blame History
1import { describe, expect, it } from "vitest";2import type { ConnectorContext, RawDocument } from "@dci/connectors";3import { extractAnnouncement, parseAllMoney, parseExpectedOpening, parsePhaseCount, projectMoneyFigures, qualifiesAsProject } from "./extract-project.js";4import { detectOperators, OPERATORS } from "./operators-lexicon.js";5import { detectLocation } from "./locations-lexicon.js";6import { newsArticleParser } from "./article-parser.js";7import { detectDecision, planningPdfParser } from "./planning-pdf-parser.js";8import { edgarFtsParser } from "./edgar.js";910const NOW = new Date("2026-09-11T12:00:00Z");11const x = (title: string, text: string, publishedAt = "2026-09-10") => extractAnnouncement(title, text, { publishedAt, now: NOW });1213describe("announcement → project extraction", () => {14  it("1. Vantage Frederick: MW, USD, city/state, operator, status, phase date", () => {15    const a = x(16      "Vantage Data Centers announces 192MW campus in Frederick, Maryland with $2 billion investment; first phase slated for Q2 2027",17      "DENVER, September 10, 2026 — Vantage Data Centers, a leading global provider of hyperscale data center campuses, today announced plans for a new 192MW campus in Frederick, Maryland. The company will invest more than $2 billion in the site. The first phase is slated for Q2 2027, with the remaining buildings delivered in two additional phases.",18    );19    expect(a.headlineMw).toBe(192);20    expect(a.money).toEqual({ amount: 2_000_000_000, currency: "USD" });21    expect(a.investmentUsd).toBe(2_000_000_000);22    expect(a.location?.city).toBe("Frederick");23    expect(a.location?.region).toBe("Maryland");24    expect(a.location?.country).toBe("US");25    expect(a.operator?.name).toBe("Vantage Data Centers");26    expect(a.status).toBe("announced");27    expect(a.expectedOpening).toBe("2027-Q2");28    expect(a.projectName).toBe("Vantage Data Centers Frederick campus");29    expect(qualifiesAsProject(a)).toBe(true);30  });3132  it("2. Loudoun approval: approved > other statuses, acreage in acres, RFS date beats construction start", () => {33    const a = x(34      "Loudoun County approves rezoning for 300 MW Aligned data center campus in Leesburg",35      "The Loudoun County Board of Supervisors voted 7-2 on Tuesday to approve the rezoning of a 118-acre site along Sycolin Road for a data center campus proposed by Aligned Data Centers. Construction is expected to begin in 2026. The first buildings are expected to be ready for service in 2028.",36    );37    expect(a.status).toBe("approved");38    expect(a.headlineMw).toBe(300);39    expect(a.acreage).toBe(118);40    expect(a.operator?.name).toBe("Aligned Data Centers");41    expect(a.location?.city).toBe("Leesburg");42    expect(a.location?.country).toBe("US");43    expect(a.expectedOpening).toBe("2028");44    expect(qualifiesAsProject(a)).toBe(true);45  });4647  it("3. Microsoft groundbreaking: under_construction, $1bn short form, City, State", () => {48    const a = x("Microsoft breaks ground on $1bn data centre in Mount Pleasant, Wisconsin", "Microsoft has broken ground on its $1bn data centre in Mount Pleasant, Wisconsin, on land once earmarked for Foxconn. The hyperscaler said the facility would support its cloud and AI services.");49    expect(a.status).toBe("under_construction");50    expect(a.money).toEqual({ amount: 1_000_000_000, currency: "USD" });51    expect(a.location).toMatchObject({ city: "Mount Pleasant", region: "Wisconsin", country: "US" });52    expect(a.operator?.name).toBe("Microsoft");53    expect(a.headlineMw).toBeNull();54    expect(qualifiesAsProject(a)).toBe(true); // investment ≥ $50M55  });5657  it("4. Equinix opening: operational facility is not a pipeline project", () => {58    const a = x("Equinix opens 20MW PA11 data center in Paris", "Equinix has opened PA11, its newest International Business Exchange (IBX) data center in Paris, France. The 20MW facility adds 2,300 cabinets of capacity to the Paris metro.");59    expect(a.status).toBe("operational");60    expect(a.headlineMw).toBe(20);61    expect(a.location).toMatchObject({ city: "Paris", country: "FR" });62    expect(a.operator?.name).toBe("Equinix");63    expect(qualifiesAsProject(a)).toBe(false);64  });6566  it("5. Google Eemshaven: EUR money stays out of investmentUsd, expansion event type", () => {67    const a = x("Google to invest €1 billion in expansion of Eemshaven data center campus in the Netherlands", "Google will invest €1 billion to expand its data center campus in Eemshaven, the Netherlands, adding an additional 100MW of capacity. The expansion is expected to be operational in 2027.");68    expect(a.money).toEqual({ amount: 1_000_000_000, currency: "EUR" });69    expect(a.investmentUsd).toBeNull();70    expect(a.investmentUsdApprox).toBeGreaterThan(50_000_000);71    expect(a.headlineMw).toBe(100);72    expect(a.pageType).toBe("expansion");73    expect(a.eventType).toBe("expansion_announced");74    expect(a.location).toMatchObject({ city: "Eemshaven", country: "NL" });75    expect(a.operator?.name).toBe("Google");76    expect(a.expectedOpening).toBe("2027");77    expect(qualifiesAsProject(a)).toBe(true);78  });7980  it("6. Slough planning application: permitting, hectares → acres, GB city, Yondr", () => {81    const a = x("Planning application lodged for 90MW data centre in Slough", "Yondr Group has submitted plans to Slough Borough Council for a 90MW data centre on an 8.5 hectare site off the A4. If approved, the facility is expected to be operational by 2028.");82    expect(a.status).toBe("permitting");83    expect(a.headlineMw).toBe(90);84    expect(a.acreage).toBe(21);85    expect(a.location).toMatchObject({ city: "Slough", country: "GB" });86    expect(a.operator?.name).toBe("Yondr");87    expect(a.expectedOpening).toBe("2028");88    expect(a.pageType).toBe("planning_document");89    expect(qualifiesAsProject(a)).toBe(true);90  });9192  it("7. AWS Mississippi: state-only location, 'come online' is a plan not an operational status", () => {93    const a = x("AWS plans $10 billion investment in Mississippi data centers", "Amazon Web Services will build two data center complexes in Madison County, Mississippi, the largest single capital investment in the state's history. The company said the facilities would come online in 2027.");94    expect(a.operator?.name).toBe("Amazon Web Services");95    expect(a.money).toEqual({ amount: 10_000_000_000, currency: "USD" });96    expect(a.location?.region).toBe("Mississippi");97    expect(a.location?.country).toBe("US");98    expect(a.status).toBe("announced");99    expect(a.expectedOpening).toBe("2027");100    expect(qualifiesAsProject(a)).toBe(true);101  });102103  it("8. Meta Zeewolde: cancelled beats everything, ambiguous 'Meta' resolved by verb lookahead", () => {104    const a = x("Meta scraps plans for Zeewolde hyperscale data centre", "Meta has scrapped plans for a 200MW hyperscale data centre in Zeewolde after the Dutch senate moved to block the project. The company had announced the campus in 2021.");105    expect(a.status).toBe("cancelled");106    expect(a.operator?.name).toBe("Meta");107    expect(a.location).toMatchObject({ city: "Zeewolde", country: "NL" });108    expect(qualifiesAsProject(a)).toBe(false);109  });110111  it("9. Riot Corsicana: GW → MW, phase count in words", () => {112    const a = x("Riot Platforms to develop 1 GW AI data center campus in Corsicana, Texas across four phases", "Riot Platforms said it will develop the 1GW Corsicana campus in four phases, converting part of its bitcoin mining site into AI and HPC data halls.");113    expect(a.headlineMw).toBe(1000);114    expect(a.phaseCount).toBe(4);115    expect(a.location).toMatchObject({ city: "Corsicana", region: "Texas", country: "US" });116    expect(a.operator?.name).toBe("Riot Platforms");117    expect(a.status).toBe("announced");118  });119120  it("10. CyrusOne Frankfurt: approved, completion date preferred over construction start", () => {121    const a = x("CyrusOne secures approval for 240 MW Frankfurt campus", "CyrusOne has secured planning approval for a 240 MW campus in Frankfurt, Germany. Construction is scheduled to begin in 2026, with completion targeted for late 2028.");122    expect(a.status).toBe("approved");123    expect(a.headlineMw).toBe(240);124    expect(a.location).toMatchObject({ city: "Frankfurt", country: "DE" });125    expect(a.operator?.name).toBe("CyrusOne");126    expect(a.expectedOpening).toBe("2028");127  });128129  it("11. Data4 Didcot: GBP money kept as original currency", () => {130    const a = x("Data4 to invest £1.2bn in 200MW UK campus in Didcot", "French operator Data4 plans to invest £1.2bn in a new 200MW campus in Didcot, Oxfordshire, its first in the United Kingdom.");131    expect(a.money).toEqual({ amount: 1_200_000_000, currency: "GBP" });132    expect(a.investmentUsd).toBeNull();133    expect(a.headlineMw).toBe(200);134    expect(a.location).toMatchObject({ city: "Didcot", country: "GB" });135    expect(a.operator?.name).toBe("Data4");136    expect(qualifiesAsProject(a)).toBe(true);137  });138139  it("12. off-topic real-estate story is not a data center project", () => {140    const a = x("Canadian firm raises £680m for 6,000-home UK build-to-rent push", "The developer plans a campus-style residential scheme in Manchester and said the first homes would be delivered in 2028. The 40MW figure quoted by the council refers to the district heating network.");141    expect(a.relevance).not.toBe("strong");142    expect(qualifiesAsProject(a)).toBe(false);143  });144145  it("13. rumored site near Des Moines", () => {146    const a = x("Microsoft reportedly eyeing 500MW site near Des Moines, Iowa", "Microsoft is reportedly in talks to acquire a 500MW site near Des Moines, Iowa, according to local officials.");147    expect(a.status).toBe("rumored");148    expect(a.location).toMatchObject({ city: "Des Moines", region: "Iowa", country: "US" });149    expect(a.headlineMw).toBe(500);150  });151152  it("14. delayed campus keeps its delayed status", () => {153    const a = x("Aligned delays 180MW Phoenix campus amid power constraints", "Aligned Data Centers has pushed back the delivery of its 180MW Phoenix campus to 2028 as the utility works through grid interconnection constraints.");154    expect(a.status).toBe("delayed");155    expect(a.operator?.name).toBe("Aligned Data Centers");156    expect(a.location).toMatchObject({ city: "Phoenix", region: "Arizona", country: "US" });157  });158159  it("15. explicit project name and large acreage gate", () => {160    const a = x("Prince William Digital Gateway wins final approval for 2,100 acres of data centers", "The Prince William Digital Gateway, developed by QTS and Compass Datacenters, was approved by the Prince William Board of County Supervisors early Wednesday. The 2,100-acre corridor along Pageland Lane could host up to 27 million square feet of data centers.");161    expect(a.explicitName).toBe("Prince William Digital Gateway");162    expect(a.projectName).toBe("Prince William Digital Gateway");163    expect(a.status).toBe("approved");164    expect(a.acreage).toBe(2100);165    expect(a.operators.map((o) => o.name)).toEqual(expect.arrayContaining(["QTS", "Compass Datacenters"]));166    expect(a.location?.country).toBe("US");167    expect(qualifiesAsProject(a)).toBe(true);168  });169170  it("16. 'phase 1 of 3' and construction start in Toronto", () => {171    const a = x("STACK Infrastructure begins construction on phase 1 of 3 at 250MW Toronto campus", "STACK Infrastructure has started construction on the first of three buildings at its 250MW campus in Toronto, Ontario.");172    expect(a.phaseCount).toBe(3);173    expect(a.status).toBe("under_construction");174    expect(a.location).toMatchObject({ city: "Toronto", country: "CA" });175    expect(a.operator?.name).toBe("STACK Infrastructure");176  });177178  it("17. title gives the state, lead gives the county — merged location", () => {179    const a = x("Amazon plans $650 million data center in Pennsylvania", "Amazon will build the facility in Salem Township, Luzerne County, Pennsylvania, next to the Susquehanna nuclear plant, under a power purchase agreement with Talen Energy.");180    expect(a.operator?.name).toBe("Amazon Web Services");181    expect(a.location?.region).toBe("Pennsylvania");182    expect(a.location?.country).toBe("US");183    expect(a.location?.city).toBe("Luzerne County");184    expect(a.money).toEqual({ amount: 650_000_000, currency: "USD" });185  });186187  it("18. market statistics above 20 GW are not a project figure; per-MWh prices are not investments", () => {188    const a = x("US data center capacity to hit 50GW by 2030, report says", "The report estimates US data center capacity will reach 50GW by 2030. Power prices of $45/MWh were assumed. Equinix's new 36MW Dallas facility was cited as an example.");189    expect(a.headlineMw).toBe(36);190    expect(a.money).toBeNull();191  });192});193194describe("helpers", () => {195  describe("precision pass 2026-09-11 (hand-classified false positives)", () => {196    it("appointments, awards, interviews, events and leadership changes never yield a project", () => {197      for (const [t, body] of [198        ["AirTrunk appoints Laura Coad as Chief Data Centre Officer (CDCO)", "Asia Pacific & Japan (APJ) hyperscale data centre specialist, AirTrunk today announced the executive appointment of Laura Coad as Chief Data Centre Officer, responsible for 1.4GW of capacity across the region."],199        ["AirTrunk recognised alongside Blackstone at Mergermarket M&A Awards", "AirTrunk, the hyperscale data centre specialist, was recognised at the awards for the 300MW SYD1 campus transaction."],200        ["AITech interview with COO Erling Guðmundsson - atNorth", "How AI impacts digital infrastructure and how atNorth is shaping a sustainable future with its 60MW data center campus plans."],201        ["Women in Construction and Why It Matters: CyrusOne Hosts Inaugural Event in Madrid", "New 90 megawatt campus places emphasis on biodiversity, sustainability and giving back to the local community. CyrusOne plans to build the campus in phases."],202        ["Cassava Technologies Announces Leadership Changes To Accelerate Growth", "Cassava Technologies announces new strategic appointments to the leadership team as the group plans a 6MW data centre expansion in Nairobi."],203      ] as Array<[string, string]>) {204        const a = x(t, body);205        expect(a.nonProjectTitle, t).toBe(true);206        expect(qualifiesAsProject(a), t).toBe(false);207        expect(a.eventType, t).toBe("news");208      }209    });210    it("partnerships, hosting agreements, POP deployments, financing and market reports are not projects", () => {211      for (const [t, body] of [212        ["CyrusOne and E.ON Announce Strategic Partnership to Overcome Data Center Grid Capacity Constraints for Customers in Europe", "Local power generation system designed to deliver an additional 61 megawatts of electricity to CyrusOne's FRA7 data center by 2029, expanding the campus' total capacity to 126MW."],213        ["Duos Technologies Signs 55 MW AI Hosting Agreements with Axe Compute", "Five-year hosting agreements cover 55 MW of AI facility capacity. Agreements represent more than $500 million in base payments."],214        ["China Telecom Deploys POP At Africa Data Centres Johannesburg Facility", "Africa Data Centres has extended its partnership with China Telecom, which has deployed a point of presence at the 14MW Johannesburg data centre."],215        ["AirTrunk gears up for Asia-Pacific expansion with AUD $850 million financing", "AirTrunk has successfully completed an AUD $850 million financing process to fund a major expansion of its 90MW hyperscale data centre platform."],216        ["Data Center Chillers Market to Surpass $6.4 Billion by 2035", "The global data center chillers market is projected to grow from USD 2.8 billion in 2026 to USD 6.4 billion by 2035 at a 9.6% CAGR, propelled by escalating hyperscale data center construction."],217        ["Starcloud Raises $250m Series A Extension At $2.3bn Valuation For Orbital AI Data Centers", "Orbital data center company Starcloud has raised $250 million in a Series A extension, planning 20GW of orbital compute."],218        ["House Bill Would Put Federal Electricity Tax on Data Centers", "U.S. bill proposes 1¢/kWh tax on data center electricity over 1 MW, funding five federal programs."],219        ["Schneider Electric's $2.3 Billion in AI Power and Cooling Deals Sends Message to Data Center Sector", "Twin supply-capacity agreements by Schneider Electric with Switch and Digital Realty show how AI data centers are starting to lock up power and thermal infrastructure. Switch plans a 2MW pilot in Las Vegas."],220        ["Data Centre Developer Sues New Jersey Town for $300m", "Data centre developer Hexa Builders is seeking $300 million in damages from Monroe Township, New Jersey, after officials rejected its proposed 1 million sq ft data center."],221      ] as Array<[string, string]>) {222        const a = x(t, body);223        expect(qualifiesAsProject(a), t).toBe(false);224      }225    });226    it("a build headline overrides deal vocabulary: 'Vantage secures approval for 300MW campus' still qualifies", () => {227      const a = x("Vantage Data Centers secures planning approval for 300MW campus in Cardiff, Wales", "Vantage Data Centers has secured planning approval for a new 300MW data center campus in Cardiff. Construction of the first 48MW building begins in 2027.");228      expect(a.nonProjectTitle).toBe(false);229      expect(a.status).toBe("approved");230      expect(qualifiesAsProject(a)).toBe(true);231    });232    it("openings and 'we are online' headlines describe existing facilities, not pipeline projects", () => {233      for (const [t, body] of [234        ["AirTrunk opens Melbourne's largest hyperscale data centre campus", "A specialist in hyperscale data centres, AirTrunk today announced the opening of its second facility in the Asia-Pacific region, the 50MW MEL1 campus."],235        ["Utah County, We Are Online! - Meta Data Centers", "Today, we are excited to announce that the Eagle Mountain Data Center in Utah is now serving traffic. The 800 MW campus was announced in 2018 and construction began soon after."],236        ["DataBank Announces Opening of \"SLC5\" Salt Lake City Data Center", "DataBank announces the opening of its fifth Salt Lake City data center (SLC), a 66MW facility and the 20th data center the company has opened."],237        ["2MW Delivered in Serverfarm's Chicago Data Center", "Serverfarm has delivered an additional 2MW of capacity in its Chicago data center for a hyperscale customer."],238        ["AirTrunk opens AI-ready data centre in Malaysia, accelerating innovation and the energy transition", "JHB1 official opening ceremony attended by Malaysian government officials. AirTrunk has commenced operations at the 1.4GW-capable Johor campus, with plans to expand."],239      ] as Array<[string, string]>) {240        const a = x(t, body);241        expect(a.status, t).toBe("operational");242        expect(qualifiesAsProject(a), t).toBe(false);243      }244      // a future opening remains a plan245      expect(x("Equinix to open 20MW PA11 data center in Paris in 2027", "Equinix will open PA11, a 20MW facility, in Q2 2027.").status).toBe("announced");246      expect(x("Equinix opens 20MW PA11 data center in Paris", "Equinix has opened PA11.").status).toBe("operational");247    });248    it("portfolio totals are not the site figure: TOK2 (410MW in Japan, 1.2GW across APJ) and OSK1 (20MW)", () => {249      const a = x("AirTrunk scales up in Japan with announcement of second Tokyo hyperscale data centre", "TOK2 increases AirTrunk's capacity to over 410MW in Japan and 1.2GW across Asia Pacific & Japan (APJ). AirTrunk today announced TOK2, a new 110MW hyperscale data centre in Inzai, Tokyo.");250      expect(a.headlineMw).toBe(110);251      const b = x("AirTrunk breaks ground in Osaka", "Construction begins on 20+MW OSK1 in West Osaka. Asia-Pacific & Japan hyperscale data centre specialist, AirTrunk, has begun the construction of its 20 megawatt OSK1 data centre, bringing its capacity across the region to 1.4GW.");252      expect(b.headlineMw).toBe(20);253      expect(b.status).toBe("under_construction");254      expect(qualifiesAsProject(b)).toBe(true);255      // when the lead only quotes company totals, no lead figure is used256      expect(x("AirTrunk expands in Japan with new data centre development in Osaka", "OSK1 will be AirTrunk's third data centre in Japan. The company now operates over 1.4GW across Asia Pacific.").headlineMw).toBeNull();257    });258    it("names never start with a unit: '1.5 GW Texas Data Center' → generated name; '8,000 Acre AI Campus' → generated name", () => {259      const a = x("Prometheus' 1.5 GW Texas Data Center Plan Tests Private Power", "Prometheus Hyperscale plans an off-grid 1.5 GW Texas Data Center campus in Pecos, Texas. The 1.5 GW Texas Data Center would come online in 2027.");260      expect(a.explicitName).toBeNull();261      expect(a.projectName).not.toMatch(/^GW /);262      const b = x("Amazon Explores Grid Connection for 8,000 Acre AI Campus", "Amazon is exploring a grid connection for an 8,000 Acre AI Campus in Louisiana that would combine a private-generation complex with an AI data center. The 8,000 Acre AI Campus has drawn scrutiny.");263      expect(b.explicitName).toBeNull();264      expect(b.projectName).not.toMatch(/^Acre /);265    });266    it("an unknown headline subject keeps body operators out: Cerebras/G42, Greenfield/Google, JV/Dallas", () => {267      const a = x("Cerebras Begins Construction Of 165MW Data Center In Mikkeli, Finland", "Cerebras has begun construction on a 165MW data center campus in Mikkeli, Finland, with the first 50MW phase now underway. The campus is being developed with G42 as anchor customer.");268      expect(a.operator).toBeNull();269      expect(a.location?.country).toBe("FI"); // Mikkeli is not in the city lexicon: country only, never a guessed city270      expect(a.headlineMw).toBe(165);271      const b = x("Greenfield And Finsbury Infrastructure To Invest €360M In Milan Data Center", "UK infrastructure developer Greenfield and Finsbury Infrastructure plan to invest up to €360 million ($418m) to develop a 36MW data center east of Milan, Italy, near Google's existing campus.");272      expect(b.operator).toBeNull();273      expect(b.headlineMw).toBe(36);274      const c = x("JV Plans 54MW Data Center In Dallas, Texas", "A new 54MW data center is planned for the North Dallas Corridor, with Northampton Capital Partners and Provident Data Centers targeting a late 2027 launch.");275      expect(c.location?.city).toBe("Dallas");276      expect(c.projectName).toBe("Dallas data center project");277      // a known operator in the title is unaffected278      expect(x("Microsoft plans 300MW campus in Mount Pleasant, Wisconsin", "Microsoft plans a 300MW campus in Mount Pleasant, Wisconsin.").operator?.name).toBe("Microsoft");279    });280  });281282  it("parseAllMoney keeps the largest first and ignores small figures", () => {283    expect(parseAllMoney("a $2 billion project with a $150 million first phase and $25/MWh power")).toEqual([{ amount: 2_000_000_000, currency: "USD" }, { amount: 150_000_000, currency: "USD" }]);284  });285  it("parsePhaseCount", () => {286    expect(parsePhaseCount("delivered in three phases")).toBe(3);287    expect(parsePhaseCount("phase 2 of 5")).toBe(5);288    expect(parsePhaseCount("a two-phase development")).toBe(2);289    expect(parsePhaseCount("the first phase")).toBeNull();290  });291  it("parseExpectedOpening prefers opening sentences and honours year bounds", () => {292    expect(parseExpectedOpening("Construction will begin in 2026. The campus is expected to open in the second half of 2027.", { minYear: 2026, maxYear: 2041 }).value).toBe("2027-H2");293    expect(parseExpectedOpening("The company was founded in 2010 and expects to complete the build in 2009.", { minYear: 2026, maxYear: 2041 }).value).toBeNull();294  });295  it("operator lexicon has ≥ 100 operators and does not match ordinary words", () => {296    expect(OPERATORS.length).toBeGreaterThanOrEqual(100);297    expect(detectOperators("Please switch the meta description to apple pie")).toEqual([]);298    expect(detectOperators("Switch to build 1GW campus").map((o) => o.name)).toEqual(["Switch"]);299  });300  it("location lexicon: City, ST pattern, Georgia is the US state, Latin America is not the US", () => {301    expect(detectLocation("a site in Pryor, OK")).toMatchObject({ city: "Pryor", region: "Oklahoma", country: "US" });302    expect(detectLocation("Meta's campus in Georgia")).toMatchObject({ region: "Georgia", country: "US" });303    expect(detectLocation("expansion across Latin America")).toBeNull();304    expect(detectLocation("Dublin, Ohio campus")).toMatchObject({ city: "Dublin", region: "Ohio", country: "US" });305    expect(detectLocation("a campus in Dublin")).toMatchObject({ city: "Dublin", country: "IE" });306  });307});308309// ─── parser-level checks with a synthetic HTML article ─────────────────────────────────────────────310function ctx(connectorId = "test-news"): ConnectorContext {311  return {312    connectorId, sourceId: "src_test", runId: "run_test", dryRun: true, env: {}, now: () => NOW.toISOString(),313    fetch: async () => { throw new Error("no network in tests"); }, getState: async () => null, setState: async () => {}, log: () => {}, isKnownUnchanged: async () => false,314    provenance: (url) => ({ sourceId: "src_test", connectorId, url, firstObserved: NOW.toISOString(), lastObserved: NOW.toISOString(), retrievedAt: NOW.toISOString(), confidence: "moderate" }),315  };316}317function htmlDoc(url: string, html: string, extra: Partial<RawDocument> = {}): RawDocument {318  const body = Buffer.from(html);319  return { url, finalUrl: url, fetchedAt: NOW.toISOString(), status: 200, contentType: "text/html; charset=utf-8", body, text: html, headers: {}, etag: null, lastModified: null, notModified: false, fetcher: "direct", level: 1, durationMs: 1, credits: 0, group: "newsroom", pageType: "press_release", ...extra };320}321322const ARTICLE = `<!doctype html><html><head><title>Vantage Data Centers announces 192MW campus in Frederick | DCD</title>323<meta property="og:title" content="Vantage Data Centers announces 192MW campus in Frederick, Maryland">324<meta property="article:published_time" content="2026-09-10T09:30:00Z">325<meta name="description" content="Vantage will invest $2 billion in a new 192MW Maryland campus; first phase slated for Q2 2027.">326</head><body><nav>Home News</nav><article><h1>Vantage Data Centers announces 192MW campus in Frederick, Maryland</h1>327<p>Vantage Data Centers today announced plans for a new 192MW data center campus in Frederick, Maryland, representing an investment of more than $2 billion. The 150-acre site will be developed in three phases; the first phase is slated for Q2 2027. Vantage said the campus would serve hyperscale customers in the Mid-Atlantic region and create 200 permanent jobs.</p>328<p>The company operates campuses in Ashburn, Phoenix and Frankfurt.</p></article><footer>© publisher</footer></body></html>`;329330describe("news_article_v1", () => {331  it("emits a news_event without body text and a project with facts and methods", async () => {332    const recs = await newsArticleParser.parse(htmlDoc("https://example.com/news/vantage-frederick", ARTICLE), ctx(), { keepText: false });333    expect(recs.map((r) => r.kind)).toEqual(["news_event", "project"]);334    const [ev, pr] = recs;335    expect(ev!.key).toMatch(/^test-news:news:/);336    expect(ev!.data.title).toBe("Vantage Data Centers announces 192MW campus in Frederick, Maryland");337    expect(ev!.data.publishedAt).toBe("2026-09-10");338    expect(ev!.data.text).toBeNull();339    expect(String(ev!.data.summary).length).toBeLessThanOrEqual(400);340    expect(ev!.data.mw).toEqual([192]);341    expect(ev!.data.status).toBeNull(); // status travels with the project record, not the event342    expect(ev!.data.operators).toEqual(["Vantage Data Centers"]);343    expect(ev!.data.countries).toEqual(["US"]);344    expect(ev!.data.cities).toEqual(["Frederick"]);345    expect(pr!.key).toMatch(/^test-news:project:/);346    expect(pr!.data).toMatchObject({ name: "Vantage Data Centers Frederick campus", operatorName: "Vantage Data Centers", city: "Frederick", regionName: "Maryland", countryIso2: "US", status: "announced", announcedOn: "2026-09-10", expectedOpening: "2027-Q2", plannedMw: 192, investmentUsd: 2_000_000_000, acreage: 150, phaseCount: 3, sourceUrl: "https://example.com/news/vantage-frederick" });347    expect(pr!.methods).toMatchObject({ plannedMw: "regex:mw_v1:title", status: "regex:status_v1:title", operatorName: "lexicon:operator" });348    expect(pr!.certainty).toBeGreaterThanOrEqual(0.5);349    expect(pr!.certainty).toBeLessThanOrEqual(0.8);350  });351  it("keeps the text for public-sector sources when asked", async () => {352    const recs = await newsArticleParser.parse(htmlDoc("https://county.gov/news/vantage", ARTICLE), ctx("gov"), { keepText: true });353    expect(String(recs[0]!.data.text)).toContain("192MW");354  });355  it("returns a nameless sentinel for index pages (dropped by the normalizer) and nothing for irrelevant articles", async () => {356    const idx = await newsArticleParser.parse(htmlDoc("https://example.com/news", ARTICLE, { group: "seed", pageType: "news_index" }), ctx());357    expect(idx).toHaveLength(1);358    expect(idx[0]).toMatchObject({ kind: "project", certainty: 0 });359    expect(idx[0]!.data.name).toBeUndefined();360    const off = ARTICLE.replace(/data center|Data Centers|192MW|hyperscale|campus/g, "bakery");361    expect(await newsArticleParser.parse(htmlDoc("https://example.com/news/bakery", off), ctx())).toEqual([]);362  });363  it("an operator named only in related-articles / footer teasers never becomes the project's operator", async () => {364    // the story is about an unknown developer; Microsoft, Google, Equinix and AWS appear only in teaser blocks (DCD "More in …" shape)365    const html = `<!doctype html><html><head><title>Riverside Estates to build 30MW data center in Leesburg | Trade Press</title>366<meta property="article:published_time" content="2026-09-10T09:30:00Z"></head><body><nav>Home News</nav><main>367<article class="card"><a href="/t1">Microsoft expands plans for La Porte, Indiana, data center campus</a></article>368<article itemprop="mainEntity"><h1>Riverside Estates to build 30MW data center in Leesburg</h1>369<p>Property developer Riverside Estates has secured planning permission for a 30MW data center in Leesburg, Virginia. Named ‘Aire Park One,’ the facility is expected to open in 2028 and will offer 4,000 sqm of white space to cloud providers and financial institutions.</p>370<p>Founded in 1998, Riverside Estates manages an estate of 600,000 sqm across the region and a total investment volume of £2.1 billion ($2.7bn). The Leesburg site is its first data center.</p>371<div class="block-auto_featured_content"><h2>More in Construction &amp; Site Selection</h2><a href="/t2">Google to build 300MW data center campus in Aragon, Spain</a></div></article>372<div class="related-articles"><a href="/t3">Equinix opens LD14 in Slough</a></div></main>373<aside><a href="/t4">Trending: Amazon Web Services buys 1GW site in Ohio</a></aside>374<footer><p>Microsoft, Google and Equinix are trademarks of their owners.</p></footer></body></html>`;375    const recs = await newsArticleParser.parse(htmlDoc("https://example.com/news/riverside-leesburg", html), ctx(), { keepText: false });376    expect(recs.map((r) => r.kind)).toEqual(["news_event", "project"]);377    const [ev, pr] = recs;378    expect(ev!.data.operators).toEqual([]);379    expect(pr!.data).toMatchObject({ operatorName: null, name: "Aire Park One", plannedMw: 30, city: "Leesburg", countryIso2: "US", status: "announced", investmentUsd: null });380    expect(pr!.methods?.operatorName).toBe("none");381    // the same article with the developer in the lexicon keeps its operator (the teasers still count for nothing)382    const known = html.replace(/Riverside Estates/g, "Vantage Data Centers");383    const a = extractAnnouncement("Vantage Data Centers to build 30MW data center in Leesburg", (await articleContentOf(known)).text, { publishedAt: "2026-09-10", now: NOW });384    expect(a.operator?.name).toBe("Vantage Data Centers");385    expect(a.operators.map((o) => o.name)).toEqual(["Vantage Data Centers"]);386  });387  it("company-background money (portfolio volume, revenue) is never the project's investment", () => {388    const text = "S+B Gruppe manages an estate of 414,000 sqm across central and eastern Europe and a total investment volume of €8.1 billion ($9.4bn). The 12MW facility will cost $60 million.";389    expect(parseAllMoney(text)).toHaveLength(3);390    expect(projectMoneyFigures(text)).toEqual([{ amount: 60_000_000, currency: "USD" }]);391    expect(projectMoneyFigures("The company reported revenue of $4.2 billion last year.")).toEqual([]);392    expect(projectMoneyFigures("Vantage will invest $2 billion in the Frederick campus.")).toEqual([{ amount: 2_000_000_000, currency: "USD" }]);393    const a = x("S+B Gruppe to build data center in Bucharest, Romania", "Real estate developer S+B Gruppe has secured planning permission to build a 12MW data center in Bucharest. Named ‘NIMB,’ the facility is expected to open in 2028. Founded in 1986 in Vienna, S+B Gruppe manages an estate of more than 414,000 sqm and a total investment volume of €8.1 billion ($9.4bn).");394    expect(a.money).toBeNull();395    expect(a.investmentUsd).toBeNull();396    expect(a.explicitName).toBe("NIMB");397    expect(a.operator).toBeNull();398    expect(a.classification.mayCreateProject).toBe(true);399  });400});401402async function articleContentOf(html: string) {403  const { articleContent } = await import("./article-parser.js");404  return articleContent(htmlDoc("https://example.com/news/x", html));405}406407describe("news_planning_pdf_v1", () => {408  const REPORT = `<html><head><title>Staff Report — Rezoning Application REZ 2026-0017</title></head><body><main>409<h1>Planning Commission Staff Report</h1>410<p>Application: REZ 2026-0017. Applicant: Compass Datacenters LLC. Date of meeting: 8 September 2026.</p>411<p>Proposal: Rezoning of 212 acres at 41500 Lovettsville Road, Leesburg, Virginia from AR-1 to PD-IP to permit the development of a data center campus of up to 400 MW with associated electrical substation.</p>412<p>Recommendation: Approve with conditions. The Planning Commission recommends that the Board of Supervisors approve the application subject to the proffers dated August 2026.</p>413</main></body></html>`;414  it("extracts applicant, decision, MW, acreage, address and emits an approved project", async () => {415    const recs = await planningPdfParser.parse(htmlDoc("https://county.gov/planning/rez-2026-0017", REPORT, { pageType: "planning_document" }), ctx("loudoun"));416    expect(recs.map((r) => r.kind)).toEqual(["news_event", "project"]);417    const [ev, pr] = recs;418    expect(ev!.data).toMatchObject({ decision: "approved", applicant: "Compass Datacenters", eventType: "planning_approved", pageType: "planning_document", reference: "REZ 2026-0017" });419    expect(ev!.data.publishedAt).toBe("2026-09-08");420    expect(ev!.data.text).not.toBeNull();421    expect(pr!.data).toMatchObject({ status: "approved", operatorName: "Compass Datacenters", plannedMw: 400, acreage: 212, city: "Leesburg", regionName: "Virginia", countryIso2: "US", address: "41500 Lovettsville Road" });422  });423  it("a refused application yields the event only", async () => {424    const refused = REPORT.replace("Recommendation: Approve with conditions.", "Decision: Refused.").replace("approve the application", "refuse the application");425    const recs = await planningPdfParser.parse(htmlDoc("https://county.gov/planning/rez-refused", refused, { pageType: "planning_document" }), ctx("loudoun"));426    expect(recs.map((r) => r.kind)).toEqual(["news_event"]);427    expect(recs[0]!.data.decision).toBe("refused");428  });429  it("detectDecision precedence", () => {430    expect(detectDecision("Decision: Approved").decision).toBe("approved");431    expect(detectDecision("The application was refused on 3 grounds; the applicant may appeal.").decision).toBe("refused");432    expect(detectDecision("Application registered and validated; consultation open until 1 October.").decision).toBe("pending");433  });434});435436describe("news_edgar_fts_v1", () => {437  it("turns full-text hits into one news_event per filing, preferring the exhibit", async () => {438    const json = JSON.stringify({ hits: { total: { value: 2 }, hits: [439      { _id: "0001104659-26-093406:riot-20260810x8k.htm", _source: { ciks: ["0001167419"], display_names: ["Riot Platforms, Inc.  (RIOT)  (CIK 0001167419)"], file_date: "2026-08-10", root_forms: ["8-K"], form: "8-K", file_type: "8-K", file_description: "FORM 8-K", adsh: "0001104659-26-093406", items: ["7.01", "9.01"], biz_locations: ["Castle Rock, CO"], biz_states: ["CO"] } },440      { _id: "0001104659-26-093406:riot-20260810xex99d2.htm", _source: { ciks: ["0001167419"], display_names: ["Riot Platforms, Inc.  (RIOT)  (CIK 0001167419)"], file_date: "2026-08-10", root_forms: ["8-K"], form: "8-K", file_type: "EX-99.2", file_description: "EXHIBIT 99.2", adsh: "0001104659-26-093406", items: ["7.01", "9.01"], biz_locations: ["Castle Rock, CO"], biz_states: ["CO"] } },441    ] } });442    const doc = htmlDoc('https://efts.sec.gov/LATEST/search-index?q=%22data%20center%22%20%22MW%22&forms=8-K', json, { contentType: "application/json", group: "filings", pageType: "financial_disclosure", meta: { query: '"data center" "MW"' } });443    const recs = await edgarFtsParser.parse(doc, ctx("sec-edgar"), {});444    expect(recs).toHaveLength(1);445    const r = recs[0]!;446    expect(r.key).toBe("sec-edgar:news:0001104659-26-093406");447    expect(r.url).toBe("https://www.sec.gov/Archives/edgar/data/1167419/000110465926093406/riot-20260810xex99d2.htm");448    expect(r.data).toMatchObject({ title: "Riot Platforms, Inc.: Form 8-K (EX-99.2)", publishedAt: "2026-08-10", operators: ["Riot Platforms"], countries: ["US"], cities: ["Castle Rock"], text: null });449  });450});451452describe("announcement classifier veto (2026-09 upgrade)", () => {453  const body = (extra: string) => `${extra} The company operates more than 13 GW of capacity across its global portfolio, with campuses in Northern Virginia, Dallas and Phoenix. Its newest Lancaster campus will deliver 500 MW at full build-out.`;454  it("never turns an executive appointment into a project, whatever MW the body quotes", () => {455    const a = extractAnnouncement("STACK Infrastructure Appoints Matt VanderZanden as Chief Executive Officer, STACK Americas", body("STACK Infrastructure today announced the appointment of Matt VanderZanden as CEO of STACK Americas."), { publishedAt: "2025-12-12" });456    expect(a.classification.class).toBe("EXECUTIVE_APPOINTMENT");457    expect(qualifiesAsProject(a)).toBe(false);458  });459  it("keeps portfolio totals out of the headline figure and scopes the site figure", () => {460    const a = extractAnnouncement("STACK breaks ground on new Lancaster data center campus", body("STACK Infrastructure broke ground today on its Lancaster, Texas campus."), { publishedAt: "2026-05-01" });461    expect(a.classification.class).toBe("CONSTRUCTION_START");462    expect(a.headlineMw).toBe(500);463    expect(a.capacityScope).toBe("campus");464    expect(a.evidence.plannedMw?.text).toMatch(/500 MW/);465    expect(qualifiesAsProject(a)).toBe(true);466  });467  it("does not locate a project at the company's headquarters", () => {468    const a = extractAnnouncement("Denver-based Vantage plans 192 MW data center campus in Abilene, Texas", "Denver-based Vantage Data Centers said it will build a 192 MW campus in Abilene, Texas, with the first building expected in 2027.", { publishedAt: "2026-03-01" });469    expect(a.location?.city).toBe("Abilene");470    expect(a.hqGuarded).toBe(true);471  });472  it("classifies PPA, financing and market-research headlines as non-physical", () => {473    for (const [title, cls] of [474      ["Ormat Technologies Signs 20-Year PPA with Switch for ~13 MW of Carbon-Free Geothermal Capacity to Power Data Centers", "POWER_AGREEMENT"],475      ["STACK Secures $1.3B Financing for Development", "FINANCING"],476      ["Hyperscale Data Center Market to Surpass USD 624.2 Billion by 2035", "GENERAL_COMPANY_NEWS"],477    ] as Array<[string, string]>) {478      const a = extractAnnouncement(title, `${title}. Data center capacity of 300 MW is mentioned in passing.`, { publishedAt: "2026-01-02" });479      expect(a.classification.class).toBe(cls);480      expect(qualifiesAsProject(a)).toBe(false);481    }482  });483  it("grades AI evidence from explicit wording only", () => {484    const a = extractAnnouncement("Crusoe to build 1.2 GW AI factory campus in Abilene, Texas", "Crusoe will develop a purpose-built AI campus in Abilene, Texas with NVIDIA GB200 systems.", { publishedAt: "2026-01-02" });485    expect(a.aiEvidence).toBe("confirmed");486    const b = extractAnnouncement("Equinix opens DA11 colocation data center in Dallas", "Equinix opened DA11, a colocation facility in Dallas, Texas.", { publishedAt: "2026-01-02" });487    expect(b.aiEvidence).toBe("unknown");488  });489});490