@protontech/drive-sdk 0.14.1 → 0.14.3

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.
Files changed (57) hide show
  1. package/dist/internal/batchLoading.d.ts +2 -0
  2. package/dist/internal/batchLoading.js +18 -5
  3. package/dist/internal/batchLoading.js.map +1 -1
  4. package/dist/internal/batchLoading.test.js +92 -0
  5. package/dist/internal/batchLoading.test.js.map +1 -1
  6. package/dist/internal/download/apiService.js +27 -23
  7. package/dist/internal/download/apiService.js.map +1 -1
  8. package/dist/internal/events/apiService.js +3 -1
  9. package/dist/internal/events/apiService.js.map +1 -1
  10. package/dist/internal/nodes/nodesAccess.js +1 -1
  11. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  12. package/dist/internal/nodes/nodesAccess.test.js +3 -2
  13. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  14. package/dist/internal/photos/index.js +3 -1
  15. package/dist/internal/photos/index.js.map +1 -1
  16. package/dist/internal/photos/upload.d.ts +1 -1
  17. package/dist/internal/photos/upload.js +2 -2
  18. package/dist/internal/photos/upload.js.map +1 -1
  19. package/dist/internal/sharing/events.d.ts +4 -2
  20. package/dist/internal/sharing/events.js +40 -9
  21. package/dist/internal/sharing/events.js.map +1 -1
  22. package/dist/internal/sharing/events.test.js +30 -2
  23. package/dist/internal/sharing/events.test.js.map +1 -1
  24. package/dist/internal/sharing/index.js +1 -1
  25. package/dist/internal/sharing/index.js.map +1 -1
  26. package/dist/internal/upload/fileUploader.d.ts +19 -3
  27. package/dist/internal/upload/fileUploader.js +31 -5
  28. package/dist/internal/upload/fileUploader.js.map +1 -1
  29. package/dist/internal/upload/fileUploader.test.js +1 -1
  30. package/dist/internal/upload/fileUploader.test.js.map +1 -1
  31. package/dist/internal/upload/index.js +4 -11
  32. package/dist/internal/upload/index.js.map +1 -1
  33. package/dist/internal/upload/index.test.js +104 -45
  34. package/dist/internal/upload/index.test.js.map +1 -1
  35. package/dist/internal/upload/smallFileUploader.d.ts +14 -14
  36. package/dist/internal/upload/smallFileUploader.js +38 -20
  37. package/dist/internal/upload/smallFileUploader.js.map +1 -1
  38. package/dist/internal/upload/smallFileUploader.test.js +35 -36
  39. package/dist/internal/upload/smallFileUploader.test.js.map +1 -1
  40. package/package.json +1 -1
  41. package/src/internal/batchLoading.test.ts +104 -0
  42. package/src/internal/batchLoading.ts +21 -5
  43. package/src/internal/download/apiService.ts +32 -28
  44. package/src/internal/events/apiService.ts +3 -1
  45. package/src/internal/nodes/nodesAccess.test.ts +4 -3
  46. package/src/internal/nodes/nodesAccess.ts +1 -1
  47. package/src/internal/photos/index.ts +2 -0
  48. package/src/internal/photos/upload.ts +13 -1
  49. package/src/internal/sharing/events.test.ts +35 -2
  50. package/src/internal/sharing/events.ts +47 -10
  51. package/src/internal/sharing/index.ts +1 -0
  52. package/src/internal/upload/fileUploader.test.ts +1 -0
  53. package/src/internal/upload/fileUploader.ts +60 -2
  54. package/src/internal/upload/index.test.ts +121 -63
  55. package/src/internal/upload/index.ts +4 -30
  56. package/src/internal/upload/smallFileUploader.test.ts +33 -40
  57. package/src/internal/upload/smallFileUploader.ts +47 -36
@@ -5,6 +5,7 @@ import { UploadController } from './controller';
5
5
  import { UploadCryptoService } from './cryptoService';
6
6
  import { NodeRevisionDraft } from './interface';
7
7
  import { UploadManager } from './manager';
8
+ import { SmallFileRevisionUploader, SmallFileUploader } from './smallFileUploader';
8
9
  import { StreamUploader } from './streamUploader';
9
10
  import { UploadTelemetry } from './telemetry';
10
11
 
