import { Test, TestingModule } from '@nestjs/testing'; import { BackupShareRepositoryImpl } from '../../src/infrastructure/persistence/repositories/backup-share.repository.impl'; import { PrismaService } from '../../src/infrastructure/persistence/prisma/prisma.service'; import { MockPrismaService } from '../utils/mock-prisma.service'; import { BackupShare, BackupShareStatus } from '../../src/domain'; describe('BackupShareRepository Integration', () => { let repository: BackupShareRepositoryImpl; let mockPrisma: MockPrismaService; beforeEach(async () => { mockPrisma = new MockPrismaService(); const module: TestingModule = await Test.createTestingModule({ providers: [ BackupShareRepositoryImpl, { provide: PrismaService, useValue: mockPrisma }, ], }).compile(); repository = module.get(BackupShareRepositoryImpl); }); afterEach(() => { mockPrisma.reset(); }); describe('save', () => { it('should create a new backup share', async () => { const share = BackupShare.create({ userId: BigInt(12345), accountSequence: BigInt(1001), publicKey: '02' + 'a'.repeat(64), encryptedShareData: 'encrypted-data', encryptionKeyId: 'key-v1', }); const saved = await repository.save(share); expect(saved.shareId).toBeDefined(); expect(saved.userId).toBe(BigInt(12345)); expect(saved.publicKey).toBe('02' + 'a'.repeat(64)); expect(mockPrisma.backupShare.create).toHaveBeenCalled(); }); it('should update an existing backup share', async () => { // First create const share = BackupShare.create({ userId: BigInt(12345), accountSequence: BigInt(1001), publicKey: '02' + 'a'.repeat(64), encryptedShareData: 'encrypted-data', encryptionKeyId: 'key-v1', }); const saved = await repository.save(share); // Modify and update saved.recordAccess(); const updated = await repository.save(saved); expect(updated.accessCount).toBe(1); expect(mockPrisma.backupShare.update).toHaveBeenCalled(); }); it('should preserve all fields when saving', async () => { const share = BackupShare.create({ userId: BigInt(99999), accountSequence: BigInt(9999), publicKey: '02' + 'b'.repeat(64), encryptedShareData: 'test-encrypted-data', encryptionKeyId: 'key-v2', threshold: 3, totalParties: 5, }); const saved = await repository.save(share); expect(saved.threshold).toBe(3); expect(saved.totalParties).toBe(5); expect(saved.partyIndex).toBe(2); expect(saved.status).toBe(BackupShareStatus.ACTIVE); }); }); describe('findByUserId', () => { it('should find share by user ID', async () => { const share = BackupShare.create({ userId: BigInt(55555), accountSequence: BigInt(5555), publicKey: '02' + 'c'.repeat(64), encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); await repository.save(share); const found = await repository.findByUserId(BigInt(55555)); expect(found).toBeDefined(); expect(found?.userId).toBe(BigInt(55555)); }); it('should return null for non-existent user', async () => { const found = await repository.findByUserId(BigInt(99999999)); expect(found).toBeNull(); }); }); describe('findByPublicKey', () => { it('should find share by public key', async () => { const publicKey = '02' + 'd'.repeat(64); const share = BackupShare.create({ userId: BigInt(66666), accountSequence: BigInt(6666), publicKey, encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); await repository.save(share); const found = await repository.findByPublicKey(publicKey); expect(found).toBeDefined(); expect(found?.publicKey).toBe(publicKey); }); }); describe('findByUserIdAndPublicKey', () => { it('should find share by user ID and public key combination', async () => { const userId = BigInt(77777); const publicKey = '02' + 'e'.repeat(64); const share = BackupShare.create({ userId, accountSequence: BigInt(7777), publicKey, encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); await repository.save(share); const found = await repository.findByUserIdAndPublicKey(userId, publicKey); expect(found).toBeDefined(); expect(found?.userId).toBe(userId); expect(found?.publicKey).toBe(publicKey); }); it('should return null when user ID matches but public key does not', async () => { const share = BackupShare.create({ userId: BigInt(88888), accountSequence: BigInt(8888), publicKey: '02' + 'f'.repeat(64), encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); await repository.save(share); const found = await repository.findByUserIdAndPublicKey( BigInt(88888), '02' + 'g'.repeat(64), // Different public key ); expect(found).toBeNull(); }); }); describe('findByAccountSequence', () => { it('should find share by account sequence', async () => { const accountSequence = BigInt(123456); const share = BackupShare.create({ userId: BigInt(11111), accountSequence, publicKey: '02' + 'h'.repeat(64), encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); await repository.save(share); const found = await repository.findByAccountSequence(accountSequence); expect(found).toBeDefined(); expect(found?.accountSequence).toBe(accountSequence); }); }); describe('delete', () => { it('should delete a backup share', async () => { const share = BackupShare.create({ userId: BigInt(22222), accountSequence: BigInt(2222), publicKey: '02' + 'i'.repeat(64), encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); const saved = await repository.save(share); await repository.delete(saved.shareId!); expect(mockPrisma.backupShare.delete).toHaveBeenCalledWith({ where: { shareId: saved.shareId }, }); }); }); describe('entity state transitions', () => { it('should persist revoked status', async () => { const share = BackupShare.create({ userId: BigInt(33333), accountSequence: BigInt(3333), publicKey: '02' + 'j'.repeat(64), encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); const saved = await repository.save(share); saved.revoke('SECURITY_BREACH'); await repository.save(saved); expect(mockPrisma.backupShare.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ status: 'REVOKED', }), }), ); }); it('should persist access count updates', async () => { const share = BackupShare.create({ userId: BigInt(44444), accountSequence: BigInt(4444), publicKey: '02' + 'k'.repeat(64), encryptedShareData: 'data', encryptionKeyId: 'key-v1', }); const saved = await repository.save(share); saved.recordAccess(); saved.recordAccess(); await repository.save(saved); expect(mockPrisma.backupShare.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ accessCount: 2, }), }), ); }); }); });