@@ -118,6 +118,89 @@ describe("normalizeErrorMessage", () => {
118118 const normalized = normalizeErrorMessage ( message ) ;
119119 expect ( normalized ) . toBe ( "Connection timeout" ) ;
120120 } ) ;
121+
122+ describe ( "ordering: specific patterns before generic ones" , ( ) => {
123+ it ( "ISO timestamp year should not be consumed by numeric ID regex" , ( ) => {
124+ const message = "Deadline was 2025-12-31T23:59:59Z" ;
125+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "Deadline was <timestamp>" ) ;
126+ } ) ;
127+
128+ it ( "ISO timestamp without trailing Z should normalize correctly" , ( ) => {
129+ const message = "Started at 2024-01-15T08:00:00" ;
130+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "Started at <timestamp>" ) ;
131+ } ) ;
132+
133+ it ( "Unix timestamp (10 digits) should not become <id>" , ( ) => {
134+ const message = "Token expires 1700000000" ;
135+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "Token expires <timestamp>" ) ;
136+ } ) ;
137+
138+ it ( "Unix timestamp (13 digits) should not become <id>" , ( ) => {
139+ const message = "Sent at 1700000000000" ;
140+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "Sent at <timestamp>" ) ;
141+ } ) ;
142+
143+ it ( "URL path should not be stripped before URL regex runs" , ( ) => {
144+ const message = "Webhook failed for https://hooks.example.com/webhook/abc" ;
145+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "Webhook failed for <url>" ) ;
146+ } ) ;
147+
148+ it ( "URL with port and path should normalize to <url>" , ( ) => {
149+ const message = "Cannot reach http://localhost:8080/health/ready" ;
150+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "Cannot reach <url>" ) ;
151+ } ) ;
152+
153+ it ( "URL with query string should normalize to <url>" , ( ) => {
154+ const message = "GET https://api.example.com/v2/users?page=1&limit=50 returned 500" ;
155+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "GET <url> returned 500" ) ;
156+ } ) ;
157+
158+ it ( "message with both a URL and a timestamp" , ( ) => {
159+ const message =
160+ "Request to https://api.example.com/data failed at 2025-06-15T10:30:00Z" ;
161+ expect ( normalizeErrorMessage ( message ) ) . toBe (
162+ "Request to <url> failed at <timestamp>"
163+ ) ;
164+ } ) ;
165+
166+ it ( "message with a URL and a unix timestamp" , ( ) => {
167+ const message = "Callback to https://example.com/hook timed out after 1700000000" ;
168+ expect ( normalizeErrorMessage ( message ) ) . toBe (
169+ "Callback to <url> timed out after <timestamp>"
170+ ) ;
171+ } ) ;
172+
173+ it ( "path-like string that is NOT a URL should still become <path>" , ( ) => {
174+ const message = "Cannot read /var/log/app/error.log" ;
175+ expect ( normalizeErrorMessage ( message ) ) . toBe ( "Cannot read <path>" ) ;
176+ } ) ;
177+ } ) ;
178+
179+ describe ( "fingerprint stability: same error class groups together despite dynamic values" , ( ) => {
180+ it ( "errors differing only in ISO timestamp should share a fingerprint" , ( ) => {
181+ const e1 = { type : "TimeoutError" , message : "Timed out at 2025-01-01T00:00:00Z" } ;
182+ const e2 = { type : "TimeoutError" , message : "Timed out at 2026-06-15T12:30:00Z" } ;
183+ expect ( calculateErrorFingerprint ( e1 ) ) . toBe ( calculateErrorFingerprint ( e2 ) ) ;
184+ } ) ;
185+
186+ it ( "errors differing only in URL path should share a fingerprint" , ( ) => {
187+ const e1 = {
188+ type : "FetchError" ,
189+ message : "Failed to fetch https://api.example.com/users/123" ,
190+ } ;
191+ const e2 = {
192+ type : "FetchError" ,
193+ message : "Failed to fetch https://api.example.com/orders/456" ,
194+ } ;
195+ expect ( calculateErrorFingerprint ( e1 ) ) . toBe ( calculateErrorFingerprint ( e2 ) ) ;
196+ } ) ;
197+
198+ it ( "errors differing only in unix timestamp should share a fingerprint" , ( ) => {
199+ const e1 = { type : "ExpiredError" , message : "Token expired at 1700000000" } ;
200+ const e2 = { type : "ExpiredError" , message : "Token expired at 1800000000" } ;
201+ expect ( calculateErrorFingerprint ( e1 ) ) . toBe ( calculateErrorFingerprint ( e2 ) ) ;
202+ } ) ;
203+ } ) ;
121204} ) ;
122205
123206describe ( "normalizeStackTrace" , ( ) => {
0 commit comments