Skip to content

feat: add support for score data and implement match correction mechanism - #155

Merged
Dejmenek merged 29 commits into
mainfrom
feat/102-no-match-score-data-and-no-way-to-correct-a-mistaken-result
Sep 16, 2026
Merged

Dejmenek merged 29 commits into
mainfrom
feat/102-no-match-score-data-and-no-way-to-correct-a-mistaken-result

Conversation

@Dejmenek

Copy link
Copy Markdown
Owner

Summary

Introduces a robust mechanism for correcting match results, ensuring data integrity, cascade propagation, and concurrency handling in tournament brackets. Adds explicit match statuses, audit logging, and validation enhancements. Exposes match score data.

Changes

  • Match.cs: Added MatchStatus enum, Player1Score, Player2Score, and RowVersion for concurrency control.
  • MatchMutations.cs: Added CorrectMatchResult mutation and enhanced Play mutation with score handling and concurrency guards.
  • MatchCorrectionService.cs: Implemented cascade propagation for downstream match updates and audit logging.
  • DatabaseSeeder.cs: Updated to populate MatchStatus for all seeded matches.
  • MatchValidations.cs: Added validations for negative scores, winner mismatches, and invalid statuses.

Added `Play_ReturnsNegativeScoreError_WhenScoreIsNegative` to ensure the `Play` mutation returns an error when negative scores are provided. This test verifies error codes, messages, and extensions, and ensures the match remains unmodified in the database.

Added `Play_ReturnsWinnerScoreMismatchError_WhenWinnerScoreIsNotHigher` to validate that the `Play` mutation enforces the rule that the winner's score must be higher. This test also verifies error codes, messages, and extensions, and ensures the match remains unmodified.
Tests include:
- Validation for non-negative scores for both players.
- Validation for winner having a higher score than the opponent.
- Edge cases such as tied scores and negative scores.
Added a new constant `RoundDataChanged` to the `BracketErrorCodes` class to represent a new error code for concurrent round data changes. Introduced a new method `RoundDataChangedConcurrently` in the `BracketErrors` class to generate an error object with a descriptive message, error code, and `BracketId` as context.
Updated the `ValidateAllMatchesCompleted` method in the
`BracketMutationValidations` class to check if any match's
`Status` is not equal to `MatchStatus.Played` instead of
checking for a `null` `WinnerId`. This improves the accuracy
of the validation by relying on the `Status` property, which
provides a more reliable indicator of match completion.
Added a `Status` property to the `Match` class to track match status.
Replaced the `Version` property with `RowVersion`, annotated with
`[IsProjected(true)]`. Introduced a new `MatchStatus` enum with
values `Scheduled`, `Played`, and `NeedsReplay` to represent
different match states.
Added a new `MatchCorrectionAudit` entity to track audit logs for match corrections. Updated `ApplicationDbContext` to include a `DbSet` for `MatchCorrectionAudits` and configured its relationships, primary key, and indexes in `OnModelCreating`.

The `MatchCorrectionAudit` entity includes properties for tracking match corrections, such as previous and new statuses, player IDs, scores, and the user who performed the correction. Relationships to `Match` and `ApplicationUser` entities were defined with `DeleteBehavior.Restrict`.
Enhanced error handling by adding new error codes to the
`MatchErrorCodes` class, including `MatchNotYetPlayed`,
`MatchNeedsReplay`, `InvalidVersionToken`, `MatchVersionConflict`,
and `MatchCorrectionFailed`.

Added corresponding error methods to the `MatchErrors` class to
generate detailed error messages for these scenarios, improving
clarity and robustness in match operation error reporting.
Added logic to determine if a match requires a replay by checking
its status and storing the result in the `isReplay` variable.
Introduced variables to preserve the previous state of the match,
including status, winner, player IDs, and scores.

Updated the match's properties (`WinnerId`, `Player1Score`,
`Player2Score`, and `Status`) to reflect the new state, explicitly
setting the status to `MatchStatus.Played`.

Implemented replay handling by invoking
`MatchCorrectionService.ApplyCorrectionAsync` when a replay is
detected, passing the previous match state and other relevant
parameters. Ensured that corrections are applied before saving
changes to the database asynchronously.
`MatchCascadePositionCalculator`:
- Added `GetDownstreamMatchId` to calculate downstream match IDs
  in a tournament bracket.

