Skip to content

Commit 6b88273

Browse files
committed
adds integration tests for using magnet links and torrent file while retrieving BitTorrent content from Codex Net
1 parent 9a688ba commit 6b88273

File tree

2 files changed

+131
-2
lines changed

2 files changed

+131
-2
lines changed

tests/integration/codexclient.nim

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ proc info*(
9494
let response = await client.get(client.baseurl & "/debug/info")
9595
return JsonNode.parse(await response.body)
9696

97+
proc connect*(
98+
client: CodexClient, peerId: string, address: string
99+
): Future[?!void] {.async: (raises: [CancelledError, HttpError]).} =
100+
let url = client.baseurl & "/connect/" & peerId & "?addrs=" & address
101+
let response = await client.get(url)
102+
if response.status != 200:
103+
return
104+
failure("Cannot connect to node with peerId: " & peerId & ": " & $response.status)
105+
return success()
106+
97107
proc setLogLevel*(
98108
client: CodexClient, level: string
99109
): Future[void] {.async: (raises: [CancelledError, HttpError]).} =
@@ -196,6 +206,43 @@ proc downloadTorrent*(
196206

197207
success await response.body
198208

209+
proc downloadTorrent*(
210+
client: CodexClient,
211+
contents: string,
212+
contentType = "text/plain",
213+
endpoint = "magnet",
214+
): Future[?!string] {.async: (raises: [CancelledError, HttpError]).} =
215+
if contents.len == 0:
216+
return failure("No content provided!")
217+
if endpoint != "magnet" and endpoint != "torrent-file":
218+
return failure(
219+
"Invalid endpoint: has to be either 'magnet' or 'torrent-file' but got: " &
220+
endpoint
221+
)
222+
if endpoint == "magnet" and
223+
(contentType != "application/octet-stream" and contentType != "text/plain"):
224+
return failure(
225+
"Invalid content type: for 'magnet' endpoint has to be either 'application/octet-stream' or 'text/plain' but got: " &
226+
contentType
227+
)
228+
if endpoint == "torrent-file" and
229+
(contentType != "application/octet-stream" and contentType != "application/json"):
230+
return failure(
231+
"Invalid content type: for 'torrent-file' endpoint has to be either 'application/octet-stream' or 'application/json' but got: " &
232+
contentType
233+
)
234+
235+
var headers = newSeq[HttpHeaderTuple]()
236+
headers = @[("Content-Type", contentType)]
237+
238+
let response = await client.post(
239+
client.baseurl & "/torrent/" & endpoint, body = contents, headers = headers
240+
)
241+
if not response.status == 200:
242+
return failure($response.status)
243+
244+
success await response.body
245+
199246
proc downloadManifestOnly*(
200247
client: CodexClient, cid: Cid
201248
): Future[?!string] {.async: (raises: [CancelledError, HttpError]).} =

tests/integration/testbittorrent.nim

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import std/net
2+
import std/strformat
23
import std/sequtils
4+
import std/json except `%`, `%*`
35
import pkg/nimcrypto
4-
from pkg/libp2p import `==`, `$`, MultiHash, init
6+
from pkg/libp2p import `==`, `$`, MultiHash, init, digest, hex
57
import pkg/codex/units
68
import pkg/codex/utils/iter
79
import pkg/codex/manifest
@@ -10,7 +12,6 @@ import pkg/codex/bittorrent/manifest
1012
import ./twonodes
1113
import ../examples
1214
import ../codex/examples
13-
import json
1415

1516
proc createInfoDictionaryForContent(
1617
content: seq[byte], pieceLength = DefaultPieceLength.int, name = string.none
@@ -55,12 +56,93 @@ proc createInfoDictionaryForContent(
5556
success info
5657

5758
twonodessuite "BitTorrent API":
59+
setup:
60+
# why we do not seem to need this? yet it is twice as fast with this
61+
let infoPeer1 = (await client1.info()).tryGet
62+
let peerId1 = infoPeer1["id"].getStr()
63+
let announceAddress1 = infoPeer1["announceAddresses"][0].getStr()
64+
(await client2.connect(peerId1, announceAddress1)).tryGet
65+
5866
test "uploading and downloading the content", twoNodesConfig:
5967
let exampleContent = exampleString(100)
6068
let infoHash = (await client1.uploadTorrent(exampleContent)).tryGet
6169
let downloadedContent = (await client2.downloadTorrent(infoHash)).tryGet
6270
check downloadedContent == exampleContent
6371

72+
test "downloading content using magnet link", twoNodesConfig:
73+
let exampleContent = exampleString(100)
74+
let multiHash = (await client1.uploadTorrent(exampleContent)).tryGet
75+
let infoHash = byteutils.toHex(multiHash.data.buffer[multiHash.dpos .. ^1])
76+
let magnetLink = fmt"magnet:?xt=urn:btih:{infoHash}"
77+
let downloadedContent = (await client2.downloadTorrent(magnetLink)).tryGet
78+
check downloadedContent == exampleContent
79+
80+
test "downloading content using torrent file", twoNodesConfig:
81+
let exampleFileName = "example.txt"
82+
let exampleContent = exampleString(100)
83+
let multiHash = (
84+
await client1.uploadTorrent(
85+
contents = exampleContent,
86+
filename = some exampleFileName,
87+
contentType = "text/plain",
88+
)
89+
).tryGet
90+
91+
let expectedInfo = createInfoDictionaryForContent(
92+
content = exampleContent.toBytes, name = some exampleFileName
93+
).tryGet
94+
95+
let expectedInfoBencoded = expectedInfo.bencode()
96+
let expectedMultiHash =
97+
MultiHash.digest($Sha1HashCodec, expectedInfoBencoded).mapFailure.tryGet()
98+
99+
assert expectedMultiHash == multiHash
100+
101+
let torrentFileContent = "d4:info" & string.fromBytes(expectedInfoBencoded) & "e"
102+
103+
let downloadedContent = (
104+
await client2.downloadTorrent(
105+
contents = torrentFileContent,
106+
contentType = "application/octet-stream",
107+
endpoint = "torrent-file",
108+
)
109+
).tryGet
110+
check downloadedContent == exampleContent
111+
112+
test "downloading content using torrent file (JSON format)", twoNodesConfig:
113+
let exampleFileName = "example.txt"
114+
let exampleContent = exampleString(100)
115+
let multiHash = (
116+
await client1.uploadTorrent(
117+
contents = exampleContent,
118+
filename = some exampleFileName,
119+
contentType = "text/plain",
120+
)
121+
).tryGet
122+
123+
let expectedInfo = createInfoDictionaryForContent(
124+
content = exampleContent.toBytes, name = some exampleFileName
125+
).tryGet
126+
127+
let expectedInfoBencoded = expectedInfo.bencode()
128+
let expectedMultiHash =
129+
MultiHash.digest($Sha1HashCodec, expectedInfoBencoded).mapFailure.tryGet()
130+
131+
assert expectedMultiHash == multiHash
132+
133+
let infoJson = %*{"info": %expectedInfo}
134+
135+
let torrentJson = $infoJson
136+
137+
let downloadedContent = (
138+
await client2.downloadTorrent(
139+
contents = torrentJson,
140+
contentType = "application/json",
141+
endpoint = "torrent-file",
142+
)
143+
).tryGet
144+
check downloadedContent == exampleContent
145+
64146
test "uploading and downloading the content (exactly one piece long)", twoNodesConfig:
65147
let numOfBlocksPerPiece = int(DefaultPieceLength div BitTorrentBlockSize)
66148
let bytes = await RandomChunker.example(

0 commit comments

Comments
 (0)