Skip to content

Commit 836775b

Browse files
committed
chore: added tests for edge cases
1 parent 7736870 commit 836775b

File tree

3 files changed

+200
-8
lines changed

3 files changed

+200
-8
lines changed

src/VirtualTarParser.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class VirtualTarParser {
126126
) => Promise<void> | void;
127127
onDirectory?: (header: ParsedDirectory) => Promise<void> | void;
128128
onEnd?: () => Promise<void> | void;
129-
}) {
129+
} = {}) {
130130
this.fileCallback = onFile ?? (() => Promise.resolve());
131131
this.directoryCallback = onDirectory ?? (() => Promise.resolve());
132132
this.endCallback = onEnd ?? (() => {});
@@ -313,9 +313,10 @@ class VirtualTarParser {
313313
} else {
314314
if (this.resolveDataP != null) {
315315
this.resolveDataP(token);
316-
} else {
317-
utils.never('Callback is not awaiting the next data token');
318316
}
317+
// If the resolve callback is undefined, then nothing is waiting for
318+
// the data. We can ignore sending over the data and continue as
319+
// usual.
319320
}
320321
break;
321322
}

tests/VirtualTarGenerator.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import os from 'os';
44
import { test } from '@fast-check/jest';
55
import * as tar from 'tar';
66
import { VirtualTarGenerator } from '@';
7+
import * as tarConstants from '@/constants';
78

89
describe('generator', () => {
910
let tempDir;
@@ -109,4 +110,84 @@ describe('generator', () => {
109110
const directories = await fs.promises.readdir(path.join(tempDir));
110111
expect(directories).toEqual([dirName]);
111112
});
113+
114+
test('should write to archive while reading data in parallel', async () => {
115+
// Set the file names and their data
116+
const fileName1 = 'file1.txt';
117+
const fileName2 = 'file2.txt';
118+
const dirName = 'dir';
119+
const fileData = 'testing';
120+
121+
const vtar = new VirtualTarGenerator();
122+
123+
// Write file to archive in parallel to writing data to generator
124+
const p = (async () => {
125+
vtar.addFile(fileName1, { size: fileData.length, mode: 0o777 }, fileData);
126+
vtar.addFile(fileName2, { size: fileData.length, mode: 0o777 }, fileData);
127+
vtar.addDirectory(dirName);
128+
vtar.finalize();
129+
})();
130+
131+
const archivePath = path.join(tempDir, 'archive.tar');
132+
const fd = await fs.promises.open(archivePath, 'w');
133+
for await (const chunk of vtar.yieldChunks()) {
134+
await fd.write(chunk);
135+
}
136+
await fd.close();
137+
138+
// Cleanup promise for adding files to archive
139+
await p;
140+
141+
await tar.extract({
142+
file: archivePath,
143+
cwd: tempDir,
144+
});
145+
146+
// Check if each file has been written correctly
147+
const extractedData1 = await fs.promises.readFile(
148+
path.join(tempDir, fileName1),
149+
);
150+
const extractedData2 = await fs.promises.readFile(
151+
path.join(tempDir, fileName2),
152+
);
153+
const dirStat = await fs.promises.stat(path.join(tempDir, dirName));
154+
expect(extractedData1.toString()).toEqual(fileData);
155+
expect(extractedData2.toString()).toEqual(fileData);
156+
expect(dirStat.isDirectory()).toBeTrue();
157+
});
158+
159+
test('should write file containing exactly 512 bytes of data', async () => {
160+
// Set the file names and their data
161+
const fileName = 'file.txt';
162+
const fileData = new Uint8Array(tarConstants.BLOCK_SIZE).fill(1);
163+
164+
const vtar = new VirtualTarGenerator();
165+
166+
// Write file to archive in parallel to writing data to generator
167+
vtar.addFile(fileName, { size: fileData.length, mode: 0o777 }, fileData);
168+
vtar.finalize();
169+
170+
const archivePath = path.join(tempDir, 'archive.tar');
171+
const fd = await fs.promises.open(archivePath, 'w');
172+
for await (const chunk of vtar.yieldChunks()) {
173+
await fd.write(chunk);
174+
}
175+
await fd.close();
176+
177+
await tar.extract({
178+
file: archivePath,
179+
cwd: tempDir,
180+
});
181+
182+
// Check if file has been written correctly
183+
const extractedData = await fs.promises.readFile(
184+
path.join(tempDir, fileName),
185+
);
186+
187+
// The sums of all values in both the input and output buffers must be the
188+
// same.
189+
const fileSum = fileData.reduce((sum, value) => (sum += value));
190+
const dataSum = extractedData.reduce((sum, value) => (sum += value));
191+
expect(dataSum).toEqual(fileSum);
192+
});
112193
});

