[object Object]

← back to Rentv 2026

harden(deals): Cody gate — add 5 summarize() tests (prod path, was untested), hoist the deal-parse import to the top import block, fix .js→.mjs comment typo

9300439d042007e65a12ccdbf59c54d4c1e34450 · 2026-08-06 07:32:01 -0700 · Steve

Files touched

Diff

commit 9300439d042007e65a12ccdbf59c54d4c1e34450
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 07:32:01 2026 -0700

    harden(deals): Cody gate — add 5 summarize() tests (prod path, was untested), hoist the deal-parse import to the top import block, fix .js→.mjs comment typo
---
 scripts/pull-deals.mjs    | 13 +++++--------
 test/deals/parse.test.mjs | 26 +++++++++++++++++++++++++-
 2 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/scripts/pull-deals.mjs b/scripts/pull-deals.mjs
index 9c32bd55..f25e3cdf 100644
--- a/scripts/pull-deals.mjs
+++ b/scripts/pull-deals.mjs
@@ -13,6 +13,11 @@ import { fileURLToPath } from 'node:url';
 import { createRequire } from 'node:module';
 import { parseLocation } from './lib/parse-location.mjs';
 import { recordDeals } from './lib/deal-registry.mjs';
+// Pure deal-field extractors — moved to ./lib/deal-parse.mjs so they are unit-testable in isolation
+// (test/deals/parse.test.mjs). This script keeps only the fetch/localize/registry orchestration below.
+import {
+  parseAmount, classify, parseSize, parseAddress, parseOccupancy, parseYearBuilt, summarize,
+} from './lib/deal-parse.mjs';
 const HERE = dirname(fileURLToPath(import.meta.url));
 const DATA = join(HERE, '..', 'data'); mkdirSync(DATA, { recursive: true });
 // Localize deal-card photos into public/img/news/ — the Deals grid serves them locally,
@@ -29,14 +34,6 @@ async function fetchDecoded(url) {
 const clean = (t) => t.replace(/&amp;/g, '&').replace(/&#39;/g, "'").replace(/&quot;/g, '"')
   .replace(/&nbsp;|&#160;/g, ' ').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
 
-// Pure deal-field extractors (parseAmount / classify / parseSize / parseAddress / …) now live in
-// ./lib/deal-parse.mjs so they are unit-testable in isolation (test/deals/parse.test.js). Location
-// parsing lives in ./lib/parse-location.mjs. This script keeps only the fetch/localize/registry
-// orchestration below.
-import {
-  parseAmount, classify, parseSize, parseAddress, parseOccupancy, parseYearBuilt, summarize,
-} from './lib/deal-parse.mjs';
-
 try {
   const news = JSON.parse(readFileSync(join(DATA, 'news.json'), 'utf8'));
   const items = news.items || [];
diff --git a/test/deals/parse.test.mjs b/test/deals/parse.test.mjs
index 4bb32a4d..8401923a 100644
--- a/test/deals/parse.test.mjs
+++ b/test/deals/parse.test.mjs
@@ -5,7 +5,7 @@ import { test } from 'node:test';
 import assert from 'node:assert/strict';
 import {
   parseAmount, txnOf, typeOf, classify, parseSize,
-  parseAddress, parseOccupancy, parseYearBuilt,
+  parseAddress, parseOccupancy, parseYearBuilt, summarize,
 } from '../../scripts/lib/deal-parse.mjs';
 import { parseLocation } from '../../scripts/lib/parse-location.mjs';
 
@@ -98,6 +98,30 @@ test('parseOccupancy + parseYearBuilt', () => {
   assert.equal(parseYearBuilt('no vintage given'), null);
 });
 
+// ── summarize (prod path: writes the summary field on every deal card) ────────
+test('summarize: strips the leading title echo + a date stamp, keeps 2 sentences', () => {
+  assert.equal(
+    summarize('ACME Buys Tower', 'ACME Buys Tower 08/05/2026 The asset sold for $50 mil. It spans 200k sf. A third sentence.'),
+    'The asset sold for $50 mil.  It spans 200k sf.',
+  );
+});
+test('summarize: body without the title echo → first two sentences', () => {
+  assert.equal(
+    summarize('Unrelated Title', 'First sentence about the deal. Second sentence with detail. Third one ignored.'),
+    'First sentence about the deal.  Second sentence with detail.',
+  );
+});
+test('summarize: empty body → empty string (never throws)', () => {
+  assert.equal(summarize('Any Title', ''), '');
+});
+test('summarize: single clause with no terminal punctuation is kept whole', () => {
+  assert.equal(summarize('T', 'Just one clause with no terminal punctuation'), 'Just one clause with no terminal punctuation');
+});
+test('summarize: truncates to 320 chars', () => {
+  const out = summarize('T', 'word '.repeat(100) + '.');
+  assert.equal(out.length, 320);
+});
+
 // ── classify (integration: title wins over a body leak) ───────────────────────
 test('classify: title signal beats a body leak', () => {
   // groundbreaking title + body that mentions a land acquisition

← 8f038295 test(deals): extract pull-deals field parsers to testable li  ·  back to Rentv 2026  ·  fix(deals): magnitude-aware parseSize — parse '104k sf'/'1.2 28068930 →