import { DyNTS_ArchiveDataService } from './archive-data.service'; import { DyFM_DataModel_Params, DyFM_Metadata, DyFM_Error } from '@futdevpro/fsm-dynamo'; import { DyNTS_GlobalService } from '../core/global.service'; import { DyNTS_DBService } from './db.service'; import { DyNTS_getArchivedDBName } from '../../_collections/archive.util'; class TestMetadata extends DyFM_Metadata { name: string; } describe('| DyNTS_ArchiveDataService', () => { let mockDataParams: DyFM_DataModel_Params; let mockOriginalDBService: jasmine.SpyObj>; let mockArchivedDBService: jasmine.SpyObj>; beforeEach(() => { mockDataParams = { dataName: 'test_data', addArchive: true, constructed: () => true, properties: {}, } as any; mockOriginalDBService = jasmine.createSpyObj>('OriginalDBService', [ 'getDataById', 'trueDeleteDataById', ]); mockArchivedDBService = jasmine.createSpyObj>('ArchivedDBService', [ 'createData', 'getDataById', ]); spyOn(DyNTS_GlobalService, 'getDBService').and.returnValue(mockOriginalDBService); }); it('| should throw error when addArchive is missing from dataParams', () => { const dataParamsWithoutArchive = { ...mockDataParams, addArchive: false, } as any; const data = new TestMetadata(); expect(() => { new DyNTS_ArchiveDataService(data, dataParamsWithoutArchive, 'issuer-123'); }).toThrowError('addArchive is missing from dataParams!'); }); it('| should create service with archived data name', () => { const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); expect(service).toBeInstanceOf(DyNTS_ArchiveDataService); expect(service.originalDBService).toBe(mockOriginalDBService); expect(service.dataParams.dataName).toBe('test_data_archived'); }); it('| should NOT inherit haveArchiveDataService (an archive has no archive-of-itself)', () => { // Regression guard: the archive service must NOT think it has its own archive, otherwise base // deleteData()/loadData() call the unimplemented getArchiveDataService() and throw // "getArchiveDataService is not implemented!" — which spun the overseer archive-rotation in a // tight retry loop ("Failed to delete old archive"). The constructor forces addArchive:false. const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); expect(service.haveArchiveDataService).toBe(false); expect(service.dataParams.addArchive).toBe(false); }); describe('| archiveDataById', () => { it('| should throw error when id is missing', async () => { const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); await expectAsync( service.archiveDataById('') ).toBeRejected(); }); it('| should throw error when data not found', async () => { const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); mockOriginalDBService.getDataById.and.returnValue(Promise.resolve(null)); await expectAsync( service.archiveDataById('non-existent-id') ).toBeRejected(); }); it('| should archive data when found', async () => { const data = new TestMetadata({ _id: 'test-id' }); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); mockOriginalDBService.getDataById.and.returnValue(Promise.resolve(data)); mockOriginalDBService.trueDeleteDataById.and.returnValue(Promise.resolve()); spyOn(service, 'archiveData').and.returnValue(Promise.resolve(data)); const result = await service.archiveDataById('test-id'); expect(mockOriginalDBService.getDataById).toHaveBeenCalledWith('test-id'); expect(service.archiveData).toHaveBeenCalledWith(data); expect(result).toBe(data); }); }); describe('| getDataByOriginalId', () => { it('| should find data by original id', async () => { const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); const archivedData = new TestMetadata({ _originalId: 'original-id' }); spyOn(service, 'findData').and.returnValue(Promise.resolve(archivedData)); const result = await service.getDataByOriginalId('original-id'); expect(service.findData).toHaveBeenCalled(); expect(result).toBe(archivedData); }); it('| should throw error on find failure', async () => { const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); spyOn(service, 'findData').and.returnValue(Promise.reject(new Error('Find failed'))); await expectAsync( service.getDataByOriginalId('original-id') ).toBeRejected(); }); }); describe('| archiveData', () => { it('| should throw error when originalId already exists', async () => { const data = new TestMetadata({ _id: 'test-id', _originalId: 'existing-original-id' } as TestMetadata); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); await expectAsync( service.archiveData(data) ).toBeRejected(); }); it('| should throw error when data is already archived', async () => { const data = new TestMetadata({ _id: 'test-id', _archived: new Date() } as TestMetadata); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); await expectAsync( service.archiveData(data) ).toBeRejected(); }); it('| should throw error when id is missing', async () => { const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); await expectAsync( service.archiveData(data) ).toBeRejected(); }); it('| should archive data successfully', async () => { const data = new TestMetadata({ _id: 'test-id' }); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); mockOriginalDBService.trueDeleteDataById.and.returnValue(Promise.resolve()); spyOn(service, 'saveData').and.returnValue(Promise.resolve(data)); const result = await service.archiveData(data); expect(mockOriginalDBService.trueDeleteDataById).toHaveBeenCalledWith('test-id'); expect(data._originalId).toBe('test-id'); expect(data._archived).toBeInstanceOf(Date); expect(data._id).toBeUndefined(); expect(service.saveData).toHaveBeenCalled(); expect(result).toBe(data); }); }); describe('| saveData', () => { it('| should throw error when originalId is missing', async () => { const data = new TestMetadata(); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); await expectAsync( service.saveData(data) ).toBeRejected(); }); it('| should throw error when data is not archived', async () => { const data = new TestMetadata({ _originalId: 'original-id' } as TestMetadata); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); await expectAsync( service.saveData(data) ).toBeRejected(); }); it('| should save archived data successfully', async () => { const data = new TestMetadata({ _originalId: 'original-id', _archived: new Date() } as TestMetadata); const service = new DyNTS_ArchiveDataService(data, mockDataParams, 'issuer-123'); const parentSaveDataSpy = spyOn(Object.getPrototypeOf(DyNTS_ArchiveDataService.prototype), 'saveData').and.returnValue(Promise.resolve(data)); const result = await service.saveData(data); expect(parentSaveDataSpy).toHaveBeenCalled(); expect(result).toBe(data); }); }); });