Update sync tests and refactoring

sync3
Laurent 6 months ago
parent 1ed3c66f5d
commit 2b082252ab
  1. 44
      PadelClubData/Data/GroupStage.swift
  2. 4
      PadelClubData/Data/MatchScheduler.swift
  3. 2
      PadelClubData/Data/Tournament.swift
  4. 271
      PadelClubDataTests/SyncDataAccessTests.swift
  5. 55
      PadelClubDataTests/SynchronizationTests.swift

@ -36,7 +36,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
// MARK: - Computed dependencies // MARK: - Computed dependencies
public func _matches() -> [Match] { public func matches() -> [Match] {
guard let tournamentStore = self.tournamentStore else { return [] } guard let tournamentStore = self.tournamentStore else { return [] }
return tournamentStore.matches.filter { $0.groupStage == self.id }.sorted(by: \.index) return tournamentStore.matches.filter { $0.groupStage == self.id }.sorted(by: \.index)
// Store.main.filter { $0.groupStage == self.id } // Store.main.filter { $0.groupStage == self.id }
@ -78,21 +78,21 @@ final public class GroupStage: BaseGroupStage, SideStorable {
} }
public func isRunning() -> Bool { // at least a match has started public func isRunning() -> Bool { // at least a match has started
_matches().anySatisfy({ $0.isRunning() }) matches().anySatisfy({ $0.isRunning() })
} }
public func hasStarted() -> Bool { // meaning at least one match is over public func hasStarted() -> Bool { // meaning at least one match is over
_matches().filter { $0.hasEnded() }.isEmpty == false matches().filter { $0.hasEnded() }.isEmpty == false
} }
public func hasEnded() -> Bool { public func hasEnded() -> Bool {
let _matches = _matches() let _matches = matches()
if _matches.isEmpty { return false } if _matches.isEmpty { return false }
//guard teams().count == size else { return false } //guard teams().count == size else { return false }
return _matches.anySatisfy { $0.hasEnded() == false } == false return _matches.anySatisfy { $0.hasEnded() == false } == false
} }
fileprivate func _createMatch(index: Int) -> Match { func createMatch(index: Int) -> Match {
let match: Match = Match(groupStage: self.id, let match: Match = Match(groupStage: self.id,
index: index, index: index,
format: self.matchFormat, format: self.matchFormat,
@ -104,7 +104,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
public func removeReturnMatches(onlyLast: Bool = false) { public func removeReturnMatches(onlyLast: Bool = false) {
var returnMatches = _matches().filter({ $0.index >= matchCount }) var returnMatches = matches().filter({ $0.index >= matchCount })
if onlyLast { if onlyLast {
let matchPhaseCount = matchPhaseCount - 1 let matchPhaseCount = matchPhaseCount - 1
returnMatches = returnMatches.filter({ $0.index >= matchCount * matchPhaseCount }) returnMatches = returnMatches.filter({ $0.index >= matchCount * matchPhaseCount })
@ -117,7 +117,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
} }
public var matchPhaseCount: Int { public var matchPhaseCount: Int {
let count = _matches().count let count = matches().count
if matchCount > 0 { if matchCount > 0 {
return count / matchCount return count / matchCount
} else { } else {
@ -130,7 +130,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
var matches = [Match]() var matches = [Match]()
let matchPhaseCount = matchPhaseCount let matchPhaseCount = matchPhaseCount
for i in 0..<_numberOfMatchesToBuild() { for i in 0..<_numberOfMatchesToBuild() {
let newMatch = self._createMatch(index: i + matchCount * matchPhaseCount) let newMatch = self.createMatch(index: i + matchCount * matchPhaseCount)
// let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i)) // let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i))
teamScores.append(contentsOf: newMatch.createTeamScores()) teamScores.append(contentsOf: newMatch.createTeamScores())
matches.append(newMatch) matches.append(newMatch)
@ -149,13 +149,13 @@ final public class GroupStage: BaseGroupStage, SideStorable {
_removeMatches() _removeMatches()
for i in 0..<_numberOfMatchesToBuild() { for i in 0..<_numberOfMatchesToBuild() {
let newMatch = self._createMatch(index: i) let newMatch = self.createMatch(index: i)
// let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i)) // let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i))
teamScores.append(contentsOf: newMatch.createTeamScores()) teamScores.append(contentsOf: newMatch.createTeamScores())
matches.append(newMatch) matches.append(newMatch)
} }
} else { } else {
for match in _matches() { for match in matches() {
match.resetTeamScores(outsideOf: []) match.resetTeamScores(outsideOf: [])
teamScores.append(contentsOf: match.createTeamScores()) teamScores.append(contentsOf: match.createTeamScores())
} }
@ -170,7 +170,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
} }
public func playedMatches() -> [Match] { public func playedMatches() -> [Match] {
let ordered = _matches() let ordered = matches()
let order = _matchOrder() let order = _matchOrder()
let matchCount = max(1, matchCount) let matchCount = max(1, matchCount)
let count = ordered.count / matchCount let count = ordered.count / matchCount
@ -258,7 +258,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
matchIndexes.append(index) matchIndexes.append(index)
} }
} }
return _matches().filter { matchIndexes.contains($0.index%matchCount) } return matches().filter { matchIndexes.contains($0.index%matchCount) }
} }
public func initialStartDate(forTeam team: TeamRegistration) -> Date? { public func initialStartDate(forTeam team: TeamRegistration) -> Date? {
@ -275,7 +275,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
matchIndexes.append(index) matchIndexes.append(index)
} }
} }
return _matches().first(where: { matchIndexes.contains($0.index) }) return matches().first(where: { matchIndexes.contains($0.index) })
} }
public func availableToStart(playedMatches: [Match], in runningMatches: [Match], checkCanPlay: Bool = true) -> [Match] { public func availableToStart(playedMatches: [Match], in runningMatches: [Match], checkCanPlay: Bool = true) -> [Match] {
@ -323,7 +323,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
} }
public func isReturnMatchEnabled() -> Bool { public func isReturnMatchEnabled() -> Bool {
_matches().count > matchCount matches().count > matchCount
} }
private func _matchOrder() -> [Int] { private func _matchOrder() -> [Int] {
@ -357,7 +357,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
public func returnMatchesSuffix(for matchIndex: Int) -> String { public func returnMatchesSuffix(for matchIndex: Int) -> String {
if matchCount > 0 { if matchCount > 0 {
let count = _matches().count let count = matches().count
if count > matchCount * 2 { if count > matchCount * 2 {
return " - vague \((matchIndex / matchCount) + 1)" return " - vague \((matchIndex / matchCount) + 1)"
} }
@ -398,11 +398,11 @@ final public class GroupStage: BaseGroupStage, SideStorable {
return combinations[safe: matchIndex%matchCount]?.map { teamAt(groupStagePosition: $0) } ?? [] return combinations[safe: matchIndex%matchCount]?.map { teamAt(groupStagePosition: $0) } ?? []
} }
private func _removeMatches() { func _removeMatches() {
self.tournamentStore?.matches.delete(contentOfs: _matches()) self.tournamentStore?.matches.delete(contentOfs: matches())
} }
private func _numberOfMatchesToBuild() -> Int { func _numberOfMatchesToBuild() -> Int {
(size * (size - 1)) / 2 (size * (size - 1)) / 2
} }
@ -418,7 +418,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
let indexes = [teamPosition, otherTeam].compactMap({ $0.groupStagePosition }).sorted() let indexes = [teamPosition, otherTeam].compactMap({ $0.groupStagePosition }).sorted()
let combos = Array((0..<size).combinations(ofCount: 2)) let combos = Array((0..<size).combinations(ofCount: 2))
let matchIndexes = combos.enumerated().compactMap { $0.element == indexes ? $0.offset : nil } let matchIndexes = combos.enumerated().compactMap { $0.element == indexes ? $0.offset : nil }
let matches = _matches().filter { matchIndexes.contains($0.index) } let matches = matches().filter { matchIndexes.contains($0.index) }
if matches.count > 1 { if matches.count > 1 {
let scoreA = calculateScore(for: teamPosition, matches: matches, groupStagePosition: teamPosition.groupStagePosition!) let scoreA = calculateScore(for: teamPosition, matches: matches, groupStagePosition: teamPosition.groupStagePosition!)
let scoreB = calculateScore(for: otherTeam, matches: matches, groupStagePosition: otherTeam.groupStagePosition!) let scoreB = calculateScore(for: otherTeam, matches: matches, groupStagePosition: otherTeam.groupStagePosition!)
@ -445,7 +445,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
return teamsSorted.first == teamPosition return teamsSorted.first == teamPosition
} else { } else {
if let matchIndex = combos.firstIndex(of: indexes), let match = _matches().first(where: { $0.index == matchIndex }) { if let matchIndex = combos.firstIndex(of: indexes), let match = matches().first(where: { $0.index == matchIndex }) {
return teamPosition.id == match.losingTeamId return teamPosition.id == match.losingTeamId
} else { } else {
return false return false
@ -617,7 +617,7 @@ final public class GroupStage: BaseGroupStage, SideStorable {
func insertOnServer() { func insertOnServer() {
self.tournamentStore?.groupStages.writeChangeAndInsertOnServer(instance: self) self.tournamentStore?.groupStages.writeChangeAndInsertOnServer(instance: self)
for match in self._matches() { for match in self.matches() {
match.insertOnServer() match.insertOnServer()
} }
} }
@ -645,7 +645,7 @@ extension GroupStage: Selectable {
} }
public func badgeValue() -> Int? { public func badgeValue() -> Int? {
return runningMatches(playedMatches: _matches()).count return runningMatches(playedMatches: matches()).count
} }
public func badgeValueColor() -> Color? { public func badgeValueColor() -> Color? {

@ -75,7 +75,7 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable {
groupStages = [specificGroupStage] groupStages = [specificGroupStage]
} }
let matches = groupStages.flatMap { $0._matches() } let matches = groupStages.flatMap { $0.matches() }
matches.forEach({ matches.forEach({
$0.removeCourt() $0.removeCourt()
$0.startDate = nil $0.startDate = nil
@ -153,7 +153,7 @@ final public class MatchScheduler: BaseMatchScheduler, SideStorable {
let _groupStages = groupStages let _groupStages = groupStages
// Get the maximum count of matches in any group // Get the maximum count of matches in any group
let maxMatchesCount = _groupStages.map { $0._matches().count }.max() ?? 0 let maxMatchesCount = _groupStages.map { $0.matches().count }.max() ?? 0
var flattenedMatches = [Match]() var flattenedMatches = [Match]()
if simultaneousStart { if simultaneousStart {
// Flatten matches in a round-robin order by cycling through each group // Flatten matches in a round-robin order by cycling through each group

@ -818,7 +818,7 @@ defer {
} }
public func groupStagesMatches(atStep step: Int = 0) -> [Match] { public func groupStagesMatches(atStep step: Int = 0) -> [Match] {
return groupStages(atStep: step).flatMap({ $0._matches() }) return groupStages(atStep: step).flatMap({ $0.matches() })
// return Store.main.filter(isIncluded: { $0.groupStage != nil && groupStageIds.contains($0.groupStage!) }) // return Store.main.filter(isIncluded: { $0.groupStage != nil && groupStageIds.contains($0.groupStage!) })
} }

@ -24,15 +24,28 @@ struct SyncDataAccessTests {
let conf = Config.server let conf = Config.server
let dir = "storage"
let dirA = "storageA" let dirA = "storageA"
let dirB = "storageB" let dirB = "storageB"
FileManager.default.deleteDirectoryInDocuments(directoryName: dirA) FileManager.default.deleteDirectoryInDocuments(directoryName: dir)
FileManager.default.deleteDirectoryInDocuments(directoryName: dirB) FileManager.default.deleteDirectoryInDocuments(directoryName: dirB)
self.storeCenterA = StoreCenter(directoryName: dirA) self.storeCenterA = StoreCenter(directoryName: dirA)
self.storeCenterB = StoreCenter(directoryName: dirB) self.storeCenterB = StoreCenter(directoryName: dirB)
// StoreCenter.main
StoreCenter.main.configureURLs(secureScheme: conf.secure, domain: conf.domain, webSockets: false, useSynchronization: true)
StoreCenter.main.tokenKeychain = MockKeychainStore(fileName: "\(dir)/token.json")
StoreCenter.main.deviceKeychain = MockKeychainStore(fileName: "\(dir)/device.json")
try StoreCenter.main.deviceKeychain.add(value: UUID().uuidString)
StoreCenter.main.classProject = "PadelClubData"
let token = try? StoreCenter.main.rawTokenShouldNotBeUsed()
if token == nil {
try await self.login(storeCenter: StoreCenter.main, username: self.username1, password: self.password1)
}
// StoreCenter A // StoreCenter A
self.storeCenterA.configureURLs(secureScheme: conf.secure, domain: conf.domain, webSockets: false, useSynchronization: true) self.storeCenterA.configureURLs(secureScheme: conf.secure, domain: conf.domain, webSockets: false, useSynchronization: true)
self.storeCenterA.tokenKeychain = MockKeychainStore(fileName: "\(dirA)/token.json") self.storeCenterA.tokenKeychain = MockKeychainStore(fileName: "\(dirA)/token.json")
@ -40,8 +53,8 @@ struct SyncDataAccessTests {
try self.storeCenterA.deviceKeychain.add(value: UUID().uuidString) try self.storeCenterA.deviceKeychain.add(value: UUID().uuidString)
self.storeCenterA.classProject = "PadelClubData" self.storeCenterA.classProject = "PadelClubData"
let token = try? self.storeCenterA.rawTokenShouldNotBeUsed() let tokenA = try? self.storeCenterA.rawTokenShouldNotBeUsed()
if token == nil { if tokenA == nil {
try await self.login(storeCenter: self.storeCenterA, username: self.username1, password: self.password1) try await self.login(storeCenter: self.storeCenterA, username: self.username1, password: self.password1)
} }
@ -53,8 +66,8 @@ struct SyncDataAccessTests {
self.storeCenterB.classProject = "PadelClubData" self.storeCenterB.classProject = "PadelClubData"
let token2 = try? self.storeCenterB.rawTokenShouldNotBeUsed() let tokenB = try? self.storeCenterB.rawTokenShouldNotBeUsed()
if token2 == nil { if tokenB == nil {
try await self.login(storeCenter: self.storeCenterB, username: self.username2, password: self.password2) try await self.login(storeCenter: self.storeCenterB, username: self.username2, password: self.password2)
} }
@ -346,4 +359,252 @@ struct SyncDataAccessTests {
} }
@Test func testBuildEverything() async throws {
guard let _ = StoreCenter.main.userId else {
throw TestError.notAuthenticated
}
guard let userId2 = self.storeCenterB.userId else {
throw TestError.notAuthenticated
}
// Cleanup
let tournamentColA: SyncedCollection<Tournament> = await StoreCenter.main.mainStore.asyncLoadingSynchronizedCollection()
let tournamentColB: SyncedCollection<Tournament> = await self.storeCenterB.mainStore.asyncLoadingSynchronizedCollection()
let tournamentsToDelete: [Tournament] = try await StoreCenter.main.service().get()
try await tournamentColA.deleteAsync(contentOfs: tournamentsToDelete)
// Setup tournament + build everything
let tournament = Tournament()
try await tournamentColA.addOrUpdateAsync(instance: tournament)
try await tournament.deleteAndBuildEverythingAsync()
let tourStoreA = try StoreCenter.main.store(identifier: tournament.id)
let gsColA: SyncedCollection<GroupStage> = try tourStoreA.syncedCollection()
let roundColA: SyncedCollection<Round> = try tourStoreA.syncedCollection()
let matchesColA: SyncedCollection<Match> = try tourStoreA.syncedCollection()
#expect(gsColA.count == 4)
#expect(roundColA.count == 15)
#expect(matchesColA.count == 56)
// todo add sharing
try await StoreCenter.main.setAuthorizedUsersAsync(for: tournament, users: [userId2])
// Sync with 2nd store
try await self.storeCenterB.testSynchronizeOnceAsync()
#expect(tournamentColB.count == 1)
let tourStoreB = try self.storeCenterB.store(identifier: tournament.id)
let gsColB: SyncedCollection<GroupStage> = try tourStoreB.syncedCollection()
let roundColB: SyncedCollection<Round> = try tourStoreB.syncedCollection()
let matchesColB: SyncedCollection<Match> = try tourStoreB.syncedCollection()
#expect(gsColB.count == 4)
#expect(roundColB.count == 15)
#expect(matchesColB.count == 56)
// change setup + build everything
tournament.groupStageCount = 2
tournament.teamCount = 20
try await tournamentColA.addOrUpdateAsync(instance: tournament)
try await tournament.deleteAndBuildEverythingAsync()
#expect(gsColA.count == 2)
#expect(roundColA.count == 15)
#expect(matchesColA.count == 44)
// Sync with 2nd store
let data = try await self.storeCenterB.testSynchronizeOnceAsync()
let syncData = try SyncData(data: data, storeCenter: self.storeCenterB)
#expect(gsColB.count == 2)
#expect(roundColB.count == 15)
#expect(matchesColB.count == 44)
}
}
extension Tournament {
@MainActor
public func deleteAndBuildEverythingAsync(preset: PadelTournamentStructurePreset = .manual) async throws {
resetBracketPosition()
try await deleteStructureAsync()
try await deleteGroupStagesAsync()
switch preset {
case .doubleGroupStage:
try await buildGroupStagesAsync()
try await addNewGroupStageStepAsync()
qualifiedPerGroupStage = 0
groupStageAdditionalQualified = 0
default:
try await buildGroupStagesAsync()
try await buildBracketAsync()
}
}
public func addNewGroupStageStepAsync() async throws {
let lastStep = lastStep() + 1
for i in 0..<teamsPerGroupStage {
let gs = GroupStage(tournament: id, index: i, size: groupStageCount, step: lastStep)
try await self.tournamentStore?.groupStages.addOrUpdateAsync(instance: gs)
}
groupStages(atStep: 1).forEach { $0.buildMatches() }
}
public func deleteStructureAsync() async throws {
try await self.tournamentStore?.rounds.deleteAsync(contentOfs: rounds())
}
public func deleteGroupStagesAsync() async throws {
try await self.tournamentStore?.groupStages.deleteAsync(contentOfs: allGroupStages())
}
func buildGroupStagesAsync() async throws {
guard groupStages().isEmpty, let tournamentStore = self.tournamentStore else {
return
}
var _groupStages = [GroupStage]()
for index in 0..<groupStageCount {
let groupStage = GroupStage(tournament: id, index: index, size: teamsPerGroupStage, format: groupStageFormat)
_groupStages.append(groupStage)
}
try await self.tournamentStore?.groupStages.addOrUpdateAsync(contentOfs: _groupStages)
try await refreshGroupStagesAsync()
}
public func refreshGroupStagesAsync(keepExistingMatches: Bool = false) async throws {
unsortedTeams().forEach { team in
team.groupStage = nil
team.groupStagePosition = nil
}
if groupStageCount > 0 {
switch groupStageOrderingMode {
case .random:
try await setGroupStageAsync(randomize: true, keepExistingMatches: keepExistingMatches)
case .snake:
try await setGroupStageAsync(randomize: false, keepExistingMatches: keepExistingMatches)
case .swiss:
try await setGroupStageAsync(randomize: true, keepExistingMatches: keepExistingMatches)
}
}
}
public func setGroupStageAsync(randomize: Bool, keepExistingMatches: Bool = false) async throws {
let groupStages = groupStages()
let numberOfBracketsAsInt = groupStages.count
// let teamsPerBracket = teamsPerBracket
if groupStageCount != numberOfBracketsAsInt {
try await deleteGroupStagesAsync()
try await buildGroupStagesAsync()
} else {
// setGroupStageTeams(randomize: randomize)
for groupStage in groupStages {
try await groupStage.buildMatchesAsync(keepExistingMatches: keepExistingMatches)
}
}
}
public func buildBracketAsync(minimalBracketTeamCount: Int? = nil) async throws {
guard rounds().isEmpty else { return }
let roundCount = RoundRule.numberOfRounds(forTeams: minimalBracketTeamCount ?? bracketTeamCount())
let matchCount = RoundRule.numberOfMatches(forTeams: minimalBracketTeamCount ?? bracketTeamCount())
let rounds = (0..<roundCount).map { //index 0 is the final
return Round(tournament: id, index: $0, matchFormat: matchFormat, loserBracketMode: loserBracketMode)
}
if rounds.isEmpty {
return
}
try await self.tournamentStore?.rounds.addOrUpdateAsync(contentOfs: rounds)
let matches = (0..<matchCount).map { //0 is final match
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0)
let round = rounds[roundIndex]
return Match(round: round.id, index: $0, format: round.matchFormat, name: Match.setServerTitle(upperRound: round, matchIndex: RoundRule.matchIndexWithinRound(fromMatchIndex: $0)))
}
try await self.tournamentStore?.matches.addOrUpdateAsync(contentOfs: matches)
for round in rounds {
try await round.buildLoserBracketAsync()
}
}
}
extension GroupStage {
public func buildMatchesAsync(keepExistingMatches: Bool = false) async throws {
var teamScores = [TeamScore]()
var matches = [Match]()
clearScoreCache()
if keepExistingMatches == false {
_removeMatches()
for i in 0..<_numberOfMatchesToBuild() {
let newMatch = self._createMatch(index: i)
// let newMatch = Match(groupStage: self.id, index: i, matchFormat: self.matchFormat, name: localizedMatchUpLabel(for: i))
teamScores.append(contentsOf: newMatch.createTeamScores())
matches.append(newMatch)
}
} else {
for match in matches() {
match.resetTeamScores(outsideOf: [])
teamScores.append(contentsOf: match.createTeamScores())
}
}
try await self.tournamentStore?.matches.addOrUpdateAsync(contentOfs: matches)
try await self.tournamentStore?.teamScores.addOrUpdateAsync(contentOfs: teamScores)
}
}
extension Round {
public func buildLoserBracketAsync() async throws {
guard loserRounds().isEmpty else { return }
self.invalidateCache()
let currentRoundMatchCount = RoundRule.numberOfMatches(forRoundIndex: index)
guard currentRoundMatchCount > 1 else { return }
guard let tournamentStore else { return }
let roundCount = RoundRule.numberOfRounds(forTeams: currentRoundMatchCount)
let loserBracketMatchFormat = tournamentObject()?.loserBracketMatchFormat
let rounds = (0..<roundCount).map { //index 0 is the final
let round = Round(tournament: tournament, index: $0, matchFormat: loserBracketMatchFormat)
round.parent = id //parent
//titles[round.id] = round.roundTitle(initialMode: true)
return round
}
try await tournamentStore.rounds.addOrUpdateAsync(contentOfs: rounds)
let matchCount = RoundRule.numberOfMatches(forTeams: currentRoundMatchCount)
let matches = (0..<matchCount).map { //0 is final match
let roundIndex = RoundRule.roundIndex(fromMatchIndex: $0)
let round = rounds[roundIndex]
//let title = titles[round.id]
return Match(round: round.id, index: $0, format: loserBracketMatchFormat)
//initial mode let the roundTitle give a name without considering the playable match
}
try await tournamentStore.matches.addOrUpdateAsync(contentOfs: matches)
for round in rounds {
try await round.buildLoserBracketAsync()
}
}
} }

@ -311,5 +311,60 @@ struct SynchronizationTests {
} }
@Test func testBuildEverything() async throws {
// Cleanup
let tournamentColA: SyncedCollection<Tournament> = await StoreCenter.main.mainStore.asyncLoadingSynchronizedCollection()
let tournamentColB: SyncedCollection<Tournament> = await self.secondStoreCenter.mainStore.asyncLoadingSynchronizedCollection()
let tournamentsToDelete: [Tournament] = try await StoreCenter.main.service().get()
tournamentColA.delete(contentOfs: tournamentsToDelete)
// Setup tournament + build everything
let tournament = Tournament()
try await tournamentColA.addOrUpdateAsync(instance: tournament)
try await tournament.deleteAndBuildEverything()
let tourStore = try StoreCenter.main.store(identifier: tournament.id)
let gsColA: SyncedCollection<GroupStage> = try tourStore.syncedCollection()
let roundColA: SyncedCollection<Round> = try tourStore.syncedCollection()
let matchesColA: SyncedCollection<Match> = try tourStore.syncedCollection()
#expect(gsColA.count == 4)
#expect(roundColA.count == 15)
#expect(matchesColA.count == 56)
// Sync with 2nd store
try await secondStoreCenter.testSynchronizeOnceAsync()
#expect(tournamentColB.count == 1)
let tourStoreB = try secondStoreCenter.store(identifier: tournament.id)
let gsColB: SyncedCollection<GroupStage> = try tourStoreB.syncedCollection()
let roundColB: SyncedCollection<Round> = try tourStoreB.syncedCollection()
let matchesColB: SyncedCollection<Match> = try tourStoreB.syncedCollection()
#expect(gsColB.count == 4)
#expect(roundColB.count == 15)
#expect(matchesColB.count == 56)
// change setup + build everything
tournament.groupStageCount = 2
tournament.teamCount = 20
try await tournamentColA.addOrUpdateAsync(instance: tournament)
try await tournament.deleteAndBuildEverything()
#expect(gsColA.count == 2)
#expect(roundColA.count == 15)
#expect(matchesColA.count == 44)
// Sync with 2nd store
try await secondStoreCenter.testSynchronizeOnceAsync()
#expect(gsColB.count == 2)
#expect(roundColB.count == 15)
#expect(matchesColB.count == 44)
}
} }

Loading…
Cancel
Save