From 617976e9e1bde42b4e3ccdfec3efe1bddab5ba67 Mon Sep 17 00:00:00 2001 From: Miodec Date: Sat, 30 May 2026 12:21:18 +0200 Subject: [PATCH] chore: better mismatch reporting --- backend/src/api/controllers/result.ts | 22 ++++++++++++++++++++-- frontend/src/ts/test/test-logic.ts | 14 ++++++++++++++ packages/contracts/src/results.ts | 17 +++++++++++++---- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/backend/src/api/controllers/result.ts b/backend/src/api/controllers/result.ts index 769372c3845c..db5f605cedce 100644 --- a/backend/src/api/controllers/result.ts +++ b/backend/src/api/controllers/result.ts @@ -189,7 +189,16 @@ export async function reportCompletedEventMismatch( req: MonkeyRequest, ): Promise { const { uid } = req.ctx.decodedToken; - const { notMatching, mode, mode2, difficulty, duration } = req.body; + const { + notMatching, + mismatchedKeys, + groupKey, + language, + mode, + mode2, + difficulty, + duration, + } = req.body; // Logger.warning( // `Completed event mismatch for uid ${uid}: ${notMatching.join(", ")}`, // ); @@ -197,7 +206,16 @@ export async function reportCompletedEventMismatch( // Logger.warning(`New CE: ${JSON.stringify(ce2)}`); void addLog( "completed_event_mismatch", - { notMatching, mode, mode2, difficulty, duration }, + { + notMatching, + mismatchedKeys, + groupKey, + language, + mode, + mode2, + difficulty, + duration, + }, uid, ); return new MonkeyResponse("Mismatch reported", null); diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 31e74a778964..6934fff82a95 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -914,6 +914,7 @@ function compareCompletedEvents( //compare ce and ce2, log differences const notMatching: string[] = []; + const mismatchedKeys: string[] = []; const ceKeys = Object.keys(ce) as (keyof typeof ce)[]; for (const key of ceKeys) { let val1 = ce[key]; @@ -938,6 +939,7 @@ function compareCompletedEvents( console.debug(`Completed event match on key ${key}:`, a); } else { notMatching.push(`${key} (${mismatchCount}/${total} elements differ)`); + mismatchedKeys.push(key); console.error( `Completed event mismatch on key ${key}: ${mismatchCount}/${total} elements differ`, a, @@ -962,6 +964,7 @@ function compareCompletedEvents( console.debug(`Completed event match on key charStats:`, a); } else { notMatching.push(`charStats (${diffs.join(", ")})`); + mismatchedKeys.push("charStats"); console.error(`Completed event mismatch on key charStats:`, a, b); } continue; @@ -1022,6 +1025,7 @@ function compareCompletedEvents( ); } else { notMatching.push("chartData (one is 'toolong' and the other is not)"); + mismatchedKeys.push("chartData"); console.error( `Completed event mismatch on key chartData: one is "toolong" and the other is not`, v1, @@ -1045,6 +1049,7 @@ function compareCompletedEvents( console.debug(`Completed event match on key chartData.${field}:`, a); } else { notMatching.push(`chartData.${field} (values differ)`); + mismatchedKeys.push(`chartData.${field}`); console.error( `Completed event mismatch on key chartData.${field}:`, a, @@ -1063,6 +1068,7 @@ function compareCompletedEvents( ); } else { notMatching.push(`keypressCountHistory (values differ)`); + mismatchedKeys.push("keypressCountHistory"); console.error( `Completed event mismatch on key keypressCountHistory:`, a, @@ -1084,6 +1090,7 @@ function compareCompletedEvents( const diff = Numbers.roundTo2(Math.abs(a - b)); const dir = a > b ? "ce1 larger" : "ce2 larger"; notMatching.push(`${key} (off by ${diff}, ${dir})`); + mismatchedKeys.push(key); console.error(`Completed event mismatch on key ${key}:`, a, b); } } else if (typeof val1 === "number" && typeof val2 === "number") { @@ -1093,12 +1100,14 @@ function compareCompletedEvents( const diff = Numbers.roundTo2(Math.abs(a - b)); const dir = a > b ? "ce1 larger" : "ce2 larger"; notMatching.push(`${key} (off by ${diff}, ${dir})`); + mismatchedKeys.push(key); console.error(`Completed event mismatch on key ${key}:`, a, b); } else { console.debug(`Completed event match on key ${key}:`, a); } } else if (JSON.stringify(val1) !== JSON.stringify(val2)) { notMatching.push(`${key} (values differ)`); + mismatchedKeys.push(key); console.error(`Completed event mismatch on key ${key}:`, val1, val2); } else { console.debug(`Completed event match on key ${key}:`, val1); @@ -1112,10 +1121,15 @@ function compareCompletedEvents( // `Completed event mismatch: ${notMatching.join(", ")}`, // { important: true }, // ); + mismatchedKeys.sort(); + const groupKey = mismatchedKeys.join(","); Ape.results .reportCompletedEventMismatch({ body: { notMatching, + mismatchedKeys, + groupKey, + language: ce.language, mode: ce.mode, mode2: ce.mode2, difficulty: ce.difficulty, diff --git a/packages/contracts/src/results.ts b/packages/contracts/src/results.ts index 06d8030df1c6..73909857e57d 100644 --- a/packages/contracts/src/results.ts +++ b/packages/contracts/src/results.ts @@ -13,6 +13,12 @@ import { ResultMinifiedSchema, ResultSchema, } from "@monkeytype/schemas/results"; +import { LanguageSchema } from "@monkeytype/schemas/languages"; +import { + DifficultySchema, + Mode2Schema, + ModeSchema, +} from "@monkeytype/schemas/shared"; import { IdSchema } from "@monkeytype/schemas/util"; export const GetResultsQuerySchema = z.object({ @@ -61,10 +67,13 @@ export type AddResultRequest = z.infer; export const ReportCompletedEventMismatchRequestSchema = z.object({ notMatching: z.array(z.string().max(100)).max(50), - mode: z.string().optional(), - mode2: z.string().optional(), - difficulty: z.string().optional(), - duration: z.number().optional(), + mismatchedKeys: z.array(z.string().max(100)).max(50), + groupKey: z.string().max(500), + language: LanguageSchema.optional(), + mode: ModeSchema.optional(), + mode2: Mode2Schema.optional(), + difficulty: DifficultySchema.optional(), + duration: z.number().max(200).optional(), // ce: z.record(z.unknown()), // ce2: z.record(z.unknown()), });