adds integration tests for using magnet links and torrent file while retrieving BitTorrent content from Codex Net

This commit is contained in:
Marcin Czenko 2025-06-02 02:18:58 +02:00
parent f9ce8892a7
commit 29bb01751b
No known key found for this signature in database
GPG Key ID: 33DEA0C8E30937C0
2 changed files with 131 additions and 2 deletions

View File

@ -94,6 +94,16 @@ proc info*(
let response = await client.get(client.baseurl & "/debug/info")
return JsonNode.parse(await response.body)
proc connect*(
client: CodexClient, peerId: string, address: string
): Future[?!void] {.async: (raises: [CancelledError, HttpError]).} =
let url = client.baseurl & "/connect/" & peerId & "?addrs=" & address
let response = await client.get(url)
if response.status != 200:
return
failure("Cannot connect to node with peerId: " & peerId & ": " & $response.status)
return success()
proc setLogLevel*(
client: CodexClient, level: string
): Future[void] {.async: (raises: [CancelledError, HttpError]).} =
@ -196,6 +206,43 @@ proc downloadTorrent*(
success await response.body
proc downloadTorrent*(
client: CodexClient,
contents: string,
contentType = "text/plain",
endpoint = "magnet",
): Future[?!string] {.async: (raises: [CancelledError, HttpError]).} =
if contents.len == 0:
return failure("No content provided!")
if endpoint != "magnet" and endpoint != "torrent-file":
return failure(
"Invalid endpoint: has to be either 'magnet' or 'torrent-file' but got: " &
endpoint
)
if endpoint == "magnet" and
(contentType != "application/octet-stream" and contentType != "text/plain"):
return failure(
"Invalid content type: for 'magnet' endpoint has to be either 'application/octet-stream' or 'text/plain' but got: " &
contentType
)
if endpoint == "torrent-file" and
(contentType != "application/octet-stream" and contentType != "application/json"):
return failure(
"Invalid content type: for 'torrent-file' endpoint has to be either 'application/octet-stream' or 'application/json' but got: " &
contentType
)
var headers = newSeq[HttpHeaderTuple]()
headers = @[("Content-Type", contentType)]
let response = await client.post(
client.baseurl & "/torrent/" & endpoint, body = contents, headers = headers
)
if not response.status == 200:
return failure($response.status)
success await response.body
proc downloadManifestOnly*(
client: CodexClient, cid: Cid
): Future[?!string] {.async: (raises: [CancelledError, HttpError]).} =

View File

@ -1,7 +1,9 @@
import std/net
import std/strformat
import std/sequtils
import std/json except `%`, `%*`
import pkg/nimcrypto
from pkg/libp2p import `==`, `$`, MultiHash, init
from pkg/libp2p import `==`, `$`, MultiHash, init, digest, hex
import pkg/codex/units
import pkg/codex/utils/iter
import pkg/codex/manifest
@ -10,7 +12,6 @@ import pkg/codex/bittorrent/manifest
import ./twonodes
import ../examples
import ../codex/examples
import json
proc createInfoDictionaryForContent(
content: seq[byte], pieceLength = DefaultPieceLength.int, name = string.none
@ -55,12 +56,93 @@ proc createInfoDictionaryForContent(
success info
twonodessuite "BitTorrent API":
setup:
# why we do not seem to need this? yet it is twice as fast with this
let infoPeer1 = (await client1.info()).tryGet
let peerId1 = infoPeer1["id"].getStr()
let announceAddress1 = infoPeer1["announceAddresses"][0].getStr()
(await client2.connect(peerId1, announceAddress1)).tryGet
test "uploading and downloading the content", twoNodesConfig:
let exampleContent = exampleString(100)
let infoHash = (await client1.uploadTorrent(exampleContent)).tryGet
let downloadedContent = (await client2.downloadTorrent(infoHash)).tryGet
check downloadedContent == exampleContent
test "downloading content using magnet link", twoNodesConfig:
let exampleContent = exampleString(100)
let multiHash = (await client1.uploadTorrent(exampleContent)).tryGet
let infoHash = byteutils.toHex(multiHash.data.buffer[multiHash.dpos .. ^1])
let magnetLink = fmt"magnet:?xt=urn:btih:{infoHash}"
let downloadedContent = (await client2.downloadTorrent(magnetLink)).tryGet
check downloadedContent == exampleContent
test "downloading content using torrent file", twoNodesConfig:
let exampleFileName = "example.txt"
let exampleContent = exampleString(100)
let multiHash = (
await client1.uploadTorrent(
contents = exampleContent,
filename = some exampleFileName,
contentType = "text/plain",
)
).tryGet
let expectedInfo = createInfoDictionaryForContent(
content = exampleContent.toBytes, name = some exampleFileName
).tryGet
let expectedInfoBencoded = expectedInfo.bencode()
let expectedMultiHash =
MultiHash.digest($Sha1HashCodec, expectedInfoBencoded).mapFailure.tryGet()
assert expectedMultiHash == multiHash
let torrentFileContent = "d4:info" & string.fromBytes(expectedInfoBencoded) & "e"
let downloadedContent = (
await client2.downloadTorrent(
contents = torrentFileContent,
contentType = "application/octet-stream",
endpoint = "torrent-file",
)
).tryGet
check downloadedContent == exampleContent
test "downloading content using torrent file (JSON format)", twoNodesConfig:
let exampleFileName = "example.txt"
let exampleContent = exampleString(100)
let multiHash = (
await client1.uploadTorrent(
contents = exampleContent,
filename = some exampleFileName,
contentType = "text/plain",
)
).tryGet
let expectedInfo = createInfoDictionaryForContent(
content = exampleContent.toBytes, name = some exampleFileName
).tryGet
let expectedInfoBencoded = expectedInfo.bencode()
let expectedMultiHash =
MultiHash.digest($Sha1HashCodec, expectedInfoBencoded).mapFailure.tryGet()
assert expectedMultiHash == multiHash
let infoJson = %*{"info": %expectedInfo}
let torrentJson = $infoJson
let downloadedContent = (
await client2.downloadTorrent(
contents = torrentJson,
contentType = "application/json",
endpoint = "torrent-file",
)
).tryGet
check downloadedContent == exampleContent
test "uploading and downloading the content (exactly one piece long)", twoNodesConfig:
let numOfBlocksPerPiece = int(DefaultPieceLength div BitTorrentBlockSize)
let bytes = await RandomChunker.example(