Compare commits

...

2 Commits

Author SHA1 Message Date
Laurent 441255dba4 adds a server test 7 months ago
Laurent f0d285104f sync fixes 7 months ago
  1. 4
      PadelClub.xcodeproj/project.pbxproj
  2. 6
      PadelClub/Data/Club.swift
  3. 2
      PadelClub/Data/Court.swift
  4. 2
      PadelClub/Data/DateInterval.swift
  5. 2
      PadelClub/Data/DrawLog.swift
  6. 10
      PadelClub/Data/Event.swift
  7. 6
      PadelClub/Data/GroupStage.swift
  8. 6
      PadelClub/Data/Match.swift
  9. 10
      PadelClub/Data/Round.swift
  10. 10
      PadelClub/Data/TeamRegistration.swift
  11. 24
      PadelClub/Data/Tournament.swift
  12. 6
      PadelClub/Views/Tournament/TournamentView.swift
  13. 2
      PadelClub/Views/User/ShareModelView.swift
  14. 2
      PadelClubTests/ServerDataTests.swift
  15. 67
      PadelClubTests/ServerStateTests.swift
  16. 4
      PadelClubTests/SynchronizationTests.swift

@ -157,6 +157,7 @@
C4A47DA92B85F82100ADC637 /* ChangePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */; };
C4A47DAD2B85FCCD00ADC637 /* CustomUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */; };
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47DB22B86387500ADC637 /* AccountView.swift */; };
C4AD83612DAE5EBF00022F97 /* ServerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AD83602DAE5EBF00022F97 /* ServerStateTests.swift */; };
C4B3A1552C2581DA0078EAA8 /* Patcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B3A1542C2581DA0078EAA8 /* Patcher.swift */; };
C4C01D982C481C0C0059087C /* CapsuleViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */; };
C4C33F762C9B1ED4006316DE /* CodingContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */; };
@ -1123,6 +1124,7 @@
C4A47DA82B85F82100ADC637 /* ChangePasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePasswordView.swift; sourceTree = "<group>"; };
C4A47DAC2B85FCCD00ADC637 /* CustomUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomUser.swift; sourceTree = "<group>"; };
C4A47DB22B86387500ADC637 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; };
C4AD83602DAE5EBF00022F97 /* ServerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerStateTests.swift; sourceTree = "<group>"; };
C4B3A1542C2581DA0078EAA8 /* Patcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Patcher.swift; sourceTree = "<group>"; };
C4C01D972C481C0C0059087C /* CapsuleViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapsuleViewModifier.swift; sourceTree = "<group>"; };
C4C33F752C9B1EC5006316DE /* CodingContainer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CodingContainer+Extensions.swift"; sourceTree = "<group>"; };
@ -1542,6 +1544,7 @@
C425D4112B6D249E002A7B48 /* PadelClubTests.swift */,
C49EF0412BE23BF50077B5AA /* PaymentTests.swift */,
C411C9C22BEBA453003017AD /* ServerDataTests.swift */,
C4AD83602DAE5EBF00022F97 /* ServerStateTests.swift */,
C411C9C82BF219CB003017AD /* UserDataTests.swift */,
C411C9CF2BF38F41003017AD /* TokenExemptionTests.swift */,
C4D477982CB6704C0077713D /* SynchronizationTests.swift */,
@ -2885,6 +2888,7 @@
C49EF0422BE23BF50077B5AA /* PaymentTests.swift in Sources */,
C4D477992CB6704C0077713D /* SynchronizationTests.swift in Sources */,
C425D4122B6D249E002A7B48 /* PadelClubTests.swift in Sources */,
C4AD83612DAE5EBF00022F97 /* ServerStateTests.swift in Sources */,
C411C9C92BF219CB003017AD /* UserDataTests.swift in Sources */,
C411C9C32BEBA453003017AD /* ServerDataTests.swift in Sources */,
);

@ -31,12 +31,12 @@ final class Club: BaseClub {
DataStore.shared.courts.filter { $0.club == self.id }.sorted(by: \.index)
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
let customizedCourts = self.customizedCourts
for customizedCourt in customizedCourts {
customizedCourt.deleteDependencies()
customizedCourt.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
DataStore.shared.courts.deleteDependencies(customizedCourts)
DataStore.shared.courts.deleteDependencies(customizedCourts, shouldBeSynchronized: shouldBeSynchronized)
}
}

@ -52,7 +52,7 @@ final class Court: BaseCourt {
Store.main.findById(club)
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
}
}

@ -57,7 +57,7 @@ final class DateInterval: BaseDateInterval {
date <= startDate && date <= endDate && date >= startDate && date >= endDate
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
}
// enum CodingKeys: String, CodingKey {

