feat: Working on mocks and mock storage.
This commit is contained in:
143
Sources/DatabaseClient/MockStorage.swift
Normal file
143
Sources/DatabaseClient/MockStorage.swift
Normal file
@@ -0,0 +1,143 @@
|
||||
#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) -> 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) -> 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 = 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) -> 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) -> 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) -> 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
|
||||
Reference in New Issue
Block a user