diff --git a/packages/data-sources/file-jsonl/src/index.ts b/packages/data-sources/file-jsonl/src/index.ts index c39170a..433ffb8 100644 --- a/packages/data-sources/file-jsonl/src/index.ts +++ b/packages/data-sources/file-jsonl/src/index.ts @@ -148,8 +148,8 @@ export class JsonLinesDataSource< const id = idConfig.deserialize(idSerialized); const { [idAttr]: idFromResource, ...data } = (dataWithId as any); const dataToEmplace = { - ...data, [idAttr]: id, + ...data, } as Data; if (existing) { diff --git a/packages/data-sources/file-jsonl/test/index.test.ts b/packages/data-sources/file-jsonl/test/index.test.ts index 185c9f0..1b3c126 100644 --- a/packages/data-sources/file-jsonl/test/index.test.ts +++ b/packages/data-sources/file-jsonl/test/index.test.ts @@ -6,6 +6,10 @@ import {DataSource} from '@modal-sh/yasumi/dist/types/backend'; vi.mock('fs/promises'); +const toJsonl = (dummyItems: unknown[]) => dummyItems.map((i) => JSON.stringify(i)).join('\n'); + +const ID_ATTR = 'id' as const; + describe('prepareResource', () => { beforeAll(() => { const mockWriteFile = writeFile as Mock; @@ -20,8 +24,6 @@ describe('prepareResource', () => { }); }); -const toJsonl = (dummyItems: unknown[]) => dummyItems.map((i) => JSON.stringify(i)).join('\n') - describe('methods', () => { const dummyItems = [ { @@ -45,7 +47,7 @@ describe('methods', () => { beforeEach(() => { mockGenerationStrategy = vi.fn(); const r = resource(schema) - .id('id', { + .id(ID_ATTR, { generationStrategy: mockGenerationStrategy, schema: v.any(), serialize: (id) => id.toString(), @@ -100,8 +102,9 @@ describe('methods', () => { describe('getById', () => { it('works', async () => { - const item = await ds.getById('2'); // ID is always a string because it originates from URLs - const expected = dummyItems.find((i) => i.id === 2); + const id = 2; + const item = await ds.getById(id.toString()); // ID is always a string because it originates from URLs + const expected = dummyItems.find((i) => i[ID_ATTR] === id); expect(item).toEqual(expected); }); }); @@ -142,15 +145,15 @@ describe('methods', () => { await ds.delete('1'); expect(mockWriteFile).toBeCalledWith( expect.any(String), - toJsonl(dummyItems.filter((d) => d.id !== 1)), + toJsonl(dummyItems.filter((d) => d[ID_ATTR] !== 1)), ); }); }); describe('emplace', () => { - it('works', async () => { + it('replaces existing data', async () => { const data = { - id: 2, + [ID_ATTR]: 2, name: 'foo', }; const { id, ...etcData } = data; @@ -162,20 +165,41 @@ describe('methods', () => { expect(mockWriteFile).toBeCalledWith( expect.any(String), toJsonl(dummyItems.map((d) => - d.id === 2 - // ID will be defined last, since we are just writing to file, we need strict ordering - ? { ...etcData, id } + d[ID_ATTR] === data[ID_ATTR] + // ID will be defined first, since we are just writing to file, we need strict ordering + ? { [ID_ATTR]: id, ...etcData } : d )), ); expect(newItem).toEqual([data, false]); }); + + it('creates new data', async () => { + const data = { + [ID_ATTR]: 4, + name: 'quux', + }; + const { [ID_ATTR]: id, ...etcData } = data; + const newItem = await ds.emplace( + id.toString(), + etcData, + ); + + expect(mockWriteFile).toBeCalledWith( + expect.any(String), + toJsonl([ + ...dummyItems, + data + ]), + ); + expect(newItem).toEqual([data, true]); + }); }); describe('patch', () => { it('works', async () => { const data = { - id: 2, + [ID_ATTR]: 2, name: 'foo', }; const { id, ...etcData } = data; @@ -187,8 +211,9 @@ describe('methods', () => { expect(mockWriteFile).toBeCalledWith( expect.any(String), toJsonl(dummyItems.map((d) => - d.id === 2 - ? { ...etcData, id } + d[ID_ATTR] === data[ID_ATTR] + // ID will be defined first, since we are just writing to file, we need strict ordering + ? { [ID_ATTR]: id, ...etcData } : d )), ); @@ -198,7 +223,7 @@ describe('methods', () => { describe('newId', () => { it('works', async () => { - const v = 'newId'; + const v = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); mockGenerationStrategy.mockResolvedValueOnce(v); const id = await ds.newId(); expect(id).toBe(v);