@ -74,7 +74,7 @@ final class DrawLog: BaseDrawLog, SideStorable {
return TournamentLibrary.shared.store(tournamentId: self.tournament)
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
}
}

@ -25,19 +25,19 @@ final class Event: BaseEvent {
super.init()
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
let tournaments = self.tournaments
for tournament in tournaments {
tournament.deleteDependencies()
tournament.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
DataStore.shared.tournaments.deleteDependencies(tournaments)
DataStore.shared.tournaments.deleteDependencies(tournaments, shouldBeSynchronized: shouldBeSynchronized)
let courtsUnavailabilities = self.courtsUnavailability
for courtsUnavailability in courtsUnavailabilities {
courtsUnavailability.deleteDependencies()
courtsUnavailability.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities)
DataStore.shared.dateIntervals.deleteDependencies(courtsUnavailabilities, shouldBeSynchronized: shouldBeSynchronized)
}
// MARK: - Computed dependencies

@ -599,12 +599,12 @@ final class GroupStage: BaseGroupStage, SideStorable {
return teams(true).firstIndex(of: team)
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
let matches = self._matches()
for match in matches {
match.deleteDependencies()
match.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
self.tournamentStore?.matches.deleteDependencies(matches)
self.tournamentStore?.matches.deleteDependencies(matches, shouldBeSynchronized: shouldBeSynchronized)
}
// init(from decoder: Decoder) throws {

@ -70,12 +70,12 @@ final class Match: BaseMatch, SideStorable {
// MARK: -
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
let teamScores = self.teamScores
for teamScore in teamScores {
teamScore.deleteDependencies()
teamScore.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
self.tournamentStore?.teamScores.deleteDependencies(teamScores)
self.tournamentStore?.teamScores.deleteDependencies(teamScores, shouldBeSynchronized: shouldBeSynchronized)
}
func indexInRound(in matches: [Match]? = nil) -> Int {

@ -711,20 +711,20 @@ defer {
self.tournamentStore?.matches.addOrUpdate(contentOfs: playedMatches)
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
let matches = self._matches()
for match in matches {
match.deleteDependencies()
match.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
self.tournamentStore?.matches.deleteDependencies(matches)
self.tournamentStore?.matches.deleteDependencies(matches, shouldBeSynchronized: shouldBeSynchronized)
let loserRounds = self.loserRounds()
for round in loserRounds {
round.deleteDependencies()
round.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
self.tournamentStore?.rounds.deleteDependencies(loserRounds)
self.tournamentStore?.rounds.deleteDependencies(loserRounds, shouldBeSynchronized: shouldBeSynchronized)
}
// enum CodingKeys: String, CodingKey {

@ -116,18 +116,18 @@ final class TeamRegistration: BaseTeamRegistration, SideStorable {
tournamentStore.teamScores.delete(contentOfs: ts)
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
let unsortedPlayers = unsortedPlayers()
for player in unsortedPlayers {
player.deleteDependencies()
player.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
self.tournamentStore?.playerRegistrations.deleteDependencies(unsortedPlayers)
self.tournamentStore?.playerRegistrations.deleteDependencies(unsortedPlayers, shouldBeSynchronized: shouldBeSynchronized)
let teamScores = teamScores()
for teamScore in teamScores {
teamScore.deleteDependencies()
teamScore.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
self.tournamentStore?.teamScores.deleteDependencies(teamScores)
self.tournamentStore?.teamScores.deleteDependencies(teamScores, shouldBeSynchronized: shouldBeSynchronized)
}
func hasArrived(isHere: Bool = false) {

@ -124,32 +124,32 @@ final class Tournament: BaseTournament {
return TournamentLibrary.shared.store(tournamentId: self.id)
}
override func deleteDependencies() {
override func deleteDependencies(shouldBeSynchronized: Bool) {
guard let store = self.tournamentStore else { return }
let drawLogs = Array(store.drawLogs)
for drawLog in drawLogs {
drawLog.deleteDependencies()
drawLog.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
store.drawLogs.deleteDependencies(drawLogs)
store.drawLogs.deleteDependencies(drawLogs, shouldBeSynchronized: shouldBeSynchronized)
let teams = Array(store.teamRegistrations)
for team in teams {
team.deleteDependencies()
team.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
store.teamRegistrations.deleteDependencies(teams)
store.teamRegistrations.deleteDependencies(teams, shouldBeSynchronized: shouldBeSynchronized)
let groups = Array(store.groupStages)
for group in groups {
group.deleteDependencies()
group.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
store.groupStages.deleteDependencies(groups)
store.groupStages.deleteDependencies(groups, shouldBeSynchronized: shouldBeSynchronized)
let rounds = Array(store.rounds)
for round in rounds {
round.deleteDependencies()
round.deleteDependencies(shouldBeSynchronized: shouldBeSynchronized)
}
store.rounds.deleteDependencies(rounds)
store.rounds.deleteDependencies(rounds, shouldBeSynchronized: shouldBeSynchronized)
store.matchSchedulers.deleteDependencies(self._matchSchedulers())
@ -2394,9 +2394,9 @@ defer {
guard shouldRefreshTeams(forced: forced), refreshInProgress == false, enableOnlineRegistration, hasEnded() == false else { return }
refreshInProgress = true
do {
try await self.tournamentStore?.playerRegistrations.loadDataFromServerIfAllowed(clear: true)
try await self.tournamentStore?.teamScores.loadDataFromServerIfAllowed(clear: true)
try await self.tournamentStore?.teamRegistrations.loadDataFromServerIfAllowed(clear: true)
// try await self.tournamentStore?.playerRegistrations.loadDataFromServerIfAllowed(clear: true)
// try await self.tournamentStore?.teamScores.loadDataFromServerIfAllowed(clear: true)
// try await self.tournamentStore?.teamRegistrations.loadDataFromServerIfAllowed(clear: true)
refreshInProgress = false
lastTeamRefresh = Date()
} catch {

@ -268,9 +268,9 @@ struct TournamentView: View {
Label("Imprimer", systemImage: "printer")
}
// NavigationLink(value: Screen.share) {
// Label("Partager", systemImage: "square.and.arrow.up")
// }
NavigationLink(value: Screen.share) {
Label("Partager", systemImage: "square.and.arrow.up")
}
Divider()

@ -70,7 +70,7 @@ struct ShareModelView<T: SyncedStorable> : View {
.listStyle(PlainListStyle())
.navigationTitle("Partage")
} else {
ContentUnavailableView("Si vous souhaitez partager votre tournoi avec d'autres utilisateurs, veuillez contacter notre support", image: "person.crop.circle.badge.xmark")
ContentUnavailableView("Si vous souhaitez partager votre tournoi avec d'autres utilisateurs, veuillez contacter notre support", systemImage: "person.crop.circle.badge.xmark")
}
}.onAppear {

@ -435,7 +435,7 @@ final class ServerDataTests: XCTestCase {
let transactionId = UInt64.random(in: 0...100000)
let quantity = Int.random(in: 0...10)
let purchase: Purchase = Purchase(user: userId, transactionId: transactionId, purchaseDate: Date(), productId: "app.padelclub.productId", quantity: quantity, revocationDate: Date(), expirationDate: Date())
let purchase: Purchase = Purchase(transactionId: transactionId, user: userId, purchaseDate: Date(), productId: "app.padelclub.productId", quantity: quantity, revocationDate: Date(), expirationDate: Date())
if let p: Purchase = try await StoreCenter.main.service().post(purchase) {

@ -0,0 +1,67 @@
//
// ServerStateTests.swift
// PadelClubTests
//
// Created by Laurent Morvillier on 15/04/2025.
//
import Testing
@testable import LeStorage
@testable import PadelClub
struct ServerStateTests {
let username: String = "sync"
let password: String = "sync123"
var user: CustomUser? = nil
init() {
StoreCenter.main.configureURLs(secureScheme: false, domain: "127.0.0.1:8000")
}
@Test func synchronizationTest() async throws {
_ = try await self.login()
try await StoreCenter.main.synchronizeLastUpdates()
}
@Test func createTournament() async throws {
let user = try await self.login()
// Cleanup
let events = DataStore.shared.events
await DataStore.shared.events.deleteAsync(contentOfs: Array(events))
try await DataStore.shared.events.loadDataFromServerIfAllowed(clear: true)
#expect(DataStore.shared.events.count == 0)
try await DataStore.shared.tournaments.loadDataFromServerIfAllowed(clear: true)
#expect(DataStore.shared.tournaments.count == 0)
// Create
let event: Event = Event(creator: user.id, club: nil, name: "test")
await DataStore.shared.events.addOrUpdateAsync(instance: event)
let tournament: Tournament = Tournament.fake()
tournament.event = event.id
await DataStore.shared.tournaments.addOrUpdateAsync(instance: tournament)
// Test server content
try await DataStore.shared.events.loadDataFromServerIfAllowed(clear: true)
#expect(DataStore.shared.events.count == 1)
try await DataStore.shared.tournaments.loadDataFromServerIfAllowed(clear: true)
#expect(DataStore.shared.tournaments.count == 1)
}
func login() async throws -> CustomUser {
let user: CustomUser = try await StoreCenter.main.service().login(username: self.username, password: self.password)
return user
}
}

@ -30,4 +30,8 @@ struct SynchronizationTests {
return user
}
}

Loading…
Cancel
Save