253 lines
7.8 KiB
TypeScript
253 lines
7.8 KiB
TypeScript
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>(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,
|
|
}),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
});
|