tests/VirtualTarParser.test.ts

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('parser', () => {
2020

2121
test('should read files and directories', async () => {
2222
// Set the file names and their data
23-
const dirName = 'dir';
23+
const dirName = 'dir/';
2424
const fileName1 = 'file.txt';
2525
const fileName2 = 'dir/file.txt';
2626
const fileData = 'testing';
@@ -37,7 +37,7 @@ describe('parser', () => {
3737
[fileName1, dirName, fileName2],
3838
);
3939

40-
const entries: Record<string, string | undefined> = {};
40+
const entries: Record<string, string | null> = {};
4141

4242
// Read files and directories and add it to the entries record
4343
const vtar = new VirtualTarParser({
@@ -50,7 +50,7 @@ describe('parser', () => {
5050
entries[header.path] = fileContent;
5151
},
5252
onDirectory: async (header) => {
53-
entries[header.path] = undefined;
53+
entries[header.path] = null;
5454
},
5555
});
5656

@@ -59,11 +59,121 @@ describe('parser', () => {
5959
await vtar.write(chunk);
6060
}
6161

62-
// Make sure all the callbacks settle
63-
await vtar.settled();
62+
expect(entries[dirName]).toBeNull();
63+
expect(entries[fileName1]).toEqual(fileData);
64+
expect(entries[fileName2]).toEqual(fileData);
65+
});
66+
67+
test('should ignore files if callback is not provided', async () => {
68+
// Set the file names and their data
69+
const dirName = 'dir/';
70+
const fileName1 = 'file.txt';
71+
const fileName2 = 'dir/file.txt';
72+
const fileData = 'testing';
73+
74+
await fs.promises.mkdir(path.join(tempDir, dirName));
75+
await fs.promises.writeFile(path.join(tempDir, fileName1), fileData);
76+
await fs.promises.writeFile(path.join(tempDir, fileName2), fileData);
77+
78+
const archive = tar.create(
79+
{
80+
cwd: tempDir,
81+
preservePaths: true,
82+
},
83+
[fileName1, dirName, fileName2],
84+
);
85+
86+
const entries: Record<string, string | null> = {};
87+
88+
// Read files and directories and add it to the entries record
89+
const vtar = new VirtualTarParser({
90+
onDirectory: async (header) => {
91+
entries[header.path] = null;
92+
},
93+
});
94+
95+
// Enqueue each generated chunk from the archive
96+
for await (const chunk of archive) {
97+
await vtar.write(chunk);
98+
}
99+
100+
expect(entries[dirName]).toBeNull();
101+
expect(entries[fileName1]).toBeUndefined();
102+
expect(entries[fileName2]).toBeUndefined();
103+
});
104+
105+
test('should ignore directories if callback is not provided', async () => {
106+
// Set the file names and their data
107+
const dirName = 'dir/';
108+
const fileName1 = 'file.txt';
109+
const fileName2 = 'dir/file.txt';
110+
const fileData = 'testing';
111+
112+
await fs.promises.mkdir(path.join(tempDir, dirName));
113+
await fs.promises.writeFile(path.join(tempDir, fileName1), fileData);
114+
await fs.promises.writeFile(path.join(tempDir, fileName2), fileData);
115+
116+
const archive = tar.create(
117+
{
118+
cwd: tempDir,
119+
preservePaths: true,
120+
},
121+
[fileName1, dirName, fileName2],
122+
);
123+
124+
const entries: Record<string, string | null> = {};
125+
126+
// Read files and directories and add it to the entries record
127+
const vtar = new VirtualTarParser({
128+
onFile: async (header, data) => {
129+
const content: Array<Uint8Array> = [];
130+
for await (const chunk of data()) {
131+
content.push(chunk);
132+
}
133+
const fileContent = Buffer.concat(content).toString();
134+
entries[header.path] = fileContent;
135+
},
136+
});
137+
138+
// Enqueue each generated chunk from the archive
139+
for await (const chunk of archive) {
140+
await vtar.write(chunk);
141+
}
64142

65143
expect(entries[dirName]).toBeUndefined();
66144
expect(entries[fileName1]).toEqual(fileData);
67145
expect(entries[fileName2]).toEqual(fileData);
68146
});
147+
148+
test('should ignore everything if callback is not provided', async () => {
149+
// Set the file names and their data
150+
const dirName = 'dir/';
151+
const fileName1 = 'file.txt';
152+
const fileName2 = 'dir/file.txt';
153+
const fileData = 'testing';
154+
155+
await fs.promises.mkdir(path.join(tempDir, dirName));
156+
await fs.promises.writeFile(path.join(tempDir, fileName1), fileData);
157+
await fs.promises.writeFile(path.join(tempDir, fileName2), fileData);
158+
159+
const archive = tar.create(
160+
{
161+
cwd: tempDir,
162+
preservePaths: true,
163+
},
164+
[fileName1, dirName, fileName2],
165+
);
166+
167+
const entries: Record<string, string | null> = {};
168+
169+
// Read files and directories and add it to the entries record
170+
const vtar = new VirtualTarParser();
171+
172+
// Enqueue each generated chunk from the archive
173+
for await (const chunk of archive) {
174+
await vtar.write(chunk);
175+
}
176+
177+
expect(Object.keys(entries).length).toEqual(0);
178+
});
69179
});

0 commit comments

Comments
 (0)