@torrent-tv/proxy 2.80.7 → 2.80.9

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.
@@ -1,100 +1,100 @@
1
- /**
2
- * @file How far a run may work, and who decides it.
3
- *
4
- * These cases used to be asked of `planRunInterval`, a second authority inside
5
- * the session manager that answered by its own rules: it walked the whole track
6
- * for the first free number, MOVED the start there, and counted every live run
7
- * as claiming up to `head + look-ahead`. It contradicted the plan directly, and
8
- * the two together produced the field oscillation of 2026-09-05 — the plan
9
- * commanded a start at #46, this moved it to #78, the plan killed the run for
10
- * standing outside the window it had asked for, and the same start was
11
- * commanded again, 350-700ms per cycle, no segment ever produced, the viewer's
12
- * picture stopped for 125 seconds.
13
- *
14
- * It is gone. WHERE a run starts is the plan's decision and nothing moves it;
15
- * HOW FAR it may work is a fact of the one coverage map, and that is what these
16
- * cases now ask. The map is the same object the plan reads, so there is no
17
- * second set of rules for the two to disagree about.
18
- */
19
-
20
- import test from "node:test";
21
- import assert from "node:assert/strict";
22
- import { CoverageMap } from "../services/encode/CoverageMap.js";
23
-
24
- /** A stand-in for a run: the map identifies one by being it. */
25
- function run(name) {
26
- return { name };
27
- }
28
-
29
- test("with nothing made, a run gets everything from where it was asked", () => {
30
- const coverage = new CoverageMap({ segmentCount: 100 });
31
-
32
- assert.equal(coverage.freeRunFrom(0), 100, "the whole track is free");
33
- assert.equal(coverage.firstGapFrom(0), 0);
34
- });
35
-
36
- test("a run stops before material that is already made", () => {
37
- const coverage = new CoverageMap({ segmentCount: 100 });
38
- coverage.markReadyAll([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
39
-
40
- assert.equal(coverage.freeRunFrom(0), 10, "0..9, and it stops where 10 begins");
41
- });
42
-
43
- test("a run stops before a stretch another live run was given", () => {
44
- const coverage = new CoverageMap({ segmentCount: 100 });
45
- const other = run("other");
46
- coverage.claim(other, 40, 60);
47
-
48
- assert.equal(coverage.freeRunFrom(0), 40, "0..39, and the other run's claim begins at 40");
49
- assert.equal(coverage.firstGapFrom(45), 61, "the first thing nobody holds after it");
50
- });
51
-
52
- test("a run's own claim does not hold it back", () => {
53
- const coverage = new CoverageMap({ segmentCount: 100 });
54
- const mine = run("mine");
55
- coverage.claim(mine, 40, 60);
56
-
57
- assert.equal(
58
- coverage.freeRunFrom(40, mine),
59
- 60,
60
- "asked by the run that holds it, the stretch is its own to work through"
61
- );
62
- });
63
-
64
- test("a run that has ended holds nothing back", () => {
65
- const coverage = new CoverageMap({ segmentCount: 100 });
66
- const dead = run("dead");
67
- coverage.claim(dead, 40, 60);
68
- coverage.release(dead);
69
-
70
- assert.equal(coverage.freeRunFrom(0), 100, "what it did not finish is free again");
71
- });
72
-
73
- test("what a dead run DID finish stays made", () => {
74
- const coverage = new CoverageMap({ segmentCount: 100 });
75
- const dead = run("dead");
76
- coverage.claim(dead, 40, 60);
77
- coverage.markReadyAll([40, 41, 42]);
78
- coverage.release(dead);
79
-
80
- assert.equal(coverage.firstGapFrom(40), 43, "a closed file is closed whoever made it");
81
- });
82
-
83
- test("nothing left to make is answered with nothing", () => {
84
- const coverage = new CoverageMap({ segmentCount: 5 });
85
- coverage.markReadyAll([0, 1, 2, 3, 4]);
86
-
87
- assert.equal(coverage.firstGapFrom(0), null, "and a run started here would only repeat somebody's work");
88
- });
89
-
90
- test("a run without an end does not take the film away from a viewer further in", () => {
91
- // The case that used to need a second authority's `head + look-ahead` guess.
92
- // A run's claim is now the stretch the plan GAVE it, so a viewer opening the
93
- // same film in the middle finds their own position free.
94
- const coverage = new CoverageMap({ segmentCount: 1000 });
95
- const first = run("first");
96
- coverage.claim(first, 0, 499);
97
-
98
- assert.equal(coverage.firstGapFrom(500), 500, "the second viewer's own position is free");
99
- assert.equal(coverage.freeRunFrom(500), 500, "and everything from there to the end");
100
- });
1
+ /**
2
+ * @file How far a run may work, and who decides it.
3
+ *
4
+ * These cases used to be asked of `planRunInterval`, a second authority inside
5
+ * the session manager that answered by its own rules: it walked the whole track
6
+ * for the first free number, MOVED the start there, and counted every live run
7
+ * as claiming up to `head + look-ahead`. It contradicted the plan directly, and
8
+ * the two together produced the field oscillation of 2026-09-05 — the plan
9
+ * commanded a start at #46, this moved it to #78, the plan killed the run for
10
+ * standing outside the window it had asked for, and the same start was
11
+ * commanded again, 350-700ms per cycle, no segment ever produced, the viewer's
12
+ * picture stopped for 125 seconds.
13
+ *
14
+ * It is gone. WHERE a run starts is the plan's decision and nothing moves it;
15
+ * HOW FAR it may work is a fact of the one coverage map, and that is what these
16
+ * cases now ask. The map is the same object the plan reads, so there is no
17
+ * second set of rules for the two to disagree about.
18
+ */
19
+
20
+ import test from "node:test";
21
+ import assert from "node:assert/strict";
22
+ import { CoverageMap } from "../services/encode/CoverageMap.js";
23
+
24
+ /** A stand-in for a run: the map identifies one by being it. */
25
+ function run(name) {
26
+ return { name };
27
+ }
28
+
29
+ test("with nothing made, a run gets everything from where it was asked", () => {
30
+ const coverage = new CoverageMap({ segmentCount: 100 });
31
+
32
+ assert.equal(coverage.freeRunFrom(0), 100, "the whole track is free");
33
+ assert.equal(coverage.firstGapFrom(0), 0);
34
+ });
35
+
36
+ test("a run stops before material that is already made", () => {
37
+ const coverage = new CoverageMap({ segmentCount: 100 });
38
+ coverage.setReady([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
39
+
40
+ assert.equal(coverage.freeRunFrom(0), 10, "0..9, and it stops where 10 begins");
41
+ });
42
+
43
+ test("a run stops before a stretch another live run was given", () => {
44
+ const coverage = new CoverageMap({ segmentCount: 100 });
45
+ const other = run("other");
46
+ coverage.claim(other, 40, 60);
47
+
48
+ assert.equal(coverage.freeRunFrom(0), 40, "0..39, and the other run's claim begins at 40");
49
+ assert.equal(coverage.firstGapFrom(45), 61, "the first thing nobody holds after it");
50
+ });
51
+
52
+ test("a run's own claim does not hold it back", () => {
53
+ const coverage = new CoverageMap({ segmentCount: 100 });
54
+ const mine = run("mine");
55
+ coverage.claim(mine, 40, 60);
56
+
57
+ assert.equal(
58
+ coverage.freeRunFrom(40, mine),
59
+ 60,
60
+ "asked by the run that holds it, the stretch is its own to work through"
61
+ );
62
+ });
63
+
64
+ test("a run that has ended holds nothing back", () => {
65
+ const coverage = new CoverageMap({ segmentCount: 100 });
66
+ const dead = run("dead");
67
+ coverage.claim(dead, 40, 60);
68
+ coverage.release(dead);
69
+
70
+ assert.equal(coverage.freeRunFrom(0), 100, "what it did not finish is free again");
71
+ });
72
+
73
+ test("what a dead run DID finish stays made", () => {
74
+ const coverage = new CoverageMap({ segmentCount: 100 });
75
+ const dead = run("dead");
76
+ coverage.claim(dead, 40, 60);
77
+ coverage.setReady([40, 41, 42]);
78
+ coverage.release(dead);
79
+
80
+ assert.equal(coverage.firstGapFrom(40), 43, "a closed file is closed whoever made it");
81
+ });
82
+
83
+ test("nothing left to make is answered with nothing", () => {
84
+ const coverage = new CoverageMap({ segmentCount: 5 });
85
+ coverage.setReady([0, 1, 2, 3, 4]);
86
+
87
+ assert.equal(coverage.firstGapFrom(0), null, "and a run started here would only repeat somebody's work");
88
+ });
89
+
90
+ test("a run without an end does not take the film away from a viewer further in", () => {
91
+ // The case that used to need a second authority's `head + look-ahead` guess.
92
+ // A run's claim is now the stretch the plan GAVE it, so a viewer opening the
93
+ // same film in the middle finds their own position free.
94
+ const coverage = new CoverageMap({ segmentCount: 1000 });
95
+ const first = run("first");
96
+ coverage.claim(first, 0, 499);
97
+
98
+ assert.equal(coverage.firstGapFrom(500), 500, "the second viewer's own position is free");
99
+ assert.equal(coverage.freeRunFrom(500), 500, "and everything from there to the end");
100
+ });
@@ -1,187 +1,216 @@
1
- /**
2
- * @file One store of segments, addressed by what they are, and what it makes of
3
- * what a killed process left behind.
4
- */
5
-
6
- import test from "node:test";
7
- import assert from "node:assert/strict";
8
- import { mkdtempSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
9
- import os from "node:os";
10
- import path from "node:path";
11
- import { directoryNameFor, SegmentStore } from "../services/encode/SegmentStore.js";
12
- import { fmp4Format } from "../services/segment-formats/fmp4.js";
13
-
14
- /**
15
- * @returns {{ store: SegmentStore, root: string, lines: string[] }}
16
- */
17
- function storeInATempRoot() {
18
- const root = mkdtempSync(path.join(os.tmpdir(), "segment-store-"));
19
- const lines = [];
20
- const store = new SegmentStore({
21
- root,
22
- logger: { info: (line) => lines.push(line), warn: (line) => lines.push(line) }
23
- });
24
- return { store, root, lines };
25
- }
26
-
27
- /**
28
- * @param {string} dir
29
- * @param {number} index
30
- * @param {number} bytes
31
- */
32
- function writeSegment(dir, index, bytes = 16) {
33
- writeFileSync(path.join(dir, fmp4Format.segmentFileName(index)), Buffer.alloc(bytes));
34
- }
35
-
36
- const KEY = "torrent:abc:fmt=fmp4:grid=kf@0:video-only:v=0/copy";
37
-
38
- test("two viewers of one output are given the same directory", (t) => {
39
- const { store, root } = storeInATempRoot();
40
- t.after(() => rmSync(root, { recursive: true, force: true }));
41
-
42
- // Which viewer asked never enters it — the address is what the segments are.
43
- assert.equal(store.directoryFor(KEY), store.directoryFor(KEY));
44
- assert.notEqual(store.directoryFor(KEY), store.directoryFor(`${KEY}:other`));
45
- });
46
-
47
- test("the directory says what it holds, so a later process can tell", (t) => {
48
- const { store, root } = storeInATempRoot();
49
- t.after(() => rmSync(root, { recursive: true, force: true }));
50
-
51
- const dir = store.directoryFor(KEY);
52
- const names = readdirSync(dir);
53
- assert.deepEqual(names, ["key.txt"]);
54
- });
55
-
56
- test("a segment is proven closed by the existence of the next one", (t) => {
57
- const { store, root } = storeInATempRoot();
58
- t.after(() => rmSync(root, { recursive: true, force: true }));
59
-
60
- const dir = store.directoryFor(KEY);
61
- store.useFormat(KEY, fmp4Format);
62
- writeSegment(dir, 0);
63
- writeSegment(dir, 1);
64
- writeSegment(dir, 2);
65
-
66
- // The `segment` muxer writes no temporary file, so a file that exists may
67
- // still be growing; only a run that has moved past it proves otherwise.
68
- assert.deepEqual(store.provenNumbers(KEY), [0, 1]);
69
- assert.equal(store.unprovenNumber(KEY), 2);
70
- });
71
-
72
- test("a file of no bytes is not a segment, whatever it is called", (t) => {
73
- const { store, root } = storeInATempRoot();
74
- t.after(() => rmSync(root, { recursive: true, force: true }));
75
-
76
- const dir = store.directoryFor(KEY);
77
- store.useFormat(KEY, fmp4Format);
78
- writeSegment(dir, 0);
79
- writeSegment(dir, 1);
80
- writeSegment(dir, 2, 0);
81
-
82
- // What a run killed the moment after opening its next piece leaves behind.
83
- // Counted as a segment once, it closed the only hole in the numbering and
84
- // convinced the look-ahead the encoder had produced it.
85
- assert.equal(store.pathOf(KEY, 2), null);
86
- assert.deepEqual(store.provenNumbers(KEY), [0]);
87
- });
88
-
89
- test("a directory that has not moved is not read again", (t) => {
90
- const { store, root } = storeInATempRoot();
91
- t.after(() => rmSync(root, { recursive: true, force: true }));
92
-
93
- const dir = store.directoryFor(KEY);
94
- store.useFormat(KEY, fmp4Format);
95
- writeSegment(dir, 0);
96
- writeSegment(dir, 1);
97
-
98
- const first = store.refresh(KEY);
99
- const second = store.refresh(KEY);
100
- assert.equal(first, second, "the same reading is handed back, not a fresh listing");
101
- });
102
-
103
- test("what a killed process left is found, named and counted", (t) => {
104
- const { store, root, lines } = storeInATempRoot();
105
- t.after(() => rmSync(root, { recursive: true, force: true }));
106
-
107
- const dir = path.join(root, directoryNameFor(KEY));
108
- mkdirSync(dir, { recursive: true });
109
- writeFileSync(path.join(dir, "key.txt"), `${KEY}\n`);
110
- writeSegment(dir, 0);
111
- writeSegment(dir, 1);
112
- const orphan = path.join(root, "0123456789abcdef");
113
- mkdirSync(orphan, { recursive: true });
114
- writeSegment(orphan, 0);
115
-
116
- const swept = store.sweep();
117
- assert.equal(swept.directories, 2);
118
- assert.equal(swept.unidentified, 1, "the one with no key file cannot be matched to a request");
119
- assert.equal(swept.segments, 3);
120
- assert.match(lines.join("\n"), /startup sweep/);
121
- assert.match(lines.join("\n"), /ended\s+without anything recording why/);
122
- });
123
-
124
- test("what survived a kill is taken back, minus the piece nothing proves", (t) => {
125
- const { store, root } = storeInATempRoot();
126
- t.after(() => rmSync(root, { recursive: true, force: true }));
127
-
128
- const dir = path.join(root, directoryNameFor(KEY));
129
- mkdirSync(dir, { recursive: true });
130
- writeFileSync(path.join(dir, "key.txt"), `${KEY}\n`);
131
- for (let index = 0; index <= 5; index += 1) {
132
- writeSegment(dir, index);
133
- }
134
-
135
- const taken = store.adoptWhatSurvived(() => fmp4Format);
136
-
137
- assert.equal(taken.adopted, 1);
138
- assert.equal(taken.unprovenRemoved, 1, "the highest number was being written when the run died");
139
- // Five kept rather than six thrown away: a copied segment's bytes depend only
140
- // on the source, and re-encoding them costs a machine already short of it.
141
- assert.deepEqual(store.provenNumbers(KEY), [0, 1, 2, 3, 4]);
142
- assert.equal(store.pathOf(KEY, 5), null);
143
- });
144
-
145
- test("a directory that cannot name itself is thrown away rather than served", (t) => {
146
- const { store, root } = storeInATempRoot();
147
- t.after(() => rmSync(root, { recursive: true, force: true }));
148
-
149
- const orphan = path.join(root, "0123456789abcdef");
150
- mkdirSync(orphan, { recursive: true });
151
- writeSegment(orphan, 0);
152
-
153
- const taken = store.adoptWhatSurvived(() => fmp4Format);
154
-
155
- assert.equal(taken.adopted, 0);
156
- assert.equal(taken.dropped, 1);
157
- assert.equal(readdirSync(root).length, 0);
158
- });
159
-
160
- test("an output this proxy can no longer serve is thrown away too", (t) => {
161
- const { store, root } = storeInATempRoot();
162
- t.after(() => rmSync(root, { recursive: true, force: true }));
163
-
164
- const dir = path.join(root, directoryNameFor(KEY));
165
- mkdirSync(dir, { recursive: true });
166
- writeFileSync(path.join(dir, "key.txt"), `${KEY}\n`);
167
- writeSegment(dir, 0);
168
-
169
- const taken = store.adoptWhatSurvived(() => null);
170
-
171
- assert.equal(taken.dropped, 1);
172
- assert.equal(readdirSync(root).length, 0);
173
- });
174
-
175
- test("what the store weighs is reported, and dropping one output frees it", (t) => {
176
- const { store, root } = storeInATempRoot();
177
- t.after(() => rmSync(root, { recursive: true, force: true }));
178
-
179
- const dir = store.directoryFor(KEY);
180
- store.useFormat(KEY, fmp4Format);
181
- writeSegment(dir, 0, 1000);
182
- writeSegment(dir, 1, 1000);
183
-
184
- assert.equal(store.stats().bytes, 2000);
185
- store.drop(KEY, "nobody is watching it");
186
- assert.equal(store.stats().outputs, 0);
187
- });
1
+ /**
2
+ * @file One store of segments, addressed by what they are, and what it makes of
3
+ * what a killed process left behind.
4
+ */
5
+
6
+ import test from "node:test";
7
+ import assert from "node:assert/strict";
8
+ import { mkdtempSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
+ import { directoryNameFor, SegmentStore } from "../services/encode/SegmentStore.js";
12
+ import { fmp4Format } from "../services/segment-formats/fmp4.js";
13
+
14
+ /**
15
+ * @returns {{ store: SegmentStore, root: string, lines: string[] }}
16
+ */
17
+ function storeInATempRoot() {
18
+ const root = mkdtempSync(path.join(os.tmpdir(), "segment-store-"));
19
+ const lines = [];
20
+ const store = new SegmentStore({
21
+ root,
22
+ logger: { info: (line) => lines.push(line), warn: (line) => lines.push(line) }
23
+ });
24
+ return { store, root, lines };
25
+ }
26
+
27
+ /**
28
+ * @param {string} dir
29
+ * @param {number} index
30
+ * @param {number} bytes
31
+ */
32
+ function writeSegment(dir, index, bytes = 16) {
33
+ writeFileSync(path.join(dir, fmp4Format.segmentFileName(index)), Buffer.alloc(bytes));
34
+ }
35
+
36
+ const KEY = "torrent:abc:fmt=fmp4:grid=kf@0:video-only:v=0/copy";
37
+
38
+ test("two viewers of one output are given the same directory", (t) => {
39
+ const { store, root } = storeInATempRoot();
40
+ t.after(() => rmSync(root, { recursive: true, force: true }));
41
+
42
+ // Which viewer asked never enters it — the address is what the segments are.
43
+ assert.equal(store.directoryFor(KEY), store.directoryFor(KEY));
44
+ assert.notEqual(store.directoryFor(KEY), store.directoryFor(`${KEY}:other`));
45
+ });
46
+
47
+ test("the directory says what it holds, so a later process can tell", (t) => {
48
+ const { store, root } = storeInATempRoot();
49
+ t.after(() => rmSync(root, { recursive: true, force: true }));
50
+
51
+ const dir = store.directoryFor(KEY);
52
+ const names = readdirSync(dir);
53
+ assert.deepEqual(names, ["key.txt"]);
54
+ });
55
+
56
+ test("a segment is proven closed by the existence of the next one", (t) => {
57
+ const { store, root } = storeInATempRoot();
58
+ t.after(() => rmSync(root, { recursive: true, force: true }));
59
+
60
+ const dir = store.directoryFor(KEY);
61
+ store.useFormat(KEY, fmp4Format);
62
+ writeSegment(dir, 0);
63
+ writeSegment(dir, 1);
64
+ writeSegment(dir, 2);
65
+
66
+ // The `hls` muxer carries no channel to report on and renames into place when
67
+ // it closes a piece, so on that branch existence IS the proof; the same
68
+ // answers for the pieces a previous life of this process left behind.
69
+ assert.deepEqual(store.provenNumbers(KEY), [0, 1]);
70
+ assert.equal(store.unprovenNumber(KEY), 2);
71
+ });
72
+
73
+ test("a file of no bytes is not a segment, whatever it is called", (t) => {
74
+ const { store, root } = storeInATempRoot();
75
+ t.after(() => rmSync(root, { recursive: true, force: true }));
76
+
77
+ const dir = store.directoryFor(KEY);
78
+ store.useFormat(KEY, fmp4Format);
79
+ writeSegment(dir, 0);
80
+ writeSegment(dir, 1);
81
+ writeSegment(dir, 2, 0);
82
+
83
+ // What a run killed the moment after opening its next piece leaves behind.
84
+ // Counted as a segment once, it closed the only hole in the numbering and
85
+ // convinced the look-ahead the encoder had produced it.
86
+ assert.equal(store.pathOf(KEY, 2), null);
87
+ assert.deepEqual(store.provenNumbers(KEY), [0]);
88
+ });
89
+
90
+ test("a run's own stretch bounds what it unproves", (t) => {
91
+ const { store, root } = storeInATempRoot();
92
+ t.after(() => rmSync(root, { recursive: true, force: true }));
93
+
94
+ store.useFormat(KEY, fmp4Format);
95
+ store.directoryFor(KEY);
96
+ for (let index = 0; index <= 5; index += 1) {
97
+ store.markClosed(KEY, index);
98
+ }
99
+
100
+ // A run of #0..#0 rewrites #0 and nothing else. Unbounded, this forgot the
101
+ // whole film beyond it — and with readiness a projection of what is proven,
102
+ // that is an output declaring itself unmade whenever an encoder starts near
103
+ // the beginning, which is a fresh encoder for every segment of it.
104
+ //
105
+ // Asked with no files on the disk, so the successor rule cannot answer for
106
+ // the statements and only the statements are under test.
107
+ store.forgetClosed(KEY, 0, 0);
108
+ assert.equal(store.isClosed(KEY, 0), false, "the one number it will rewrite");
109
+ assert.equal(store.isClosed(KEY, 1), true, "and nothing beyond its stretch");
110
+ assert.equal(store.isClosed(KEY, 5), true);
111
+
112
+ store.forgetClosed(KEY, 3);
113
+ assert.equal(store.isClosed(KEY, 2), true);
114
+ assert.equal(store.isClosed(KEY, 3), false, "no end given means to the end of the film");
115
+ assert.equal(store.isClosed(KEY, 5), false);
116
+ });
117
+
118
+ test("a directory that has not moved is not read again", (t) => {
119
+ const { store, root } = storeInATempRoot();
120
+ t.after(() => rmSync(root, { recursive: true, force: true }));
121
+
122
+ const dir = store.directoryFor(KEY);
123
+ store.useFormat(KEY, fmp4Format);
124
+ writeSegment(dir, 0);
125
+ writeSegment(dir, 1);
126
+
127
+ const first = store.refresh(KEY);
128
+ const second = store.refresh(KEY);
129
+ assert.equal(first, second, "the same reading is handed back, not a fresh listing");
130
+ });
131
+
132
+ test("what a killed process left is found, named and counted", (t) => {
133
+ const { store, root, lines } = storeInATempRoot();
134
+ t.after(() => rmSync(root, { recursive: true, force: true }));
135
+
136
+ const dir = path.join(root, directoryNameFor(KEY));
137
+ mkdirSync(dir, { recursive: true });
138
+ writeFileSync(path.join(dir, "key.txt"), `${KEY}\n`);
139
+ writeSegment(dir, 0);
140
+ writeSegment(dir, 1);
141
+ const orphan = path.join(root, "0123456789abcdef");
142
+ mkdirSync(orphan, { recursive: true });
143
+ writeSegment(orphan, 0);
144
+
145
+ const swept = store.sweep();
146
+ assert.equal(swept.directories, 2);
147
+ assert.equal(swept.unidentified, 1, "the one with no key file cannot be matched to a request");
148
+ assert.equal(swept.segments, 3);
149
+ assert.match(lines.join("\n"), /startup sweep/);
150
+ assert.match(lines.join("\n"), /ended\s+without anything recording why/);
151
+ });
152
+
153
+ test("what survived a kill is taken back, minus the piece nothing proves", (t) => {
154
+ const { store, root } = storeInATempRoot();
155
+ t.after(() => rmSync(root, { recursive: true, force: true }));
156
+
157
+ const dir = path.join(root, directoryNameFor(KEY));
158
+ mkdirSync(dir, { recursive: true });
159
+ writeFileSync(path.join(dir, "key.txt"), `${KEY}\n`);
160
+ for (let index = 0; index <= 5; index += 1) {
161
+ writeSegment(dir, index);
162
+ }
163
+
164
+ const taken = store.adoptWhatSurvived(() => fmp4Format);
165
+
166
+ assert.equal(taken.adopted, 1);
167
+ assert.equal(taken.unprovenRemoved, 1, "the highest number was being written when the run died");
168
+ // Five kept rather than six thrown away: a copied segment's bytes depend only
169
+ // on the source, and re-encoding them costs a machine already short of it.
170
+ assert.deepEqual(store.provenNumbers(KEY), [0, 1, 2, 3, 4]);
171
+ assert.equal(store.pathOf(KEY, 5), null);
172
+ });
173
+
174
+ test("a directory that cannot name itself is thrown away rather than served", (t) => {
175
+ const { store, root } = storeInATempRoot();
176
+ t.after(() => rmSync(root, { recursive: true, force: true }));
177
+
178
+ const orphan = path.join(root, "0123456789abcdef");
179
+ mkdirSync(orphan, { recursive: true });
180
+ writeSegment(orphan, 0);
181
+
182
+ const taken = store.adoptWhatSurvived(() => fmp4Format);
183
+
184
+ assert.equal(taken.adopted, 0);
185
+ assert.equal(taken.dropped, 1);
186
+ assert.equal(readdirSync(root).length, 0);
187
+ });
188
+
189
+ test("an output this proxy can no longer serve is thrown away too", (t) => {
190
+ const { store, root } = storeInATempRoot();
191
+ t.after(() => rmSync(root, { recursive: true, force: true }));
192
+
193
+ const dir = path.join(root, directoryNameFor(KEY));
194
+ mkdirSync(dir, { recursive: true });
195
+ writeFileSync(path.join(dir, "key.txt"), `${KEY}\n`);
196
+ writeSegment(dir, 0);
197
+
198
+ const taken = store.adoptWhatSurvived(() => null);
199
+
200
+ assert.equal(taken.dropped, 1);
201
+ assert.equal(readdirSync(root).length, 0);
202
+ });
203
+
204
+ test("what the store weighs is reported, and dropping one output frees it", (t) => {
205
+ const { store, root } = storeInATempRoot();
206
+ t.after(() => rmSync(root, { recursive: true, force: true }));
207
+
208
+ const dir = store.directoryFor(KEY);
209
+ store.useFormat(KEY, fmp4Format);
210
+ writeSegment(dir, 0, 1000);
211
+ writeSegment(dir, 1, 1000);
212
+
213
+ assert.equal(store.stats().bytes, 2000);
214
+ store.drop(KEY, "nobody is watching it");
215
+ assert.equal(store.stats().outputs, 0);
216
+ });