159 lines
4.8 KiB
Nim
159 lines
4.8 KiB
Nim
|
import std/sets
|
||
|
import std/sequtils
|
||
|
import std/sugar
|
||
|
import std/times
|
||
|
import pkg/asynctest
|
||
|
import pkg/chronos
|
||
|
import pkg/codex/sales
|
||
|
import pkg/codex/sales/salesagent
|
||
|
import pkg/codex/sales/salescontext
|
||
|
import pkg/codex/sales/statemachine
|
||
|
import pkg/codex/sales/states/errorhandling
|
||
|
import pkg/codex/proving
|
||
|
import ../helpers/mockmarket
|
||
|
import ../helpers/mockclock
|
||
|
import ../helpers/eventually
|
||
|
import ../examples
|
||
|
|
||
|
var onCancelCalled = false
|
||
|
var onFailedCalled = false
|
||
|
var onSlotFilledCalled = false
|
||
|
var onErrorCalled = false
|
||
|
|
||
|
type
|
||
|
MockState = ref object of SaleState
|
||
|
MockErrorState = ref object of ErrorHandlingState
|
||
|
|
||
|
method `$`*(state: MockState): string = "MockState"
|
||
|
|
||
|
method onCancelled*(state: MockState, request: StorageRequest): ?State =
|
||
|
onCancelCalled = true
|
||
|
|
||
|
method onFailed*(state: MockState, request: StorageRequest): ?State =
|
||
|
onFailedCalled = true
|
||
|
|
||
|
method onSlotFilled*(state: MockState, requestId: RequestId,
|
||
|
slotIndex: UInt256): ?State =
|
||
|
onSlotFilledCalled = true
|
||
|
|
||
|
method onError*(state: MockErrorState, err: ref CatchableError): ?State =
|
||
|
onErrorCalled = true
|
||
|
|
||
|
method run*(state: MockErrorState, machine: Machine): Future[?State] {.async.} =
|
||
|
raise newException(ValueError, "failure")
|
||
|
|
||
|
suite "Sales agent":
|
||
|
|
||
|
var request = StorageRequest(
|
||
|
ask: StorageAsk(
|
||
|
slots: 4,
|
||
|
slotSize: 100.u256,
|
||
|
duration: 60.u256,
|
||
|
reward: 10.u256,
|
||
|
),
|
||
|
content: StorageContent(
|
||
|
cid: "some cid"
|
||
|
),
|
||
|
expiry: (getTime() + initDuration(hours=1)).toUnix.u256
|
||
|
)
|
||
|
|
||
|
var agent: SalesAgent
|
||
|
var context: SalesContext
|
||
|
var slotIndex: UInt256
|
||
|
var market: MockMarket
|
||
|
var clock: MockClock
|
||
|
|
||
|
setup:
|
||
|
market = MockMarket.new()
|
||
|
clock = MockClock.new()
|
||
|
context = SalesContext(market: market, clock: clock)
|
||
|
slotIndex = 0.u256
|
||
|
onCancelCalled = false
|
||
|
onFailedCalled = false
|
||
|
onSlotFilledCalled = false
|
||
|
agent = newSalesAgent(context,
|
||
|
request.id,
|
||
|
slotIndex,
|
||
|
some request)
|
||
|
request.expiry = (getTime() + initDuration(hours=1)).toUnix.u256
|
||
|
|
||
|
teardown:
|
||
|
await agent.stop()
|
||
|
|
||
|
test "can retrieve request":
|
||
|
agent = newSalesAgent(context,
|
||
|
request.id,
|
||
|
slotIndex,
|
||
|
none StorageRequest)
|
||
|
market.requested = @[request]
|
||
|
await agent.retrieveRequest()
|
||
|
check agent.data.request == some request
|
||
|
|
||
|
test "subscribe assigns subscriptions/futures":
|
||
|
await agent.subscribe()
|
||
|
check not agent.data.cancelled.isNil
|
||
|
check not agent.data.failed.isNil
|
||
|
check not agent.data.fulfilled.isNil
|
||
|
check not agent.data.slotFilled.isNil
|
||
|
|
||
|
test "unsubscribe deassigns subscriptions/futures":
|
||
|
await agent.subscribe()
|
||
|
await agent.unsubscribe()
|
||
|
check agent.data.cancelled.isNil
|
||
|
check agent.data.failed.isNil
|
||
|
check agent.data.fulfilled.isNil
|
||
|
check agent.data.slotFilled.isNil
|
||
|
|
||
|
test "subscribe can be called multiple times, without overwriting subscriptions/futures":
|
||
|
await agent.subscribe()
|
||
|
let cancelled = agent.data.cancelled
|
||
|
let failed = agent.data.failed
|
||
|
let fulfilled = agent.data.fulfilled
|
||
|
let slotFilled = agent.data.slotFilled
|
||
|
await agent.subscribe()
|
||
|
check cancelled == agent.data.cancelled
|
||
|
check failed == agent.data.failed
|
||
|
check fulfilled == agent.data.fulfilled
|
||
|
check slotFilled == agent.data.slotFilled
|
||
|
|
||
|
test "unsubscribe can be called multiple times":
|
||
|
await agent.subscribe()
|
||
|
await agent.unsubscribe()
|
||
|
await agent.unsubscribe()
|
||
|
|
||
|
test "subscribe can be called when request expiry has lapsed":
|
||
|
# succeeds when agent.data.fulfilled.isNil
|
||
|
request.expiry = (getTime() - initDuration(seconds=1)).toUnix.u256
|
||
|
agent.data.request = some request
|
||
|
check agent.data.fulfilled.isNil
|
||
|
await agent.subscribe()
|
||
|
|
||
|
test "current state onCancelled called when cancel emitted":
|
||
|
let state = MockState.new()
|
||
|
agent.start(state)
|
||
|
await agent.subscribe()
|
||
|
clock.set(request.expiry.truncate(int64))
|
||
|
check eventually onCancelCalled
|
||
|
|
||
|
test "cancelled future is finished (cancelled) when fulfillment emitted":
|
||
|
agent.start(MockState.new())
|
||
|
await agent.subscribe()
|
||
|
market.emitRequestFulfilled(request.id)
|
||
|
check eventually agent.data.cancelled.cancelled()
|
||
|
|
||
|
test "current state onFailed called when failed emitted":
|
||
|
agent.start(MockState.new())
|
||
|
await agent.subscribe()
|
||
|
market.emitRequestFailed(request.id)
|
||
|
check eventually onFailedCalled
|
||
|
|
||
|
test "current state onSlotFilled called when slot filled emitted":
|
||
|
agent.start(MockState.new())
|
||
|
await agent.subscribe()
|
||
|
market.emitSlotFilled(request.id, slotIndex)
|
||
|
check eventually onSlotFilledCalled
|
||
|
|
||
|
test "ErrorHandlingState.onError can be overridden at the state level":
|
||
|
agent.start(MockErrorState.new())
|
||
|
check eventually onErrorCalled
|