import { describe, it, expect, vi, beforeEach } from 'vitest' import * as fc from 'fast-check' import ConfigurationService from '../../app/services/configuration_service.js' import ConfigurationModel from '../../app/models/configuration.js' import ConfigurationVersionModel from '../../app/models/configuration_version.js' /** * **Validates: Requirements 1.5** * * Property 2: Delete removes Configuration completely * After deleting a Configuration, findById returns null and all versions are also deleted. */ vi.mock('../../app/models/configuration.js', () => ({ default: { deleteByIdentifier: vi.fn(), findByIdentifier: vi.fn(), }, })) vi.mock('../../app/models/configuration_version.js', () => ({ default: { deleteByConfigurationIdentifier: vi.fn(), }, })) // Arbitrary: generates a valid configuration identifier (alphanumeric + hyphens/underscores, starts with alphanumeric) const identifierArb = fc .tuple( fc.constantFrom( ...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.split('') ), fc.array( fc.constantFrom( ...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split('') ), { minLength: 0, maxLength: 29 } ) ) .map(([first, rest]) => first + rest.join('')) describe('Property 2: Delete removes Configuration completely', () => { beforeEach(() => { vi.clearAllMocks() }) it('delete calls deleteByIdentifier on ConfigurationModel for any identifier', async () => { await fc.assert( fc.asyncProperty(identifierArb, async (identifier) => { vi.mocked(ConfigurationModel.deleteByIdentifier).mockResolvedValue(true) vi.mocked(ConfigurationVersionModel.deleteByConfigurationIdentifier).mockResolvedValue(0) const service = new ConfigurationService() const result = await service.delete(identifier) expect(result).toBe(true) expect(ConfigurationModel.deleteByIdentifier).toHaveBeenCalledWith(identifier) vi.clearAllMocks() }), { numRuns: 100 } ) }) it('delete calls deleteByConfigurationIdentifier on ConfigurationVersionModel when configuration exists', async () => { await fc.assert( fc.asyncProperty(identifierArb, async (identifier) => { vi.mocked(ConfigurationModel.deleteByIdentifier).mockResolvedValue(true) vi.mocked(ConfigurationVersionModel.deleteByConfigurationIdentifier).mockResolvedValue(3) const service = new ConfigurationService() await service.delete(identifier) expect(ConfigurationVersionModel.deleteByConfigurationIdentifier).toHaveBeenCalledWith( identifier ) vi.clearAllMocks() }), { numRuns: 100 } ) }) it('delete does NOT call deleteByConfigurationIdentifier when configuration does not exist', async () => { await fc.assert( fc.asyncProperty(identifierArb, async (identifier) => { vi.mocked(ConfigurationModel.deleteByIdentifier).mockResolvedValue(false) const service = new ConfigurationService() const result = await service.delete(identifier) expect(result).toBe(false) expect( ConfigurationVersionModel.deleteByConfigurationIdentifier ).not.toHaveBeenCalled() vi.clearAllMocks() }), { numRuns: 100 } ) }) it('after delete returns true, findById returns null for any identifier', async () => { await fc.assert( fc.asyncProperty(identifierArb, async (identifier) => { vi.mocked(ConfigurationModel.deleteByIdentifier).mockResolvedValue(true) vi.mocked(ConfigurationVersionModel.deleteByConfigurationIdentifier).mockResolvedValue(0) vi.mocked(ConfigurationModel.findByIdentifier).mockResolvedValue(null) const service = new ConfigurationService() await service.delete(identifier) const found = await service.findById(identifier) expect(found).toBeNull() expect(ConfigurationModel.findByIdentifier).toHaveBeenCalledWith(identifier) vi.clearAllMocks() }), { numRuns: 100 } ) }) })