diff --git a/src/create.ts b/src/create.ts index a6628d0..ab7fd2d 100644 --- a/src/create.ts +++ b/src/create.ts @@ -351,9 +351,8 @@ export class Create { if (this.stage.type === 'round_robin' && opponent1 === null && opponent2 === null) return; - const status = helpers.getMatchByeStatus(opponents); - let existing: Match | null = null; + let status = helpers.getMatchStatus(opponents); if (this.updateMode) { existing = await this.storage.selectFirst('match', { @@ -363,6 +362,13 @@ export class Create { const currentChildCount = existing?.child_count; childCount = currentChildCount === undefined ? childCount : currentChildCount; + + if (existing) { + // Keep the most advanced status when updating a match. + const existingStatus = helpers.getMatchStatus(existing); + if (existingStatus > status) + status = existingStatus; + } } const parentId = await this.insertMatch({ @@ -706,7 +712,8 @@ export class Create { if (!existing) return this.storage.insert('match', match); - if (!await this.storage.update('match', existing.id, { ...existing, ...helpers.getUpdatedMatchResults(match) })) + const updated = helpers.getUpdatedMatchResults(match, existing) as Match; + if (!await this.storage.update('match', existing.id, updated)) throw Error('Could not update the match.'); return existing.id; @@ -730,7 +737,8 @@ export class Create { if (!existing) return this.storage.insert('match_game', matchGame); - if (!await this.storage.update('match_game', existing.id, { ...existing, ...helpers.getUpdatedMatchResults(matchGame) })) + const updated = helpers.getUpdatedMatchResults(matchGame, existing) as MatchGame; + if (!await this.storage.update('match_game', existing.id, updated)) throw Error('Could not update the match game.'); return existing.id; diff --git a/src/helpers.ts b/src/helpers.ts index 04dadf7..a859c4d 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -591,18 +591,6 @@ export function isMatchParticipantLocked(match: MatchResults): boolean { return match.status >= Status.Running; } -/** - * Returns the status of a match based on the presence of the opponents. - * - * @param opponents The opponents of a match. - */ -export function getMatchByeStatus(opponents: Duel): Status { - return getMatchStatus({ - opponent1: opponents[0], - opponent2: opponents[1], - }); -} - /** * Indicates whether a match has at least one BYE or not. * @@ -612,12 +600,31 @@ export function hasBye(match: Partial): boolean { return match.opponent1 === null || match.opponent2 === null; } +/** + * Returns the status of a match based on the opponents of a match. + * + * @param opponents The opponents of a match. + */ +export function getMatchStatus(opponents: Duel): Status; + /** * Returns the status of a match based on the results of a match. * * @param match Partial match results. */ -export function getMatchStatus(match: Partial): Status { +export function getMatchStatus(match: Partial): Status; + +/** + * Returns the status of a match based on information about it. + * + * @param arg The opponents or partial results of the match. + */ +export function getMatchStatus(arg: Duel | Partial): Status { + const match = Array.isArray(arg) ? { + opponent1: arg[0], + opponent2: arg[1], + } : arg; + if (hasBye(match)) // At least one BYE. return Status.Locked; @@ -1250,12 +1257,14 @@ export function getParentMatchResults(storedParent: Match, scores: Scores): Part * Gets the values which need to be updated in a match when it's updated on insertion. * * @param match The up to date match. + * @param existing The base match. */ -export function getUpdatedMatchResults(match: MatchResults): MatchResults { +export function getUpdatedMatchResults(match: T, existing: T): T { return { - status: match.status, - opponent1: match.opponent1, - opponent2: match.opponent2, + ...existing, + ...match, + opponent1: { ...existing.opponent1, ...match.opponent1 }, + opponent2: { ...existing.opponent2, ...match.opponent2 }, }; } diff --git a/test/update.spec.js b/test/update.spec.js index 4953c7e..bb1b650 100644 --- a/test/update.spec.js +++ b/test/update.spec.js @@ -665,6 +665,54 @@ describe('Seeding', () => { assert.strictEqual((await storage.select('participant')).length, 8 + 6); }); + it('should update the seeding and keep completed matches completed', async () => { + await manager.update.seeding(0, [ + 'Team 1', 'Team 2', + 'Team 3', 'Team 4', + 'Team 5', 'Team 6', + 'Team 7', 'Team 8', + ]); + + await manager.update.match({ + id: 0, + opponent1: { score: 1, result: 'win' }, + opponent2: { score: 0 }, + }); + + await manager.update.seeding(0, [ + 'Team 1', 'Team 2', // Keep this pair. + 'Team 4', 'Team 3', + 'Team 6', 'Team 5', + 'Team 8', 'Team 7', + ]); + + const match = await storage.select('match', 0); + assert.strictEqual(match.opponent1.result, 'win'); + assert.strictEqual(match.status, Status.Completed); + }); + + it('should throw if a match is completed and would have to be changed', async () => { + await manager.update.seeding(0, [ + 'Team 1', 'Team 2', + 'Team 3', 'Team 4', + 'Team 5', 'Team 6', + 'Team 7', 'Team 8', + ]); + + await manager.update.match({ + id: 0, + opponent1: { score: 1, result: 'win' }, + opponent2: { score: 0 }, + }); + + await assert.isRejected(manager.update.seeding(0, [ + 'Team 2', 'Team 1', // Change this pair. + 'Team 3', 'Team 4', + 'Team 5', 'Team 6', + 'Team 7', 'Team 8', + ]), 'A match is locked.'); + }); + it('should throw if a match is locked and would have to be changed', async () => { await manager.update.seeding(0, [ 'Team 1', 'Team 2',