/ tests / pipeline / timezone-detector.test.js
timezone-detector.test.js
  1  /**
  2   * Tests for Timezone Detector
  3   */
  4  
  5  import { test, mock } from 'node:test';
  6  import assert from 'node:assert';
  7  import Database from 'better-sqlite3';
  8  import { createPgMock } from '../helpers/pg-mock.js';
  9  import { detectTimezone, getSiteTimezone } from '../../src/utils/timezone-detector.js';
 10  
 11  // ── In-memory DB for getSiteTimezone tests ───────────────────────────────────
 12  
 13  const db = new Database(':memory:');
 14  
 15  mock.module('../../src/utils/db.js', { namedExports: createPgMock(db) });
 16  
 17  db.exec(`
 18    CREATE TABLE sites (
 19      id INTEGER PRIMARY KEY AUTOINCREMENT,
 20      domain TEXT NOT NULL,
 21      city TEXT,
 22      country_code TEXT,
 23      rescored_at DATETIME
 24    );
 25  `);
 26  
 27  // ── detectTimezone tests (pure function, no DB) ───────────────────────────────
 28  
 29  test('detectTimezone - US Eastern Time (city-specific)', () => {
 30    const timezone = detectTimezone('New York', 'US');
 31    assert.strictEqual(timezone, 'America/New_York');
 32  });
 33  
 34  test('detectTimezone - US Pacific Time (city-specific)', () => {
 35    const timezone = detectTimezone('Los Angeles', 'US');
 36    assert.strictEqual(timezone, 'America/Los_Angeles');
 37  });
 38  
 39  test('detectTimezone - US Central Time (city-specific)', () => {
 40    const timezone = detectTimezone('Chicago', 'US');
 41    assert.strictEqual(timezone, 'America/Chicago');
 42  });
 43  
 44  test('detectTimezone - Australia Sydney', () => {
 45    const timezone = detectTimezone('Sydney', 'AU');
 46    assert.strictEqual(timezone, 'Australia/Sydney');
 47  });
 48  
 49  test('detectTimezone - Australia Melbourne', () => {
 50    const timezone = detectTimezone('Melbourne', 'AU');
 51    assert.strictEqual(timezone, 'Australia/Melbourne');
 52  });
 53  
 54  test('detectTimezone - Australia Perth (different from default)', () => {
 55    const timezone = detectTimezone('Perth', 'AU');
 56    assert.strictEqual(timezone, 'Australia/Perth');
 57  });
 58  
 59  test('detectTimezone - UK London', () => {
 60    const timezone = detectTimezone('London', 'GB');
 61    assert.strictEqual(timezone, 'Europe/London');
 62  });
 63  
 64  test('detectTimezone - Canada Toronto', () => {
 65    const timezone = detectTimezone('Toronto', 'CA');
 66    assert.strictEqual(timezone, 'America/Toronto');
 67  });
 68  
 69  test('detectTimezone - Canada Vancouver (Pacific)', () => {
 70    const timezone = detectTimezone('Vancouver', 'CA');
 71    assert.strictEqual(timezone, 'America/Vancouver');
 72  });
 73  
 74  test('detectTimezone - country-level fallback (Japan)', () => {
 75    const timezone = detectTimezone('Tokyo', 'JP');
 76    assert.strictEqual(timezone, 'Asia/Tokyo');
 77  });
 78  
 79  test('detectTimezone - country-level fallback (Singapore)', () => {
 80    const timezone = detectTimezone('Singapore', 'SG');
 81    assert.strictEqual(timezone, 'Asia/Singapore');
 82  });
 83  
 84  test('detectTimezone - country only (no city)', () => {
 85    const timezone = detectTimezone(null, 'AU');
 86    assert.strictEqual(timezone, 'Australia/Sydney');
 87  });
 88  
 89  test('detectTimezone - country only (US)', () => {
 90    const timezone = detectTimezone(null, 'US');
 91    assert.strictEqual(timezone, 'America/New_York');
 92  });
 93  
 94  test('detectTimezone - unknown country defaults to US Eastern', () => {
 95    const timezone = detectTimezone('Unknown City', 'ZZ');
 96    assert.strictEqual(timezone, 'America/New_York');
 97  });
 98  
 99  test('detectTimezone - no data defaults to US Eastern', () => {
100    const timezone = detectTimezone(null, null);
101    assert.strictEqual(timezone, 'America/New_York');
102  });
103  
104  test('detectTimezone - case insensitive city names', () => {
105    const timezone1 = detectTimezone('sydney', 'AU');
106    const timezone2 = detectTimezone('SYDNEY', 'AU');
107    const timezone3 = detectTimezone('Sydney', 'AU');
108  
109    assert.strictEqual(timezone1, 'Australia/Sydney');
110    assert.strictEqual(timezone2, 'Australia/Sydney');
111    assert.strictEqual(timezone3, 'Australia/Sydney');
112  });
113  
114  test('detectTimezone - case insensitive country codes', () => {
115    const timezone1 = detectTimezone('Sydney', 'au');
116    const timezone2 = detectTimezone('Sydney', 'AU');
117    const timezone3 = detectTimezone('Sydney', 'Au');
118  
119    assert.strictEqual(timezone1, 'Australia/Sydney');
120    assert.strictEqual(timezone2, 'Australia/Sydney');
121    assert.strictEqual(timezone3, 'Australia/Sydney');
122  });
123  
124  test('detectTimezone - partial city name match', () => {
125    const timezone = detectTimezone('San Francisco Bay Area', 'US');
126    assert.strictEqual(timezone, 'America/Los_Angeles');
127  });
128  
129  test('getSiteTimezone - from database', () => {
130    // Insert test site with location
131    db.prepare('DELETE FROM sites').run();
132    const result = db.prepare('INSERT INTO sites (domain, city, country_code) VALUES (?, ?, ?)').run(
133      'example.com',
134      'Sydney',
135      'AU'
136    );
137  
138    const timezone = getSiteTimezone(result.lastInsertRowid, db);
139    assert.strictEqual(timezone, 'Australia/Sydney');
140  });
141  
142  test('getSiteTimezone - no location data defaults', () => {
143    db.prepare('DELETE FROM sites').run();
144    const result = db.prepare('INSERT INTO sites (domain) VALUES (?)').run('example.com');
145  
146    const timezone = getSiteTimezone(result.lastInsertRowid, db);
147    assert.strictEqual(timezone, 'America/New_York');
148  });
149  
150  test('getSiteTimezone - non-existent site defaults', () => {
151    const timezone = getSiteTimezone(999999, db);
152    assert.strictEqual(timezone, 'America/New_York');
153  });
154  
155  test('detectTimezone - multiple US cities', () => {
156    assert.strictEqual(detectTimezone('Boston', 'US'), 'America/New_York');
157    assert.strictEqual(detectTimezone('Miami', 'US'), 'America/New_York');
158    assert.strictEqual(detectTimezone('Dallas', 'US'), 'America/Chicago');
159    assert.strictEqual(detectTimezone('Houston', 'US'), 'America/Chicago');
160    assert.strictEqual(detectTimezone('Denver', 'US'), 'America/Denver');
161    assert.strictEqual(detectTimezone('Phoenix', 'US'), 'America/Phoenix');
162    assert.strictEqual(detectTimezone('Seattle', 'US'), 'America/Los_Angeles');
163    assert.strictEqual(detectTimezone('Portland', 'US'), 'America/Los_Angeles');
164  });
165  
166  test('detectTimezone - European cities', () => {
167    assert.strictEqual(detectTimezone('Paris', 'FR'), 'Europe/Paris');
168    assert.strictEqual(detectTimezone('Berlin', 'DE'), 'Europe/Berlin');
169    assert.strictEqual(detectTimezone('Madrid', 'ES'), 'Europe/Madrid');
170    assert.strictEqual(detectTimezone('Rome', 'IT'), 'Europe/Rome');
171    assert.strictEqual(detectTimezone('Amsterdam', 'NL'), 'Europe/Amsterdam');
172  });
173  
174  test('detectTimezone - Asian cities', () => {
175    assert.strictEqual(detectTimezone('Tokyo', 'JP'), 'Asia/Tokyo');
176    assert.strictEqual(detectTimezone('Shanghai', 'CN'), 'Asia/Shanghai');
177    assert.strictEqual(detectTimezone('Mumbai', 'IN'), 'Asia/Kolkata');
178    assert.strictEqual(detectTimezone('Bangkok', 'TH'), 'Asia/Bangkok');
179    assert.strictEqual(detectTimezone('Singapore', 'SG'), 'Asia/Singapore');
180  });