-
Notifications
You must be signed in to change notification settings - Fork 392
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* CB-3898 add base CachedMapResource tests * CB-3908 add tests for the handlers * CB-3908 add error tests * CB-3908 add initial tests for the CachedDataResource * CB-3908 remove extra test * CB-3908 add clear data test * CB-3908 add CachedTreeResource tests * CB-3908 review fixes * CB-3908 transform mock data to getters
- Loading branch information
Showing
4 changed files
with
488 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
webapp/packages/core-sdk/src/Resource/CachedDataResource.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* CloudBeaver - Cloud Database Manager | ||
* Copyright (C) 2020-2023 DBeaver Corp and others | ||
* | ||
* Licensed under the Apache License, Version 2.0. | ||
* you may not use this file except in compliance with the License. | ||
*/ | ||
import { CachedDataResource } from './CachedDataResource'; | ||
|
||
interface IEntityData { | ||
id: string; | ||
value: number; | ||
} | ||
|
||
const DEFAULT_STATE_GETTER: () => IEntityData[] = () => []; | ||
const DATA_MOCK_GETTER: () => IEntityData[] = () => [ | ||
{ | ||
id: '1', | ||
value: 1, | ||
}, | ||
{ | ||
id: '2', | ||
value: 2, | ||
}, | ||
]; | ||
|
||
async function fetchMock(): Promise<IEntityData[]> { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
resolve(DATA_MOCK_GETTER()); | ||
}, 1); | ||
}); | ||
} | ||
|
||
class TestDataResource extends CachedDataResource<IEntityData[]> { | ||
constructor() { | ||
super(DEFAULT_STATE_GETTER); | ||
} | ||
|
||
protected async loader() { | ||
const data = await fetchMock(); | ||
return data; | ||
} | ||
} | ||
|
||
describe('CachedDataResource', () => { | ||
let dataResource: TestDataResource; | ||
|
||
beforeEach(() => { | ||
dataResource = new TestDataResource(); | ||
}); | ||
|
||
test('should instantiate correctly', () => { | ||
expect(dataResource).toBeInstanceOf(CachedDataResource); | ||
}); | ||
|
||
test('should initialize with the default state', () => { | ||
expect(dataResource.data).toEqual(DEFAULT_STATE_GETTER()); | ||
}); | ||
|
||
test('should load data', async () => { | ||
await dataResource.load(); | ||
expect(dataResource.data).toEqual(DATA_MOCK_GETTER()); | ||
}); | ||
|
||
test('should be able to outdate the data', () => { | ||
dataResource.markOutdated(); | ||
expect(dataResource.isOutdated()).toBe(true); | ||
}); | ||
|
||
test('should run onDataOutdated handlers on data outdate', () => { | ||
const handler = jest.fn(); | ||
|
||
dataResource.onDataOutdated.addHandler(handler); | ||
dataResource.markOutdated(); | ||
|
||
expect(handler).toHaveBeenCalled(); | ||
}); | ||
|
||
test('should run onDataUpdate handlers on data update', () => { | ||
const handler = jest.fn(); | ||
|
||
dataResource.onDataUpdate.addHandler(handler); | ||
dataResource.dataUpdate(); | ||
|
||
expect(handler).toHaveBeenCalled(); | ||
}); | ||
|
||
test('should be able to clear the data', async () => { | ||
await dataResource.load(); | ||
dataResource.clear(); | ||
|
||
expect(dataResource.data).toEqual([]); | ||
}); | ||
}); |
254 changes: 254 additions & 0 deletions
254
webapp/packages/core-sdk/src/Resource/CachedMapResource.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
/* | ||
* CloudBeaver - Cloud Database Manager | ||
* Copyright (C) 2020-2023 DBeaver Corp and others | ||
* | ||
* Licensed under the Apache License, Version 2.0. | ||
* you may not use this file except in compliance with the License. | ||
*/ | ||
import { toJS } from 'mobx'; | ||
|
||
import { CachedMapResource } from './CachedMapResource'; | ||
import type { ResourceKey } from './ResourceKey'; | ||
import { resourceKeyList } from './ResourceKeyList'; | ||
|
||
interface IEntityData { | ||
id: string; | ||
value: number; | ||
} | ||
|
||
const ERROR_ITEM_ID = 'error'; | ||
|
||
const DATA_MOCK_GETTER: () => IEntityData[] = () => [ | ||
{ | ||
id: '1', | ||
value: 1, | ||
}, | ||
{ | ||
id: '2', | ||
value: 2, | ||
}, | ||
{ | ||
id: ERROR_ITEM_ID, | ||
value: 3, | ||
}, | ||
]; | ||
|
||
const TEST_ERROR_MESSAGE = 'Test error'; | ||
const DEFAULT_STATE_GETTER = () => new Map(); | ||
|
||
async function fetchMock(key: ResourceKey<string> | undefined): Promise<IEntityData[]> { | ||
const data = DATA_MOCK_GETTER(); | ||
|
||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
if (key) { | ||
if (key === ERROR_ITEM_ID) { | ||
reject(new Error(TEST_ERROR_MESSAGE)); | ||
} | ||
|
||
const item = data.find(d => d.id === key); | ||
if (item) { | ||
resolve([item]); | ||
} | ||
} else { | ||
resolve(data); | ||
} | ||
}, 1); | ||
}); | ||
} | ||
|
||
class TestMapResource extends CachedMapResource<string, IEntityData> { | ||
constructor() { | ||
super(DEFAULT_STATE_GETTER); | ||
} | ||
|
||
protected async loader(key: ResourceKey<string>) { | ||
const data = await fetchMock(key); | ||
this.replace(resourceKeyList(data.map(d => d.id)), data); | ||
return this.data; | ||
} | ||
|
||
protected validateKey(key: string): boolean { | ||
return typeof key === 'string'; | ||
} | ||
} | ||
|
||
describe('CachedMapResource', () => { | ||
let mapResource: TestMapResource; | ||
|
||
beforeEach(() => { | ||
mapResource = new TestMapResource(); | ||
}); | ||
|
||
test('should instantiate correctly', () => { | ||
expect(mapResource).toBeInstanceOf(CachedMapResource); | ||
}); | ||
|
||
test('should initialize with the initial value', () => { | ||
expect(toJS(mapResource.data)).toEqual(DEFAULT_STATE_GETTER()); | ||
}); | ||
|
||
test('should return all entries', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
expect(mapResource.entries).toEqual([ | ||
['key1', { id: 'key1', value: 1 }], | ||
['key2', { id: 'key2', value: 2 }], | ||
]); | ||
}); | ||
|
||
test('should return all values', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
expect(mapResource.values).toEqual([ | ||
{ id: 'key1', value: 1 }, | ||
{ id: 'key2', value: 2 }, | ||
]); | ||
}); | ||
|
||
test('should return all keys', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
expect(mapResource.keys).toEqual(['key1', 'key2']); | ||
}); | ||
|
||
test('should load data for a specific key', async () => { | ||
await mapResource.load('1'); | ||
expect(mapResource.get('1')).toEqual({ id: '1', value: 1 }); | ||
expect(mapResource.get('2')).toBeUndefined(); // This key was not loaded | ||
}); | ||
|
||
test('should NOT load data for a key that produces an error', async () => { | ||
await expect(mapResource.load(ERROR_ITEM_ID)).rejects.toThrow(TEST_ERROR_MESSAGE); | ||
expect(mapResource.get(ERROR_ITEM_ID)).toBeUndefined(); | ||
}); | ||
|
||
test('should mark loaded data as loaded', async () => { | ||
await mapResource.load('1'); | ||
expect(mapResource.isLoaded('1')).toBe(true); | ||
}); | ||
|
||
test('should set and get a value', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
expect(mapResource.get('key1')).toStrictEqual({ id: 'key1', value: 1 }); | ||
}); | ||
|
||
test('should delete a value', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.delete('key1'); | ||
expect(mapResource.get('key1')).toBeUndefined(); | ||
}); | ||
|
||
test('should check if a key exists', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
expect(mapResource.has('key1')).toBe(true); | ||
expect(mapResource.has('key2')).toBe(false); | ||
}); | ||
|
||
test('should replace multiple keys', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
mapResource.set('key4', { id: 'key4', value: 4 }); | ||
|
||
mapResource.replace(resourceKeyList(['key1', 'key3']), [ | ||
{ id: 'key1', value: 11 }, | ||
{ id: 'key3', value: 33 }, | ||
]); | ||
|
||
expect(mapResource.get('key1')).toStrictEqual({ id: 'key1', value: 11 }); | ||
expect(mapResource.get('key2')).toBeUndefined(); | ||
expect(mapResource.get('key3')).toStrictEqual({ id: 'key3', value: 33 }); | ||
expect(mapResource.data.size).toBe(2); | ||
}); | ||
|
||
test('should outdated certain keys', () => { | ||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
|
||
mapResource.markOutdated('key1'); | ||
|
||
expect(mapResource.isOutdated('key1')).toBe(true); | ||
expect(mapResource.isOutdated('key2')).toBe(false); | ||
}); | ||
|
||
test('should run onDataOutdated handlers on data outdate', () => { | ||
const handler = jest.fn(); | ||
|
||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
|
||
mapResource.onDataOutdated.addHandler(key => { | ||
handler(); | ||
expect(key).toBe('key1'); | ||
}); | ||
|
||
mapResource.markOutdated('key1'); | ||
expect(handler).toHaveBeenCalled(); | ||
}); | ||
|
||
test('should run onDataUpdate handlers on data update', () => { | ||
const handler = jest.fn(); | ||
|
||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
|
||
mapResource.onDataUpdate.addHandler(key => { | ||
handler(); | ||
expect(key).toBe('key2'); | ||
}); | ||
|
||
mapResource.dataUpdate('key2'); | ||
expect(handler).toHaveBeenCalled(); | ||
}); | ||
|
||
test('should run onItemDelete handlers on data delete', () => { | ||
const handler = jest.fn(); | ||
|
||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
|
||
mapResource.onItemDelete.addHandler(key => { | ||
handler(); | ||
expect(key).toBe('key1'); | ||
}); | ||
|
||
mapResource.delete('key1'); | ||
expect(handler).toHaveBeenCalled(); | ||
}); | ||
|
||
test('should run onItemUpdate handlers on item update', () => { | ||
const handler = jest.fn(); | ||
|
||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
|
||
mapResource.onItemUpdate.addHandler(key => { | ||
handler(); | ||
expect(key).toBe('key2'); | ||
}); | ||
|
||
mapResource.set('key2', { id: 'key2', value: 22 }); | ||
expect(handler).toHaveBeenCalled(); | ||
}); | ||
|
||
test('should run onDataError handlers on data error', async () => { | ||
const handler = jest.fn(); | ||
|
||
mapResource.set('key1', { id: 'key1', value: 1 }); | ||
mapResource.set('key2', { id: 'key2', value: 2 }); | ||
|
||
mapResource.onDataError.addHandler(data => { | ||
handler(); | ||
expect(data.param).toBe(ERROR_ITEM_ID); | ||
expect(data.exception.message).toBe(TEST_ERROR_MESSAGE); | ||
}); | ||
|
||
await expect(mapResource.load(ERROR_ITEM_ID)).rejects.toThrow(TEST_ERROR_MESSAGE); | ||
expect(handler).toHaveBeenCalled(); | ||
}); | ||
|
||
test('should be able to get an exception if the one occurred for the key', async () => { | ||
await expect(mapResource.load(ERROR_ITEM_ID)).rejects.toThrow(TEST_ERROR_MESSAGE); | ||
expect(mapResource.getException(ERROR_ITEM_ID)?.message).toBe(TEST_ERROR_MESSAGE); | ||
}); | ||
}); |
Oops, something went wrong.