From 713b57756185fa4c3501d0d8babaf5ec3323253c Mon Sep 17 00:00:00 2001 From: Hadi Sharghi Date: Sat, 17 Jun 2023 20:55:40 +0330 Subject: [PATCH] fix aggregates bug for postgres. --- Sources/VaporWallet/HasWallet.swift | 26 +- .../VaporWallet/Migrations/CreateWallet.swift | 64 ++-- .../Migrations/CreateWalletTransaction.swift | 73 ++-- Sources/VaporWallet/WalletRepository.swift | 25 +- Tests/VaporWalletTests/VaporWalletTests.swift | 357 +++++++++--------- 5 files changed, 277 insertions(+), 268 deletions(-) diff --git a/Sources/VaporWallet/HasWallet.swift b/Sources/VaporWallet/HasWallet.swift index 91cde0e..fa29e9d 100644 --- a/Sources/VaporWallet/HasWallet.swift +++ b/Sources/VaporWallet/HasWallet.swift @@ -8,6 +8,7 @@ import Vapor import Fluent +import FluentPostgresDriver public protocol HasWallet: FluentKit.Model { static var idKey: KeyPath> { get } @@ -27,12 +28,27 @@ extension HasWallet { extension Wallet { public func refreshBalanceAsync(on db: Database) async throws -> Double { - let balance = try await self.$transactions - .query(on: db) - .filter(\.$confirmed == true) - .sum(\.$amount) + + var balance: Int + // Temporary workaround for sum and average aggregates on Postgres DB + if let _ = db as? PostgresDatabase { + let balanceOptional = try? await self.$transactions + .query(on: db) + .filter(\.$confirmed == true) + .aggregate(.sum, \.$amount, as: Double.self) + + balance = balanceOptional == nil ? 0 : Int(balanceOptional!) + } else { + let intBalance = try await self.$transactions + .query(on: db) + .filter(\.$confirmed == true) + .sum(\.$amount) + + balance = intBalance ?? 0 + } - self.balance = balance ?? 0 + self.balance = balance + try await self.update(on: db) return Double(self.balance) } diff --git a/Sources/VaporWallet/Migrations/CreateWallet.swift b/Sources/VaporWallet/Migrations/CreateWallet.swift index 8900a2e..0ddca1a 100644 --- a/Sources/VaporWallet/Migrations/CreateWallet.swift +++ b/Sources/VaporWallet/Migrations/CreateWallet.swift @@ -1,38 +1,38 @@ import Fluent import SQLKit -public struct CreateWallet: Migration { - private var idKey: String - public init(foreignKeyColumnName idKey: String = "id") { - self.idKey = idKey - } - - public func prepare(on database: Database) -> EventLoopFuture { - return database.schema(Wallet.schema) - .id() - .field("name", .string, .required) - .field("owner_type", .string, .required) - .field("owner_id", .uuid, .required) - .field("min_allowed_balance", .int, .required) - .field("balance", .int, .required) - .field("decimal_places", .uint8, .required) - .field("created_at", .datetime, .required) - .field("updated_at", .datetime, .required) - .field("deleted_at", .datetime) - .create().flatMap { _ in - let sqlDB = (database as! SQLDatabase) - return sqlDB - .create(index: "type_idx") - .on(Wallet.schema) - .column("owner_type") - .run() - } - } - - public func revert(on database: Database) -> EventLoopFuture { - return database.schema(Wallet.schema).delete() - } -} +//public struct CreateWallet: Migration { +// private var idKey: String +// public init(foreignKeyColumnName idKey: String = "id") { +// self.idKey = idKey +// } +// +// public func prepare(on database: Database) -> EventLoopFuture { +// return database.schema(Wallet.schema) +// .id() +// .field("name", .string, .required) +// .field("owner_type", .string, .required) +// .field("owner_id", .uuid, .required) +// .field("min_allowed_balance", .int, .required) +// .field("balance", .int, .required) +// .field("decimal_places", .uint8, .required) +// .field("created_at", .datetime, .required) +// .field("updated_at", .datetime, .required) +// .field("deleted_at", .datetime) +// .create().flatMap { _ in +// let sqlDB = (database as! SQLDatabase) +// return sqlDB +// .create(index: "type_idx") +// .on(Wallet.schema) +// .column("owner_type") +// .run() +// } +// } +// +// public func revert(on database: Database) -> EventLoopFuture { +// return database.schema(Wallet.schema).delete() +// } +//} public struct CreateWalletAsync: AsyncMigration { private var idKey: String diff --git a/Sources/VaporWallet/Migrations/CreateWalletTransaction.swift b/Sources/VaporWallet/Migrations/CreateWalletTransaction.swift index 6c4843a..e5103d2 100644 --- a/Sources/VaporWallet/Migrations/CreateWalletTransaction.swift +++ b/Sources/VaporWallet/Migrations/CreateWalletTransaction.swift @@ -1,50 +1,47 @@ import Fluent -public struct CreateWalletTransaction: Migration { - public init() { } - - public func prepare(on database: Database) -> EventLoopFuture { - return database.enum("type") - .case("deposit") - .case("withdraw") - .create().flatMap { transactionType in - return database.schema(WalletTransaction.schema) - .id() - .field("wallet_id", .uuid, .required, .references(Wallet.schema, "id", onDelete: .cascade)) - .field("type", transactionType, .required) - .field("amount", .int, .required) - .field("confirmed", .bool, .required) - .field("meta", .json) - .field("created_at", .datetime, .required) - .field("updated_at", .datetime, .required) - .create() - } - } - - public func revert(on database: Database) -> EventLoopFuture { - return database.enum("type") - .deleteCase("deposit") - .deleteCase("withdraw") - .update().flatMap { _ in - return database.schema(WalletTransaction.schema).delete() - } - - } -} +//public struct CreateWalletTransaction: Migration { +// public init() { } +// +// public func prepare(on database: Database) -> EventLoopFuture { +// return database.enum("type") +// .case("deposit") +// .case("withdraw") +// .create().flatMap { transactionType in +// return database.schema(WalletTransaction.schema) +// .id() +// .field("wallet_id", .uuid, .required, .references(Wallet.schema, "id", onDelete: .cascade)) +// .field("type", transactionType, .required) +// .field("amount", .int, .required) +// .field("confirmed", .bool, .required) +// .field("meta", .json) +// .field("created_at", .datetime, .required) +// .field("updated_at", .datetime, .required) +// .create() +// } +// } +// +// public func revert(on database: Database) -> EventLoopFuture { +// return database.enum("type") +// .deleteCase("deposit") +// .deleteCase("withdraw") +// .update().flatMap { _ in +// return database.schema(WalletTransaction.schema).delete() +// } +// +// } +//} public struct CreateWalletTransactionAsync: AsyncMigration { public init() { } public func prepare(on database: Database) async throws { - do { - try await database.enum("transaction_type").delete() - } catch { } - + let transactionType = try await database.enum("transaction_type") .case("deposit") .case("withdraw") .create() - + try await database.schema(WalletTransaction.schema) .id() .field("wallet_id", .uuid, .required, .references(Wallet.schema, "id", onDelete: .cascade)) @@ -59,10 +56,8 @@ public struct CreateWalletTransactionAsync: AsyncMigration { public func revert(on database: Database) async throws { - do { - try await database.enum("transaction_type").delete() - } catch { } try await database.schema(WalletTransaction.schema).delete() + try await database.enum("transaction_type").delete() } } diff --git a/Sources/VaporWallet/WalletRepository.swift b/Sources/VaporWallet/WalletRepository.swift index 00fe0dc..55f9537 100644 --- a/Sources/VaporWallet/WalletRepository.swift +++ b/Sources/VaporWallet/WalletRepository.swift @@ -7,6 +7,7 @@ import Vapor import Fluent +import FluentPostgresDriver /// This calss gives access to wallet methods for a `HasWallet` model. /// Creating multiple wallets, accessing them and getting balance of each wallet, @@ -53,7 +54,7 @@ extension WalletsRepository { .filter(\.$owner == self.id) .filter(\.$ownerType == self.type) .filter(\.$name == name.value) - + if (withTransactions) { walletQuery = walletQuery.with(\.$transactions) } @@ -72,13 +73,21 @@ extension WalletsRepository { public func balanceAsync(type name: WalletType = .default, withUnconfirmed: Bool = false, asDecimal: Bool = false) async throws -> Double { let wallet = try await getAsync(type: name) if withUnconfirmed { - let intBalance = try await wallet.$transactions - .query(on: self.db) - .sum(\.$amount) - .get() - - let balance = intBalance == nil ? 0.0 : Double(intBalance!) - + // (1) Temporary workaround for sum and average aggregates, + var balance: Double + if let _ = self.db as? PostgresDatabase { + let balanceOptional = try? await wallet.$transactions + .query(on: self.db) + .aggregate(.sum, \.$amount, as: Double.self) + + balance = balanceOptional ?? 0.0 + } else { + let intBalance = try await wallet.$transactions + .query(on: self.db) + .sum(\.$amount) + + balance = intBalance == nil ? 0.0 : Double(intBalance!) + } return asDecimal ? balance.toDecimal(with: wallet.decimalPlaces) : balance } return asDecimal ? Double(wallet.balance).toDecimal(with: wallet.decimalPlaces) : Double(wallet.balance) diff --git a/Tests/VaporWalletTests/VaporWalletTests.swift b/Tests/VaporWalletTests/VaporWalletTests.swift index d6dde8d..d93fe1c 100644 --- a/Tests/VaporWalletTests/VaporWalletTests.swift +++ b/Tests/VaporWalletTests/VaporWalletTests.swift @@ -16,16 +16,16 @@ class VaporWalletTests: XCTestCase { app = Application(.testing) app.logger.logLevel = .debug - - app.databases.use(.postgres( - hostname: "localhost", - port: 5432, - username: "catgpt", - password: "catgpt", - database: "catgpt" - ), as: .psql) -// app.databases.use(.mysql(hostname: "127.0.0.1", port: 3306, username: "vapor", password: "vapor", database: "vp-test"), as: .mysql) -// app.databases.use(.sqlite(.memory), as: .sqlite) + + // app.databases.use(.postgres( + // hostname: "localhost", + // port: 5432, + // username: "catgpt", + // password: "catgpt", + // database: "catgpt" + // ), as: .psql) + // app.databases.use(.mysql(hostname: "127.0.0.1", port: 3306, username: "vapor", password: "vapor", database: "vp-test"), as: .mysql) + app.databases.use(.sqlite(.memory), as: .sqlite) try! migrations(app) try! app.autoRevert().wait() @@ -35,25 +35,25 @@ class VaporWalletTests: XCTestCase { } func resetDB() throws { - let db = (app.db as! SQLDatabase) - let query = db.raw(""" + let db = (app.db as! SQLDatabase) + let query = db.raw(""" SELECT Concat('DELETE FROM ', TABLE_NAME, ';') as truncate_query FROM INFORMATION_SCHEMA.TABLES where `TABLE_SCHEMA` = 'vp-test' and `TABLE_NAME` not like '_fluent_%'; """) - - return try query.all().flatMap { results in - return results.compactMap { row in - try? row.decode(column: "truncate_query", as: String.self) - }.map { query in - return (db as! MySQLDatabase).simpleQuery(query).transform(to: ()) - }.flatten(on: self.app.db.eventLoop) - }.wait() - } - + + return try query.all().flatMap { results in + return results.compactMap { row in + try? row.decode(column: "truncate_query", as: String.self) + }.map { query in + return (db as! MySQLDatabase).simpleQuery(query).transform(to: ()) + }.flatten(on: self.app.db.eventLoop) + }.wait() + } + override func tearDown() { try! app.autoRevert().wait() app.shutdown() - + } @@ -66,7 +66,7 @@ class VaporWalletTests: XCTestCase { let game = try await Game.create(name: "new_game", on: app.db) XCTAssert(game.name == "new_game") } - + func testUserHasNoDefaultWallet() async throws { let userWithNoWallet = try await User.create(on: app.db) await XCTAssertThrowsError(try await userWithNoWallet.walletsRepository(on: app.db).defaultAsync(), "expected throw") { (error) in @@ -78,365 +78,354 @@ class VaporWalletTests: XCTestCase { app.databases.middleware.use(AsyncWalletMiddleware()) let userWithDefaultWallet = try await User.create(on: app.db) let defaultWallet = try await userWithDefaultWallet.walletsRepository(on: app.db).defaultAsync() - + XCTAssertEqual(defaultWallet.name, WalletType.default.value) } - + func testCreateWallet() async throws { let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) try await wallets.createAsync(type: .init(name: "savings")) - + let userWallets = try await wallets.allAsync() XCTAssertEqual(userWallets.count, 1) XCTAssertEqual(userWallets.first?.name, "savings") - + } - + func testWalletDeposit() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - - + + try! await wallets.depositAsync(amount: 10) let balance = try await wallets.balanceAsync() - + XCTAssertEqual(balance, 0) - + let refreshedBalance = try await wallets.refreshBalanceAsync() XCTAssertEqual(refreshedBalance, 10) - + } - + func testWalletTransactionMiddleware() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let user = try await User.create(on: app.db) let walletsRepoWithMiddleware = user.walletsRepository(on: app.db) - + try! await walletsRepoWithMiddleware.depositAsync(amount: 40) - + let balance = try await walletsRepoWithMiddleware.balanceAsync() - + XCTAssertEqual(balance, 40) - + } - + func testWalletWithdraw() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - + try await wallets.depositAsync(amount: 100) - + var balance = try await wallets.balanceAsync() - + XCTAssertEqual(balance, 100) - + await XCTAssertThrowsError(try await wallets.withdrawAsync(amount: 200), "expected throw") { (error) in XCTAssertEqual(error as! WalletError, WalletError.insufficientBalance) } - + try! await wallets.withdrawAsync(amount: 50) - + balance = try await wallets.balanceAsync() - + XCTAssertEqual(balance, 50) - + } - - + + func testWalletCanWithdraw() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - + try await wallets.depositAsync(amount: 100) - + var can = try! await wallets.canWithdrawAsync(amount: 100) XCTAssertTrue(can) can = try! await wallets.canWithdrawAsync(amount: 200) XCTAssertFalse(can) - + } func testWalletCanWithdrawWithMinAllowedBalance() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) try await wallets.createAsync(type: .init(name: "magical"), minAllowedBalance: -50) - try await wallets.depositAsync(to: .init(name: "magical"), amount: 100) - - var can = try! await wallets.canWithdrawAsync(from: .default, amount: 130) + var can = try await wallets.canWithdrawAsync(from: .default, amount: 130) XCTAssertFalse(can) - can = try! await wallets.canWithdrawAsync(from: .init(name: "magical"), amount: 130) + + can = try await wallets.canWithdrawAsync(from: .init(name: "magical"), amount: 130) XCTAssertTrue(can) - } - + func testMultiWallet() async throws { app.databases.middleware.use(AsyncWalletTransactionMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - + let savingsWallet = WalletType(name: "savings") let myWallet = WalletType(name: "my-wallet") let notExistsWallet = WalletType(name: "not-exists") - + try await wallets.createAsync(type: myWallet) try await wallets.createAsync(type: savingsWallet) - + try await wallets.depositAsync(to: myWallet, amount: 100) try await wallets.depositAsync(to: savingsWallet, amount: 200) - + do { try await wallets.depositAsync(to: notExistsWallet, amount: 1000) } catch { XCTAssertEqual(error as! WalletError, WalletError.walletNotFound(name: "not-exists")) } - + let balance1 = try await wallets.balanceAsync(type: myWallet) let balance2 = try await wallets.balanceAsync(type: savingsWallet) - + XCTAssertEqual(balance1, 100) XCTAssertEqual(balance2, 200) - + } - - + + func testTransactionMetadata() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - + try await wallets.depositAsync(amount: 100, meta: ["description": "tax payments"]) - + let transaction = try await wallets.defaultAsync() .$transactions.get(on: app.db) .first! - + XCTAssertEqual(transaction.meta!["description"] , "tax payments") } - + func testWalletDecimalBalance() async throws { app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let user = try await User.create(on: app.db) let wallets = user.walletsRepository(on: app.db) try await wallets.createAsync(type: .default, decimalPlaces: 2) - + try await wallets.depositAsync(amount: 100) - + var balance = try await wallets.balanceAsync() XCTAssertEqual(balance, 100) - + try await wallets.depositAsync(amount: 1.45) balance = try await wallets.balanceAsync() XCTAssertEqual(balance, 245) - + balance = try await wallets.balanceAsync(asDecimal: true) XCTAssertEqual(balance, 2.45) - - + + // decmial values will be truncated to wallet's decimalPlace value try await wallets.depositAsync(amount: 1.555) balance = try await wallets.balanceAsync() XCTAssertEqual(balance, 400) - + } - - + + func testConfirmTransaction() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - + try await wallets.depositAsync(amount: 10, confirmed: true) sleep(1) try await wallets.depositAsync(amount: 40, confirmed: false) - + var balance = try await wallets.balanceAsync() let unconfirmedBalance = try await wallets.balanceAsync(withUnconfirmed: true) XCTAssertEqual(balance, 10) XCTAssertEqual(unconfirmedBalance, 50) - + let transaction = try await wallets .unconfirmedTransactionsAsync() .items .first! - + balance = try await wallets.confirmAsync(transaction: transaction) - + XCTAssertEqual(balance, 50) - + } - + func testConfirmAllTransactionsOfWallet() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - + try await wallets.depositAsync(amount: 10, confirmed: false) try await wallets.depositAsync(amount: 40, confirmed: false) - + var balance = try await wallets.balanceAsync() XCTAssertEqual(balance, 0) - + balance = try await wallets.confirmAllAsync() - + XCTAssertEqual(balance, 50) } - - + + func testTransferBetweenAUsersWallets() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let (_, wallets) = try await setupUserAndWalletsRepo(on: app.db) - + let wallet1 = WalletType(name: "wallet1") let wallet2 = WalletType(name: "wallet2") - + try await wallets.createAsync(type: wallet1) try await wallets.createAsync(type: wallet2) - - + + try await wallets.depositAsync(to: wallet1, amount: 100) - + try await wallets.transferAsync(from: wallet1, to: wallet2, amount: 20) - + let balance1 = try await wallets.balanceAsync(type: wallet1) let balance2 = try await wallets.balanceAsync(type: wallet2) - + XCTAssertEqual(balance1, 80) XCTAssertEqual(balance2, 20) - + } - + func testTransferBetweenTwoUsersWallets() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - + let user1 = try await User.create(username: "user1", on: app.db) let user2 = try await User.create(username: "user2", on: app.db) - + let repo1 = user1.walletsRepository(on: app.db) let repo2 = user2.walletsRepository(on: app.db) - + try await repo1.depositAsync(amount: 100) - + try await repo1.transferAsync(from: try await repo1.defaultAsync(), to: try await repo2.defaultAsync(), amount: 20) - + var balance1 = try await repo1.balanceAsync() var balance2 = try await repo2.balanceAsync() - + XCTAssertEqual(balance1, 80) XCTAssertEqual(balance2, 20) - + try await repo1.transferAsync(from: .default, to: try await repo2.defaultAsync(), amount: 20) - + balance1 = try await repo1.balanceAsync() balance2 = try await repo2.balanceAsync() - + XCTAssertEqual(balance1, 60) XCTAssertEqual(balance2, 40) - + let savings = WalletType(name: "savings") try await repo1.createAsync(type: savings) try await repo1.transferAsync(from: .default, to: savings, amount: 20) - + balance1 = try await repo1.balanceAsync() balance2 = try await repo1.balanceAsync(type: savings) - + XCTAssertEqual(balance1, 40) XCTAssertEqual(balance2, 20) - + } - - + + func testMultiModelWallet() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - - do { - let user = try await User.create(username: "user1", on: app.db) - let game = Game(id: user.id, name: "game1") - try await game.save(on: app.db) - - let repo1 = user.walletsRepository(on: app.db) - let repo2 = game.walletsRepository(on: app.db) - - try await repo1.depositAsync(amount: 100) - try await repo2.depositAsync(amount: 500) - - let balance1 = try await repo1.balanceAsync() - let balance2 = try await repo2.balanceAsync() - - XCTAssertEqual(balance1, 100) - XCTAssertEqual(balance2, 500) - } catch { - print("error: \(String(reflecting: error))") - } - + + let user = try await User.create(username: "user1", on: app.db) + let game = Game(id: user.id, name: "game1") + try await game.save(on: app.db) + + let repo1 = user.walletsRepository(on: app.db) + let repo2 = game.walletsRepository(on: app.db) + + try await repo1.depositAsync(amount: 100) + try await repo2.depositAsync(amount: 500) + + let balance1 = try await repo1.balanceAsync() + let balance2 = try await repo2.balanceAsync() + + XCTAssertEqual(balance1, 100) + XCTAssertEqual(balance2, 500) + } - + func testMultiModelWalletTransfer() async throws { app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletMiddleware()) app.databases.middleware.use(AsyncWalletTransactionMiddleware()) - - do { - let user = try await User.create(username: "user1", on: app.db) - let game = Game(id: user.id, name: "game1") - try await game.save(on: app.db) - - let repo1 = user.walletsRepository(on: app.db) - let repo2 = game.walletsRepository(on: app.db) - - try await repo1.depositAsync(amount: 100) - try await repo2.depositAsync(amount: 500) - - let userWallet = try await repo1.defaultAsync() - let gameWallet = try await repo2.defaultAsync() - - try await repo1.transferAsync(from: gameWallet, to: userWallet, amount: 100) - - let balance1 = try await repo1.balanceAsync() - let balance2 = try await repo2.balanceAsync() - - XCTAssertEqual(balance1, 200) - XCTAssertEqual(balance2, 400) - } catch { - print("###### error: #########\n\(String(reflecting: error))") - } - + + let user = try await User.create(username: "user1", on: app.db) + let game = Game(id: user.id, name: "game1") + try await game.save(on: app.db) + + let repo1 = user.walletsRepository(on: app.db) + let repo2 = game.walletsRepository(on: app.db) + + try await repo1.depositAsync(amount: 100) + try await repo2.depositAsync(amount: 500) + + let userWallet = try await repo1.defaultAsync() + let gameWallet = try await repo2.defaultAsync() + + try await repo1.transferAsync(from: gameWallet, to: userWallet, amount: 100) + + let balance1 = try await repo1.balanceAsync() + let balance2 = try await repo2.balanceAsync() + + XCTAssertEqual(balance1, 200) + XCTAssertEqual(balance2, 400) } - - - - + + + + private func setupUserAndWalletsRepo(on: Database) async throws -> (User, WalletsRepository) { let user = try! await User.create(on: app.db) let wallets = user.walletsRepository(on: app.db) - + return (user, wallets) } - + private func migrations(_ app: Application) throws { // Initial Migrations app.migrations.add(CreateUser()) app.migrations.add(CreateGame()) - app.migrations.add(CreateWallet()) + app.migrations.add(CreateWalletAsync()) app.migrations.add(CreateWalletTransactionAsync()) } } @@ -445,5 +434,5 @@ extension WalletError: Equatable { public static func == (lhs: WalletError, rhs: WalletError) -> Bool { return lhs.errorDescription == rhs.errorDescription } - + }