import std/random import std/sequtils import pkg/questionable import pkg/questionable/results import pkg/chronos import pkg/datastore import pkg/codex/stores import pkg/codex/errors import pkg/codex/sales import pkg/codex/utils/json import ../../asynctest import ../examples import ../helpers const CONCURRENCY_TESTS_COUNT = 1000 asyncchecksuite "Reservations module": var repo: RepoStore repoDs: Datastore metaDs: Datastore reservations: Reservations let repoTmp = TempLevelDb.new() metaTmp = TempLevelDb.new() setup: randomize(1.int64) # create reproducible results repoDs = repoTmp.newDb() metaDs = metaTmp.newDb() repo = RepoStore.new(repoDs, metaDs) reservations = Reservations.new(repo) teardown: await repoTmp.destroyDb() await metaTmp.destroyDb() proc createAvailability(): Availability = let example = Availability.example let totalSize = rand(100000..200000) let availability = waitFor reservations.createAvailability( totalSize.u256, example.duration, example.minPrice, example.maxCollateral ) return availability.get proc createReservation(availability: Availability): Reservation = let size = rand(1.. orig check (updated.freeSize - orig) == 200.u256 check (repo.quotaReservedBytes - origQuota) == 200.NBytes test "update releases quota when lowering size": let availability = createAvailability() origQuota = repo.quotaReservedBytes availability.totalSize = availability.totalSize - 100 check isOk await reservations.update(availability) check (origQuota - repo.quotaReservedBytes) == 100.NBytes test "update reserves quota when growing size": let availability = createAvailability() origQuota = repo.quotaReservedBytes availability.totalSize = availability.totalSize + 100 check isOk await reservations.update(availability) check (repo.quotaReservedBytes - origQuota) == 100.NBytes test "reservation can be partially released": let availability = createAvailability() let reservation = createReservation(availability) check isOk await reservations.release( reservation.id, reservation.availabilityId, 1 ) let key = reservation.key.get let updated = !(await reservations.get(key, Reservation)) check updated.size == reservation.size - 1 test "cannot release more bytes than size of reservation": let availability = createAvailability() let reservation = createReservation(availability) let updated = await reservations.release( reservation.id, reservation.availabilityId, (reservation.size + 1).truncate(uint) ) check updated.isErr check updated.error of BytesOutOfBoundsError test "cannot release bytes from non-existant reservation": let availability = createAvailability() let reservation = createReservation(availability) let updated = await reservations.release( ReservationId.example, availability.id, 1 ) check updated.isErr check updated.error of NotExistsError test "onAvailabilityAdded called when availability is created": var added: Availability reservations.onAvailabilityAdded = proc(a: Availability) {.async.} = added = a let availability = createAvailability() check added == availability test "onAvailabilityAdded called when availability size is increased": var availability = createAvailability() var added: Availability reservations.onAvailabilityAdded = proc(a: Availability) {.async.} = added = a availability.freeSize += 1.u256 discard await reservations.update(availability) check added == availability test "onAvailabilityAdded is not called when availability size is decreased": var availability = createAvailability() var called = false reservations.onAvailabilityAdded = proc(a: Availability) {.async.} = called = true availability.freeSize -= 1.u256 discard await reservations.update(availability) check not called test "availabilities can be found": let availability = createAvailability() let found = await reservations.findAvailability( availability.freeSize, availability.duration, availability.minPrice, availability.maxCollateral) check found.isSome check found.get == availability test "non-matching availabilities are not found": let availability = createAvailability() let found = await reservations.findAvailability( availability.freeSize + 1, availability.duration, availability.minPrice, availability.maxCollateral) check found.isNone test "non-existant availability cannot be found": let availability = Availability.example let found = (await reservations.findAvailability( availability.freeSize, availability.duration, availability.minPrice, availability.maxCollateral )) check found.isNone test "non-existant availability cannot be retrieved": let key = AvailabilityId.example.key.get let got = await reservations.get(key, Availability) check got.error of NotExistsError test "can get available bytes in repo": check reservations.available == DefaultQuotaBytes.uint test "reports quota available to be reserved": check reservations.hasAvailable(DefaultQuotaBytes.uint - 1) test "reports quota not available to be reserved": check not reservations.hasAvailable(DefaultQuotaBytes.uint + 1) test "fails to create availability with size that is larger than available quota": let created = await reservations.createAvailability( (DefaultQuotaBytes.uint + 1).u256, UInt256.example, UInt256.example, UInt256.example ) check created.isErr check created.error of ReserveFailedError check created.error.parent of QuotaNotEnoughError