Skip to content

Commit a6c7a56

Browse files
Bryan Powellsfsholden
authored andcommitted
fix: zip tree container adding duplicate entries (#158)
1 parent dfe28aa commit a6c7a56

File tree

2 files changed

+41
-27
lines changed

2 files changed

+41
-27
lines changed

src/metadata-registry/treeContainers.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,13 @@ export class ZipTreeContainer extends BaseTreeContainer {
114114
}
115115

116116
private populate(directory: unzipper.CentralDirectory): void {
117-
for (const { path, stream, buffer } of directory.files) {
118-
// normalize path to use OS separator since zip entries always use forward slash
119-
const entry = { path: normalize(path), stream, buffer };
120-
this.tree.set(entry.path, entry);
121-
this.ensureDirPathExists(entry);
117+
for (const { path, type, stream, buffer } of directory.files) {
118+
if (type === 'File') {
119+
// normalize path to use OS separator since zip entries always use forward slash
120+
const entry = { path: normalize(path), stream, buffer };
121+
this.tree.set(entry.path, entry);
122+
this.ensureDirPathExists(entry);
123+
}
122124
}
123125
}
124126

test/metadata-registry/treeContainers.test.ts

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
import { expect, assert } from 'chai';
1414
import { createSandbox } from 'sinon';
1515
import * as fs from 'fs';
16-
import { join } from 'path';
16+
import { join, normalize } from 'path';
1717
import { LibraryError } from '../../src/errors';
1818
import { nls } from '../../src/i18n';
1919
import { VirtualDirectory } from '../../src';
@@ -110,6 +110,8 @@ describe('Tree Containers', () => {
110110
let tree: ZipTreeContainer;
111111
let zipBuffer: Buffer;
112112

113+
const filesRoot = join('.', 'main', 'default');
114+
113115
before(async () => {
114116
const pipeline = promisify(cbPipeline);
115117
const archive = createArchive('zip', { zlib: { level: 3 } });
@@ -120,9 +122,13 @@ describe('Tree Containers', () => {
120122
cb();
121123
};
122124
pipeline(archive, bufferWritable);
123-
archive.append('test text', { name: 'test.txt' });
124-
archive.append('test text 2', { name: 'files/test2.txt' });
125-
archive.append('test text 3', { name: 'files/test3.txt' });
125+
126+
archive.append(null, { name: 'main/' });
127+
archive.append(null, { name: 'main/default/' });
128+
archive.append('test text', { name: 'main/default/test.txt' });
129+
archive.append('test text 2', { name: 'main/default/test2.txt' });
130+
archive.append('test text 3', { name: 'main/default/morefiles/test3.txt' });
131+
126132
await archive.finalize();
127133

128134
zipBuffer = Buffer.concat(buffers);
@@ -131,35 +137,34 @@ describe('Tree Containers', () => {
131137

132138
describe('exists', () => {
133139
it('should return true for file that exists', () => {
134-
const path = join('.', 'test.txt');
140+
const path = join(filesRoot, 'test.txt');
135141
expect(tree.exists(path)).to.be.true;
136142
});
137143

138144
it('should return true for directory that exists', () => {
139-
const path = join('.', 'files');
145+
const path = join(filesRoot, 'morefiles');
140146
expect(tree.exists(path)).to.be.true;
141147
});
142148

143149
it('should return false for file that does not exist', () => {
144-
const path = join('.', 'files', 'test4.txt');
150+
const path = join(filesRoot, 'test4.txt');
145151
expect(tree.exists(path)).to.be.false;
146152
});
147153

148154
it('should return false for directory that does not exist', () => {
149-
const path = join('.', 'otherfiles');
155+
const path = join('.', 'dne');
150156
expect(tree.exists(path)).to.be.false;
151157
});
152158
});
153159

154160
describe('isDirectory', () => {
155161
it('should return false for isDirectory', () => {
156-
const path = join('.', 'test.txt');
162+
const path = join(filesRoot, 'test.txt');
157163
expect(tree.isDirectory(path)).to.be.false;
158164
});
159165

160166
it('should return true for isDirectory', () => {
161-
const path = join('.', 'files');
162-
expect(tree.isDirectory(path)).to.be.true;
167+
expect(tree.isDirectory(filesRoot)).to.be.true;
163168
});
164169

165170
it('should throw an error if path does not exist', () => {
@@ -173,12 +178,21 @@ describe('Tree Containers', () => {
173178
});
174179

175180
describe('readDirectory', () => {
176-
it('should return directory entries for readDirectory', () => {
177-
expect(tree.readDirectory('.')).to.deep.equal(['test.txt', 'files']);
181+
it('should return correct directory entries for directory with files and directories', () => {
182+
expect(tree.readDirectory(filesRoot)).to.deep.equal(['test.txt', 'test2.txt', 'morefiles']);
183+
});
184+
185+
it('should return correct directory entries for directory only files', () => {
186+
const path = join(filesRoot, 'morefiles');
187+
expect(tree.readDirectory(path)).to.deep.equal(['test3.txt']);
188+
});
189+
190+
it('should return correct directory entries for directory with only directories', () => {
191+
expect(tree.readDirectory('.')).to.deep.equal(['main']);
178192
});
179193

180194
it('should throw an error if path is not a directory', () => {
181-
const path = join('.', 'files', 'test2.txt');
195+
const path = join(filesRoot, 'test2.txt');
182196
assert.throws(
183197
() => tree.readDirectory(path),
184198
LibraryError,
@@ -189,35 +203,33 @@ describe('Tree Containers', () => {
189203

190204
describe('readFile', () => {
191205
it('should read contents of zip entry into buffer', async () => {
192-
const path = join('.', 'test.txt');
206+
const path = join(filesRoot, 'test.txt');
193207
const contents = (await tree.readFile(path)).toString();
194208
expect(contents).to.equal('test text');
195209
});
196210

197211
it('should throw an error if path is to directory', () => {
198-
const path = join('.', 'files');
199212
assert.throws(
200-
() => tree.readFile(path),
213+
() => tree.readFile(filesRoot),
201214
LibraryError,
202-
nls.localize('error_expected_file_path', path)
215+
nls.localize('error_expected_file_path', filesRoot)
203216
);
204217
});
205218
});
206219

207220
describe('stream', () => {
208221
it('should return a readable stream', async () => {
209-
const path = join('.', 'test.txt');
222+
const path = join(filesRoot, 'test.txt');
210223
const zipDir = await unzipper.Open.buffer(zipBuffer);
211-
const expectedStream = zipDir.files.find((f) => f.path === path)?.stream();
224+
const expectedStream = zipDir.files.find((f) => normalize(f.path) === path)?.stream();
212225
const actual = tree.stream(path);
213226
expect(actual instanceof Readable).to.be.true;
214227
expect((actual as unzipper.Entry).path).to.equal(expectedStream.path);
215228
});
216229

217230
it('should throw an error if given path is to a directory', () => {
218-
const path = join('.', 'files');
219231
assert.throws(
220-
() => tree.stream(path),
232+
() => tree.stream(filesRoot),
221233
LibraryError,
222234
nls.localize('error_no_directory_stream', tree.constructor.name)
223235
);

0 commit comments

Comments
 (0)