`MatchCorrectionService`:
- Added `ApplyCorrectionAsync` to handle match corrections,
  including audit logging and downstream propagation.
- Added `RecordIdempotentDuplicateAsync` to log duplicate
  correction requests.
- Added `PropagateAsync` to propagate corrections downstream,
  updating match participants, status, and winner as needed.
- Added `BuildAuditRow` to create detailed audit logs for corrections.
Added the `CorrectMatchResult` method in `MatchMutations` to allow authorized users to correct match results. This includes extensive validation checks for match existence, user ownership, tournament status, and score validity. Enhanced error handling ensures proper reporting of concurrency conflicts and database update failures.

Introduced the `GetVersion` resolver in `MatchResolvers` to encode and return the `RowVersion` of a match for version tracking.

Updated `ValidateMatchNotPlayed` in `MatchValidations` to use `MatchStatus` for determining if a match has been played. Added new validation methods: `ValidateMatchNotScheduled` and `ValidateMatchNotNeedsReplay`.
Implemented comprehensive test cases to cover:
- Score-only corrections without cascading changes.
- Winner changes with and without downstream matches.
- Cascade handling for downstream matches.
- Error handling for invalid inputs (e.g., non-existent
  matches, unauthorized users, negative scores, stale
  versions, etc.).
- Concurrency handling for identical requests.
- Audit logging with correlation IDs and chains.
- Champion self-healing after invalidated final matches.
The test ensures:
- Replay operation completes successfully without errors.
- Match status updates to `Played` and winner remains unchanged.
- Downstream matches are updated correctly.
- An additional audit row is written to `MatchCorrectionAudits`.
Added `Status` and `Version` properties to the `MatchNode` class
to store additional metadata about matches. Introduced the
`CorrectMatchResultResponse` and `CorrectMatchResultResult`
classes to handle responses for correcting match results.

Added the `CorrectMatchResult` GraphQL mutation to enable
correcting match results programmatically. Also added the
`GetMatchesForRoundWithStatusAndVersion` query to retrieve
matches for a specific round, including the new `Status` and
`Version` fields for better data handling and visibility.
Implemented the following unit tests for the `GetDownstreamMatchId`
method:
- Verifies correct downstream match ID for even positions.
- Verifies correct downstream match ID for odd positions.
- Ensures `null` is returned when the next round is not generated.
- Ensures `null` is returned for invalid match IDs.
- Ensures `null` is returned for trailing byes not yet generated.
- Added `EncodeThenTryDecode_RoundTripsToTheOriginalBytes` to
  verify encoding and decoding round-trip functionality.
- Added `TryDecode_WhenInputIsNull_ReturnsFalse` to ensure null
  input handling.
- Added `TryDecode_WhenInputIsEmpty_ReturnsFalse` to ensure empty
  string handling.
- Added `TryDecode_WhenInputIsMalformedBase64_ReturnsFalse` to
  validate malformed Base64 input handling.
…em needs replay

- Updated `ValidateAllMatchesCompleted_WhenSomeMatchesHaveNoWinner_ReturnsError` to use `MatchStatus.Played` and `MatchStatus.Scheduled`.
- Updated `ValidateAllMatchesCompleted_WhenAllMatchesHaveWinner_ReturnsNull` to use `MatchStatus.Played`.
- Added `ValidateAllMatchesCompleted_WhenAMatchNeedsReplay_ReturnsError` to test replay-needed matches and validate error handling.
Introduced new test methods:
- `ValidateMatchNotPlayed` now tests for `NeedsReplay`.
- `ValidateMatchNotScheduled` tests for `Scheduled`, `Played`,
  and `NeedsReplay`.
- `ValidateMatchNotNeedsReplay` tests for `NeedsReplay`,
  `Scheduled`, and `Played`.
@Dejmenek Dejmenek linked an issue Sep 16, 2026 that may be closed by this pull request
@Dejmenek
Dejmenek merged commit 74535f6 into main Sep 16, 2026
3 checks passed
@Dejmenek
Dejmenek deleted the feat/102-no-match-score-data-and-no-way-to-correct-a-mistaken-result branch September 16, 2026 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No match score data and no way to correct a mistaken result

1 participant