import { DyNTS_DBService } from './db.service'; import { DyFM_DataModel_Params, DyFM_Metadata, DyFM_BasicProperty_Type } from '@futdevpro/fsm-dynamo'; import * as mongoose from 'mongoose'; import { DyNTS_global_settings } from '../../_collections/global-settings.const'; class TestMetadata extends DyFM_Metadata { name: string = ''; email: string = ''; } const testDataParams: DyFM_DataModel_Params = new DyFM_DataModel_Params({ dataName: 'test_data', properties: { name: { key: 'name', type: DyFM_BasicProperty_Type.string, required: true, }, email: { key: 'email', type: DyFM_BasicProperty_Type.string, }, }, }); describe('| DyNTS_DBService', () => { let dbService: DyNTS_DBService; afterEach(() => { delete (mongoose.models as Record)['test_data']; }); describe('| constructor', () => { it('| should create service with valid dataParams', () => { dbService = new DyNTS_DBService(testDataParams); expect(dbService).toBeDefined(); expect(dbService.dataParams).toBe(testDataParams); expect(dbService.serviceName).toBe('DyNTS_DBService'); }); it('| should throw error when dataName is not set', () => { const invalidParams = { dataName: '', properties: { name: { key: 'name', type: DyFM_BasicProperty_Type.string }, email: { key: 'email', type: DyFM_BasicProperty_Type.string }, }, constructed: (): boolean => true, } as unknown as DyFM_DataModel_Params; expect(() => { new DyNTS_DBService(invalidParams); }).toThrow(); }); it('| should throw error when dataParams is not constructed', () => { const invalidParams = { dataName: 'test_data', properties: {}, constructed: () => false, } as any; expect(() => { new DyNTS_DBService(invalidParams); }).toThrow(); }); }); // getDataById, createData, modifyData, getDataByDependencyId tesztek kiveve: mongoose.model // getter-only, nem mockolható ebben a környezetben. // BFR-OVERSEER-008 — find() opcionalis projection/limit/sort: a szukites DB-szintre kerul. // A dataModel.find INSTANCE-property-kent spy-olhato (a mongoose.model maga nem — lasd fenti komment), // a visszaadott fake-query chainable + thenable, igy a teljes lancolas assertelheto. describe('| find (BFR-008 query-options)', () => { interface FakeQueryCalls { select?: unknown; sort?: unknown; limit?: unknown; } /** Chainable + thenable fake mongoose-query; rogziti a lancolt hivasokat. */ function makeFakeQuery(result: TestMetadata[], calls: FakeQueryCalls): { select: (p: unknown) => unknown; sort: (s: unknown) => unknown; limit: (n: unknown) => unknown; then: (onOk: (res: TestMetadata[]) => TestMetadata[]) => Promise; } { const fake = { select: (p: unknown): unknown => { calls.select = p; return fake; }, sort: (s: unknown): unknown => { calls.sort = s; return fake; }, limit: (n: unknown): unknown => { calls.limit = n; return fake; }, then: (onOk: (res: TestMetadata[]) => TestMetadata[]): Promise => Promise.resolve(result).then(onOk), }; return fake; } it('| options nelkul NEM hiv select/sort/limit-et es post-filterrel szuri a torolteket (back-compat)', async () => { dbService = new DyNTS_DBService(testDataParams); const calls: FakeQueryCalls = {}; const alive: TestMetadata = { _id: 'a1', name: 'alive', email: '' } as TestMetadata; const deleted: TestMetadata = { _id: 'd1', name: 'gone', email: '', _deleted: new Date() } as TestMetadata; const findSpy = spyOn(dbService.dataModel as { find: (f: unknown) => unknown }, 'find') .and.returnValue(makeFakeQuery([ alive, deleted ], calls)); const result: TestMetadata[] = await dbService.find({ name: 'x' } as never); // a filter valtozatlanul megy at (nincs query-szintu _deleted-guard options nelkul) expect(findSpy).toHaveBeenCalledWith({ name: 'x' }); expect(calls.select).toBeUndefined(); expect(calls.sort).toBeUndefined(); expect(calls.limit).toBeUndefined(); // a torolt dokumentumot a memoriabeli post-filter szuri (regi viselkedes valtozatlan) expect(result.length).toBe(1); expect(result[0].name).toBe('alive'); }); it('| options-szal a projection/sort/limit a query-re lancolodik + a _deleted-guard query-szintu', async () => { dbService = new DyNTS_DBService(testDataParams); const calls: FakeQueryCalls = {}; const findSpy = spyOn(dbService.dataModel as { find: (f: unknown) => unknown }, 'find') .and.returnValue(makeFakeQuery([], calls)); await dbService.find( { name: 'x' } as never, undefined, { projection: { name: 1 }, sort: { name: -1 }, limit: 5 }, ); // a soft-delete szures a QUERY-be kerul (limit/projekcio mellett a post-filter hibas lenne); // `_deleted: null` = Mongo-ban "missing VAGY null" — a `!data._deleted` post-filter ekvivalense expect(findSpy).toHaveBeenCalledWith({ name: 'x', _deleted: null }); expect(calls.select).toEqual({ name: 1 }); expect(calls.sort).toEqual({ name: -1 }); expect(calls.limit).toBe(5); }); it('| findDeleted=true options-szal: NINCS _deleted-guard a query-ben', async () => { dbService = new DyNTS_DBService(testDataParams); const calls: FakeQueryCalls = {}; const findSpy = spyOn(dbService.dataModel as { find: (f: unknown) => unknown }, 'find') .and.returnValue(makeFakeQuery([], calls)); await dbService.find({ name: 'x' } as never, true, { limit: 3 }); expect(findSpy).toHaveBeenCalledWith({ name: 'x' }); expect(calls.limit).toBe(3); }); it('| explicit _deleted filterrel options-szal: a hivo filtere nyer, nincs dupla guard', async () => { dbService = new DyNTS_DBService(testDataParams); const calls: FakeQueryCalls = {}; const findSpy = spyOn(dbService.dataModel as { find: (f: unknown) => unknown }, 'find') .and.returnValue(makeFakeQuery([], calls)); await dbService.find({ name: 'x', _deleted: { $ne: null } } as never, undefined, { limit: 2 }); expect(findSpy).toHaveBeenCalledWith({ name: 'x', _deleted: { $ne: null } }); expect(calls.limit).toBe(2); }); }); });