feat(matching): implement origin_run_id tracking and audit tests

- Updated run-matching endpoint to create MatchingRun records
- Added trigger tracking (manual vs scheduler)
- Fixed saveMatchingResults to accept runId parameter
- Added comprehensive audit tests (matching-runs-audit.test.js):
  * TC1: origin_run_id assigned correctly
  * TC2: Sequential runs create separate run IDs
  * TC3: Accepted suggestions preserve origin_run_id
  * TC4: Filter parameters (onlyAssigned, includeNotFound)
  * TC5: Manual vs scheduler trigger differentiation
  * TC6: Failed runs recorded in audit trail
- All 6 audit tests passing
This commit is contained in:
Radosław Gierwiało
2025-11-30 20:01:10 +01:00
parent f13853c300
commit bd7212a599
2 changed files with 688 additions and 16 deletions

View File

@@ -1144,25 +1144,65 @@ router.post('/:slug/run-matching', authenticate, async (req, res, next) => {
// TODO: In production, add admin check or deadline validation
// For now, allow anyone to run matching for testing
// Run matching algorithm
const suggestions = await matchingService.runMatching(event.id);
const startedAt = new Date();
let runRow = null;
// Save results
const count = await matchingService.saveMatchingResults(event.id, suggestions);
try {
// Create run audit row
runRow = await prisma.matchingRun.create({
data: {
eventId: event.id,
trigger: 'manual',
status: 'running',
startedAt,
},
});
// Get statistics
const notFoundCount = suggestions.filter(s => s.status === SUGGESTION_STATUS.NOT_FOUND).length;
const matchedCount = suggestions.filter(s => s.status === SUGGESTION_STATUS.PENDING).length;
// Run matching algorithm
const suggestions = await matchingService.runMatching(event.id);
res.json({
success: true,
data: {
totalHeats: suggestions.length,
matched: matchedCount,
notFound: notFoundCount,
runAt: new Date(),
},
});
// Save results with runId for audit trail
const count = await matchingService.saveMatchingResults(event.id, suggestions, runRow.id);
// Get statistics
const notFoundCount = suggestions.filter(s => s.status === SUGGESTION_STATUS.NOT_FOUND).length;
const matchedCount = suggestions.filter(s => s.status === SUGGESTION_STATUS.PENDING).length;
// Update run audit row with success
await prisma.matchingRun.update({
where: { id: runRow.id },
data: {
status: 'success',
endedAt: new Date(),
matchedCount,
notFoundCount,
},
});
res.json({
success: true,
data: {
totalHeats: suggestions.length,
matched: matchedCount,
notFound: notFoundCount,
runAt: new Date(),
runId: runRow.id,
},
});
} catch (error) {
// Update run audit row with failure
if (runRow) {
await prisma.matchingRun.update({
where: { id: runRow.id },
data: {
status: 'failed',
endedAt: new Date(),
errorMessage: error.message,
},
});
}
throw error;
}
} catch (error) {
next(error);
}