outlook-mcp/tests/date-filter-timezone.test.js

255 lines
8.4 KiB
JavaScript

/**
* Static Node tests for timezone-aware date filtering in odata-helpers.js.
*
* These tests exercise buildDateFilter / localDateStringToInstant / processDateRange
* with known local dates and assert that the produced UTC ISO boundaries are correct.
*
* Run with:
* node tests/date-filter-timezone.test.js
*/
const assert = require('assert');
const {
buildDateFilter,
processDateRange,
startOfDay,
endOfDay,
localDateStringToInstant,
getEffectiveTimeZone
} = require('../utils/odata-helpers');
function assertBoundary(actualISO, expectedISO, label) {
if (actualISO !== expectedISO) {
throw new Error(`${label}: expected ${expectedISO}, got ${actualISO}`);
}
}
// We control the timezone via MS_TIMEZONE env var before requiring config.
// For these tests we set America/New_York explicitly.
assert.strictEqual(getEffectiveTimeZone('America/New_York'), 'America/New_York', 'IANA timezone should pass through');
assert.strictEqual(getEffectiveTimeZone('Eastern Standard Time'), 'America/New_York', 'Windows timezone should map to IANA');
// Test 1: dateFrom/dateTo as plain date strings in America/New_York (EDT).
// 2024-06-21 EDT is UTC-4, so local midnight is 2024-06-21T04:00:00.000Z,
// and end-of-day is 2024-06-22T03:59:59.999Z.
{
const conditions = buildDateFilter('2024-06-21', '2024-06-21', null, 'America/New_York');
console.log('Test 1 conditions (NY EDT):', conditions);
assert.strictEqual(conditions.length, 2, 'should produce two conditions');
assertBoundary(
conditions[0],
'receivedDateTime ge 2024-06-21T04:00:00.000Z',
'Test 1 start of local day in EDT'
);
assertBoundary(
conditions[1],
'receivedDateTime le 2024-06-22T03:59:59.999Z',
'Test 1 end of local day in EDT'
);
}
// Test 2: Plain date strings in America/New_York during EST (UTC-5).
{
const conditions = buildDateFilter('2024-01-15', '2024-01-15', null, 'America/New_York');
console.log('Test 2 conditions (NY EST):', conditions);
assertBoundary(
conditions[0],
'receivedDateTime ge 2024-01-15T05:00:00.000Z',
'Test 2 start of local day in EST'
);
assertBoundary(
conditions[1],
'receivedDateTime le 2024-01-16T04:59:59.999Z',
'Test 2 end of local day in EST'
);
}
// Test 3: Plain date strings in Pacific Time during PDT (UTC-7).
{
const conditions = buildDateFilter('2024-06-21', '2024-06-21', null, 'America/Los_Angeles');
console.log('Test 3 conditions (LA PDT):', conditions);
assertBoundary(
conditions[0],
'receivedDateTime ge 2024-06-21T07:00:00.000Z',
'Test 3 start of local day in PDT'
);
assertBoundary(
conditions[1],
'receivedDateTime le 2024-06-22T06:59:59.999Z',
'Test 3 end of local day in PDT'
);
}
// Test 4: ISO strings with explicit offsets are respected as-is.
{
const conditions = buildDateFilter('2024-06-21T08:00:00-04:00', '2024-06-21T18:00:00-04:00', null, 'America/New_York');
console.log('Test 4 conditions (offset provided):', conditions);
assertBoundary(
conditions[0],
'receivedDateTime ge 2024-06-21T12:00:00.000Z',
'Test 4 explicit offset fromDate'
);
assertBoundary(
conditions[1],
'receivedDateTime le 2024-06-21T22:00:00.000Z',
'Test 4 explicit offset toDate'
);
}
// Test 5: Windows timezone name should map correctly.
{
const conditions = buildDateFilter('2024-06-21', '2024-06-21', null, 'Pacific Standard Time');
console.log('Test 5 conditions (Windows PST in summer -> PDT):', conditions);
assertBoundary(
conditions[0],
'receivedDateTime ge 2024-06-21T07:00:00.000Z',
'Test 5 Windows Pacific start'
);
}
// Test 6: dateRange='today' boundary for a known UTC instant.
// Force "now" by passing a specific date into processDateRange indirectly via startOfDay.
// We instead test processDateRange by stubbing Date in a narrow scope.
{
const savedDate = Date;
const fixedNow = new Date('2024-06-21T02:30:00.000Z'); // 2024-06-20 22:30 EDT
global.Date = class extends savedDate {
constructor(...args) {
if (args.length === 0) {
super(fixedNow);
} else {
super(...args);
}
}
static now() { return fixedNow.getTime(); }
};
// Reload helpers so the new Date constructor is used.
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
const helpers = require('../utils/odata-helpers');
const range = helpers.processDateRange('today', 'America/New_York');
console.log('Test 6 today range:', range);
assertBoundary(
range.from.toISOString(),
'2024-06-20T04:00:00.000Z',
'Test 6 today start in EDT (local date is 2024-06-20)'
);
assertBoundary(
range.to.toISOString(),
'2024-06-21T03:59:59.999Z',
'Test 6 today end in EDT'
);
global.Date = savedDate;
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
}
// Test 7: dateRange='yesterday' for the same fixed EDT instant.
{
const savedDate = Date;
const fixedNow = new Date('2024-06-21T02:30:00.000Z');
global.Date = class extends savedDate {
constructor(...args) {
if (args.length === 0) {
super(fixedNow);
} else {
super(...args);
}
}
static now() { return fixedNow.getTime(); }
};
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
const helpers = require('../utils/odata-helpers');
const range = helpers.processDateRange('yesterday', 'America/New_York');
console.log('Test 7 yesterday range:', range);
assertBoundary(
range.from.toISOString(),
'2024-06-19T04:00:00.000Z',
'Test 7 yesterday start in EDT'
);
assertBoundary(
range.to.toISOString(),
'2024-06-20T03:59:59.999Z',
'Test 7 yesterday end in EDT'
);
global.Date = savedDate;
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
}
// Test 8: dateRange='thisweek' (Sunday-based) in EDT around the same instant.
// Local date is Thursday 2024-06-20, so Sunday is 2024-06-16.
{
const savedDate = Date;
const fixedNow = new Date('2024-06-21T02:30:00.000Z');
global.Date = class extends savedDate {
constructor(...args) {
if (args.length === 0) {
super(fixedNow);
} else {
super(...args);
}
}
static now() { return fixedNow.getTime(); }
};
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
const helpers = require('../utils/odata-helpers');
const range = helpers.processDateRange('thisweek', 'America/New_York');
console.log('Test 8 thisweek range:', range);
assertBoundary(
range.from.toISOString(),
'2024-06-16T04:00:00.000Z',
'Test 8 thisweek start (Sunday 2024-06-16 EDT)'
);
global.Date = savedDate;
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
}
// Test 9: dateRange='thismonth' in EDT.
{
const savedDate = Date;
const fixedNow = new Date('2024-06-21T02:30:00.000Z');
global.Date = class extends savedDate {
constructor(...args) {
if (args.length === 0) {
super(fixedNow);
} else {
super(...args);
}
}
static now() { return fixedNow.getTime(); }
};
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
const helpers = require('../utils/odata-helpers');
const range = helpers.processDateRange('thismonth', 'America/New_York');
console.log('Test 9 thismonth range:', range);
assertBoundary(
range.from.toISOString(),
'2024-06-01T04:00:00.000Z',
'Test 9 thismonth start (June 1 EDT)'
);
global.Date = savedDate;
delete require.cache[require.resolve('../utils/odata-helpers')];
delete require.cache[require.resolve('../utils/timezone-mapper')];
delete require.cache[require.resolve('../config')];
}
console.log('\nAll timezone-aware date filter tests passed.');