Skip to content

Commit d99cb6e

Browse files
🧪 test(uploads): Cover fetching originals from client.
1 parent b39415f commit d99cb6e

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

imports/_test/png.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ export const randomPNGBlob = async (): Promise<Blob> => {
2424
const response = await randomPNGResponse();
2525
return response.blob();
2626
};
27+
28+
export const magic = [137, 80, 78, 71, 13, 10, 26, 10];

imports/api/uploads.app-tests.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {assert} from 'chai';
2+
3+
import {type FileObj} from 'meteor/ostrio:files';
4+
5+
import {client, randomPassword, randomUserId} from '../_test/fixtures';
6+
import {magic} from '../_test/png';
7+
import absoluteURL from '../app/absoluteURL';
8+
import head from '../util/stream/head';
9+
10+
import {newUpload} from './_dev/populate/uploads';
11+
import {type MetadataType} from './uploads';
12+
import createUserWithPasswordAndLogin from './user/createUserWithPasswordAndLogin';
13+
import logout from './user/logout';
14+
15+
const uploads = (path: string) => absoluteURL(`cdn/storage/uploads/${path}`);
16+
const original = (upload: FileObj<MetadataType>) =>
17+
uploads(`${upload._id}/original/${upload.name}`);
18+
19+
client(__filename, () => {
20+
it('should not be possible to download a document when unauthenticated', async () => {
21+
const username = randomUserId();
22+
const password = randomPassword();
23+
await createUserWithPasswordAndLogin(username, password);
24+
const upload = await newUpload(undefined, {type: 'image/png'});
25+
await logout();
26+
27+
const response = await fetch(original(upload));
28+
29+
assert.strictEqual(response.status, 401);
30+
assert.strictEqual(await response.text(), 'Access denied!');
31+
});
32+
33+
it('should be possible for the owner of a document to download it', async () => {
34+
const username = randomUserId();
35+
const password = randomPassword();
36+
await createUserWithPasswordAndLogin(username, password);
37+
const upload = await newUpload(undefined, {type: 'image/png'});
38+
39+
const response = await fetch(original(upload));
40+
41+
assert.strictEqual(response.status, 200);
42+
43+
const body = response.body;
44+
assert.isNotNull(body);
45+
46+
const firstBytes = await head(body, magic.length);
47+
48+
assert.deepEqual(firstBytes, magic);
49+
});
50+
51+
it('should not be possible for someone other than the owner of a document to download it', async () => {
52+
const usernameA = randomUserId();
53+
const passwordA = randomPassword();
54+
await createUserWithPasswordAndLogin(usernameA, passwordA);
55+
const upload = await newUpload(undefined, {type: 'image/png'});
56+
await logout();
57+
58+
const usernameB = randomUserId();
59+
const passwordB = randomPassword();
60+
await createUserWithPasswordAndLogin(usernameB, passwordB);
61+
62+
const response = await fetch(original(upload));
63+
64+
assert.strictEqual(response.status, 401);
65+
assert.strictEqual(await response.text(), 'Access denied!');
66+
});
67+
});

imports/util/stream/head.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {asyncIterableToArray} from '@async-iterable-iterator/async-iterable-to-array';
2+
3+
const _head = async function* (reader: ReadableStreamDefaultReader, n: number) {
4+
while (n > 0) {
5+
// eslint-disable-next-line no-await-in-loop
6+
const {value, done} = await reader.read();
7+
if (done) break;
8+
yield value;
9+
n -= value.length;
10+
}
11+
};
12+
13+
const head = async (stream: ReadableStream, n: number) => {
14+
const reader = stream.getReader();
15+
const chunks = await asyncIterableToArray(_head(reader, n));
16+
reader.releaseLock();
17+
18+
return chunks.flatMap((chunk) => Array.from(chunk)).slice(0, n);
19+
};
20+
21+
export default head;

0 commit comments

Comments
 (0)