@toa.io/extensions.storages 0.22.0 → 0.23.0-dev.0
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.
- package/package.json +9 -5
- package/readme.md +27 -12
- package/source/Scanner.ts +41 -17
- package/source/Storage.test.ts +312 -289
- package/source/Storage.ts +6 -4
- package/source/deployment.ts +4 -0
- package/source/index.ts +2 -0
- package/source/manifest.ts +2 -2
- package/source/providers/S3.ts +91 -0
- package/source/providers/index.test.ts +14 -9
- package/source/providers/index.ts +3 -1
- package/source/providers/readme.md +6 -1
- package/source/test/.env.example +1 -0
- package/source/test/util.ts +74 -0
- package/source/.test/util.ts +0 -50
- /package/source/{.test → test}/albert.jpg +0 -0
- /package/source/{.test → test}/empty.txt +0 -0
- /package/source/{.test → test}/lenna.ascii +0 -0
- /package/source/{.test → test}/lenna.png +0 -0
- /package/source/{.test → test}/sample.avif +0 -0
- /package/source/{.test → test}/sample.gif +0 -0
- /package/source/{.test → test}/sample.heic +0 -0
- /package/source/{.test → test}/sample.jpeg +0 -0
- /package/source/{.test → test}/sample.jxl +0 -0
- /package/source/{.test → test}/sample.webp +0 -0
package/source/Storage.test.ts
CHANGED
|
@@ -1,445 +1,468 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
|
-
import { match } from '
|
|
2
|
+
import { match } from 'matchacho'
|
|
3
3
|
import { buffer } from '@toa.io/generic'
|
|
4
4
|
import { Storage } from './Storage'
|
|
5
|
-
import { cases, open, rnd } from '
|
|
5
|
+
import { cases, open, rnd } from './test/util'
|
|
6
6
|
import { type Entry } from './Entry'
|
|
7
7
|
import { providers } from './providers'
|
|
8
|
+
import type { ErrorType } from 'error-value'
|
|
8
9
|
|
|
9
10
|
let storage: Storage
|
|
10
11
|
let dir: string
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
dir = '/' + rnd()
|
|
13
|
+
const [, url, secrets] = cases[0]
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
dir = '/' + rnd()
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const Provider = providers[url.protocol]
|
|
19
|
+
const provider = new Provider(url, secrets)
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
})
|
|
21
|
+
storage = new Storage(provider)
|
|
22
|
+
})
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
it('should be', async () => {
|
|
25
|
+
expect(storage).toBeInstanceOf(Storage)
|
|
26
|
+
})
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
})
|
|
28
|
+
it('should return error if entry is not found', async () => {
|
|
29
|
+
const result = await storage.get('not-found')
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if (!(result instanceof Error))
|
|
32
|
+
throw new Error('Expected error')
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
expect(result).toMatchObject({ code: 'NOT_FOUND' })
|
|
35
|
+
})
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
describe('put', () => {
|
|
38
|
+
let lenna: Entry
|
|
39
|
+
let startCreation: number
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
})
|
|
41
|
+
beforeEach(async () => {
|
|
42
|
+
const stream = open('lenna.png')
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
startCreation = Date.now()
|
|
45
|
+
lenna = await storage.put(dir, stream) as Entry
|
|
46
|
+
})
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const copy = await storage.put(dir2, stream) as Entry
|
|
48
|
+
it('should not return error', async () => {
|
|
49
|
+
expect(lenna).not.toBeInstanceOf(Error)
|
|
50
|
+
})
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
it('should return entry id', async () => {
|
|
53
|
+
expect(lenna.id).toBeDefined()
|
|
54
|
+
})
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
it('should return id as checksum', async () => {
|
|
57
|
+
const stream = open('lenna.png')
|
|
58
|
+
const dir2 = '/' + rnd()
|
|
59
|
+
const copy = await storage.put(dir2, stream) as Entry
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
expect(copy.id).toBe(lenna.id)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should detect file type', async () => {
|
|
65
|
+
expect(lenna.type).toBe('image/png')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should count size', async () => {
|
|
69
|
+
expect(lenna.size).toBe(473831)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should return entry', async () => {
|
|
73
|
+
expect(lenna).toMatchObject({
|
|
74
|
+
id: lenna.id,
|
|
75
|
+
type: 'image/png',
|
|
76
|
+
variants: [],
|
|
77
|
+
meta: {}
|
|
64
78
|
})
|
|
79
|
+
})
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
it('should create entry', async () => {
|
|
82
|
+
const entry = await storage.get(`${dir}/${lenna.id}`)
|
|
83
|
+
|
|
84
|
+
match(entry,
|
|
85
|
+
{
|
|
68
86
|
id: lenna.id,
|
|
69
87
|
type: 'image/png',
|
|
70
88
|
variants: [],
|
|
71
89
|
meta: {}
|
|
72
|
-
})
|
|
73
|
-
|
|
90
|
+
}, undefined)
|
|
91
|
+
})
|
|
74
92
|
|
|
75
|
-
|
|
76
|
-
|
|
93
|
+
it('should set timestamp', async () => {
|
|
94
|
+
const now = Date.now()
|
|
95
|
+
const entry = await storage.get(`${dir}/${lenna.id}`) as Entry
|
|
77
96
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
type: 'image/png',
|
|
82
|
-
variants: [],
|
|
83
|
-
meta: {}
|
|
84
|
-
}, undefined)
|
|
85
|
-
})
|
|
97
|
+
expect(entry.created).toBeLessThanOrEqual(now)
|
|
98
|
+
expect(entry.created).toBeGreaterThanOrEqual(startCreation)
|
|
99
|
+
})
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
101
|
+
describe('existing entry', () => {
|
|
102
|
+
it('should unhide existing', async () => {
|
|
103
|
+
const stream = open('lenna.png')
|
|
104
|
+
const path = `${dir}/${lenna.id}`
|
|
90
105
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
106
|
+
await storage.conceal(path)
|
|
107
|
+
await storage.put(dir, stream)
|
|
108
|
+
|
|
109
|
+
const list = await storage.list(dir)
|
|
94
110
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const stream = await open('lenna.png')
|
|
98
|
-
const path = `${dir}/${lenna.id}`
|
|
111
|
+
expect(list).toContainEqual(lenna.id)
|
|
112
|
+
})
|
|
99
113
|
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
it('should preserve meta', async () => {
|
|
115
|
+
const path = `${dir}/${lenna.id}`
|
|
116
|
+
const stream = open('lenna.png')
|
|
102
117
|
|
|
103
|
-
|
|
118
|
+
await storage.annotate(path, 'foo', 'bar')
|
|
119
|
+
await storage.put(dir, stream)
|
|
104
120
|
|
|
105
|
-
|
|
106
|
-
})
|
|
121
|
+
const entry = await storage.get(path) as Entry
|
|
107
122
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
expect(entry.meta).toMatchObject({ foo: 'bar' })
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|
|
111
127
|
|
|
112
|
-
|
|
113
|
-
|
|
128
|
+
describe('list', () => {
|
|
129
|
+
let albert: Entry
|
|
130
|
+
let lenna: Entry
|
|
114
131
|
|
|
115
|
-
|
|
132
|
+
beforeEach(async () => {
|
|
133
|
+
const stream0 = open('albert.jpg')
|
|
134
|
+
const stream1 = open('lenna.png')
|
|
116
135
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
})
|
|
136
|
+
albert = await storage.put(dir, stream0) as Entry
|
|
137
|
+
lenna = await storage.put(dir, stream1) as Entry
|
|
120
138
|
})
|
|
121
139
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
let lenna: Entry
|
|
140
|
+
it('should list entries', async () => {
|
|
141
|
+
const list = await storage.list(dir)
|
|
125
142
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const stream1 = await open('lenna.png')
|
|
143
|
+
expect(list).toMatchObject([albert.id, lenna.id])
|
|
144
|
+
})
|
|
129
145
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
})
|
|
146
|
+
it('should permutate', async () => {
|
|
147
|
+
const error = await storage.permute(dir, [lenna.id, albert.id])
|
|
133
148
|
|
|
134
|
-
|
|
135
|
-
const list = await storage.list(dir)
|
|
149
|
+
expect(error).toBeUndefined()
|
|
136
150
|
|
|
137
|
-
|
|
138
|
-
})
|
|
151
|
+
const list = await storage.list(dir)
|
|
139
152
|
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
expect(list).toMatchObject([lenna.id, albert.id])
|
|
154
|
+
})
|
|
142
155
|
|
|
143
|
-
|
|
156
|
+
it('should return PERMUTATION_MISMATCH', async () => {
|
|
157
|
+
const cases = [
|
|
158
|
+
[lenna.id],
|
|
159
|
+
[albert.id, lenna.id, 'unknown'],
|
|
160
|
+
[lenna.id, lenna.id],
|
|
161
|
+
[lenna.id, lenna.id, albert.id]
|
|
162
|
+
]
|
|
144
163
|
|
|
145
|
-
|
|
164
|
+
for (const permutation of cases) {
|
|
165
|
+
const error = await storage.permute(dir, permutation)
|
|
146
166
|
|
|
147
|
-
expect(
|
|
148
|
-
|
|
167
|
+
expect(error).toBeInstanceOf(Error)
|
|
168
|
+
expect(error).toMatchObject({ code: 'PERMUTATION_MISMATCH' })
|
|
169
|
+
}
|
|
170
|
+
})
|
|
149
171
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
[lenna.id],
|
|
153
|
-
[albert.id, lenna.id, 'unknown'],
|
|
154
|
-
[lenna.id, lenna.id],
|
|
155
|
-
[lenna.id, lenna.id, albert.id]
|
|
156
|
-
]
|
|
172
|
+
it('should exclude concealed', async () => {
|
|
173
|
+
const path = `${dir}/${lenna.id}`
|
|
157
174
|
|
|
158
|
-
|
|
159
|
-
const error = await storage.reorder(dir, permutation)
|
|
175
|
+
await storage.conceal(path)
|
|
160
176
|
|
|
161
|
-
|
|
162
|
-
expect(error).toMatchObject({ message: 'PERMUTATION_MISMATCH' })
|
|
163
|
-
}
|
|
164
|
-
})
|
|
177
|
+
const entries = await storage.list(dir)
|
|
165
178
|
|
|
166
|
-
|
|
167
|
-
|
|
179
|
+
expect(entries).toMatchObject([albert.id])
|
|
180
|
+
})
|
|
168
181
|
|
|
169
|
-
|
|
182
|
+
it('should reveal', async () => {
|
|
183
|
+
const path = `${dir}/${lenna.id}`
|
|
170
184
|
|
|
171
|
-
|
|
185
|
+
await storage.conceal(path)
|
|
186
|
+
await storage.reveal(path)
|
|
187
|
+
await storage.reveal(path) // test that no duplicates are created
|
|
172
188
|
|
|
173
|
-
|
|
174
|
-
})
|
|
189
|
+
const entries = await storage.list(dir)
|
|
175
190
|
|
|
176
|
-
|
|
177
|
-
|
|
191
|
+
expect(entries).toMatchObject([albert.id, lenna.id])
|
|
192
|
+
})
|
|
178
193
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
await storage.reveal(path) // test that no duplicates are created
|
|
194
|
+
it('should return ERR_NOT_FOOUD if entry doesnt exist', async () => {
|
|
195
|
+
const path = `${dir}/oopsie`
|
|
182
196
|
|
|
183
|
-
|
|
197
|
+
const methods: Array<'reveal' | 'conceal'> = ['reveal', 'conceal']
|
|
184
198
|
|
|
185
|
-
|
|
186
|
-
|
|
199
|
+
for (const method of methods) {
|
|
200
|
+
const error = await storage[method](path)
|
|
187
201
|
|
|
188
|
-
|
|
189
|
-
|
|
202
|
+
expect(error).toBeInstanceOf(Error)
|
|
203
|
+
expect(error).toMatchObject({ code: 'NOT_FOUND' })
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
})
|
|
190
207
|
|
|
191
|
-
|
|
208
|
+
describe('annotate', () => {
|
|
209
|
+
let lenna: Entry
|
|
192
210
|
|
|
193
|
-
|
|
194
|
-
|
|
211
|
+
beforeEach(async () => {
|
|
212
|
+
const stream = open('lenna.png')
|
|
195
213
|
|
|
196
|
-
|
|
197
|
-
expect(error).toMatchObject({ message: 'NOT_FOUND' })
|
|
198
|
-
}
|
|
199
|
-
})
|
|
214
|
+
lenna = await storage.put(dir, stream) as Entry
|
|
200
215
|
})
|
|
201
216
|
|
|
202
|
-
|
|
203
|
-
|
|
217
|
+
it('should set meta', async () => {
|
|
218
|
+
const path = `${dir}/${lenna.id}`
|
|
204
219
|
|
|
205
|
-
|
|
206
|
-
const stream = await open('lenna.png')
|
|
220
|
+
await storage.annotate(path, 'foo', 'bar')
|
|
207
221
|
|
|
208
|
-
|
|
209
|
-
})
|
|
222
|
+
const state0 = await storage.get(path) as Entry
|
|
210
223
|
|
|
211
|
-
|
|
212
|
-
const path = `${dir}/${lenna.id}`
|
|
224
|
+
expect(state0.meta).toMatchObject({ foo: 'bar' })
|
|
213
225
|
|
|
214
|
-
|
|
226
|
+
await storage.annotate(path, 'foo')
|
|
215
227
|
|
|
216
|
-
|
|
228
|
+
const state1 = await storage.get(path) as Entry
|
|
217
229
|
|
|
218
|
-
|
|
230
|
+
expect('foo' in state1.meta).toBe(false)
|
|
231
|
+
})
|
|
232
|
+
})
|
|
219
233
|
|
|
220
|
-
|
|
234
|
+
describe('variants', () => {
|
|
235
|
+
let lenna: Entry
|
|
221
236
|
|
|
222
|
-
|
|
237
|
+
beforeEach(async () => {
|
|
238
|
+
const stream = open('lenna.png')
|
|
223
239
|
|
|
224
|
-
|
|
225
|
-
})
|
|
240
|
+
lenna = await storage.put(dir, stream) as Entry
|
|
226
241
|
})
|
|
227
242
|
|
|
228
|
-
|
|
229
|
-
|
|
243
|
+
it('should add variant', async () => {
|
|
244
|
+
const stream = open('sample.jpeg')
|
|
230
245
|
|
|
231
|
-
|
|
232
|
-
const stream = await open('lenna.png')
|
|
246
|
+
const path = `${dir}/${lenna.id}`
|
|
233
247
|
|
|
234
|
-
|
|
235
|
-
})
|
|
248
|
+
await storage.diversify(path, 'foo', stream)
|
|
236
249
|
|
|
237
|
-
|
|
238
|
-
const stream = await open('sample.jpeg')
|
|
250
|
+
const state = await storage.get(path) as Entry
|
|
239
251
|
|
|
240
|
-
|
|
252
|
+
expect(state.variants).toMatchObject([{ name: 'foo', size: 73444, type: 'image/jpeg' }])
|
|
253
|
+
})
|
|
241
254
|
|
|
242
|
-
|
|
255
|
+
it('should replace variant', async () => {
|
|
256
|
+
const stream0 = open('sample.jpeg')
|
|
257
|
+
const stream1 = open('sample.webp')
|
|
258
|
+
const path = `${dir}/${lenna.id}`
|
|
243
259
|
|
|
244
|
-
|
|
260
|
+
await storage.diversify(path, 'foo', stream0)
|
|
261
|
+
await storage.diversify(path, 'foo', stream1)
|
|
245
262
|
|
|
246
|
-
|
|
247
|
-
})
|
|
263
|
+
const state = await storage.get(path) as Entry
|
|
248
264
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const path = `${dir}/${lenna.id}`
|
|
265
|
+
expect(state.variants).toMatchObject([{ name: 'foo', type: 'image/webp' }])
|
|
266
|
+
})
|
|
267
|
+
})
|
|
253
268
|
|
|
254
|
-
|
|
255
|
-
|
|
269
|
+
describe('fetch', () => {
|
|
270
|
+
let lenna: Entry
|
|
256
271
|
|
|
257
|
-
|
|
272
|
+
beforeEach(async () => {
|
|
273
|
+
const stream = open('lenna.png')
|
|
258
274
|
|
|
259
|
-
|
|
260
|
-
})
|
|
275
|
+
lenna = await storage.put(dir, stream) as Entry
|
|
261
276
|
})
|
|
262
277
|
|
|
263
|
-
|
|
264
|
-
|
|
278
|
+
it('should fetch', async () => {
|
|
279
|
+
const path = `${dir}/${lenna.id}`
|
|
280
|
+
const stream = await storage.fetch(path)
|
|
265
281
|
|
|
266
|
-
|
|
267
|
-
|
|
282
|
+
const stored: Buffer = await match(stream,
|
|
283
|
+
Readable, async (stream: Readable) => await buffer(stream))
|
|
268
284
|
|
|
269
|
-
|
|
270
|
-
})
|
|
285
|
+
const buf = await buffer(open('lenna.png'))
|
|
271
286
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const stream = await storage.fetch(path)
|
|
287
|
+
expect(stored.compare(buf)).toBe(0)
|
|
288
|
+
})
|
|
275
289
|
|
|
276
|
-
|
|
277
|
-
|
|
290
|
+
it('should fetch blob by id', async () => {
|
|
291
|
+
const stream = open('lenna.ascii')
|
|
292
|
+
const entry = await storage.put(dir, stream) as Entry
|
|
293
|
+
const stored = await storage.fetch(entry.id)
|
|
278
294
|
|
|
279
|
-
|
|
295
|
+
if (stored instanceof Error)
|
|
296
|
+
throw stored
|
|
280
297
|
|
|
281
|
-
|
|
282
|
-
|
|
298
|
+
const buf = await buffer(stored)
|
|
299
|
+
const expected = await buffer(open('lenna.ascii'))
|
|
283
300
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const entry = await storage.put(dir, stream) as Entry
|
|
287
|
-
const stored = await storage.fetch(entry.id)
|
|
301
|
+
expect(buf.compare(expected)).toBe(0)
|
|
302
|
+
})
|
|
288
303
|
|
|
289
|
-
|
|
290
|
-
|
|
304
|
+
it('should fetch variant', async () => {
|
|
305
|
+
const stream = open('sample.jpeg')
|
|
291
306
|
|
|
292
|
-
|
|
293
|
-
|
|
307
|
+
const buf = await buffer(stream)
|
|
308
|
+
const path = `${dir}/${lenna.id}`
|
|
294
309
|
|
|
295
|
-
|
|
296
|
-
})
|
|
310
|
+
await storage.diversify(path, '100x100.jpeg', Readable.from(buf))
|
|
297
311
|
|
|
298
|
-
|
|
299
|
-
const stream = await open('sample.jpeg')
|
|
312
|
+
const variant = await storage.fetch(`${path}.100x100.jpeg`)
|
|
300
313
|
|
|
301
|
-
|
|
302
|
-
|
|
314
|
+
const stored = await match<Promise<Buffer>>(variant,
|
|
315
|
+
Readable, async (stream: Readable) => await buffer(stream))
|
|
303
316
|
|
|
304
|
-
|
|
317
|
+
expect(stored.compare(buf)).toBe(0)
|
|
318
|
+
})
|
|
305
319
|
|
|
306
|
-
|
|
320
|
+
it('should not fetch blob by id and fake path', async () => {
|
|
321
|
+
const stored = await storage.fetch(`fake/${lenna.id}`)
|
|
307
322
|
|
|
308
|
-
|
|
309
|
-
|
|
323
|
+
match(stored,
|
|
324
|
+
Error, (error: ErrorType) => expect(error.code).toBe('NOT_FOUND'))
|
|
325
|
+
})
|
|
326
|
+
})
|
|
310
327
|
|
|
311
|
-
|
|
312
|
-
|
|
328
|
+
describe('delete', () => {
|
|
329
|
+
let lenna: Entry
|
|
313
330
|
|
|
314
|
-
|
|
315
|
-
|
|
331
|
+
beforeEach(async () => {
|
|
332
|
+
const stream = open('lenna.png')
|
|
316
333
|
|
|
317
|
-
|
|
318
|
-
Error, (error: Error) => expect(error.message).toBe('NOT_FOUND'))
|
|
319
|
-
})
|
|
334
|
+
lenna = await storage.put(dir, stream) as Entry
|
|
320
335
|
})
|
|
321
336
|
|
|
322
|
-
|
|
323
|
-
|
|
337
|
+
it('should remove from the list', async () => {
|
|
338
|
+
await storage.delete(`${dir}/${lenna.id}`)
|
|
324
339
|
|
|
325
|
-
|
|
326
|
-
const stream = await open('lenna.png')
|
|
340
|
+
const list = await storage.list(dir)
|
|
327
341
|
|
|
328
|
-
|
|
329
|
-
|
|
342
|
+
expect(list).not.toContain(lenna.id)
|
|
343
|
+
})
|
|
330
344
|
|
|
331
|
-
|
|
332
|
-
|
|
345
|
+
it('should delete entry', async () => {
|
|
346
|
+
await storage.delete(`${dir}/${lenna.id}`)
|
|
333
347
|
|
|
334
|
-
|
|
348
|
+
const result = await storage.get(`${dir}/${lenna.id}`)
|
|
335
349
|
|
|
336
|
-
|
|
337
|
-
|
|
350
|
+
match(result,
|
|
351
|
+
Error, (error: ErrorType) => expect(error.code).toBe('NOT_FOUND'))
|
|
352
|
+
})
|
|
338
353
|
|
|
339
|
-
|
|
340
|
-
|
|
354
|
+
it('should delete variants', async () => {
|
|
355
|
+
const stream = open('sample.jpeg')
|
|
341
356
|
|
|
342
|
-
|
|
357
|
+
const path = `${dir}/${lenna.id}`
|
|
343
358
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
})
|
|
359
|
+
await storage.diversify(path, 'foo', stream)
|
|
360
|
+
await storage.delete(`${dir}/${lenna.id}`)
|
|
347
361
|
|
|
348
|
-
|
|
349
|
-
const stream = await open('sample.jpeg')
|
|
362
|
+
const variant = await storage.fetch(`${path}.foo`)
|
|
350
363
|
|
|
351
|
-
|
|
364
|
+
match(variant,
|
|
365
|
+
Error, (error: ErrorType) => expect(error.code).toBe('NOT_FOUND'))
|
|
352
366
|
|
|
353
|
-
|
|
354
|
-
|
|
367
|
+
stream.destroy()
|
|
368
|
+
})
|
|
355
369
|
|
|
356
|
-
|
|
370
|
+
it('should throw if path is not an entry', async () => {
|
|
371
|
+
const result = await storage.delete(dir)
|
|
357
372
|
|
|
358
|
-
|
|
359
|
-
|
|
373
|
+
expect(result).toBeInstanceOf(Error)
|
|
374
|
+
expect(result).toMatchObject({ code: 'NOT_FOUND' })
|
|
375
|
+
})
|
|
376
|
+
})
|
|
360
377
|
|
|
361
|
-
|
|
362
|
-
|
|
378
|
+
describe('signatures', () => {
|
|
379
|
+
it.each(['jpeg', 'gif', 'webp', 'heic', 'jxl', 'avif'])('should detect image/%s',
|
|
380
|
+
async (type) => {
|
|
381
|
+
const stream = open('sample.' + type)
|
|
363
382
|
|
|
364
|
-
|
|
365
|
-
const result = await storage.delete(dir)
|
|
383
|
+
const entry = await storage.put(dir, stream) as Entry
|
|
366
384
|
|
|
367
|
-
expect(
|
|
368
|
-
expect(result).toMatchObject({ message: 'NOT_FOUND' })
|
|
385
|
+
expect(entry.type).toBe('image/' + type)
|
|
369
386
|
})
|
|
370
|
-
|
|
387
|
+
})
|
|
371
388
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
async (type) => {
|
|
375
|
-
const stream = await open('sample.' + type)
|
|
389
|
+
it('should return error if type doesnt match', async () => {
|
|
390
|
+
const stream = open('sample.jpeg')
|
|
376
391
|
|
|
377
|
-
|
|
392
|
+
const result = await storage.put(dir, stream, { claim: 'image/png' })
|
|
378
393
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
394
|
+
match(result,
|
|
395
|
+
Error, (error: ErrorType) => expect(error.code).toBe('TYPE_MISMATCH'))
|
|
396
|
+
})
|
|
382
397
|
|
|
383
|
-
|
|
384
|
-
|
|
398
|
+
it('should trust unknown types', async () => {
|
|
399
|
+
const stream = open('lenna.ascii')
|
|
385
400
|
|
|
386
|
-
|
|
401
|
+
const result = await storage.put(dir, stream, { claim: 'text/plain' })
|
|
387
402
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
403
|
+
expect(result).not.toBeInstanceOf(Error)
|
|
404
|
+
expect(result).toMatchObject({ type: 'text/plain' })
|
|
405
|
+
})
|
|
391
406
|
|
|
392
|
-
|
|
393
|
-
|
|
407
|
+
it('should return error if type is identifiable', async () => {
|
|
408
|
+
const stream = open('lenna.ascii')
|
|
394
409
|
|
|
395
|
-
|
|
410
|
+
const result = await storage.put(dir, stream, { claim: 'image/jpeg' })
|
|
396
411
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
412
|
+
expect(result).toBeInstanceOf(Error)
|
|
413
|
+
expect(result).toMatchObject({ code: 'TYPE_MISMATCH' })
|
|
414
|
+
})
|
|
400
415
|
|
|
401
|
-
|
|
402
|
-
|
|
416
|
+
it('should not return error if type application/octet-stream', async () => {
|
|
417
|
+
const stream = open('sample.jpeg')
|
|
403
418
|
|
|
404
|
-
|
|
419
|
+
const result = await storage.put(dir, stream, { claim: 'application/octet-stream' })
|
|
405
420
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
421
|
+
expect(result).not.toBeInstanceOf(Error)
|
|
422
|
+
expect(result).toMatchObject({ type: 'image/jpeg' })
|
|
423
|
+
})
|
|
409
424
|
|
|
410
|
-
|
|
411
|
-
|
|
425
|
+
it('should return error if type is not acceptable', async () => {
|
|
426
|
+
const stream = open('sample.jpeg')
|
|
412
427
|
|
|
413
|
-
|
|
428
|
+
const result = await storage.put(dir, stream, { accept: 'image/png' })
|
|
414
429
|
|
|
415
|
-
|
|
416
|
-
expect(
|
|
417
|
-
|
|
430
|
+
match(result,
|
|
431
|
+
Error, (error: ErrorType) => expect(error.code).toBe('NOT_ACCEPTABLE'))
|
|
432
|
+
})
|
|
418
433
|
|
|
419
|
-
|
|
420
|
-
|
|
434
|
+
it('should accept wildcard types', async () => {
|
|
435
|
+
const stream = open('sample.jpeg')
|
|
421
436
|
|
|
422
|
-
|
|
437
|
+
const result = await storage.put(dir, stream, { accept: 'image/*' })
|
|
423
438
|
|
|
424
|
-
|
|
439
|
+
expect(result).not.toBeInstanceOf(Error)
|
|
440
|
+
expect(result).toMatchObject({ type: 'image/jpeg' })
|
|
441
|
+
})
|
|
425
442
|
|
|
426
|
-
|
|
443
|
+
it('should handle root entries', async () => {
|
|
444
|
+
const stream = open('sample.jpeg')
|
|
427
445
|
|
|
428
|
-
|
|
429
|
-
})
|
|
446
|
+
const result = await storage.put('hello', stream) as Entry
|
|
430
447
|
|
|
431
|
-
|
|
432
|
-
const stream = await open('empty.txt')
|
|
433
|
-
const result = await storage.put('empty', stream) as Entry
|
|
448
|
+
expect(result).not.toBeInstanceOf(Error)
|
|
434
449
|
|
|
435
|
-
|
|
450
|
+
const stored = await storage.fetch(result.id)
|
|
436
451
|
|
|
437
|
-
|
|
452
|
+
expect(stored).not.toBeInstanceOf(Error)
|
|
453
|
+
})
|
|
438
454
|
|
|
439
|
-
|
|
455
|
+
it('should store empty file', async () => {
|
|
456
|
+
const stream = open('empty.txt')
|
|
457
|
+
const result = await storage.put('empty', stream) as Entry
|
|
440
458
|
|
|
441
|
-
|
|
459
|
+
expect(result.size).toBe(0)
|
|
442
460
|
|
|
443
|
-
|
|
444
|
-
|
|
461
|
+
const stored = await storage.fetch(result.id) as Readable
|
|
462
|
+
|
|
463
|
+
expect(stored).not.toBeInstanceOf(Error)
|
|
464
|
+
|
|
465
|
+
const buf = await buffer(stored)
|
|
466
|
+
|
|
467
|
+
expect(buf.length).toBe(0)
|
|
445
468
|
})
|