feat: Adds script to generate database seeded values and removes old mock storage.
This commit is contained in:
49
Sources/App/SeedCommand.swift
Normal file
49
Sources/App/SeedCommand.swift
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#if DEBUG
|
||||||
|
import DatabaseClientLive
|
||||||
|
import Dependencies
|
||||||
|
import FluentSQLiteDriver
|
||||||
|
import SharedModels
|
||||||
|
import Vapor
|
||||||
|
|
||||||
|
struct SeedCommand: AsyncCommand {
|
||||||
|
|
||||||
|
struct Signature: CommandSignature {}
|
||||||
|
|
||||||
|
var help: String {
|
||||||
|
"Generate random seed database."
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(using context: CommandContext, signature: Signature) async throws {
|
||||||
|
let database = DatabaseClient.live(database: context.application.db(.sqlite))
|
||||||
|
|
||||||
|
var users: [User] = []
|
||||||
|
var employees: [Employee] = []
|
||||||
|
var vendors: [Vendor] = []
|
||||||
|
var vendorBranches: [VendorBranch] = []
|
||||||
|
|
||||||
|
for user in User.Create.generateMocks() {
|
||||||
|
let created = try await database.users.create(user)
|
||||||
|
users.append(created)
|
||||||
|
}
|
||||||
|
|
||||||
|
for employee in Employee.Create.generateMocks() {
|
||||||
|
let created = try await database.employees.create(employee)
|
||||||
|
employees.append(created)
|
||||||
|
}
|
||||||
|
|
||||||
|
for vendor in Vendor.Create.generateMocks() {
|
||||||
|
let created = try await database.vendors.create(vendor)
|
||||||
|
vendors.append(created)
|
||||||
|
}
|
||||||
|
|
||||||
|
for branch in VendorBranch.Create.generateMocks(vendors: vendors) {
|
||||||
|
let created = try await database.vendorBranches.create(branch)
|
||||||
|
vendorBranches.append(created)
|
||||||
|
}
|
||||||
|
|
||||||
|
for purchaseOrder in PurchaseOrder.Create.generateMocks(employees: employees, vendorBranches: vendorBranches) {
|
||||||
|
_ = try await database.purchaseOrders.create(purchaseOrder, users.randomElement()!.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -30,7 +30,8 @@ public func configure(_ app: Application) async throws {
|
|||||||
|
|
||||||
switch app.environment {
|
switch app.environment {
|
||||||
case .production, .development:
|
case .production, .development:
|
||||||
app.databases.use(DatabaseConfigurationFactory.sqlite(.file("db.sqlite")), as: .sqlite)
|
let dbFileName = Environment.get("SQLITE_FILENAME") ?? "db.sqlite"
|
||||||
|
app.databases.use(DatabaseConfigurationFactory.sqlite(.file(dbFileName)), as: .sqlite)
|
||||||
default:
|
default:
|
||||||
app.databases.use(DatabaseConfigurationFactory.sqlite(.memory), as: .sqlite)
|
app.databases.use(DatabaseConfigurationFactory.sqlite(.memory), as: .sqlite)
|
||||||
}
|
}
|
||||||
@@ -48,5 +49,6 @@ public func configure(_ app: Application) async throws {
|
|||||||
|
|
||||||
if app.environment != .production {
|
if app.environment != .production {
|
||||||
try await app.autoMigrate()
|
try await app.autoMigrate()
|
||||||
|
app.asyncCommands.use(SeedCommand(), as: "seed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,70 +32,3 @@ extension Employee.Update: Content {}
|
|||||||
extension DatabaseClient.Employees: TestDependencyKey {
|
extension DatabaseClient.Employees: TestDependencyKey {
|
||||||
public static let testValue = Self()
|
public static let testValue = Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
typealias EmployeeMockStorage = MockStorage<
|
|
||||||
Employee,
|
|
||||||
Employee.Create,
|
|
||||||
DatabaseClient.Employees.FetchRequest,
|
|
||||||
Void,
|
|
||||||
Employee.Update
|
|
||||||
>
|
|
||||||
|
|
||||||
private extension EmployeeMockStorage {
|
|
||||||
|
|
||||||
init(_ mocks: [Employee]) {
|
|
||||||
@Dependency(\.date.now) var now
|
|
||||||
@Dependency(\.uuid) var uuid
|
|
||||||
self.init(
|
|
||||||
mocks,
|
|
||||||
create: { employee in
|
|
||||||
Employee(
|
|
||||||
id: uuid(),
|
|
||||||
active: employee.active ?? true,
|
|
||||||
createdAt: now,
|
|
||||||
firstName: employee.firstName,
|
|
||||||
lastName: employee.lastName,
|
|
||||||
updatedAt: now
|
|
||||||
)
|
|
||||||
},
|
|
||||||
fetch: { request in
|
|
||||||
switch request {
|
|
||||||
case .all:
|
|
||||||
return { _ in true }
|
|
||||||
case .active:
|
|
||||||
return { $0.active == true }
|
|
||||||
case .inactive:
|
|
||||||
return { $0.active == false }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
update: { employee, updates in
|
|
||||||
let model = Employee(
|
|
||||||
id: employee.id,
|
|
||||||
active: updates.active ?? employee.active,
|
|
||||||
createdAt: employee.createdAt,
|
|
||||||
firstName: updates.firstName ?? employee.firstName,
|
|
||||||
lastName: updates.lastName ?? employee.lastName,
|
|
||||||
updatedAt: now
|
|
||||||
)
|
|
||||||
|
|
||||||
employee = model
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension DatabaseClient.Employees {
|
|
||||||
static func mock(_ mocks: [Employee] = []) -> Self {
|
|
||||||
let storage = EmployeeMockStorage(mocks)
|
|
||||||
return .init(
|
|
||||||
create: { try await storage.create($0) },
|
|
||||||
delete: { try await storage.delete($0) },
|
|
||||||
fetchAll: { try await storage.fetchAll($0) },
|
|
||||||
get: { try await storage.get($0) },
|
|
||||||
update: { try await storage.update($0, $1) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
#if DEBUG
|
|
||||||
import Dependencies
|
|
||||||
import Vapor
|
|
||||||
|
|
||||||
actor MockStorage<
|
|
||||||
Model: Identifiable,
|
|
||||||
Create: Sendable,
|
|
||||||
Fetch: Sendable,
|
|
||||||
Get: Sendable,
|
|
||||||
Update: Sendable
|
|
||||||
> where Model: Sendable {
|
|
||||||
@Dependency(\.date.now) var now
|
|
||||||
@Dependency(\.uuid) var uuid
|
|
||||||
|
|
||||||
private var storage: [Model.ID: Model]
|
|
||||||
|
|
||||||
private let modelFromCreate: @Sendable (Create) async throws -> Model
|
|
||||||
private let fetchToPredicate: @Sendable (Fetch) -> ((Model) -> Bool)
|
|
||||||
private let fetchExtras: @Sendable (Fetch, [Model]) async throws -> [Model]
|
|
||||||
private let applyUpdates: @Sendable (inout Model, Update, Get?) async throws -> Void
|
|
||||||
private let getExtras: @Sendable (Get?, Model) async throws -> Model
|
|
||||||
|
|
||||||
init(
|
|
||||||
_ mocks: [Model] = [],
|
|
||||||
create modelFromCreate: @Sendable @escaping (Create) async throws -> Model,
|
|
||||||
fetch fetchToPredicate: @Sendable @escaping (Fetch) -> ((Model) -> Bool),
|
|
||||||
fetchExtras: @Sendable @escaping (Fetch, [Model]) async throws -> [Model] = { $1 },
|
|
||||||
get getExtras: @Sendable @escaping (Get?, Model) async throws -> Model,
|
|
||||||
update applyUpdates: @Sendable @escaping (inout Model, Update, Get?) async throws -> Void
|
|
||||||
) {
|
|
||||||
self.storage = mocks.reduce(into: [Model.ID: Model]()) { $0[$1.id] = $1 }
|
|
||||||
self.modelFromCreate = modelFromCreate
|
|
||||||
self.fetchToPredicate = fetchToPredicate
|
|
||||||
self.fetchExtras = fetchExtras
|
|
||||||
self.applyUpdates = applyUpdates
|
|
||||||
self.getExtras = getExtras
|
|
||||||
}
|
|
||||||
|
|
||||||
func count() async throws -> Int {
|
|
||||||
storage.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func create(_ create: Create) async throws -> Model {
|
|
||||||
let model = try await modelFromCreate(create)
|
|
||||||
storage[model.id] = model
|
|
||||||
return model
|
|
||||||
}
|
|
||||||
|
|
||||||
func delete(_ id: Model.ID) async throws {
|
|
||||||
storage[id] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchAll(_ request: Fetch) async throws -> [Model] {
|
|
||||||
let predicate = fetchToPredicate(request)
|
|
||||||
return try await fetchExtras(request, Array(storage.values.filter { predicate($0) }))
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(_ id: Model.ID, _ request: Get?) async throws -> Model? {
|
|
||||||
if let model = storage[id] {
|
|
||||||
return try await getExtras(request, model)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func update(_ id: Model.ID, _ updates: Update, _ get: Get?) async throws -> Model {
|
|
||||||
guard var model = storage[id] else {
|
|
||||||
throw Abort(.badRequest, reason: "Model not found.")
|
|
||||||
}
|
|
||||||
try await applyUpdates(&model, updates, get)
|
|
||||||
storage[id] = model
|
|
||||||
return model
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MockStorage where Get == Void {
|
|
||||||
init(
|
|
||||||
_ mocks: [Model] = [],
|
|
||||||
create modelFromCreate: @Sendable @escaping (Create) async throws -> Model,
|
|
||||||
fetch fetchToPredicate: @Sendable @escaping (Fetch) -> ((Model) -> Bool),
|
|
||||||
fetchExtras: @Sendable @escaping (Fetch, [Model]) async throws -> [Model] = { $1 },
|
|
||||||
update applyUpdates: @Sendable @escaping (inout Model, Update) async throws -> Void
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
mocks,
|
|
||||||
create: modelFromCreate,
|
|
||||||
fetch: fetchToPredicate,
|
|
||||||
fetchExtras: fetchExtras,
|
|
||||||
get: { _, model in model },
|
|
||||||
update: { model, updates, _ in try await applyUpdates(&model, updates) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func get(_ id: Model.ID) async throws -> Model? {
|
|
||||||
storage[id]
|
|
||||||
}
|
|
||||||
|
|
||||||
func update(_ id: Model.ID, _ updates: Update) async throws -> Model {
|
|
||||||
try await update(id, updates, ())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MockStorage where Fetch == Void {
|
|
||||||
init(
|
|
||||||
_ mocks: [Model] = [],
|
|
||||||
create modelFromCreate: @Sendable @escaping (Create) async throws -> Model,
|
|
||||||
fetchExtras: @Sendable @escaping (Fetch, [Model]) async throws -> [Model] = { $1 },
|
|
||||||
get getExtras: @Sendable @escaping (Get?, Model) async throws -> Model,
|
|
||||||
update applyUpdates: @Sendable @escaping (inout Model, Update, Get?) async throws -> Void
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
mocks,
|
|
||||||
create: modelFromCreate,
|
|
||||||
fetch: { _ in { _ in true } },
|
|
||||||
fetchExtras: fetchExtras,
|
|
||||||
get: getExtras,
|
|
||||||
update: applyUpdates
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchAll() async throws -> [Model] {
|
|
||||||
try await fetchAll(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MockStorage where Fetch == Void, Get == Void {
|
|
||||||
init(
|
|
||||||
_ mocks: [Model] = [],
|
|
||||||
create modelFromCreate: @Sendable @escaping (Create) async throws -> Model,
|
|
||||||
fetchExtras: @Sendable @escaping (Fetch, [Model]) async throws -> [Model] = { $1 },
|
|
||||||
update applyUpdates: @Sendable @escaping (inout Model, Update) async throws -> Void
|
|
||||||
) {
|
|
||||||
self.init(
|
|
||||||
mocks,
|
|
||||||
create: modelFromCreate,
|
|
||||||
fetchExtras: fetchExtras,
|
|
||||||
get: { _, model in model },
|
|
||||||
update: { model, updates, _ in try await applyUpdates(&model, updates) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -22,74 +22,3 @@ extension PurchaseOrder.Create: Content {}
|
|||||||
extension DatabaseClient.PurchaseOrders: TestDependencyKey {
|
extension DatabaseClient.PurchaseOrders: TestDependencyKey {
|
||||||
public static let testValue: DatabaseClient.PurchaseOrders = Self()
|
public static let testValue: DatabaseClient.PurchaseOrders = Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
typealias PurchaseOrderMockStorage = MockStorage<
|
|
||||||
PurchaseOrder,
|
|
||||||
PurchaseOrder.Create,
|
|
||||||
Void,
|
|
||||||
Void,
|
|
||||||
Void
|
|
||||||
>
|
|
||||||
|
|
||||||
public extension DependencyValues {
|
|
||||||
var purchaseOrderID: PurchaseOrderIDGenerator {
|
|
||||||
get { self[PurchaseOrderIDGenerator.self] }
|
|
||||||
set { self[PurchaseOrderIDGenerator.self] = newValue }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@DependencyClient
|
|
||||||
public struct PurchaseOrderIDGenerator: Sendable {
|
|
||||||
var nextID: @Sendable () async throws -> Int
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PurchaseOrderIDGenerator: DependencyKey {
|
|
||||||
public static let testValue: PurchaseOrderIDGenerator = .liveValue
|
|
||||||
|
|
||||||
public static var liveValue: Self {
|
|
||||||
let counter = Counter()
|
|
||||||
return .init(nextID: { await counter.nextID() })
|
|
||||||
}
|
|
||||||
|
|
||||||
actor Counter {
|
|
||||||
private var count: Int
|
|
||||||
|
|
||||||
init(starting: Int = 1) {
|
|
||||||
self.count = starting
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextID() async -> Int {
|
|
||||||
count += 1
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// private extension PurchaseOrderMockStorage {
|
|
||||||
// static func make(_ mocks: [PurchaseOrder]) -> Self {
|
|
||||||
// @Dependency(\.date.now) var now
|
|
||||||
// @Dependency(\.purchaseOrderID) var purchaseOrderID
|
|
||||||
//
|
|
||||||
// return .init(
|
|
||||||
// mocks,
|
|
||||||
// create: { model in
|
|
||||||
// try await PurchaseOrder(
|
|
||||||
// id: purchaseOrderID.nextID(),
|
|
||||||
// workOrder: model.workOrder,
|
|
||||||
// materials: model.materials,
|
|
||||||
// customer: model.customer,
|
|
||||||
// truckStock: model.truckStock,
|
|
||||||
// createdBy: model.createdForID,
|
|
||||||
// createdFor: model.createdForID,
|
|
||||||
// vendorBranch: .i
|
|
||||||
//
|
|
||||||
// )
|
|
||||||
// },
|
|
||||||
// update: { _, _ in
|
|
||||||
// fatalError()
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -28,66 +28,3 @@ extension User.Update: Content {}
|
|||||||
extension DatabaseClient.Users: TestDependencyKey {
|
extension DatabaseClient.Users: TestDependencyKey {
|
||||||
public static let testValue: DatabaseClient.Users = Self()
|
public static let testValue: DatabaseClient.Users = Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
typealias UserMockStorage = MockStorage<
|
|
||||||
User,
|
|
||||||
User.Create,
|
|
||||||
Void,
|
|
||||||
Void,
|
|
||||||
User.Update
|
|
||||||
>
|
|
||||||
|
|
||||||
private extension UserMockStorage {
|
|
||||||
static func make(_ mocks: [User]) -> Self {
|
|
||||||
@Dependency(\.date.now) var now
|
|
||||||
@Dependency(\.uuid) var uuid
|
|
||||||
|
|
||||||
return .init(
|
|
||||||
create: { model in
|
|
||||||
User(
|
|
||||||
id: uuid(),
|
|
||||||
email: model.email,
|
|
||||||
username: model.username,
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now
|
|
||||||
)
|
|
||||||
},
|
|
||||||
update: { model, updates in
|
|
||||||
let user = User(
|
|
||||||
id: model.id,
|
|
||||||
email: updates.email ?? model.email,
|
|
||||||
username: updates.username ?? model.username,
|
|
||||||
createdAt: model.createdAt,
|
|
||||||
updatedAt: now
|
|
||||||
)
|
|
||||||
model = user
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension User.Token {
|
|
||||||
static func mock(id: User.ID) -> Self {
|
|
||||||
.init(id: UUID(0), userID: id, value: "test")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension DatabaseClient.Users {
|
|
||||||
|
|
||||||
static func mock(_ mocks: [User]) -> Self {
|
|
||||||
let storage = UserMockStorage.make(mocks)
|
|
||||||
return .init(
|
|
||||||
count: { try await storage.count() },
|
|
||||||
create: { try await storage.create($0) },
|
|
||||||
delete: { try await storage.delete($0) },
|
|
||||||
fetchAll: { try await storage.fetchAll() },
|
|
||||||
get: { try await storage.get($0) },
|
|
||||||
login: { _ in .mock(id: UUID(0)) },
|
|
||||||
logout: { _ in },
|
|
||||||
token: { .mock(id: $0) },
|
|
||||||
update: { try await storage.update($0, $1) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -31,61 +31,3 @@ extension DatabaseClient.VendorBranches.FetchRequest: Content {}
|
|||||||
extension DatabaseClient.VendorBranches: TestDependencyKey {
|
extension DatabaseClient.VendorBranches: TestDependencyKey {
|
||||||
public static let testValue: DatabaseClient.VendorBranches = Self()
|
public static let testValue: DatabaseClient.VendorBranches = Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
typealias VendorBranchMockStorage = MockStorage<
|
|
||||||
VendorBranch,
|
|
||||||
VendorBranch.Create,
|
|
||||||
DatabaseClient.VendorBranches.FetchRequest,
|
|
||||||
Void,
|
|
||||||
VendorBranch.Update
|
|
||||||
>
|
|
||||||
|
|
||||||
private extension VendorBranchMockStorage {
|
|
||||||
|
|
||||||
init(_ mocks: [VendorBranch]) {
|
|
||||||
@Dependency(\.date.now) var now
|
|
||||||
@Dependency(\.uuid) var uuid
|
|
||||||
|
|
||||||
self.init(
|
|
||||||
mocks,
|
|
||||||
create: {
|
|
||||||
VendorBranch(id: uuid(), name: $0.name, vendorID: $0.vendorID, createdAt: now, updatedAt: now)
|
|
||||||
},
|
|
||||||
fetch: { request in
|
|
||||||
switch request {
|
|
||||||
case .all:
|
|
||||||
return { _ in true }
|
|
||||||
case let .for(vendorID):
|
|
||||||
return { $0.vendorID == vendorID }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
update: { branch, updates in
|
|
||||||
let model = VendorBranch(
|
|
||||||
id: branch.id,
|
|
||||||
name: updates.name ?? branch.name,
|
|
||||||
vendorID: branch.vendorID,
|
|
||||||
createdAt: branch.createdAt,
|
|
||||||
updatedAt: now
|
|
||||||
)
|
|
||||||
|
|
||||||
branch = model
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension DatabaseClient.VendorBranches {
|
|
||||||
static func mock(_ mocks: [VendorBranch] = []) -> Self {
|
|
||||||
let storage = VendorBranchMockStorage(mocks)
|
|
||||||
return .init(
|
|
||||||
create: { try await storage.create($0) },
|
|
||||||
delete: { try await storage.delete($0) },
|
|
||||||
fetchAll: { try await storage.fetchAll($0) },
|
|
||||||
get: { try await storage.get($0) },
|
|
||||||
update: { try await storage.update($0, $1) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -51,94 +51,3 @@ extension DatabaseClient.Vendors.GetRequest: Content {}
|
|||||||
extension DatabaseClient.Vendors: TestDependencyKey {
|
extension DatabaseClient.Vendors: TestDependencyKey {
|
||||||
public static let testValue: DatabaseClient.Vendors = Self()
|
public static let testValue: DatabaseClient.Vendors = Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
typealias VendorMockStorage = MockStorage<
|
|
||||||
Vendor,
|
|
||||||
Vendor.Create,
|
|
||||||
DatabaseClient.Vendors.FetchRequest,
|
|
||||||
DatabaseClient.Vendors.GetRequest,
|
|
||||||
Vendor.Update
|
|
||||||
>
|
|
||||||
|
|
||||||
private extension VendorMockStorage {
|
|
||||||
|
|
||||||
// swiftlint:disable function_body_length
|
|
||||||
static func vendors(_ mocks: [Vendor]) -> Self {
|
|
||||||
@Dependency(\.date.now) var now
|
|
||||||
@Dependency(\.uuid) var uuid
|
|
||||||
@Dependency(\.database.vendorBranches) var vendorBranches
|
|
||||||
|
|
||||||
return .init(
|
|
||||||
mocks,
|
|
||||||
create: {
|
|
||||||
Vendor(
|
|
||||||
id: uuid(),
|
|
||||||
name: $0.name,
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now
|
|
||||||
)
|
|
||||||
},
|
|
||||||
fetch: { _ in
|
|
||||||
{ _ in true }
|
|
||||||
},
|
|
||||||
fetchExtras: { request, models in
|
|
||||||
guard request == .withBranches else { return models }
|
|
||||||
let branches = try await vendorBranches.fetchAll()
|
|
||||||
return models.map { model in
|
|
||||||
Vendor(
|
|
||||||
id: model.id,
|
|
||||||
name: model.name,
|
|
||||||
branches: Array(branches.filter { $0.vendorID == model.id }),
|
|
||||||
createdAt: model.createdAt,
|
|
||||||
updatedAt: model.updatedAt
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
get: { req, model in
|
|
||||||
guard req == .withBranches else { return model }
|
|
||||||
let branches = try await vendorBranches.fetchAll(.for(vendorID: model.id))
|
|
||||||
return Vendor(
|
|
||||||
id: model.id,
|
|
||||||
name: model.name,
|
|
||||||
branches: branches,
|
|
||||||
createdAt: model.createdAt,
|
|
||||||
updatedAt: model.updatedAt
|
|
||||||
)
|
|
||||||
},
|
|
||||||
update: { model, updates, get in
|
|
||||||
var branches: [VendorBranch]?
|
|
||||||
|
|
||||||
if get == .withBranches {
|
|
||||||
branches = try await vendorBranches.fetchAll(.for(vendorID: model.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
let vendor = Vendor(
|
|
||||||
id: model.id,
|
|
||||||
name: updates.name ?? model.name,
|
|
||||||
branches: branches ?? model.branches,
|
|
||||||
createdAt: model.createdAt,
|
|
||||||
updatedAt: now
|
|
||||||
)
|
|
||||||
model = vendor
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// swiftlint:enable function_body_length
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension DatabaseClient.Vendors {
|
|
||||||
static func mock(_ mocks: [Vendor]) -> Self {
|
|
||||||
let storage = VendorMockStorage.vendors(mocks)
|
|
||||||
return .init(
|
|
||||||
create: { try await storage.create($0) },
|
|
||||||
delete: { try await storage.delete($0) },
|
|
||||||
fetchAll: { try await storage.fetchAll($0) },
|
|
||||||
get: { try await storage.get($0, $1) },
|
|
||||||
update: { req, updates, get in try await storage.update(req, updates, get) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ public extension DatabaseClient.PurchaseOrders {
|
|||||||
.init { create, createdById in
|
.init { create, createdById in
|
||||||
let model = try create.toModel(createdByID: createdById)
|
let model = try create.toModel(createdByID: createdById)
|
||||||
try await model.save(on: database)
|
try await model.save(on: database)
|
||||||
return try model.toDTO()
|
let fetched = try await PurchaseOrderModel.allQuery(on: database).filter(\.$id == model.id!).first()!
|
||||||
|
return try fetched.toDTO()
|
||||||
} fetchAll: {
|
} fetchAll: {
|
||||||
try await PurchaseOrderModel.allQuery(on: database)
|
try await PurchaseOrderModel.allQuery(on: database)
|
||||||
.all()
|
.all()
|
||||||
|
|||||||
@@ -60,6 +60,46 @@ public extension Employee {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
//
|
||||||
|
// public extension Employee {
|
||||||
|
//
|
||||||
|
// static func generateMocks(count: Int = 10) -> [Self] {
|
||||||
|
// @Dependency(\.date.now) var now
|
||||||
|
// @Dependency(\.uuid) var uuid
|
||||||
|
//
|
||||||
|
// var output = [Self]()
|
||||||
|
//
|
||||||
|
// for _ in 0 ... count {
|
||||||
|
// output.append(.init(
|
||||||
|
// id: uuid(),
|
||||||
|
// active: Bool.random(),
|
||||||
|
// createdAt: now,
|
||||||
|
// firstName: RandomNames.firstNames.randomElement()!,
|
||||||
|
// lastName: RandomNames.lastNames.randomElement()!,
|
||||||
|
// updatedAt: now
|
||||||
|
// ))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return output
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public extension Employee.Create {
|
||||||
|
|
||||||
|
static func generateMocks(count: Int = 5) -> [Self] {
|
||||||
|
(0 ... count).reduce(into: [Self]()) { array, _ in
|
||||||
|
array.append(.init(
|
||||||
|
firstName: RandomNames.firstNames.randomElement()! + String(RandomNames.characterString.randomElement()!),
|
||||||
|
lastName: RandomNames.lastNames.randomElement()! + String(RandomNames.characterString.randomElement()!),
|
||||||
|
active: Bool.random()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// public extension Employee {
|
// public extension Employee {
|
||||||
// static var mocks: [Self] {
|
// static var mocks: [Self] {
|
||||||
// [
|
// [
|
||||||
|
|||||||
150
Sources/SharedModels/MockHelpers.swift
Normal file
150
Sources/SharedModels/MockHelpers.swift
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#if DEBUG
|
||||||
|
enum RandomNames {
|
||||||
|
|
||||||
|
static let characterString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
// Static variables for first names
|
||||||
|
static let firstNames: [String] = [
|
||||||
|
"Oliver",
|
||||||
|
"Emma",
|
||||||
|
"Liam",
|
||||||
|
"Ava",
|
||||||
|
"Noah",
|
||||||
|
"Sophia",
|
||||||
|
"Elijah",
|
||||||
|
"Isabella",
|
||||||
|
"James",
|
||||||
|
"Mia",
|
||||||
|
"Benjamin",
|
||||||
|
"Charlotte",
|
||||||
|
"Lucas",
|
||||||
|
"Amelia",
|
||||||
|
"Henry",
|
||||||
|
"Harper",
|
||||||
|
"Alexander",
|
||||||
|
"Evelyn",
|
||||||
|
"William",
|
||||||
|
"Abigail"
|
||||||
|
]
|
||||||
|
|
||||||
|
// Static variables for last names
|
||||||
|
static let lastNames: [String] = [
|
||||||
|
"Smith",
|
||||||
|
"Johnson",
|
||||||
|
"Brown",
|
||||||
|
"Williams",
|
||||||
|
"Jones",
|
||||||
|
"Garcia",
|
||||||
|
"Martinez",
|
||||||
|
"Davis",
|
||||||
|
"Rodriguez",
|
||||||
|
"Martins",
|
||||||
|
"Hernandez",
|
||||||
|
"Lopez",
|
||||||
|
"Gonzalez",
|
||||||
|
"Wilson",
|
||||||
|
"Anderson",
|
||||||
|
"Thomas",
|
||||||
|
"Taylor",
|
||||||
|
"Moore",
|
||||||
|
"Jackson",
|
||||||
|
"Martin"
|
||||||
|
]
|
||||||
|
|
||||||
|
// Static variables for user names
|
||||||
|
static let userNames: [String] = [
|
||||||
|
"CoolCat123",
|
||||||
|
"FastFrog99",
|
||||||
|
"SunnySky",
|
||||||
|
"RocketRider",
|
||||||
|
"PixelPanda",
|
||||||
|
"ShadowNinja",
|
||||||
|
"ThunderWolf",
|
||||||
|
"GoldenEagle",
|
||||||
|
"SilverFox",
|
||||||
|
"MightyBear",
|
||||||
|
"IronLion",
|
||||||
|
"BlueTiger",
|
||||||
|
"FirePhoenix",
|
||||||
|
"CrystalDragon",
|
||||||
|
"NeonKnight",
|
||||||
|
"ElectricZebra",
|
||||||
|
"MagicUnicorn",
|
||||||
|
"StealthShark",
|
||||||
|
"GalaxyRanger",
|
||||||
|
"CosmicTurtle"
|
||||||
|
]
|
||||||
|
|
||||||
|
// Static variables for city names
|
||||||
|
static let cityNames: [String] = [
|
||||||
|
"Springfield",
|
||||||
|
"Rivertown",
|
||||||
|
"Sunnyvale",
|
||||||
|
"Lakeside",
|
||||||
|
"Hillcrest",
|
||||||
|
"Oakwood",
|
||||||
|
"Mapleton",
|
||||||
|
"Pinehill",
|
||||||
|
"Brookfield",
|
||||||
|
"Riverbend",
|
||||||
|
"Clearwater",
|
||||||
|
"Greystone",
|
||||||
|
"Westhaven",
|
||||||
|
"Eastwood",
|
||||||
|
"Silverlake",
|
||||||
|
"Goldenfield",
|
||||||
|
"Highland",
|
||||||
|
"Cedarville",
|
||||||
|
"Willowbrook",
|
||||||
|
"Fairview"
|
||||||
|
]
|
||||||
|
|
||||||
|
// Static variables for company names
|
||||||
|
static let companyNames: [String] = [
|
||||||
|
"TechNova",
|
||||||
|
"GreenLeaf Co.",
|
||||||
|
"Skyline Ventures",
|
||||||
|
"Pioneer Systems",
|
||||||
|
"Quantum Dynamics",
|
||||||
|
"Blue Horizon",
|
||||||
|
"NextGen Solutions",
|
||||||
|
"Summit Enterprises",
|
||||||
|
"FutureWorks",
|
||||||
|
"BrightPath",
|
||||||
|
"Eclipse Innovations",
|
||||||
|
"Golden Gate Technologies",
|
||||||
|
"Silverline Corp.",
|
||||||
|
"Vertex Industries",
|
||||||
|
"DynamicEdge",
|
||||||
|
"CrestPoint",
|
||||||
|
"EcoSphere",
|
||||||
|
"PrimeSource",
|
||||||
|
"InfinityTech",
|
||||||
|
"TrueNorth Inc."
|
||||||
|
]
|
||||||
|
|
||||||
|
// Static variables for fake emails
|
||||||
|
static let emails: [String] = [
|
||||||
|
"oliver.smith@example.com",
|
||||||
|
"emma.johnson@example.org",
|
||||||
|
"liam.brown@example.net",
|
||||||
|
"ava.williams@example.com",
|
||||||
|
"noah.jones@example.org",
|
||||||
|
"sophia.garcia@example.net",
|
||||||
|
"elijah.martinez@example.com",
|
||||||
|
"isabella.davis@example.org",
|
||||||
|
"james.rodriguez@example.net",
|
||||||
|
"mia.martins@example.com",
|
||||||
|
"benjamin.hernandez@example.org",
|
||||||
|
"charlotte.lopez@example.net",
|
||||||
|
"lucas.gonzalez@example.com",
|
||||||
|
"amelia.wilson@example.org",
|
||||||
|
"henry.anderson@example.net",
|
||||||
|
"harper.thomas@example.com",
|
||||||
|
"alexander.taylor@example.org",
|
||||||
|
"evelyn.moore@example.net",
|
||||||
|
"william.jackson@example.com",
|
||||||
|
"abigail.martin@example.org"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -72,3 +72,57 @@ public extension PurchaseOrder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
// public extension PurchaseOrder {
|
||||||
|
|
||||||
|
// static func generateMocks(
|
||||||
|
// count: Int = 50,
|
||||||
|
// employees: [Employee],
|
||||||
|
// users: [User],
|
||||||
|
// vendorBranches: [VendorBranch]
|
||||||
|
// ) -> [Self] {
|
||||||
|
// @Dependency(\.date.now) var now
|
||||||
|
// @Dependency(\.uuid) var uuid
|
||||||
|
//
|
||||||
|
// var output = [Self]()
|
||||||
|
//
|
||||||
|
// for id in 0 ... count {
|
||||||
|
// output.append(.init(
|
||||||
|
// id: id,
|
||||||
|
// workOrder: Int.random(in: 0 ... 100),
|
||||||
|
// materials: "Some thing",
|
||||||
|
// customer: "\(RandomNames.firstNames.randomElement()!) \(RandomNames.lastNames.randomElement()!)",
|
||||||
|
// truckStock: Bool.random(),
|
||||||
|
// createdBy: users.randomElement()!,
|
||||||
|
// createdFor: employees.randomElement()!,
|
||||||
|
// vendorBranch: vendorBranches.randomElement()!,
|
||||||
|
// createdAt: now,
|
||||||
|
// updatedAt: now
|
||||||
|
// ))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return output
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public extension PurchaseOrder.Create {
|
||||||
|
static func generateMocks(
|
||||||
|
count: Int = 50,
|
||||||
|
employees: [Employee],
|
||||||
|
vendorBranches: [VendorBranch]
|
||||||
|
) -> [Self] {
|
||||||
|
precondition(employees.count > 0)
|
||||||
|
precondition(vendorBranches.count > 0)
|
||||||
|
return (0 ... count).reduce(into: [Self]()) { array, _ in
|
||||||
|
array.append(.init(
|
||||||
|
materials: "Some materials",
|
||||||
|
customer: "\(RandomNames.firstNames.randomElement()!) \(RandomNames.lastNames.randomElement()!)",
|
||||||
|
createdForID: employees.randomElement()!.id,
|
||||||
|
vendorBranchID: vendorBranches.randomElement()!.id
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -84,12 +84,18 @@ public extension User {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// public extension User {
|
#if DEBUG
|
||||||
// static var mocks: [Self] {
|
public extension User.Create {
|
||||||
// [
|
static func generateMocks(count: Int = 5) -> [Self] {
|
||||||
// .init(email: "blob@test.com", username: "blob"),
|
(0 ... count).reduce(into: [Self]()) { array, _ in
|
||||||
// .init(email: "blob-jr@test.com", username: "blob-jr"),
|
array.append(.init(
|
||||||
// .init(email: "blob-sr@test.com", username: "blob-sr")
|
username: RandomNames.userNames.randomElement()! + String(RandomNames.characterString.randomElement()!),
|
||||||
// ]
|
email: String(RandomNames.characterString.randomElement()!) + RandomNames.emails.randomElement()!,
|
||||||
// }
|
password: "super-secret",
|
||||||
// }
|
confirmPassword: "super-secret"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -41,3 +41,40 @@ public extension Vendor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
//
|
||||||
|
// public extension Vendor {
|
||||||
|
//
|
||||||
|
// static func generateMocks(count: Int = 20) -> [Self] {
|
||||||
|
// @Dependency(\.date.now) var now
|
||||||
|
// @Dependency(\.uuid) var uuid
|
||||||
|
//
|
||||||
|
// var output = [Self]()
|
||||||
|
//
|
||||||
|
// for _ in 0 ... count {
|
||||||
|
// output.append(.init(
|
||||||
|
// id: uuid(),
|
||||||
|
// name: RandomNames.companyNames.randomElement()!,
|
||||||
|
// branches: nil,
|
||||||
|
// createdAt: now,
|
||||||
|
// updatedAt: now
|
||||||
|
// ))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return output
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public extension Vendor.Create {
|
||||||
|
static func generateMocks(count: Int = 5) -> [Self] {
|
||||||
|
(0 ... count).reduce(into: [Self]()) { array, _ in
|
||||||
|
array.append(.init(
|
||||||
|
name: RandomNames.companyNames.randomElement()! + String(RandomNames.characterString.randomElement()!)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -42,3 +42,41 @@ public extension VendorBranch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
// public extension VendorBranch {
|
||||||
|
//
|
||||||
|
// static func generateMocks(countPerVendor: Int = 3, vendors: [Vendor]) -> [Self] {
|
||||||
|
// @Dependency(\.date.now) var now
|
||||||
|
// @Dependency(\.uuid) var uuid
|
||||||
|
//
|
||||||
|
// var output = [Self]()
|
||||||
|
//
|
||||||
|
// for vendor in vendors {
|
||||||
|
// for _ in 0 ... countPerVendor {
|
||||||
|
// output.append(.init(
|
||||||
|
// id: uuid(),
|
||||||
|
// name: RandomNames.cityNames.randomElement()!,
|
||||||
|
// vendorID: vendor.id,
|
||||||
|
// createdAt: now,
|
||||||
|
// updatedAt: now
|
||||||
|
// ))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return output
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public extension VendorBranch.Create {
|
||||||
|
|
||||||
|
static func generateMocks(countPerVendor: Int = 3, vendors: [Vendor]) -> [Self] {
|
||||||
|
return vendors.reduce(into: [Self]()) { output, vendor in
|
||||||
|
output = (0 ... countPerVendor).reduce(into: output) { array, _ in
|
||||||
|
array.append(.init(name: RandomNames.cityNames.randomElement()!, vendorID: vendor.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -49,12 +49,10 @@ struct DatabaseClientTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(arguments: EmployeeTestFactory.testCases)
|
@Test
|
||||||
func employees(factory: EmployeeTestFactory) async throws {
|
func employees() async throws {
|
||||||
try await withDatabase(migrations: Employee.Migrate()) {
|
try await withDatabase(migrations: Employee.Migrate()) {
|
||||||
$0.uuid = .incrementing
|
$0.database.employees = .live(database: $1)
|
||||||
$0.date = .init { Date() }
|
|
||||||
$0.database.employees = factory.handler($1)
|
|
||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.database.employees) var employees
|
@Dependency(\.database.employees) var employees
|
||||||
|
|
||||||
@@ -93,11 +91,11 @@ struct DatabaseClientTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(arguments: VendorTestFactory.testCases)
|
@Test
|
||||||
func vendors(factory: VendorTestFactory) async throws {
|
func vendors() async throws {
|
||||||
try await withDatabase(migrations: Vendor.Migrate(), VendorBranch.Migrate()) {
|
try await withDatabase(migrations: Vendor.Migrate(), VendorBranch.Migrate()) {
|
||||||
$0.database.vendorBranches = factory.handler($1).0
|
$0.database.vendorBranches = .live(database: $1)
|
||||||
$0.database.vendors = factory.handler($1).1
|
$0.database.vendors = .live(database: $1)
|
||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.database.vendorBranches) var branches
|
@Dependency(\.database.vendorBranches) var branches
|
||||||
@Dependency(\.database.vendors) var vendors
|
@Dependency(\.database.vendors) var vendors
|
||||||
@@ -132,11 +130,11 @@ struct DatabaseClientTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(arguments: VendorTestFactory.testCases)
|
@Test
|
||||||
func vendorBranches(factory: VendorTestFactory) async throws {
|
func vendorBranches() async throws {
|
||||||
try await withDatabase(migrations: Vendor.Migrate(), VendorBranch.Migrate()) {
|
try await withDatabase(migrations: Vendor.Migrate(), VendorBranch.Migrate()) {
|
||||||
$0.database.vendorBranches = factory.handler($1).0
|
$0.database.vendorBranches = .live(database: $1)
|
||||||
$0.database.vendors = factory.handler($1).1
|
$0.database.vendors = .live(database: $1)
|
||||||
} operation: {
|
} operation: {
|
||||||
@Dependency(\.database.vendorBranches) var branches
|
@Dependency(\.database.vendorBranches) var branches
|
||||||
@Dependency(\.database.vendors) var vendors
|
@Dependency(\.database.vendors) var vendors
|
||||||
@@ -197,22 +195,3 @@ struct DatabaseClientTests {
|
|||||||
await dbs.shutdownAsync()
|
await dbs.shutdownAsync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EmployeeTestFactory {
|
|
||||||
let handler: (any Database) -> DatabaseClient.Employees
|
|
||||||
|
|
||||||
static var testCases: [Self] { [
|
|
||||||
.init(handler: { .live(database: $0) }),
|
|
||||||
.init(handler: { _ in .mock([]) })
|
|
||||||
] }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VendorTestFactory {
|
|
||||||
let handler: (any Database) -> (DatabaseClient.VendorBranches, DatabaseClient.Vendors)
|
|
||||||
|
|
||||||
static var testCases: [Self] { [
|
|
||||||
.init(handler: { (.live(database: $0), .live(database: $0)) }),
|
|
||||||
.init(handler: { _ in (.mock([]), .mock([])) })
|
|
||||||
] }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user