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 });