Skip to content

Commit bb2f998

Browse files
authored
test error responses in file and bucket apis. coverage 84% (#223)
* typo in collectCoverage * test error responses * test bucket error responses * test non-storage error responses in bucket api * test error handling in storage File API
1 parent 6bf02b9 commit bb2f998

File tree

4 files changed

+750
-5
lines changed

4 files changed

+750
-5
lines changed

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = {
22
preset: 'ts-jest',
33
testEnvironment: 'jsdom',
4-
ollectCoverage: true,
4+
collectCoverage: true,
55
coverageDirectory: './test/coverage',
66
coverageReporters: ['lcov'],
77
collectCoverageFrom: [

test/storageBucketApi.test.ts

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
import { StorageClient } from '../src/index'
2+
import { StorageError, StorageUnknownError } from '../src/lib/errors'
3+
4+
// Create a simple Response implementation for testing
5+
class MockResponse {
6+
ok: boolean
7+
status: number
8+
statusText: string
9+
private body: string
10+
11+
constructor(body: string, options: { status: number; statusText: string }) {
12+
this.body = body
13+
this.status = options.status
14+
this.statusText = options.statusText
15+
this.ok = this.status >= 200 && this.status < 300
16+
}
17+
18+
json() {
19+
return Promise.resolve(JSON.parse(this.body))
20+
}
21+
}
22+
23+
// Mock URL and credentials for testing
24+
const URL = 'http://localhost:8000/storage/v1'
25+
const KEY = 'test-api-key'
26+
27+
describe('Bucket API Error Handling', () => {
28+
beforeEach(() => {
29+
jest.resetAllMocks()
30+
})
31+
32+
afterEach(() => {
33+
jest.restoreAllMocks()
34+
})
35+
36+
describe('listBuckets', () => {
37+
it('handles network errors', async () => {
38+
const mockError = new Error('Network failure')
39+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(mockError))
40+
const storage = new StorageClient(URL, { apikey: KEY })
41+
42+
const { data, error } = await storage.listBuckets()
43+
expect(data).toBeNull()
44+
expect(error).not.toBeNull()
45+
expect(error?.message).toBe('Network failure')
46+
})
47+
48+
it('wraps non-Response errors as StorageUnknownError', async () => {
49+
// Create an error that's not a Response object
50+
const nonResponseError = new TypeError('Invalid argument')
51+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(nonResponseError))
52+
53+
const storage = new StorageClient(URL, { apikey: KEY })
54+
55+
// Check that the error is wrapped in a StorageUnknownError
56+
const { data, error } = await storage.listBuckets()
57+
expect(data).toBeNull()
58+
expect(error).toBeInstanceOf(StorageUnknownError)
59+
expect(error?.message).toBe('Invalid argument')
60+
})
61+
62+
it('throws non-StorageError exceptions', async () => {
63+
// Create a storage client
64+
const storage = new StorageClient(URL, { apikey: KEY })
65+
66+
// Create a spy on the get method that will throw a non-StorageError
67+
const mockFn = jest.spyOn(require('../src/lib/fetch'), 'get').mockImplementationOnce(() => {
68+
const error = new Error('Unexpected error')
69+
// Ensure it's not recognized as a StorageError
70+
Object.defineProperty(error, 'name', { value: 'CustomError' })
71+
throw error
72+
})
73+
74+
// The error should be thrown all the way up
75+
await expect(storage.listBuckets()).rejects.toThrow('Unexpected error')
76+
77+
// Clean up
78+
mockFn.mockRestore()
79+
})
80+
})
81+
82+
describe('getBucket', () => {
83+
it('handles network errors', async () => {
84+
const mockError = new Error('Network failure')
85+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(mockError))
86+
const storage = new StorageClient(URL, { apikey: KEY })
87+
88+
const { data, error } = await storage.getBucket('non-existent-bucket')
89+
expect(data).toBeNull()
90+
expect(error).not.toBeNull()
91+
expect(error?.message).toBe('Network failure')
92+
})
93+
94+
it('wraps non-Response errors as StorageUnknownError', async () => {
95+
const nonResponseError = new TypeError('Invalid bucket format')
96+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(nonResponseError))
97+
98+
const storage = new StorageClient(URL, { apikey: KEY })
99+
100+
const { data, error } = await storage.getBucket('test-bucket')
101+
expect(data).toBeNull()
102+
expect(error).toBeInstanceOf(StorageUnknownError)
103+
expect(error?.message).toBe('Invalid bucket format')
104+
})
105+
106+
it('throws non-StorageError exceptions', async () => {
107+
// Create a storage client
108+
const storage = new StorageClient(URL, { apikey: KEY })
109+
110+
// Create a spy on the get method that will throw a non-StorageError
111+
const mockFn = jest.spyOn(require('../src/lib/fetch'), 'get').mockImplementationOnce(() => {
112+
const error = new Error('Unexpected error in getBucket')
113+
// Ensure it's not recognized as a StorageError
114+
Object.defineProperty(error, 'name', { value: 'CustomError' })
115+
throw error
116+
})
117+
118+
// The error should be thrown all the way up
119+
await expect(storage.getBucket('test-bucket')).rejects.toThrow(
120+
'Unexpected error in getBucket'
121+
)
122+
123+
// Clean up
124+
mockFn.mockRestore()
125+
})
126+
})
127+
128+
describe('createBucket', () => {
129+
it('handles network errors', async () => {
130+
const mockError = new Error('Network failure')
131+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(mockError))
132+
const storage = new StorageClient(URL, { apikey: KEY })
133+
134+
const { data, error } = await storage.createBucket('new-bucket')
135+
expect(data).toBeNull()
136+
expect(error).not.toBeNull()
137+
expect(error?.message).toBe('Network failure')
138+
})
139+
140+
it('wraps non-Response errors as StorageUnknownError', async () => {
141+
const nonResponseError = new TypeError('Invalid bucket configuration')
142+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(nonResponseError))
143+
144+
const storage = new StorageClient(URL, { apikey: KEY })
145+
146+
const { data, error } = await storage.createBucket('test-bucket')
147+
expect(data).toBeNull()
148+
expect(error).toBeInstanceOf(StorageUnknownError)
149+
expect(error?.message).toBe('Invalid bucket configuration')
150+
})
151+
152+
it('throws non-StorageError exceptions', async () => {
153+
// Create a storage client
154+
const storage = new StorageClient(URL, { apikey: KEY })
155+
156+
// Create a spy on the post method that will throw a non-StorageError
157+
const mockFn = jest.spyOn(require('../src/lib/fetch'), 'post').mockImplementationOnce(() => {
158+
const error = new Error('Unexpected error in createBucket')
159+
// Ensure it's not recognized as a StorageError
160+
Object.defineProperty(error, 'name', { value: 'CustomError' })
161+
throw error
162+
})
163+
164+
// The error should be thrown all the way up
165+
await expect(storage.createBucket('test-bucket')).rejects.toThrow(
166+
'Unexpected error in createBucket'
167+
)
168+
169+
// Clean up
170+
mockFn.mockRestore()
171+
})
172+
})
173+
174+
describe('updateBucket', () => {
175+
it('handles network errors', async () => {
176+
const mockError = new Error('Network failure')
177+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(mockError))
178+
const storage = new StorageClient(URL, { apikey: KEY })
179+
180+
const { data, error } = await storage.updateBucket('existing-bucket', { public: true })
181+
expect(data).toBeNull()
182+
expect(error).not.toBeNull()
183+
expect(error?.message).toBe('Network failure')
184+
})
185+
186+
it('wraps non-Response errors as StorageUnknownError', async () => {
187+
const nonResponseError = new TypeError('Invalid update options')
188+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(nonResponseError))
189+
190+
const storage = new StorageClient(URL, { apikey: KEY })
191+
192+
const { data, error } = await storage.updateBucket('test-bucket', { public: true })
193+
expect(data).toBeNull()
194+
expect(error).toBeInstanceOf(StorageUnknownError)
195+
expect(error?.message).toBe('Invalid update options')
196+
})
197+
198+
it('throws non-StorageError exceptions', async () => {
199+
// Create a storage client
200+
const storage = new StorageClient(URL, { apikey: KEY })
201+
202+
// Create a spy on the put method that will throw a non-StorageError
203+
const mockFn = jest.spyOn(require('../src/lib/fetch'), 'put').mockImplementationOnce(() => {
204+
const error = new Error('Unexpected error in updateBucket')
205+
// Ensure it's not recognized as a StorageError
206+
Object.defineProperty(error, 'name', { value: 'CustomError' })
207+
throw error
208+
})
209+
210+
// The error should be thrown all the way up
211+
await expect(storage.updateBucket('test-bucket', { public: true })).rejects.toThrow(
212+
'Unexpected error in updateBucket'
213+
)
214+
215+
// Clean up
216+
mockFn.mockRestore()
217+
})
218+
})
219+
220+
describe('emptyBucket', () => {
221+
it('handles network errors', async () => {
222+
const mockError = new Error('Network failure')
223+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(mockError))
224+
const storage = new StorageClient(URL, { apikey: KEY })
225+
226+
const { data, error } = await storage.emptyBucket('existing-bucket')
227+
expect(data).toBeNull()
228+
expect(error).not.toBeNull()
229+
expect(error?.message).toBe('Network failure')
230+
})
231+
232+
it('wraps non-Response errors as StorageUnknownError', async () => {
233+
const nonResponseError = new TypeError('Operation not supported')
234+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(nonResponseError))
235+
236+
const storage = new StorageClient(URL, { apikey: KEY })
237+
238+
const { data, error } = await storage.emptyBucket('test-bucket')
239+
expect(data).toBeNull()
240+
expect(error).toBeInstanceOf(StorageUnknownError)
241+
expect(error?.message).toBe('Operation not supported')
242+
})
243+
244+
it('throws non-StorageError exceptions', async () => {
245+
// Create a storage client
246+
const storage = new StorageClient(URL, { apikey: KEY })
247+
248+
// Create a spy on the post method that will throw a non-StorageError
249+
const mockFn = jest.spyOn(require('../src/lib/fetch'), 'post').mockImplementationOnce(() => {
250+
const error = new Error('Unexpected error in emptyBucket')
251+
// Ensure it's not recognized as a StorageError
252+
Object.defineProperty(error, 'name', { value: 'CustomError' })
253+
throw error
254+
})
255+
256+
// The error should be thrown all the way up
257+
await expect(storage.emptyBucket('test-bucket')).rejects.toThrow(
258+
'Unexpected error in emptyBucket'
259+
)
260+
261+
// Clean up
262+
mockFn.mockRestore()
263+
})
264+
})
265+
266+
describe('deleteBucket', () => {
267+
it('handles network errors', async () => {
268+
const mockError = new Error('Network failure')
269+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(mockError))
270+
const storage = new StorageClient(URL, { apikey: KEY })
271+
272+
const { data, error } = await storage.deleteBucket('existing-bucket')
273+
expect(data).toBeNull()
274+
expect(error).not.toBeNull()
275+
expect(error?.message).toBe('Network failure')
276+
})
277+
278+
it('wraps non-Response errors as StorageUnknownError', async () => {
279+
const nonResponseError = new TypeError('Invalid delete operation')
280+
global.fetch = jest.fn().mockImplementation(() => Promise.reject(nonResponseError))
281+
282+
const storage = new StorageClient(URL, { apikey: KEY })
283+
284+
const { data, error } = await storage.deleteBucket('test-bucket')
285+
expect(data).toBeNull()
286+
expect(error).toBeInstanceOf(StorageUnknownError)
287+
expect(error?.message).toBe('Invalid delete operation')
288+
})
289+
290+
it('throws non-StorageError exceptions', async () => {
291+
// Create a storage client
292+
const storage = new StorageClient(URL, { apikey: KEY })
293+
294+
// Create a spy on the remove method that will throw a non-StorageError
295+
const mockFn = jest
296+
.spyOn(require('../src/lib/fetch'), 'remove')
297+
.mockImplementationOnce(() => {
298+
const error = new Error('Unexpected error in deleteBucket')
299+
// Ensure it's not recognized as a StorageError
300+
Object.defineProperty(error, 'name', { value: 'CustomError' })
301+
throw error
302+
})
303+
304+
// The error should be thrown all the way up
305+
await expect(storage.deleteBucket('test-bucket')).rejects.toThrow(
306+
'Unexpected error in deleteBucket'
307+
)
308+
309+
// Clean up
310+
mockFn.mockRestore()
311+
})
312+
})
313+
})

0 commit comments

Comments
 (0)