diff --git a/frontend/app/javascript/dashboard/helper/specs/actionCable.spec.js b/frontend/app/javascript/dashboard/helper/specs/actionCable.spec.js index 54753f3f..a6cd9923 100644 --- a/frontend/app/javascript/dashboard/helper/specs/actionCable.spec.js +++ b/frontend/app/javascript/dashboard/helper/specs/actionCable.spec.js @@ -124,7 +124,7 @@ describe('ActionCableConnector - Copilot Tests', () => { ); }); - it('does not apply replayed activity timestamps', async () => { + it('does not apply replayed or 1e10-millisecond activity timestamps', async () => { const currentMessage = { id: 42, conversation_id: 7, @@ -148,7 +148,7 @@ describe('ActionCableConnector - Copilot Tests', () => { }); await actionCable.onMessageCreated({ ...currentMessage, - updated_at: '2026-08-24T01:00:00Z', + updated_at: 10_000_000_000, conversation: { last_activity_at: 50 }, }); diff --git a/frontend/app/javascript/shared/helpers/specs/timeHelper.spec.js b/frontend/app/javascript/shared/helpers/specs/timeHelper.spec.js index 19240a25..728da66b 100644 --- a/frontend/app/javascript/shared/helpers/specs/timeHelper.spec.js +++ b/frontend/app/javascript/shared/helpers/specs/timeHelper.spec.js @@ -23,12 +23,29 @@ afterEach(() => { describe('#isNewerTimestamp', () => { it.each([ - ['Unix seconds', 1_700_000_000, 1_700_000_001], - ['numeric strings', '1700000000', '1700000001'], - ['early Unix milliseconds', 978_307_200_000, 1_700_000_000], - ['below the former 1e12 threshold', 999_999_999_999, 1_000_000_000_000], - ])('orders %s', (_label, current, next) => { - expect(isNewerTimestamp(current, next)).toBe(true); + ['Unix seconds', 1_700_000_000, 1_700_000_001, true], + ['numeric strings', '1700000000', '1700000001', true], + ['early Unix milliseconds', 978_307_200_000, 1_700_000_000, true], + [ + 'below the former 1e12 threshold', + 999_999_999_999, + 1_000_000_000_000, + true, + ], + ['below 1e10 as seconds', 9_999_999_998, 9_999_999_999, true], + ['1e10 as milliseconds', 9_999_999_999, 10_000_000_000, false], + ['above 1e10 as milliseconds', 10_000_000_000, 10_000_000_001, true], + ['minimum ECMAScript Date', 'invalid', -8_640_000_000_000_000, true], + ['below minimum Date', 'invalid', -8_640_000_000_000_001, false], + [ + 'maximum ECMAScript Date', + 8_639_999_999_999_999, + 8_640_000_000_000_000, + true, + ], + ['above maximum Date', 8_639_999_999_999_999, 8_640_000_000_000_001, false], + ])('handles %s', (_label, current, next, expected) => { + expect(isNewerTimestamp(current, next)).toBe(expected); }); it('preserves missing and invalid timestamp compatibility', () => { diff --git a/frontend/app/javascript/widget/helpers/specs/actionCable.spec.js b/frontend/app/javascript/widget/helpers/specs/actionCable.spec.js index 5ed3cb90..3b21e363 100644 --- a/frontend/app/javascript/widget/helpers/specs/actionCable.spec.js +++ b/frontend/app/javascript/widget/helpers/specs/actionCable.spec.js @@ -113,7 +113,7 @@ describe('Widget ActionCableConnector', () => { expect(IFrameHelper.sendMessage).not.toHaveBeenCalled(); }); - it('rejects an older numeric-string timestamp against an ISO timestamp', async () => { + it('treats an exact 1e10 numeric string as milliseconds', async () => { const currentMessage = { id: 42, conversation_id: 7, @@ -126,7 +126,7 @@ describe('Widget ActionCableConnector', () => { await actionCable.onMessageUpdated({ ...currentMessage, content: 'older', - updated_at: '1787533200', + updated_at: '10000000000', previous_changes: { content_attributes: [{}, { submitted_values: ['yes'] }], },