209 lines
7.4 KiB
Nim
209 lines
7.4 KiB
Nim
import std/sequtils
|
|
import std/sugar
|
|
|
|
import pkg/questionable
|
|
import pkg/questionable/results
|
|
|
|
import pkg/chronos
|
|
import pkg/asynctest
|
|
import pkg/datastore
|
|
import pkg/json_serialization
|
|
import pkg/json_serialization/std/options
|
|
import pkg/stew/byteutils
|
|
|
|
import pkg/codex/stores
|
|
import pkg/codex/sales
|
|
|
|
import ../examples
|
|
import ./helpers
|
|
|
|
suite "Reservations module":
|
|
|
|
var
|
|
repo: RepoStore
|
|
repoDs: Datastore
|
|
metaDs: Datastore
|
|
availability: Availability
|
|
reservations: Reservations
|
|
|
|
setup:
|
|
repoDs = SQLiteDatastore.new(Memory).tryGet()
|
|
metaDs = SQLiteDatastore.new(Memory).tryGet()
|
|
repo = RepoStore.new(repoDs, metaDs)
|
|
reservations = Reservations.new(repo)
|
|
availability = Availability.example
|
|
|
|
test "has no availability initially":
|
|
check (await reservations.allAvailabilities()).len == 0
|
|
|
|
test "generates unique ids for storage availability":
|
|
let availability1 = Availability.init(1.u256, 2.u256, 3.u256)
|
|
let availability2 = Availability.init(1.u256, 2.u256, 3.u256)
|
|
check availability1.id != availability2.id
|
|
|
|
test "can reserve available storage":
|
|
let availability1 = Availability.example
|
|
let availability2 = Availability.example
|
|
check isOk await reservations.reserve(availability1)
|
|
check isOk await reservations.reserve(availability2)
|
|
|
|
let availabilities = await reservations.allAvailabilities()
|
|
check:
|
|
availabilities.len == 2
|
|
availabilities.contains(availability1)
|
|
availabilities.contains(availability2)
|
|
|
|
test "reserved availability exists":
|
|
check isOk await reservations.reserve(availability)
|
|
|
|
without exists =? await reservations.exists(availability.id):
|
|
fail()
|
|
|
|
check exists
|
|
|
|
test "reserved availability can be released":
|
|
check isOk await reservations.reserve(availability)
|
|
check isOk await reservations.release(availability.id)
|
|
|
|
without exists =? await reservations.exists(availability.id):
|
|
fail()
|
|
|
|
check not exists
|
|
|
|
test "non-existant availability cannot be released":
|
|
let r = await reservations.release(availability.id)
|
|
check r.error of AvailabilityGetFailedError
|
|
check r.error.parent of AvailabilityNotExistsError
|
|
|
|
test "added availability is not used initially":
|
|
check isOk await reservations.reserve(availability)
|
|
|
|
without available =? await reservations.get(availability.id):
|
|
fail()
|
|
|
|
check not available.used
|
|
|
|
test "availability can be marked used":
|
|
check isOk await reservations.reserve(availability)
|
|
|
|
check isOk await reservations.markUsed(availability, SlotId.example)
|
|
|
|
without available =? await reservations.get(availability.id):
|
|
fail()
|
|
|
|
check available.used
|
|
|
|
test "availability can be marked unused":
|
|
check isOk await reservations.reserve(availability)
|
|
|
|
check isOk await reservations.markUsed(availability, SlotId.example)
|
|
check isOk await reservations.markUnused(availability)
|
|
|
|
without available =? await reservations.get(availability.id):
|
|
fail()
|
|
|
|
check not available.used
|
|
|
|
test "used availability can be found":
|
|
check isOk await reservations.reserve(availability)
|
|
|
|
check isOk await reservations.markUsed(availability, SlotId.example)
|
|
|
|
without available =? await reservations.find(availability.size,
|
|
availability.duration, availability.minPrice, used = true):
|
|
|
|
fail()
|
|
|
|
test "unused availability can be found":
|
|
check isOk await reservations.reserve(availability)
|
|
|
|
without available =? await reservations.find(availability.size,
|
|
availability.duration, availability.minPrice, used = false):
|
|
|
|
fail()
|
|
|
|
test "non-existant availability cannot be found":
|
|
check isNone (await reservations.find(availability.size,
|
|
availability.duration, availability.minPrice, used = false))
|
|
|
|
test "non-existant availability cannot be retrieved":
|
|
let r = await reservations.get(availability.id)
|
|
check r.error of AvailabilityNotExistsError
|
|
|
|
test "same availability cannot be reserved twice":
|
|
check isOk await reservations.reserve(availability)
|
|
let r = await reservations.reserve(availability)
|
|
check r.error of AvailabilityAlreadyExistsError
|
|
|
|
test "can get available bytes in repo":
|
|
check reservations.available == DefaultQuotaBytes
|
|
|
|
test "reserving availability reduces available bytes":
|
|
check isOk await reservations.reserve(availability)
|
|
check reservations.available ==
|
|
DefaultQuotaBytes - availability.size.truncate(uint)
|
|
|
|
test "reports quota available to be reserved":
|
|
check reservations.available(availability.size.truncate(uint))
|
|
|
|
test "reports quota not available to be reserved":
|
|
repo = RepoStore.new(repoDs, metaDs,
|
|
quotaMaxBytes = availability.size.truncate(uint) - 1)
|
|
reservations = Reservations.new(repo)
|
|
check not reservations.available(availability.size.truncate(uint))
|
|
|
|
test "fails to reserve availability size that is larger than available quota":
|
|
repo = RepoStore.new(repoDs, metaDs,
|
|
quotaMaxBytes = availability.size.truncate(uint) - 1)
|
|
reservations = Reservations.new(repo)
|
|
let r = await reservations.reserve(availability)
|
|
check r.error of AvailabilityReserveFailedError
|
|
check r.error.parent of QuotaNotEnoughError
|
|
|
|
test "rolls back persisted availability if repo reservation fails":
|
|
repo = RepoStore.new(repoDs, metaDs,
|
|
quotaMaxBytes = availability.size.truncate(uint) - 1)
|
|
reservations = Reservations.new(repo)
|
|
discard await reservations.reserve(availability)
|
|
check exists =? (await reservations.exists(availability.id)) and not exists
|
|
|
|
test "fails to release availability size that is larger than available quota":
|
|
repo = RepoStore.new(repoDs, metaDs,
|
|
quotaMaxBytes = availability.size.truncate(uint))
|
|
reservations = Reservations.new(repo)
|
|
discard await reservations.reserve(availability)
|
|
# increase size of availability past repo quota, so that the next release
|
|
# will fail
|
|
availability.size += 1.u256
|
|
let key = !(availability.key)
|
|
check isOk await metaDs.put(key, @(availability.toJson.toBytes))
|
|
let r = await reservations.release(availability.id)
|
|
check r.error of AvailabilityReleaseFailedError
|
|
check r.error.parent.msg == "Cannot release this many bytes"
|
|
|
|
test "rolls back persisted availability if repo release fails":
|
|
repo = RepoStore.new(repoDs, metaDs,
|
|
quotaMaxBytes = availability.size.truncate(uint))
|
|
reservations = Reservations.new(repo)
|
|
discard await reservations.reserve(availability)
|
|
# increase size of availability past repo quota, so that the next release
|
|
# will fail
|
|
availability.size += 1.u256
|
|
let key = !(availability.key)
|
|
check isOk await metaDs.put(key, @(availability.toJson.toBytes))
|
|
discard await reservations.release(availability.id)
|
|
check exists =? (await reservations.exists(availability.id)) and exists
|
|
|
|
test "started should be true once started":
|
|
repo = RepoStore.new(repoDs, metaDs,
|
|
quotaMaxBytes = availability.size.truncate(uint))
|
|
reservations = Reservations.new(repo)
|
|
discard await reservations.reserve(availability)
|
|
# increase size of availability past repo quota, so that the next release
|
|
# will fail
|
|
availability.size += 1.u256
|
|
let key = !(availability.key)
|
|
check isOk await metaDs.put(key, @(availability.toJson.toBytes))
|
|
discard await reservations.release(availability.id)
|
|
check exists =? (await reservations.exists(availability.id)) and exists
|