Files
vapor-po/Sources/DatabaseClient/MockStorage.swift

143 lines
4.6 KiB
Swift

#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