diff --git a/codex/rest/api.nim b/codex/rest/api.nim index 5d813188..243d4ed6 100644 --- a/codex/rest/api.nim +++ b/codex/rest/api.nim @@ -475,7 +475,7 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) = if restAv.totalSize == 0: return RestApiResponse.error( - Http400, "Total size must be larger then zero", headers = headers + Http422, "Total size must be larger then zero", headers = headers ) if not reservations.hasAvailable(restAv.totalSize): @@ -548,17 +548,23 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) = return RestApiResponse.error(Http500, error.msg) if isSome restAv.freeSize: - return RestApiResponse.error(Http400, "Updating freeSize is not allowed") + return RestApiResponse.error(Http422, "Updating freeSize is not allowed") if size =? restAv.totalSize: + if size == 0: + return RestApiResponse.error(Http422, "Total size must be larger then zero") + # we don't allow lowering the totalSize bellow currently utilized size if size < (availability.totalSize - availability.freeSize): return RestApiResponse.error( - Http400, + Http422, "New totalSize must be larger then current totalSize - freeSize, which is currently: " & $(availability.totalSize - availability.freeSize), ) + if not reservations.hasAvailable(size): + return RestApiResponse.error(Http422, "Not enough storage quota") + availability.freeSize += size - availability.totalSize availability.totalSize = size diff --git a/codex/sales.nim b/codex/sales.nim index 998a2967..a4a174c1 100644 --- a/codex/sales.nim +++ b/codex/sales.nim @@ -374,13 +374,13 @@ proc onSlotFreed(sales: Sales, requestId: RequestId, slotIndex: uint64) = if err =? queue.push(slotQueueItem).errorOption: if err of SlotQueueItemExistsError: - error "Failed to push item to queue becaue it already exists", + error "Failed to push item to queue because it already exists", error = err.msgDetail elif err of QueueNotRunningError: - warn "Failed to push item to queue becaue queue is not running", + warn "Failed to push item to queue because queue is not running", error = err.msgDetail - except CatchableError as e: - warn "Failed to add slot to queue", error = e.msg + except CancelledError as e: + trace "sales.addSlotToQueue was cancelled" # We could get rid of this by adding the storage ask in the SlotFreed event, # so we would not need to call getRequest to get the collateralPerSlot. diff --git a/openapi.yaml b/openapi.yaml index ad1b166b..c2088cc5 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -704,7 +704,7 @@ paths: "400": description: Invalid data input "422": - description: Not enough node's storage quota available + description: The provided parameters did not pass validation "500": description: Error reserving availability "503": @@ -737,7 +737,7 @@ paths: "404": description: Availability not found "422": - description: Not enough node's storage quota available + description: The provided parameters did not pass validation "500": description: Error reserving availability "503": diff --git a/tests/helpers.nim b/tests/helpers.nim index 82b544f1..b48b787e 100644 --- a/tests/helpers.nim +++ b/tests/helpers.nim @@ -1,7 +1,7 @@ import helpers/multisetup import helpers/trackers import helpers/templeveldb - +import std/times import std/sequtils, chronos export multisetup, trackers, templeveldb diff --git a/tests/integration/codexclient.nim b/tests/integration/codexclient.nim index ef76b577..5d5f0cc2 100644 --- a/tests/integration/codexclient.nim +++ b/tests/integration/codexclient.nim @@ -45,7 +45,7 @@ proc request( ).get .send() -proc post( +proc post*( self: CodexClient, url: string, body: string = "", @@ -69,7 +69,7 @@ proc delete( .} = return self.request(MethodDelete, url, headers = headers) -proc patch( +proc patch*( self: CodexClient, url: string, body: string = "", @@ -290,11 +290,11 @@ proc getSalesAgent*( except CatchableError as e: return failure e.msg -proc postAvailability*( +proc postAvailabilityRaw*( client: CodexClient, totalSize, duration: uint64, minPricePerBytePerSecond, totalCollateral: UInt256, -): Future[?!Availability] {.async: (raises: [CancelledError, HttpError]).} = +): Future[HttpClientResponseRef] {.async: (raises: [CancelledError, HttpError]).} = ## Post sales availability endpoint ## let url = client.baseurl & "/sales/availability" @@ -305,7 +305,17 @@ proc postAvailability*( "minPricePerBytePerSecond": minPricePerBytePerSecond, "totalCollateral": totalCollateral, } - let response = await client.post(url, $json) + + return await client.post(url, $json) + +proc postAvailability*( + client: CodexClient, + totalSize, duration: uint64, + minPricePerBytePerSecond, totalCollateral: UInt256, +): Future[?!Availability] {.async: (raises: [CancelledError, HttpError]).} = + let response = await client.postAvailabilityRaw( + totalSize, duration, minPricePerBytePerSecond, totalCollateral + ) let body = await response.body doAssert response.status == 201, @@ -389,3 +399,6 @@ proc requestId*( client: CodexClient, id: PurchaseId ): Future[?RequestId] {.async: (raises: [CancelledError, HttpError]).} = return (await client.getPurchase(id)).option .? requestId + +proc buildUrl*(client: CodexClient, path: string): string = + return client.baseurl & path diff --git a/tests/integration/multinodes.nim b/tests/integration/multinodes.nim index 0003b216..4b183674 100644 --- a/tests/integration/multinodes.nim +++ b/tests/integration/multinodes.nim @@ -37,10 +37,12 @@ type MultiNodeSuiteError = object of CatchableError +const jsonRpcProviderUrl* = "http://127.0.0.1:8545" + proc raiseMultiNodeSuiteError(msg: string) = raise newException(MultiNodeSuiteError, msg) -proc nextFreePort(startPort: int): Future[int] {.async.} = +proc nextFreePort*(startPort: int): Future[int] {.async.} = proc client(server: StreamServer, transp: StreamTransport) {.async.} = await transp.closeWait() @@ -60,6 +62,15 @@ proc nextFreePort(startPort: int): Future[int] {.async.} = trace "port is not free", port inc port +proc sanitize(pathSegment: string): string = + var sanitized = pathSegment + for invalid in invalidFilenameChars.items: + sanitized = sanitized.replace(invalid, '_').replace(' ', '_') + sanitized + +proc getTempDirName*(starttime: string, role: Role, roleIdx: int): string = + getTempDir() / "Codex" / sanitize($starttime) / sanitize($role & "_" & $roleIdx) + template multinodesuite*(name: string, body: untyped) = asyncchecksuite name: # Following the problem described here: @@ -82,7 +93,6 @@ template multinodesuite*(name: string, body: untyped) = # .withEthProvider("ws://localhost:8545") # .some, # ... - let jsonRpcProviderUrl = "http://127.0.0.1:8545" var running {.inject, used.}: seq[RunningNode] var bootstrapNodes: seq[string] let starttime = now().format("yyyy-MM-dd'_'HH:mm:ss") @@ -148,8 +158,7 @@ template multinodesuite*(name: string, body: untyped) = raiseMultiNodeSuiteError "Cannot start node at nodeIdx " & $nodeIdx & ", not enough eth accounts." - let datadir = - getTempDir() / "Codex" / sanitize($starttime) / sanitize($role & "_" & $roleIdx) + let datadir = getTempDirName(starttime, role, roleIdx) try: if config.logFile.isSome: diff --git a/tests/integration/testrestapi.nim b/tests/integration/testrestapi.nim index e7e185b8..415658c1 100644 --- a/tests/integration/testrestapi.nim +++ b/tests/integration/testrestapi.nim @@ -55,25 +55,6 @@ twonodessuite "REST API": check: [cid1, cid2].allIt(it in list.content.mapIt(it.cid)) - test "request storage fails for datasets that are too small", twoNodesConfig: - let cid = (await client1.upload("some file contents")).get - let response = ( - await client1.requestStorageRaw( - cid, - duration = 10.uint64, - pricePerBytePerSecond = 1.u256, - proofProbability = 3.u256, - collateralPerByte = 1.u256, - expiry = 9.uint64, - ) - ) - - check: - response.status == 422 - (await response.body) == - "Dataset too small for erasure parameters, need at least " & - $(2 * DefaultBlockSize.int) & " bytes" - test "request storage succeeds for sufficiently sized datasets", twoNodesConfig: let data = await RandomChunker.example(blocks = 2) let cid = (await client1.upload(data)).get @@ -91,176 +72,6 @@ twonodessuite "REST API": check: response.status == 200 - test "request storage fails if tolerance is zero", twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = 100.uint64 - let pricePerBytePerSecond = 1.u256 - let proofProbability = 3.u256 - let expiry = 30.uint64 - let collateralPerByte = 1.u256 - let nodes = 3 - let tolerance = 0 - - var responseBefore = ( - await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, - expiry, nodes.uint, tolerance.uint, - ) - ) - - check responseBefore.status == 422 - check (await responseBefore.body) == "Tolerance needs to be bigger then zero" - - test "request storage fails if duration exceeds limit", twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = (31 * 24 * 60 * 60).uint64 - # 31 days TODO: this should not be hardcoded, but waits for https://github.com/codex-storage/nim-codex/issues/1056 - let proofProbability = 3.u256 - let expiry = 30.uint - let collateralPerByte = 1.u256 - let nodes = 3 - let tolerance = 2 - let pricePerBytePerSecond = 1.u256 - - var responseBefore = ( - await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, - expiry, nodes.uint, tolerance.uint, - ) - ) - - check responseBefore.status == 422 - check "Duration exceeds limit of" in (await responseBefore.body) - - test "request storage fails if nodes and tolerance aren't correct", twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = 100.uint64 - let pricePerBytePerSecond = 1.u256 - let proofProbability = 3.u256 - let expiry = 30.uint64 - let collateralPerByte = 1.u256 - let ecParams = @[(1, 1), (2, 1), (3, 2), (3, 3)] - - for ecParam in ecParams: - let (nodes, tolerance) = ecParam - - var responseBefore = ( - await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, - expiry, nodes.uint, tolerance.uint, - ) - ) - - check responseBefore.status == 422 - check (await responseBefore.body) == - "Invalid parameters: parameters must satify `1 < (nodes - tolerance) ≥ tolerance`" - - test "request storage fails if tolerance > nodes (underflow protection)", - twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = 100.uint64 - let pricePerBytePerSecond = 1.u256 - let proofProbability = 3.u256 - let expiry = 30.uint64 - let collateralPerByte = 1.u256 - let ecParams = @[(0, 1), (1, 2), (2, 3)] - - for ecParam in ecParams: - let (nodes, tolerance) = ecParam - - var responseBefore = ( - await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, - expiry, nodes.uint, tolerance.uint, - ) - ) - - check responseBefore.status == 422 - check (await responseBefore.body) == - "Invalid parameters: `tolerance` cannot be greater than `nodes`" - - test "request storage fails if expiry is zero", twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = 100.uint64 - let pricePerBytePerSecond = 1.u256 - let proofProbability = 3.u256 - let expiry = 0.uint64 - let collateralPerByte = 1.u256 - let nodes = 3 - let tolerance = 1 - - var responseBefore = await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, - nodes.uint, tolerance.uint, - ) - - check responseBefore.status == 422 - check (await responseBefore.body) == - "Expiry must be greater than zero and less than the request's duration" - - test "request storage fails if proof probability is zero", twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = 100.uint64 - let pricePerBytePerSecond = 1.u256 - let proofProbability = 0.u256 - let expiry = 30.uint64 - let collateralPerByte = 1.u256 - let nodes = 3 - let tolerance = 1 - - var responseBefore = await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, - nodes.uint, tolerance.uint, - ) - - check responseBefore.status == 422 - check (await responseBefore.body) == "Proof probability must be greater than zero" - - test "request storage fails if collareral per byte is zero", twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = 100.uint64 - let pricePerBytePerSecond = 1.u256 - let proofProbability = 3.u256 - let expiry = 30.uint64 - let collateralPerByte = 0.u256 - let nodes = 3 - let tolerance = 1 - - var responseBefore = await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, - nodes.uint, tolerance.uint, - ) - - check responseBefore.status == 422 - check (await responseBefore.body) == "Collateral per byte must be greater than zero" - - test "request storage fails if price per byte per second is zero", twoNodesConfig: - let data = await RandomChunker.example(blocks = 2) - let cid = (await client1.upload(data)).get - let duration = 100.uint64 - let pricePerBytePerSecond = 0.u256 - let proofProbability = 3.u256 - let expiry = 30.uint64 - let collateralPerByte = 1.u256 - let nodes = 3 - let tolerance = 1 - - var responseBefore = await client1.requestStorageRaw( - cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, - nodes.uint, tolerance.uint, - ) - - check responseBefore.status == 422 - check (await responseBefore.body) == - "Price per byte per second must be greater than zero" - for ecParams in @[ (minBlocks: 2, nodes: 3, tolerance: 1), (minBlocks: 3, nodes: 5, tolerance: 2) ]: @@ -306,20 +117,6 @@ twonodessuite "REST API": check response.status == 200 check (await response.body) != "" - test "upload fails if content disposition contains bad filename", twoNodesConfig: - let headers = @[("Content-Disposition", "attachment; filename=\"exam*ple.txt\"")] - let response = await client1.uploadRaw("some file contents", headers) - - check response.status == 422 - check (await response.body) == "The filename is not valid." - - test "upload fails if content type is invalid", twoNodesConfig: - let headers = @[("Content-Type", "hello/world")] - let response = await client1.uploadRaw("some file contents", headers) - - check response.status == 422 - check (await response.body) == "The MIME type 'hello/world' is not valid." - test "node retrieve the metadata", twoNodesConfig: let headers = @[ diff --git a/tests/integration/testrestapivalidation.nim b/tests/integration/testrestapivalidation.nim new file mode 100644 index 00000000..00caefdd --- /dev/null +++ b/tests/integration/testrestapivalidation.nim @@ -0,0 +1,368 @@ +import std/httpclient +import std/times +import pkg/ethers +import pkg/codex/manifest +import pkg/codex/conf +import pkg/codex/contracts +from pkg/codex/stores/repostore/types import DefaultQuotaBytes +import ../asynctest +import ../checktest +import ../examples +import ../codex/examples +import ./codexconfig +import ./codexprocess + +from ./multinodes import Role, getTempDirName, jsonRpcProviderUrl, nextFreePort + +# This suite allows to run fast the basic rest api validation. +# It starts only one node for all the checks in order to speed up +# the execution. +asyncchecksuite "Rest API validation": + var node: CodexProcess + var config = CodexConfigs.init(nodes = 1).configs[0] + let starttime = now().format("yyyy-MM-dd'_'HH:mm:ss") + let nodexIdx = 0 + let datadir = getTempDirName(starttime, Role.Client, nodexIdx) + + config.addCliOption("--api-port", $(waitFor nextFreePort(8081))) + config.addCliOption("--data-dir", datadir) + config.addCliOption("--nat", "none") + config.addCliOption("--listen-addrs", "/ip4/127.0.0.1/tcp/0") + config.addCliOption("--disc-port", $(waitFor nextFreePort(8081))) + config.addCliOption(StartUpCmd.persistence, "--eth-provider", jsonRpcProviderUrl) + config.addCliOption(StartUpCmd.persistence, "--eth-account", $EthAddress.example) + + node = + waitFor CodexProcess.startNode(config.cliArgs, config.debugEnabled, $Role.Client) + + waitFor node.waitUntilStarted() + + let client = node.client() + + test "should return 422 when attempting delete of non-existing dataset": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 1.u256 + let proofProbability = 3.u256 + let expiry = 30.uint64 + let collateralPerByte = 1.u256 + let nodes = 3 + let tolerance = 0 + + var responseBefore = await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, + nodes.uint, tolerance.uint, + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == "Tolerance needs to be bigger then zero" + + test "request storage fails for datasets that are too small": + let cid = (await client.upload("some file contents")).get + let response = ( + await client.requestStorageRaw( + cid, + duration = 10.uint64, + pricePerBytePerSecond = 1.u256, + proofProbability = 3.u256, + collateralPerByte = 1.u256, + expiry = 9.uint64, + ) + ) + + check: + response.status == 422 + (await response.body) == + "Dataset too small for erasure parameters, need at least " & + $(2 * DefaultBlockSize.int) & " bytes" + + test "request storage fails if nodes and tolerance aren't correct": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 1.u256 + let proofProbability = 3.u256 + let expiry = 30.uint64 + let collateralPerByte = 1.u256 + let ecParams = @[(1, 1), (2, 1), (3, 2), (3, 3)] + + for ecParam in ecParams: + let (nodes, tolerance) = ecParam + + var responseBefore = ( + await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, + expiry, nodes.uint, tolerance.uint, + ) + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == + "Invalid parameters: parameters must satify `1 < (nodes - tolerance) ≥ tolerance`" + + test "request storage fails if tolerance > nodes (underflow protection)": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 1.u256 + let proofProbability = 3.u256 + let expiry = 30.uint64 + let collateralPerByte = 1.u256 + let nodes = 3 + let tolerance = 0 + + var responseBefore = ( + await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, + expiry, nodes.uint, tolerance.uint, + ) + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == "Tolerance needs to be bigger then zero" + + test "upload fails if content disposition contains bad filename": + let headers = @[("Content-Disposition", "attachment; filename=\"exam*ple.txt\"")] + let response = await client.uploadRaw("some file contents", headers) + + check response.status == 422 + check (await response.body) == "The filename is not valid." + + test "upload fails if content type is invalid": + let headers = @[("Content-Type", "hello/world")] + let response = await client.uploadRaw("some file contents", headers) + + check response.status == 422 + check (await response.body) == "The MIME type 'hello/world' is not valid." + + test "updating non-existing availability": + let nonExistingResponse = await client.patchAvailabilityRaw( + AvailabilityId.example, + duration = 100.uint64.some, + minPricePerBytePerSecond = 2.u256.some, + totalCollateral = 200.u256.some, + ) + check nonExistingResponse.status == 404 + + test "updating availability - freeSize is not allowed to be changed": + let availability = ( + await client.postAvailability( + totalSize = 140000.uint64, + duration = 200.uint64, + minPricePerBytePerSecond = 3.u256, + totalCollateral = 300.u256, + ) + ).get + let freeSizeResponse = + await client.patchAvailabilityRaw(availability.id, freeSize = 110000.uint64.some) + check freeSizeResponse.status == 422 + check "not allowed" in (await freeSizeResponse.body) + + test "creating availability above the node quota returns 422": + let response = await client.postAvailabilityRaw( + totalSize = 24000000000.uint64, + duration = 200.uint64, + minPricePerBytePerSecond = 3.u256, + totalCollateral = 300.u256, + ) + + check response.status == 422 + check (await response.body) == "Not enough storage quota" + + test "updating availability above the node quota returns 422": + let availability = ( + await client.postAvailability( + totalSize = 140000.uint64, + duration = 200.uint64, + minPricePerBytePerSecond = 3.u256, + totalCollateral = 300.u256, + ) + ).get + let response = await client.patchAvailabilityRaw( + availability.id, totalSize = 24000000000.uint64.some + ) + + check response.status == 422 + check (await response.body) == "Not enough storage quota" + + test "creating availability when total size is zero returns 422": + let response = await client.postAvailabilityRaw( + totalSize = 0.uint64, + duration = 200.uint64, + minPricePerBytePerSecond = 3.u256, + totalCollateral = 300.u256, + ) + + check response.status == 422 + check (await response.body) == "Total size must be larger then zero" + + test "updating availability when total size is zero returns 422": + let availability = ( + await client.postAvailability( + totalSize = 140000.uint64, + duration = 200.uint64, + minPricePerBytePerSecond = 3.u256, + totalCollateral = 300.u256, + ) + ).get + let response = + await client.patchAvailabilityRaw(availability.id, totalSize = 0.uint64.some) + + check response.status == 422 + check (await response.body) == "Total size must be larger then zero" + + test "creating availability when total size is negative returns 422": + let json = + %*{ + "totalSize": "-1", + "duration": "200", + "minPricePerBytePerSecond": "3", + "totalCollateral": "300", + } + let response = await client.post(client.buildUrl("/sales/availability"), $json) + + check response.status == 400 + check (await response.body) == "Parsed integer outside of valid range" + + test "updating availability when total size is negative returns 422": + let availability = ( + await client.postAvailability( + totalSize = 140000.uint64, + duration = 200.uint64, + minPricePerBytePerSecond = 3.u256, + totalCollateral = 300.u256, + ) + ).get + + let json = %*{"totalSize": "-1"} + let response = await client.patch( + client.buildUrl("/sales/availability/") & $availability.id, $json + ) + + check response.status == 400 + check (await response.body) == "Parsed integer outside of valid range" + + test "request storage fails if tolerance is zero": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 1.u256 + let proofProbability = 3.u256 + let expiry = 30.uint64 + let collateralPerByte = 1.u256 + let nodes = 3 + let tolerance = 0 + + var responseBefore = ( + await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, + expiry, nodes.uint, tolerance.uint, + ) + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == "Tolerance needs to be bigger then zero" + + test "request storage fails if duration exceeds limit": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = (31 * 24 * 60 * 60).uint64 + # 31 days TODO: this should not be hardcoded, but waits for https://github.com/codex-storage/nim-codex/issues/1056 + let proofProbability = 3.u256 + let expiry = 30.uint + let collateralPerByte = 1.u256 + let nodes = 3 + let tolerance = 2 + let pricePerBytePerSecond = 1.u256 + + var responseBefore = ( + await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, + expiry, nodes.uint, tolerance.uint, + ) + ) + + check responseBefore.status == 422 + check "Duration exceeds limit of" in (await responseBefore.body) + + test "request storage fails if expiry is zero": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 1.u256 + let proofProbability = 3.u256 + let expiry = 0.uint64 + let collateralPerByte = 1.u256 + let nodes = 3 + let tolerance = 1 + + var responseBefore = await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, + nodes.uint, tolerance.uint, + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == + "Expiry must be greater than zero and less than the request's duration" + + test "request storage fails if proof probability is zero": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 1.u256 + let proofProbability = 0.u256 + let expiry = 30.uint64 + let collateralPerByte = 1.u256 + let nodes = 3 + let tolerance = 1 + + var responseBefore = await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, + nodes.uint, tolerance.uint, + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == "Proof probability must be greater than zero" + + test "request storage fails if price per byte per second is zero": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 0.u256 + let proofProbability = 3.u256 + let expiry = 30.uint64 + let collateralPerByte = 1.u256 + let nodes = 3 + let tolerance = 1 + + var responseBefore = await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, + nodes.uint, tolerance.uint, + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == + "Price per byte per second must be greater than zero" + + test "request storage fails if collareral per byte is zero": + let data = await RandomChunker.example(blocks = 2) + let cid = (await client.upload(data)).get + let duration = 100.uint64 + let pricePerBytePerSecond = 1.u256 + let proofProbability = 3.u256 + let expiry = 30.uint64 + let collateralPerByte = 0.u256 + let nodes = 3 + let tolerance = 1 + + var responseBefore = await client.requestStorageRaw( + cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry, + nodes.uint, tolerance.uint, + ) + + check responseBefore.status == 422 + check (await responseBefore.body) == "Collateral per byte must be greater than zero" + + waitFor node.stop() + node.removeDataDir() diff --git a/tests/integration/testsales.nim b/tests/integration/testsales.nim index 9bf8a97c..5e9b26df 100644 --- a/tests/integration/testsales.nim +++ b/tests/integration/testsales.nim @@ -1,5 +1,6 @@ import std/httpclient import pkg/codex/contracts +from pkg/codex/stores/repostore/types import DefaultQuotaBytes import ./twonodes import ../codex/examples import ../contracts/time @@ -69,15 +70,6 @@ multinodesuite "Sales": ).get check availability in (await host.getAvailabilities()).get - test "updating non-existing availability", salesConfig: - let nonExistingResponse = await host.patchAvailabilityRaw( - AvailabilityId.example, - duration = 100.uint64.some, - minPricePerBytePerSecond = 2.u256.some, - totalCollateral = 200.u256.some, - ) - check nonExistingResponse.status == 404 - test "updating availability", salesConfig: let availability = ( await host.postAvailability( @@ -103,20 +95,6 @@ multinodesuite "Sales": check updatedAvailability.totalSize == 140000.uint64 check updatedAvailability.freeSize == 140000.uint64 - test "updating availability - freeSize is not allowed to be changed", salesConfig: - let availability = ( - await host.postAvailability( - totalSize = 140000.uint64, - duration = 200.uint64, - minPricePerBytePerSecond = 3.u256, - totalCollateral = 300.u256, - ) - ).get - let freeSizeResponse = - await host.patchAvailabilityRaw(availability.id, freeSize = 110000.uint64.some) - check freeSizeResponse.status == 400 - check "not allowed" in (await freeSizeResponse.body) - test "updating availability - updating totalSize", salesConfig: let availability = ( await host.postAvailability( @@ -176,7 +154,7 @@ multinodesuite "Sales": availability.id, totalSize = (utilizedSize - 1).some ) ) - check totalSizeResponse.status == 400 + check totalSizeResponse.status == 422 check "totalSize must be larger then current totalSize" in (await totalSizeResponse.body) diff --git a/tests/testIntegration.nim b/tests/testIntegration.nim index 9a2dc472..152d22dd 100644 --- a/tests/testIntegration.nim +++ b/tests/testIntegration.nim @@ -1,5 +1,6 @@ import ./integration/testcli import ./integration/testrestapi +import ./integration/testrestapivalidation import ./integration/testupdownload import ./integration/testsales import ./integration/testpurchasing