@@ -26,6 +27,7 @@ export abstract class Uploader {
26
27
  protected manager: UploadManager,
27
28
  protected metadata: UploadMetadata,
28
29
  protected onFinish: () => void,
30
+ protected shouldUseSmallFileUpload: (expectedSize: number) => Promise<boolean>,
29
31
  protected signal?: AbortSignal,
30
32
  ) {
31
33
  this.telemetry = telemetry;
@@ -34,6 +36,7 @@ export abstract class Uploader {
34
36
  this.manager = manager;
35
37
  this.metadata = metadata;
36
38
  this.onFinish = onFinish;
39
+ this.shouldUseSmallFileUpload = shouldUseSmallFileUpload;
37
40
 
38
41
  this.signal = signal;
39
42
  this.abortController = new AbortController();
@@ -97,10 +100,28 @@ export abstract class Uploader {
97
100
  thumbnails: Thumbnail[],
98
101
  onProgress?: (uploadedBytes: number) => void,
99
102
  ): Promise<{ nodeRevisionUid: string; nodeUid: string }> {
103
+ const expectedEncryptedTotalSize = this.getExpectedEncryptedTotalSize(thumbnails);
104
+ if (await this.shouldUseSmallFileUpload(expectedEncryptedTotalSize)) {
105
+ return this.initSmallFileUploader(stream, thumbnails, onProgress);
106
+ }
107
+
100
108
  const uploader = await this.initStreamUploader();
101
109
  return uploader.start(stream, thumbnails, onProgress);
102
110
  }
103
111
 
112
+ private getExpectedEncryptedTotalSize(thumbnails: Thumbnail[]): number {
113
+ const thumbnailSize = thumbnails.reduce((acc, thumbnail) => acc + thumbnail.thumbnail.length, 0);
114
+ const totalSize = this.metadata.expectedSize + thumbnailSize;
115
+ const expectedEncryptedTotalSize = totalSize * 1.1; // 10% margin for encryption overhead
116
+ return expectedEncryptedTotalSize;
117
+ }
118
+
119
+ protected abstract initSmallFileUploader(
120
+ stream: ReadableStream,
121
+ thumbnails: Thumbnail[],
122
+ onProgress?: (uploadedBytes: number) => void,
123
+ ): Promise<{ nodeRevisionUid: string; nodeUid: string }>;
124
+
104
125
  protected async initStreamUploader(): Promise<StreamUploader> {
105
126
  const { revisionDraft, blockVerifier } = await this.createRevisionDraft();
106
127
 
@@ -154,9 +175,10 @@ export class FileUploader extends Uploader {
154
175
  private name: string,
155
176
  metadata: UploadMetadata,
156
177
  onFinish: () => void,
178
+ protected shouldUseSmallFileUpload: (expectedSize: number) => Promise<boolean>,
157
179
  signal?: AbortSignal,
158
180
  ) {
159
- super(telemetry, apiService, cryptoService, manager, metadata, onFinish, signal);
181
+ super(telemetry, apiService, cryptoService, manager, metadata, onFinish, shouldUseSmallFileUpload, signal);
160
182
 
161
183
  this.parentFolderUid = parentFolderUid;
162
184
  this.name = name;
@@ -192,6 +214,24 @@ export class FileUploader extends Uploader {
192
214
  protected async deleteRevisionDraft(revisionDraft: NodeRevisionDraft): Promise<void> {
193
215
  await this.manager.deleteDraftNode(revisionDraft.nodeUid);
194
216
  }
217
+
218
+ protected async initSmallFileUploader(
219
+ stream: ReadableStream,
220
+ thumbnails: Thumbnail[],
221
+ onProgress?: (uploadedBytes: number) => void,
222
+ ): Promise<{ nodeRevisionUid: string; nodeUid: string }> {
223
+ const uploader = new SmallFileUploader(
224
+ this.telemetry,
225
+ this.cryptoService,
226
+ this.manager,
227
+ this.metadata,
228
+ this.onFinish,
229
+ this.signal,
230
+ this.parentFolderUid,
231
+ this.name,
232
+ );
233
+ return uploader.upload(stream, thumbnails, onProgress);
234
+ }
195
235
  }
196
236
 
197
237
  /**
@@ -206,9 +246,10 @@ export class FileRevisionUploader extends Uploader {
206
246
  private nodeUid: string,
207
247
  metadata: UploadMetadata,
208
248
  onFinish: () => void,
249
+ protected shouldUseSmallFileUpload: (expectedSize: number) => Promise<boolean>,
209
250
  signal?: AbortSignal,
210
251
  ) {
211
- super(telemetry, apiService, cryptoService, manager, metadata, onFinish, signal);
252
+ super(telemetry, apiService, cryptoService, manager, metadata, onFinish, shouldUseSmallFileUpload, signal);
212
253
 
213
254
  this.nodeUid = nodeUid;
214
255
  }
@@ -243,4 +284,21 @@ export class FileRevisionUploader extends Uploader {
243
284
  protected async deleteRevisionDraft(revisionDraft: NodeRevisionDraft): Promise<void> {
244
285
  await this.manager.deleteDraftRevision(revisionDraft.nodeRevisionUid);
245
286
  }
287
+
288
+ protected async initSmallFileUploader(
289
+ stream: ReadableStream,
290
+ thumbnails: Thumbnail[],
291
+ onProgress?: (uploadedBytes: number) => void,
292
+ ): Promise<{ nodeRevisionUid: string; nodeUid: string }> {
293
+ const uploader = new SmallFileRevisionUploader(
294
+ this.telemetry,
295
+ this.cryptoService,
296
+ this.manager,
297
+ this.metadata,
298
+ this.onFinish,
299
+ this.signal,
300
+ this.nodeUid,
301
+ );
302
+ return uploader.upload(stream, thumbnails, onProgress);
303
+ }
246
304
  }
@@ -1,18 +1,23 @@
1
- import { FeatureFlagProvider, FeatureFlags, UploadMetadata } from '../../interface';
1
+ import { FeatureFlagProvider, ThumbnailType, UploadMetadata } from '../../interface';
2
2
  import { getMockTelemetry } from '../../tests/telemetry';
3
- import { FileRevisionUploader, FileUploader } from './fileUploader';
3
+ import { FileRevisionUploader, FileUploader, Uploader } from './fileUploader';
4
4
  import { initUploadModule } from './index';
5
- import { SmallFileRevisionUploader, SmallFileUploader } from './smallFileUploader';
6
5
 
7
- const SMALL_FILE_SIZE_LIMIT = 128 * 1024; // 128 KiB, must match index.ts
6
+ const RAW_SMALL_FILE_SIZE_LIMIT = (128 * 1024) / 1.1; // 128 KiB, must match index.ts
8
7
 
9
- describe('initUploadModule - uploader selection', () => {
8
+ describe('initUploadModule', () => {
10
9
  const parentFolderUid = 'parent-folder-uid';
11
10
  const name = 'test-file.txt';
12
11
  const nodeUid = 'node-uid';
13
12
 
14
13
  let featureFlagProvider: jest.Mocked<FeatureFlagProvider>;
15
14
  let uploadModule: ReturnType<typeof initUploadModule>;
15
+ let initSmallFileSpy: jest.SpyInstance;
16
+ let initSmallRevisionSpy: jest.SpyInstance;
17
+ let initStreamSpy: jest.SpyInstance;
18
+
19
+ let stream: ReadableStream;
20
+ const thumbnail100k = { type: ThumbnailType.Type1, thumbnail: new Uint8Array(100_000) };
16
21
 
17
22
  beforeEach(() => {
18
23
  const apiService = {};
@@ -31,69 +36,122 @@ describe('initUploadModule - uploader selection', () => {
31
36
  nodesService as any,
32
37
  featureFlagProvider as any,
33
38
  );
34
- });
35
-
36
- describe('getFileUploader', () => {
37
- it('returns SmallFileUploader when feature flag is enabled and file size is below limit', async () => {
38
- featureFlagProvider.isEnabled.mockResolvedValue(true);
39
39
 
40
- const metadata: UploadMetadata = { expectedSize: 1, mediaType: 'text/plain' };
41
- const uploader = await uploadModule.getFileUploader(parentFolderUid, name, metadata);
42
-
43
- expect(uploader).toBeInstanceOf(SmallFileUploader);
40
+ initSmallFileSpy = jest.spyOn(FileUploader.prototype as any, 'initSmallFileUploader').mockResolvedValue({
41
+ nodeRevisionUid: 'revision-uid',
42
+ nodeUid: 'node-uid',
44
43
  });
45
-
46
- it('returns FileUploader when feature flag is enabled but file size exceeds limit', async () => {
47
- featureFlagProvider.isEnabled.mockResolvedValue(true);
48
-
49
- const metadata: UploadMetadata = {
50
- expectedSize: SMALL_FILE_SIZE_LIMIT,
51
- mediaType: 'text/plain',
52
- };
53
- const uploader = await uploadModule.getFileUploader(parentFolderUid, name, metadata);
54
-
55
- expect(uploader).toBeInstanceOf(FileUploader);
56
- });
57
-
58
- it('returns FileUploader when feature flag is disabled even for small file', async () => {
59
- featureFlagProvider.isEnabled.mockResolvedValue(false);
60
-
61
- const metadata: UploadMetadata = { expectedSize: 1, mediaType: 'text/plain' };
62
- const uploader = await uploadModule.getFileUploader(parentFolderUid, name, metadata);
63
-
64
- expect(uploader).toBeInstanceOf(FileUploader);
44
+ initSmallRevisionSpy = jest
45
+ .spyOn(FileRevisionUploader.prototype as any, 'initSmallFileUploader')
46
+ .mockResolvedValue({
47
+ nodeRevisionUid: 'revision-uid',
48
+ nodeUid: 'node-uid',
49
+ });
50
+ initStreamSpy = jest.spyOn(Uploader.prototype as any, 'initStreamUploader').mockResolvedValue({
51
+ start: jest.fn().mockResolvedValue({
52
+ nodeRevisionUid: 'revision-uid',
53
+ nodeUid: 'node-uid',
54
+ }),
55
+ } as any);
56
+
57
+ stream = new ReadableStream({
58
+ start(controller) {
59
+ controller.close();
60
+ },
65
61
  });
66
62
  });
67
63
 
68
- describe('getFileRevisionUploader', () => {
69
- it('returns SmallFileRevisionUploader when feature flag is enabled and file size is below limit', async () => {
70
- featureFlagProvider.isEnabled.mockResolvedValue(true);
71
-
72
- const metadata: UploadMetadata = { expectedSize: 1, mediaType: 'text/plain' };
73
- const uploader = await uploadModule.getFileRevisionUploader(nodeUid, metadata);
74
-
75
- expect(uploader).toBeInstanceOf(SmallFileRevisionUploader);
76
- });
77
-
78
- it('returns FileRevisionUploader when feature flag is enabled but file size exceeds limit', async () => {
79
- featureFlagProvider.isEnabled.mockResolvedValue(true);
80
-
81
- const metadata: UploadMetadata = {
82
- expectedSize: SMALL_FILE_SIZE_LIMIT + 1,
83
- mediaType: 'text/plain',
84
- };
85
- const uploader = await uploadModule.getFileRevisionUploader(nodeUid, metadata);
86
-
87
- expect(uploader).toBeInstanceOf(FileRevisionUploader);
88
- });
89
-
90
- it('returns FileRevisionUploader when feature flag is disabled even for small file', async () => {
91
- featureFlagProvider.isEnabled.mockResolvedValue(false);
92
-
93
- const metadata: UploadMetadata = { expectedSize: 1, mediaType: 'text/plain' };
94
- const uploader = await uploadModule.getFileRevisionUploader(nodeUid, metadata);
64
+ afterEach(() => {
65
+ jest.restoreAllMocks();
66
+ });
95
67
 
96
- expect(uploader).toBeInstanceOf(FileRevisionUploader);
68
+ async function drainUpload(controller: { completion(): Promise<unknown> }) {
69
+ await controller.completion();
70
+ }
71
+
72
+ const suites = [
73
+ {
74
+ method: 'getFileUploader',
75
+ getUploader: (metadata: UploadMetadata) => uploadModule.getFileUploader(parentFolderUid, name, metadata),
76
+ expect: (option: 'small' | 'stream') => {
77
+ if (option === 'stream') {
78
+ expect(initStreamSpy).toHaveBeenCalled();
79
+ expect(initSmallFileSpy).not.toHaveBeenCalled();
80
+ expect(initSmallRevisionSpy).not.toHaveBeenCalled();
81
+ } else {
82
+ expect(initSmallFileSpy).toHaveBeenCalled();
83
+ expect(initStreamSpy).not.toHaveBeenCalled();
84
+ expect(initSmallRevisionSpy).not.toHaveBeenCalled();
85
+ }
86
+ },
87
+ },
88
+ {
89
+ method: 'getFileRevisionUploader',
90
+ getUploader: (metadata: UploadMetadata) => uploadModule.getFileRevisionUploader(nodeUid, metadata),
91
+ expect: (option: 'small' | 'stream') => {
92
+ if (option === 'stream') {
93
+ expect(initStreamSpy).toHaveBeenCalled();
94
+ expect(initSmallFileSpy).not.toHaveBeenCalled();
95
+ expect(initSmallRevisionSpy).not.toHaveBeenCalled();
96
+ } else {
97
+ expect(initSmallRevisionSpy).toHaveBeenCalled();
98
+ expect(initSmallFileSpy).not.toHaveBeenCalled();
99
+ expect(initStreamSpy).not.toHaveBeenCalled();
100
+ }
101
+ },
102
+ },
103
+ ];
104
+ for (const suite of suites) {
105
+ describe(suite.method, () => {
106
+ it('uses stream path when feature flag is disabled even for small file', async () => {
107
+ featureFlagProvider.isEnabled.mockResolvedValue(false);
108
+
109
+ const metadata: UploadMetadata = { expectedSize: 1, mediaType: 'text/plain' };
110
+ const uploader = await suite.getUploader(metadata);
111
+ await drainUpload(await uploader.uploadFromStream(stream, []));
112
+
113
+ suite.expect('stream');
114
+ });
115
+
116
+ it('uses small-file path when flag is on and encrypted total size is below cap', async () => {
117
+ featureFlagProvider.isEnabled.mockResolvedValue(true);
118
+
119
+ const metadata: UploadMetadata = { expectedSize: 100, mediaType: 'text/plain' };
120
+ const uploader = await suite.getUploader(metadata);
121
+ await drainUpload(await uploader.uploadFromStream(stream, []));
122
+
123
+ suite.expect('small');
124
+ });
125
+
126
+ it('uses small-file path when flag is on and encrypted total size with thumbnails is below cap', async () => {
127
+ featureFlagProvider.isEnabled.mockResolvedValue(true);
128
+
129
+ const metadata: UploadMetadata = { expectedSize: 100, mediaType: 'image/jpeg' };
130
+ const uploader = await suite.getUploader(metadata);
131
+ await drainUpload(await uploader.uploadFromStream(stream, [thumbnail100k]));
132
+
133
+ suite.expect('small');
134
+ });
135
+
136
+ it('uses stream path when feature flag is enabled but raw file size exceeds limit', async () => {
137
+ featureFlagProvider.isEnabled.mockResolvedValue(true);
138
+
139
+ const metadata: UploadMetadata = { expectedSize: RAW_SMALL_FILE_SIZE_LIMIT, mediaType: 'text/plain' };
140
+ const uploader = await suite.getUploader(metadata);
141
+ await drainUpload(await uploader.uploadFromStream(stream, []));
142
+
143
+ suite.expect('stream');
144
+ });
145
+
146
+ it('uses stream path when thumbnail bytes push encrypted total size with thumbnail exceeds limit', async () => {
147
+ featureFlagProvider.isEnabled.mockResolvedValue(true);
148
+
149
+ const metadata: UploadMetadata = { expectedSize: 100_000, mediaType: 'image/jpeg' };
150
+ const uploader = await suite.getUploader(metadata);
151
+ await drainUpload(await uploader.uploadFromStream(stream, [thumbnail100k]));
152
+
153
+ suite.expect('stream');
154
+ });
97
155
  });
98
- });
156
+ }
99
157
  });
@@ -8,7 +8,6 @@ import { FileUploader as FileUploaderClass, FileRevisionUploader } from './fileU
8
8
  import { NodesService, SharesService } from './interface';
9
9
  import { UploadManager } from './manager';
10
10
  import { UploadQueue } from './queue';
11
- import { SmallFileRevisionUploader, SmallFileUploader } from './smallFileUploader';
12
11
  import { UploadTelemetry } from './telemetry';
13
12
 
14
13
  const SMALL_FILE_SIZE_LIMIT = 128 * 1024; // 128 KiB
@@ -38,13 +37,13 @@ export function initUploadModule(
38
37
 
39
38
  const queue = new UploadQueue();
40
39
 
41
- async function useSmallFileUpload(metadata: UploadMetadata): Promise<boolean> {
40
+ async function shouldUseSmallFileUpload(expectedSize: number): Promise<boolean> {
42
41
  const isEnabled =
43
42
  allowSmallFileUpload && (await featureFlagProvider.isEnabled(FeatureFlags.DriveSmallFileUpload));
44
43
  if (!isEnabled) {
45
44
  return false;
46
45
  }
47
- return metadata.expectedSize < SMALL_FILE_SIZE_LIMIT;
46
+ return expectedSize < SMALL_FILE_SIZE_LIMIT;
48
47
  }
49
48
 
50
49
  /**
@@ -66,20 +65,6 @@ export function initUploadModule(
66
65
  queue.releaseCapacity(metadata.expectedSize);
67
66
  };
68
67
 
69
- if (await useSmallFileUpload(metadata)) {
70
- return new SmallFileUploader(
71
- uploadTelemetry,
72
- api,
73
- cryptoService,
74
- manager,
75
- metadata,
76
- onFinish,
77
- signal,
78
- parentFolderUid,
79
- name,
80
- );
81
- }
82
-
83
68
  return new FileUploaderClass(
84
69
  uploadTelemetry,
85
70
  api,
@@ -89,6 +74,7 @@ export function initUploadModule(
89
74
  name,
90
75
  metadata,
91
76
  onFinish,
77
+ shouldUseSmallFileUpload,
92
78
  signal,
93
79
  );
94
80
  }
@@ -111,19 +97,6 @@ export function initUploadModule(
111
97
  queue.releaseCapacity(metadata.expectedSize);
112
98
  };
113
99
 
114
- if (await useSmallFileUpload(metadata)) {
115
- return new SmallFileRevisionUploader(
116
- uploadTelemetry,
117
- api,
118
- cryptoService,
119
- manager,
120
- metadata,
121
- onFinish,
122
- signal,
123
- nodeUid,
124
- );
125
- }
126
-
127
100
  return new FileRevisionUploader(
128
101
  uploadTelemetry,
129
102
  api,
@@ -132,6 +105,7 @@ export function initUploadModule(
132
105
  nodeUid,
133
106
  metadata,
134
107
  onFinish,
108
+ shouldUseSmallFileUpload,
135
109
  signal,
136
110
  );
137
111
  }
@@ -6,9 +6,10 @@ import { UploadAPIService } from './apiService';
6
6
  import { UploadCryptoService } from './cryptoService';
7
7
  import { UploadManager } from './manager';
8
8
  import { NodeCrypto } from './interface';
9
+ import { mergeUint8Arrays } from '../utils';
9
10
 
10
- const MOCK_BLOCK_HASH = new Uint8Array(32).fill(1);
11
- const MOCK_VERIFICATION_TOKEN = new Uint8Array(16).fill(2);
11
+ const MOCK_BLOCK_HASH = new Uint8Array(32).fill(4);
12
+ const MOCK_VERIFICATION_TOKEN = new Uint8Array(16).fill(5);
12
13
 
13
14
  function createStream(bytes: number[]): ReadableStream<Uint8Array> {
14
15
  return new ReadableStream({
@@ -108,7 +109,7 @@ describe('SmallFileUploader', () => {
108
109
  encryptedData: new Uint8Array(thumbnail.thumbnail),
109
110
  originalSize: thumbnail.thumbnail.length,
110
111
  encryptedSize: thumbnail.thumbnail.length + 100,
111
- hash: 'thumbnailHash',
112
+ hashPromise: Promise.resolve(new Uint8Array(32).fill(thumbnail.type)),
112
113
  })),
113
114
  encryptBlock: jest.fn().mockImplementation(mockEncryptBlock),
114
115
  verifyBlock: jest.fn().mockResolvedValue({ verificationToken: MOCK_VERIFICATION_TOKEN }),
@@ -138,7 +139,6 @@ describe('SmallFileUploader', () => {
138
139
  function createUploader() {
139
140
  return new SmallFileUploader(
140
141
  telemetry,
141
- apiService,
142
142
  cryptoService,
143
143
  uploadManager,
144
144
  metadata,
@@ -157,24 +157,13 @@ describe('SmallFileUploader', () => {
157
157
  const uploader = createUploader();
158
158
  const stream = createStream([1, 2, 3]);
159
159
 
160
- const controller = await uploader.uploadFromStream(stream, thumbnails, onProgress);
161
- const result = await controller.completion();
160
+ const result = await uploader.upload(stream, thumbnails, onProgress);
162
161
 
163
162
  expect(uploadManager.generateNewFileCrypto).toHaveBeenCalledWith(parentFolderUid, name);
164
163
  expect(uploadManager.uploadFile).toHaveBeenCalledTimes(1);
165
164
  expect(result).toEqual({ nodeUid: 'nodeUid', nodeRevisionUid: 'nodeRevisionUid' });
166
165
  expect(onProgress).toHaveBeenCalledWith(metadata.expectedSize);
167
166
  });
168
-
169
- it('should throw if upload already started', async () => {
170
- const uploader = createUploader();
171
- const stream = createStream([1, 2, 3]);
172
-
173
- await uploader.uploadFromStream(stream, thumbnails, onProgress);
174
- await expect(uploader.uploadFromStream(stream, thumbnails, onProgress)).rejects.toThrow(
175
- 'Upload already started',
176
- );
177
- });
178
167
  });
179
168
 
180
169
  describe('buildPayloads (via upload flow)', () => {
@@ -186,8 +175,7 @@ describe('SmallFileUploader', () => {
186
175
  { type: ThumbnailType.Type2, thumbnail: new Uint8Array([30, 40, 50]) },
187
176
  ];
188
177
 
189
- await uploader.uploadFromStream(stream, thumbnails, undefined);
190
- await (uploader as any).controller.completion();
178
+ await uploader.upload(stream, thumbnails, undefined);
191
179
 
192
180
  expect(uploadManager.uploadFile).toHaveBeenCalledWith(
193
181
  parentFolderUid,
@@ -203,8 +191,16 @@ describe('SmallFileUploader', () => {
203
191
  verificationToken: MOCK_VERIFICATION_TOKEN,
204
192
  }),
205
193
  [
206
- { type: ThumbnailType.Type1, encryptedData: expect.any(Uint8Array) },
207
- { type: ThumbnailType.Type2, encryptedData: expect.any(Uint8Array) },
194
+ {
195
+ type: ThumbnailType.Type1,
196
+ encryptedData: expect.any(Uint8Array),
197
+ blockHash: new Uint8Array(32).fill(ThumbnailType.Type1),
198
+ },
199
+ {
200
+ type: ThumbnailType.Type2,
201
+ encryptedData: expect.any(Uint8Array),
202
+ blockHash: new Uint8Array(32).fill(ThumbnailType.Type2),
203
+ },
208
204
  ],
209
205
  );
210
206
 
@@ -212,7 +208,11 @@ describe('SmallFileUploader', () => {
212
208
  expect(cryptoService.encryptThumbnail).toHaveBeenCalledTimes(2);
213
209
  expect(cryptoService.commitFile).toHaveBeenCalledWith(
214
210
  expect.anything(),
215
- MOCK_BLOCK_HASH,
211
+ mergeUint8Arrays([
212
+ new Uint8Array(32).fill(ThumbnailType.Type1),
213
+ new Uint8Array(32).fill(ThumbnailType.Type2),
214
+ MOCK_BLOCK_HASH,
215
+ ]),
216
216
  expect.any(String),
217
217
  );
218
218
  });
@@ -223,8 +223,7 @@ describe('SmallFileUploader', () => {
223
223
  metadata.expectedSize = content.length;
224
224
  const stream = createStream(content);
225
225
 
226
- await uploader.uploadFromStream(stream, [], undefined);
227
- await (uploader as any).controller.completion();
226
+ await uploader.upload(stream, [], undefined);
228
227
 
229
228
  expect(cryptoService.encryptBlock).toHaveBeenCalledWith(
230
229
  expect.any(Function),
@@ -241,8 +240,7 @@ describe('SmallFileUploader', () => {
241
240
  ];
242
241
  const stream = createStream([1, 2, 3]);
243
242
 
244
- await uploader.uploadFromStream(stream, thumbnails, undefined);
245
- await (uploader as any).controller.completion();
243
+ await uploader.upload(stream, thumbnails, undefined);
246
244
 
247
245
  expect(cryptoService.encryptThumbnail).toHaveBeenCalledWith(
248
246
  expect.objectContaining({
@@ -259,8 +257,7 @@ describe('SmallFileUploader', () => {
259
257
  const uploader = createUploader();
260
258
  const stream = createStream([1, 2, 3]);
261
259
 
262
- await uploader.uploadFromStream(stream, [], undefined);
263
- await (uploader as any).controller.completion();
260
+ await uploader.upload(stream, [], undefined);
264
261
 
265
262
  const [nodeKeys, manifest, extendedAttributes] = (cryptoService.commitFile as jest.Mock).mock.calls[0];
266
263
  expect(manifest).toEqual(MOCK_BLOCK_HASH);
@@ -275,10 +272,10 @@ describe('SmallFileUploader', () => {
275
272
  metadata.expectedSize = 5;
276
273
  const stream = createStream([1, 2, 3]); // only 3 bytes
277
274
 
278
- const controller = await uploader.uploadFromStream(stream, [], undefined);
275
+ const promise = uploader.upload(stream, [], undefined);
279
276
 
280
- await expect(controller.completion()).rejects.toThrow(IntegrityError);
281
- await expect(controller.completion()).rejects.toMatchObject({
277
+ await expect(promise).rejects.toThrow(IntegrityError);
278
+ await expect(promise).rejects.toMatchObject({
282
279
  debug: { actual: 3, expected: 5 },
283
280
  });
284
281
  });
@@ -288,10 +285,10 @@ describe('SmallFileUploader', () => {
288
285
  metadata.expectedSha1 = 'a'.repeat(40); // wrong sha1
289
286
  const stream = createStream([1, 2, 3]);
290
287
 
291
- const controller = await uploader.uploadFromStream(stream, [], undefined);
288
+ const promise = uploader.upload(stream, [], undefined);
292
289
 
293
- await expect(controller.completion()).rejects.toThrow(IntegrityError);
294
- await expect(controller.completion()).rejects.toMatchObject({
290
+ await expect(promise).rejects.toThrow(IntegrityError);
291
+ await expect(promise).rejects.toMatchObject({
295
292
  debug: expect.objectContaining({
296
293
  expectedSha1: 'a'.repeat(40),
297
294
  }),
@@ -306,8 +303,7 @@ describe('SmallFileUploader', () => {
306
303
  const stream = createStream([]);
307
304
  const onProgress = jest.fn();
308
305
 
309
- const controller = await uploader.uploadFromStream(stream, [], onProgress);
310
- const result = await controller.completion();
306
+ const result = await uploader.upload(stream, [], onProgress);
311
307
 
312
308
  expect(result).toEqual({ nodeUid: 'nodeUid', nodeRevisionUid: 'nodeRevisionUid' });
313
309
  expect(cryptoService.encryptBlock).not.toHaveBeenCalled();
@@ -430,7 +426,6 @@ describe('SmallFileRevisionUploader', () => {
430
426
  function createUploader() {
431
427
  return new SmallFileRevisionUploader(
432
428
  telemetry,
433
- apiService,
434
429
  cryptoService,
435
430
  uploadManager,
436
431
  metadata,
@@ -444,8 +439,7 @@ describe('SmallFileRevisionUploader', () => {
444
439
  const uploader = createUploader();
445
440
  const stream = createStream([1, 2, 3]);
446
441
 
447
- const controller = await uploader.uploadFromStream(stream, [], undefined);
448
- const result = await controller.completion();
442
+ const result = await uploader.upload(stream, [], undefined);
449
443
 
450
444
  expect(result).toEqual({ nodeUid: 'nodeUid', nodeRevisionUid: 'nodeRevisionUid' });
451
445
  expect(cryptoService.encryptBlock).toHaveBeenCalledWith(expect.any(Function), expect.anything(), Uint8Array.from([1, 2, 3]), 0);
@@ -471,8 +465,7 @@ describe('SmallFileRevisionUploader', () => {
471
465
  const uploader = createUploader();
472
466
  const stream = createStream([]);
473
467
 
474
- const controller = await uploader.uploadFromStream(stream, [], undefined);
475
- const result = await controller.completion();
468
+ const result = await uploader.upload(stream, [], undefined);
476
469
 
477
470
  expect(result).toEqual({ nodeUid: 'nodeUid', nodeRevisionUid: 'nodeRevisionUid' });
478
471
  expect(cryptoService.encryptBlock).not.toHaveBeenCalled();