2024-06-27 21:26:19 +00:00
|
|
|
import std/httpclient
|
2024-05-23 15:29:30 +00:00
|
|
|
import std/sequtils
|
|
|
|
from pkg/libp2p import `==`
|
2024-06-20 22:46:06 +00:00
|
|
|
import pkg/codex/units
|
2024-05-23 15:29:30 +00:00
|
|
|
import ./twonodes
|
2024-06-27 21:26:19 +00:00
|
|
|
import ../examples
|
2024-10-25 13:43:19 +00:00
|
|
|
import json
|
2024-05-23 15:29:30 +00:00
|
|
|
|
|
|
|
twonodessuite "REST API", debug1 = false, debug2 = false:
|
|
|
|
test "nodes can print their peer information":
|
|
|
|
check !client1.info() != !client2.info()
|
|
|
|
|
|
|
|
test "nodes can set chronicles log level":
|
|
|
|
client1.setLogLevel("DEBUG;TRACE:codex")
|
|
|
|
|
|
|
|
test "node accepts file uploads":
|
|
|
|
let cid1 = client1.upload("some file contents").get
|
|
|
|
let cid2 = client1.upload("some other contents").get
|
2024-10-25 13:43:19 +00:00
|
|
|
|
2024-05-23 15:29:30 +00:00
|
|
|
check cid1 != cid2
|
|
|
|
|
|
|
|
test "node shows used and available space":
|
|
|
|
discard client1.upload("some file contents").get
|
|
|
|
discard client1.postAvailability(totalSize=12.u256, duration=2.u256, minPrice=3.u256, maxCollateral=4.u256).get
|
|
|
|
let space = client1.space().tryGet()
|
|
|
|
check:
|
2024-06-20 22:46:06 +00:00
|
|
|
space.totalBlocks == 2
|
|
|
|
space.quotaMaxBytes == 8589934592.NBytes
|
2024-10-25 13:43:19 +00:00
|
|
|
space.quotaUsedBytes == 65598.NBytes
|
2024-06-20 22:46:06 +00:00
|
|
|
space.quotaReservedBytes == 12.NBytes
|
2024-05-23 15:29:30 +00:00
|
|
|
|
|
|
|
test "node lists local files":
|
|
|
|
let content1 = "some file contents"
|
|
|
|
let content2 = "some other contents"
|
|
|
|
|
|
|
|
let cid1 = client1.upload(content1).get
|
|
|
|
let cid2 = client1.upload(content2).get
|
|
|
|
let list = client1.list().get
|
|
|
|
|
|
|
|
check:
|
|
|
|
[cid1, cid2].allIt(it in list.content.mapIt(it.cid))
|
2024-06-27 21:26:19 +00:00
|
|
|
|
|
|
|
test "request storage fails for datasets that are too small":
|
|
|
|
let cid = client1.upload("some file contents").get
|
2024-09-24 08:37:08 +00:00
|
|
|
let response = client1.requestStorageRaw(cid, duration=10.u256, reward=2.u256, proofProbability=3.u256, collateral=200.u256, expiry=9)
|
2024-06-27 21:26:19 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
response.status == "400 Bad Request"
|
|
|
|
response.body == "Dataset too small for erasure parameters, need at least " & $(2*DefaultBlockSize.int) & " bytes"
|
|
|
|
|
|
|
|
test "request storage succeeds for sufficiently sized datasets":
|
|
|
|
let data = await RandomChunker.example(blocks=2)
|
|
|
|
let cid = client1.upload(data).get
|
|
|
|
let response = client1.requestStorageRaw(cid, duration=10.u256, reward=2.u256, proofProbability=3.u256, collateral=200.u256, expiry=9)
|
|
|
|
|
|
|
|
check:
|
|
|
|
response.status == "200 OK"
|
|
|
|
|
2024-09-24 08:37:08 +00:00
|
|
|
test "request storage fails if tolerance is zero":
|
|
|
|
let data = await RandomChunker.example(blocks=2)
|
|
|
|
let cid = client1.upload(data).get
|
|
|
|
let duration = 100.u256
|
|
|
|
let reward = 2.u256
|
|
|
|
let proofProbability = 3.u256
|
|
|
|
let expiry = 30.uint
|
|
|
|
let collateral = 200.u256
|
|
|
|
let nodes = 3
|
|
|
|
let tolerance = 0
|
|
|
|
|
|
|
|
var responseBefore = client1.requestStorageRaw(cid,
|
|
|
|
duration,
|
|
|
|
reward,
|
|
|
|
proofProbability,
|
|
|
|
collateral,
|
|
|
|
expiry,
|
|
|
|
nodes.uint,
|
|
|
|
tolerance.uint)
|
|
|
|
|
|
|
|
check responseBefore.status == "400 Bad Request"
|
|
|
|
check responseBefore.body == "Tolerance needs to be bigger then zero"
|
|
|
|
|
2024-06-27 21:26:19 +00:00
|
|
|
test "request storage fails if nodes and tolerance aren't correct":
|
|
|
|
let data = await RandomChunker.example(blocks=2)
|
|
|
|
let cid = client1.upload(data).get
|
|
|
|
let duration = 100.u256
|
|
|
|
let reward = 2.u256
|
|
|
|
let proofProbability = 3.u256
|
|
|
|
let expiry = 30.uint
|
|
|
|
let collateral = 200.u256
|
2024-09-24 08:37:08 +00:00
|
|
|
let ecParams = @[(1, 1), (2, 1), (3, 2), (3, 3)]
|
2024-06-27 21:26:19 +00:00
|
|
|
|
|
|
|
for ecParam in ecParams:
|
|
|
|
let (nodes, tolerance) = ecParam
|
|
|
|
|
|
|
|
var responseBefore = client1.requestStorageRaw(cid,
|
|
|
|
duration,
|
|
|
|
reward,
|
|
|
|
proofProbability,
|
|
|
|
collateral,
|
|
|
|
expiry,
|
|
|
|
nodes.uint,
|
|
|
|
tolerance.uint)
|
|
|
|
|
|
|
|
check responseBefore.status == "400 Bad Request"
|
|
|
|
check 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 = client1.upload(data).get
|
|
|
|
let duration = 100.u256
|
|
|
|
let reward = 2.u256
|
|
|
|
let proofProbability = 3.u256
|
|
|
|
let expiry = 30.uint
|
|
|
|
let collateral = 200.u256
|
|
|
|
let ecParams = @[(0, 1), (1, 2), (2, 3)]
|
|
|
|
|
|
|
|
for ecParam in ecParams:
|
|
|
|
let (nodes, tolerance) = ecParam
|
|
|
|
|
|
|
|
var responseBefore = client1.requestStorageRaw(cid,
|
|
|
|
duration,
|
|
|
|
reward,
|
|
|
|
proofProbability,
|
|
|
|
collateral,
|
|
|
|
expiry,
|
|
|
|
nodes.uint,
|
|
|
|
tolerance.uint)
|
|
|
|
|
|
|
|
check responseBefore.status == "400 Bad Request"
|
|
|
|
check responseBefore.body == "Invalid parameters: `tolerance` cannot be greater than `nodes`"
|
|
|
|
|
|
|
|
test "request storage succeeds if nodes and tolerance within range":
|
|
|
|
let data = await RandomChunker.example(blocks=2)
|
|
|
|
let cid = client1.upload(data).get
|
|
|
|
let duration = 100.u256
|
|
|
|
let reward = 2.u256
|
|
|
|
let proofProbability = 3.u256
|
|
|
|
let expiry = 30.uint
|
|
|
|
let collateral = 200.u256
|
2024-09-24 08:37:08 +00:00
|
|
|
let ecParams = @[(3, 1), (5, 2)]
|
2024-06-27 21:26:19 +00:00
|
|
|
|
|
|
|
for ecParam in ecParams:
|
|
|
|
let (nodes, tolerance) = ecParam
|
|
|
|
|
|
|
|
var responseBefore = client1.requestStorageRaw(cid,
|
|
|
|
duration,
|
|
|
|
reward,
|
|
|
|
proofProbability,
|
|
|
|
collateral,
|
|
|
|
expiry,
|
|
|
|
nodes.uint,
|
|
|
|
tolerance.uint)
|
|
|
|
|
|
|
|
check responseBefore.status == "200 OK"
|
2024-10-25 13:43:19 +00:00
|
|
|
|
|
|
|
test "node accepts file uploads with content type":
|
|
|
|
let headers = newHttpHeaders({"Content-Type": "text/plain"})
|
|
|
|
let response = client1.uploadRaw("some file contents", headers)
|
|
|
|
|
|
|
|
check response.status == "200 OK"
|
|
|
|
check response.body != ""
|
|
|
|
|
|
|
|
test "node accepts file uploads with content disposition":
|
|
|
|
let headers = newHttpHeaders({"Content-Disposition": "attachment; filename=\"example.txt\""})
|
|
|
|
let response = client1.uploadRaw("some file contents", headers)
|
|
|
|
|
|
|
|
check response.status == "200 OK"
|
|
|
|
check response.body != ""
|
|
|
|
|
|
|
|
test "node accepts file uploads with content disposition without filename":
|
|
|
|
let headers = newHttpHeaders({"Content-Disposition": "attachment"})
|
|
|
|
let response = client1.uploadRaw("some file contents", headers)
|
|
|
|
|
|
|
|
check response.status == "200 OK"
|
|
|
|
check response.body != ""
|
|
|
|
|
|
|
|
test "upload fails if content disposition contains bad filename":
|
|
|
|
let headers = newHttpHeaders({"Content-Disposition": "attachment; filename=\"exam*ple.txt\""})
|
|
|
|
let response = client1.uploadRaw("some file contents", headers)
|
|
|
|
|
|
|
|
check response.status == "422 Unprocessable Entity"
|
|
|
|
check response.body == "The filename is not valid."
|
|
|
|
|
|
|
|
test "upload fails if content type is invalid":
|
|
|
|
let headers = newHttpHeaders({"Content-Type": "hello/world"})
|
|
|
|
let response = client1.uploadRaw("some file contents", headers)
|
|
|
|
|
|
|
|
check response.status == "422 Unprocessable Entity"
|
|
|
|
check response.body == "The MIME type is not valid."
|
|
|
|
|
|
|
|
test "node retrieve the metadata":
|
|
|
|
let headers = newHttpHeaders({"Content-Type": "text/plain", "Content-Disposition": "attachment; filename=\"example.txt\""})
|
|
|
|
let uploadResponse = client1.uploadRaw("some file contents", headers)
|
|
|
|
let cid = uploadResponse.body
|
|
|
|
let listResponse = client1.listRaw()
|
|
|
|
|
|
|
|
let jsonData = parseJson(listResponse.body)
|
|
|
|
|
|
|
|
check jsonData.hasKey("content") == true
|
|
|
|
|
|
|
|
let content = jsonData["content"][0]
|
|
|
|
|
|
|
|
check content.hasKey("manifest") == true
|
|
|
|
|
|
|
|
let manifest = content["manifest"]
|
|
|
|
|
|
|
|
check manifest.hasKey("filename") == true
|
|
|
|
check manifest["filename"].getStr() == "example.txt"
|
|
|
|
check manifest.hasKey("mimetype") == true
|
|
|
|
check manifest["mimetype"].getStr() == "text/plain"
|
|
|
|
check manifest.hasKey("uploadedAt") == true
|
|
|
|
check manifest["uploadedAt"].getInt() > 0
|
|
|
|
|
|
|
|
test "node set the headers when for download":
|
|
|
|
let headers = newHttpHeaders({
|
|
|
|
"Content-Disposition": "attachment; filename=\"example.txt\"",
|
|
|
|
"Content-Type": "text/plain"
|
|
|
|
})
|
|
|
|
|
|
|
|
let uploadResponse = client1.uploadRaw("some file contents", headers)
|
|
|
|
let cid = uploadResponse.body
|
|
|
|
|
|
|
|
check uploadResponse.status == "200 OK"
|
|
|
|
|
|
|
|
let response = client1.downloadRaw(cid)
|
|
|
|
|
|
|
|
check response.status == "200 OK"
|
|
|
|
check response.headers.hasKey("Content-Type") == true
|
|
|
|
check response.headers["Content-Type"] == "text/plain"
|
|
|
|
check response.headers.hasKey("Content-Disposition") == true
|
|
|
|
check response.headers["Content-Disposition"] == "attachment; filename=\"example.txt\""
|
|
|
|
|
|
|
|
let local = true
|
|
|
|
let localResponse = client1.downloadRaw(cid, local)
|
|
|
|
|
|
|
|
check localResponse.status == "200 OK"
|
|
|
|
check localResponse.headers.hasKey("Content-Type") == true
|
|
|
|
check localResponse.headers["Content-Type"] == "text/plain"
|
|
|
|
check localResponse.headers.hasKey("Content-Disposition") == true
|
|
|
|
check localResponse.headers["Content-Disposition"] == "attachment; filename=\"example.txt\""
|