@torrent-tv/proxy 2.9.76 → 2.9.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @file Naming a source that is still being added.
3
+ *
4
+ * Adding a magnet takes as long as its metadata does — seconds to tens of
5
+ * seconds — while the browser is already polling stats and the planner is
6
+ * already asking for a plan. Until 2.9.77 the worker registered the torrent
7
+ * only once the add had finished, so everything arriving in that window was
8
+ * told `Unknown source`, which is false: the source exists, it is not ready.
9
+ * Reproduced with a magnet nobody seeds — stats, the file listing and a read
10
+ * all failed instantly while the add was in flight.
11
+ */
12
+
13
+ import test from "node:test";
14
+ import assert from "node:assert/strict";
15
+ import crypto from "node:crypto";
16
+ import { TorrentWorkerClient } from "../services/torrent-worker/client.js";
17
+
18
+ /** A well-formed infohash nobody is seeding, so the add stays in flight. */
19
+ function pendingMagnet() {
20
+ return `magnet:?xt=urn:btih:${crypto.randomBytes(20).toString("hex")}&dn=nobody-has-this`;
21
+ }
22
+
23
+ /**
24
+ * What a promise did within `ms` — settled how, or still waiting.
25
+ *
26
+ * @param {Promise<unknown>} promise
27
+ * @param {number} ms
28
+ * @returns {Promise<string>}
29
+ */
30
+ function outcomeWithin(promise, ms) {
31
+ return Promise.race([
32
+ promise.then(() => "resolved", (error) => `rejected: ${error?.message}`),
33
+ new Promise((resolve) => setTimeout(() => resolve("waiting"), ms))
34
+ ]);
35
+ }
36
+
37
+ test("commands wait for a source that is still being added", async () => {
38
+ const client = new TorrentWorkerClient({ memoryBytes: 16 * 1024 * 1024 });
39
+ try {
40
+ // Without this the worker's own startup would keep the commands waiting and
41
+ // the test would pass for the wrong reason.
42
+ await client.listFiles("warm-up").catch(() => undefined);
43
+
44
+ const sourceKey = "pending-source";
45
+ // Deliberately not awaited: this is the window under test.
46
+ const adding = client.addSource({ sourceKey, sourceType: "magnet", source: pendingMagnet() });
47
+ adding.catch(() => undefined);
48
+
49
+ await new Promise((resolve) => setTimeout(resolve, 500));
50
+
51
+ const stats = await outcomeWithin(client.getFileStats({ sourceKey, fileIndex: 0 }), 3000);
52
+ const files = await outcomeWithin(client.listFiles(sourceKey), 3000);
53
+
54
+ assert.equal(stats, "waiting", `stats did not wait for the add: ${stats}`);
55
+ assert.equal(files, "waiting", `the file listing did not wait for the add: ${files}`);
56
+ } finally {
57
+ await client.destroyAll();
58
+ }
59
+ });
60
+
61
+ test("a source that was never added is still reported as unknown", async () => {
62
+ const client = new TorrentWorkerClient({ memoryBytes: 16 * 1024 * 1024 });
63
+ try {
64
+ // Starting the worker takes several seconds — it builds a torrent client,
65
+ // a DHT and the rest — so the first command measures startup, not the
66
+ // behaviour under test. Wait for one to come back before timing anything.
67
+ await client.listFiles("warm-up").catch(() => undefined);
68
+
69
+ // Waiting forever for something nobody ever asked for would be worse than
70
+ // an error — this case must stay an error.
71
+ const outcome = await outcomeWithin(client.listFiles("never-added"), 5000);
72
+ assert.match(outcome, /^rejected: .*never-added/, `expected an error, got "${outcome}"`);
73
+ } finally {
74
+ await client.destroyAll();
75
+ }
76
+ });