/** * TDD Tests for MediaReference value object * * Tests cover: * - Discriminated union validation * - Type-specific validation * - Serialization/deserialization * - Equality comparison * - Hash generation */ import { MediaReference } from '@core/domain/media/MediaReference'; describe('MediaReference', () => { describe('System Default Type', () => { it('should create system-default reference', () => { const ref = MediaReference.createSystemDefault(); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('avatar'); // default }); it('should create system-default with custom variant', () => { const ref = MediaReference.createSystemDefault('logo'); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('logo'); }); it('should create system-default with avatar variant', () => { const ref = MediaReference.createSystemDefault('avatar', 'male'); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('avatar'); expect(ref.avatarVariant).toBe('male'); }); it('should create system-default without avatar variant for logo', () => { const ref = MediaReference.createSystemDefault('logo', 'male'); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('logo'); expect(ref.avatarVariant).toBeUndefined(); }); it('should validate system-default type', () => { const ref = MediaReference.fromJSON({ type: 'system-default', variant: 'avatar' }); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('avatar'); }); it('should validate system-default with avatar variant', () => { const ref = MediaReference.fromJSON({ type: 'system-default', variant: 'avatar', avatarVariant: 'female' }); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('avatar'); expect(ref.avatarVariant).toBe('female'); }); it('should reject system-default with invalid variant', () => { expect(() => { MediaReference.fromJSON({ type: 'system-default', variant: 'invalid' as unknown as 'avatar' | 'logo' }); }).toThrow('Invalid variant'); }); it('should reject system-default with invalid avatar variant', () => { expect(() => { MediaReference.fromJSON({ type: 'system-default', variant: 'avatar', avatarVariant: 'invalid' as unknown as 'male' | 'female' | 'neutral' }); }).toThrow(); }); }); describe('New Static Methods', () => { it('should create system-default using systemDefault method', () => { const ref = MediaReference.systemDefault('avatar'); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('avatar'); }); it('should create system-default with avatar variant using systemDefault method', () => { const ref = MediaReference.systemDefault('male'); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('avatar'); expect(ref.avatarVariant).toBe('male'); }); it('should create generated using generated method', () => { const ref = MediaReference.generated('team', 'team-123'); expect(ref.type).toBe('generated'); expect(ref.generationRequestId).toBe('team-team-123'); }); }); describe('Generated Type', () => { it('should create generated reference with request ID', () => { const ref = MediaReference.createGenerated('req-123'); expect(ref.type).toBe('generated'); expect(ref.generationRequestId).toBe('req-123'); }); it('should validate generated type', () => { const ref = MediaReference.fromJSON({ type: 'generated', generationRequestId: 'req-456' }); expect(ref.type).toBe('generated'); expect(ref.generationRequestId).toBe('req-456'); }); it('should reject generated without request ID', () => { expect(() => { MediaReference.fromJSON({ type: 'generated' } as unknown as Record); }).toThrow('Generation request ID is required'); }); it('should reject generated with empty request ID', () => { expect(() => { MediaReference.fromJSON({ type: 'generated', generationRequestId: '' }); }).toThrow('Generation request ID is required'); }); }); describe('Uploaded Type', () => { it('should create uploaded reference with media ID', () => { const ref = MediaReference.createUploaded('media-789'); expect(ref.type).toBe('uploaded'); expect(ref.mediaId).toBe('media-789'); }); it('should validate uploaded type', () => { const ref = MediaReference.fromJSON({ type: 'uploaded', mediaId: 'media-abc' }); expect(ref.type).toBe('uploaded'); expect(ref.mediaId).toBe('media-abc'); }); it('should reject uploaded without media ID', () => { expect(() => { MediaReference.fromJSON({ type: 'uploaded' } as unknown as Record); }).toThrow('Media ID is required'); }); it('should reject uploaded with empty media ID', () => { expect(() => { MediaReference.fromJSON({ type: 'uploaded', mediaId: '' }); }).toThrow('Media ID is required'); }); }); describe('None Type', () => { it('should create none reference', () => { const ref = MediaReference.createNone(); expect(ref.type).toBe('none'); }); it('should validate none type', () => { const ref = MediaReference.fromJSON({ type: 'none' }); expect(ref.type).toBe('none'); }); it('should reject none with extra properties', () => { expect(() => { MediaReference.fromJSON({ type: 'none', mediaId: 'should-not-exist' } as unknown as Record); }).toThrow('None type should not have additional properties'); }); }); describe('Invalid Types', () => { it('should reject unknown type', () => { expect(() => { MediaReference.fromJSON({ type: 'unknown' } as unknown as Record); }).toThrow('Invalid type'); }); it('should reject missing type', () => { expect(() => { MediaReference.fromJSON({} as unknown as Record); }).toThrow('Type is required'); }); }); describe('Serialization', () => { it('should serialize system-default to JSON', () => { const ref = MediaReference.createSystemDefault('logo'); const json = ref.toJSON(); expect(json).toEqual({ type: 'system-default', variant: 'logo' }); }); it('should serialize system-default with avatar variant to JSON', () => { const ref = MediaReference.createSystemDefault('avatar', 'female'); const json = ref.toJSON(); expect(json).toEqual({ type: 'system-default', variant: 'avatar', avatarVariant: 'female' }); }); it('should deserialize system-default with avatar variant from JSON', () => { const json = { type: 'system-default', variant: 'avatar', avatarVariant: 'neutral' }; const ref = MediaReference.fromJSON(json as Record); expect(ref.type).toBe('system-default'); expect(ref.variant).toBe('avatar'); expect(ref.avatarVariant).toBe('neutral'); }); it('should serialize generated to JSON', () => { const ref = MediaReference.createGenerated('req-123'); const json = ref.toJSON(); expect(json).toEqual({ type: 'generated', generationRequestId: 'req-123' }); }); it('should serialize uploaded to JSON', () => { const ref = MediaReference.createUploaded('media-456'); const json = ref.toJSON(); expect(json).toEqual({ type: 'uploaded', mediaId: 'media-456' }); }); it('should serialize none to JSON', () => { const ref = MediaReference.createNone(); const json = ref.toJSON(); expect(json).toEqual({ type: 'none' }); }); it('should deserialize from JSON', () => { const json = { type: 'uploaded', mediaId: 'media-789' }; const ref = MediaReference.fromJSON(json); expect(ref.type).toBe('uploaded'); expect(ref.mediaId).toBe('media-789'); }); }); describe('Equality', () => { it('should be equal for same system-default with same variant', () => { const ref1 = MediaReference.createSystemDefault('avatar'); const ref2 = MediaReference.createSystemDefault('avatar'); expect(ref1.equals(ref2)).toBe(true); }); it('should not be equal for system-default with different variants', () => { const ref1 = MediaReference.createSystemDefault('avatar'); const ref2 = MediaReference.createSystemDefault('logo'); expect(ref1.equals(ref2)).toBe(false); }); it('should not be equal for system-default with different avatar variants', () => { const ref1 = MediaReference.createSystemDefault('avatar', 'male'); const ref2 = MediaReference.createSystemDefault('avatar', 'female'); expect(ref1.equals(ref2)).toBe(false); }); it('should be equal for system-default with same avatar variant', () => { const ref1 = MediaReference.createSystemDefault('avatar', 'male'); const ref2 = MediaReference.createSystemDefault('avatar', 'male'); expect(ref1.equals(ref2)).toBe(true); }); it('should not be equal for system-default with and without avatar variant', () => { const ref1 = MediaReference.createSystemDefault('avatar', 'male'); const ref2 = MediaReference.createSystemDefault('avatar'); expect(ref1.equals(ref2)).toBe(false); }); it('should be equal for same generated with same request ID', () => { const ref1 = MediaReference.createGenerated('req-123'); const ref2 = MediaReference.createGenerated('req-123'); expect(ref1.equals(ref2)).toBe(true); }); it('should not be equal for generated with different request IDs', () => { const ref1 = MediaReference.createGenerated('req-123'); const ref2 = MediaReference.createGenerated('req-456'); expect(ref1.equals(ref2)).toBe(false); }); it('should be equal for same uploaded with same media ID', () => { const ref1 = MediaReference.createUploaded('media-123'); const ref2 = MediaReference.createUploaded('media-123'); expect(ref1.equals(ref2)).toBe(true); }); it('should not be equal for uploaded with different media IDs', () => { const ref1 = MediaReference.createUploaded('media-123'); const ref2 = MediaReference.createUploaded('media-456'); expect(ref1.equals(ref2)).toBe(false); }); it('should be equal for none references', () => { const ref1 = MediaReference.createNone(); const ref2 = MediaReference.createNone(); expect(ref1.equals(ref2)).toBe(true); }); it('should not be equal for different types', () => { const ref1 = MediaReference.createSystemDefault(); const ref2 = MediaReference.createNone(); expect(ref1.equals(ref2)).toBe(false); }); it('should not be equal to non-MediaReference', () => { const ref = MediaReference.createSystemDefault(); expect(ref.equals({} as unknown as MediaReference)).toBe(false); expect(ref.equals(null as unknown as MediaReference)).toBe(false); expect(ref.equals(undefined as unknown as MediaReference)).toBe(false); }); }); describe('Hash', () => { it('should generate consistent hash for same system-default', () => { const ref1 = MediaReference.createSystemDefault('avatar'); const ref2 = MediaReference.createSystemDefault('avatar'); expect(ref1.hash()).toBe(ref2.hash()); }); it('should generate different hash for different variants', () => { const ref1 = MediaReference.createSystemDefault('avatar'); const ref2 = MediaReference.createSystemDefault('logo'); expect(ref1.hash()).not.toBe(ref2.hash()); }); it('should generate different hash for different avatar variants', () => { const ref1 = MediaReference.createSystemDefault('avatar', 'male'); const ref2 = MediaReference.createSystemDefault('avatar', 'female'); expect(ref1.hash()).not.toBe(ref2.hash()); }); it('should generate same hash for same avatar variant', () => { const ref1 = MediaReference.createSystemDefault('avatar', 'male'); const ref2 = MediaReference.createSystemDefault('avatar', 'male'); expect(ref1.hash()).toBe(ref2.hash()); }); it('should generate different hash for system-default with and without avatar variant', () => { const ref1 = MediaReference.createSystemDefault('avatar', 'male'); const ref2 = MediaReference.createSystemDefault('avatar'); expect(ref1.hash()).not.toBe(ref2.hash()); }); it('should generate consistent hash for same generated', () => { const ref1 = MediaReference.createGenerated('req-123'); const ref2 = MediaReference.createGenerated('req-123'); expect(ref1.hash()).toBe(ref2.hash()); }); it('should generate different hash for different request IDs', () => { const ref1 = MediaReference.createGenerated('req-123'); const ref2 = MediaReference.createGenerated('req-456'); expect(ref1.hash()).not.toBe(ref2.hash()); }); it('should generate consistent hash for same uploaded', () => { const ref1 = MediaReference.createUploaded('media-123'); const ref2 = MediaReference.createUploaded('media-123'); expect(ref1.hash()).toBe(ref2.hash()); }); it('should generate different hash for different media IDs', () => { const ref1 = MediaReference.createUploaded('media-123'); const ref2 = MediaReference.createUploaded('media-456'); expect(ref1.hash()).not.toBe(ref2.hash()); }); it('should generate same hash for none references', () => { const ref1 = MediaReference.createNone(); const ref2 = MediaReference.createNone(); expect(ref1.hash()).toBe(ref2.hash()); }); it('should generate different hash for different types', () => { const ref1 = MediaReference.createSystemDefault(); const ref2 = MediaReference.createNone(); expect(ref1.hash()).not.toBe(ref2.hash()); }); }); describe('Type Guards', () => { it('should correctly identify system-default type', () => { const ref = MediaReference.createSystemDefault(); expect(ref.type).toBe('system-default'); expect(ref.generationRequestId).toBeUndefined(); expect(ref.mediaId).toBeUndefined(); }); it('should correctly identify generated type', () => { const ref = MediaReference.createGenerated('req-123'); expect(ref.type).toBe('generated'); expect(ref.generationRequestId).toBe('req-123'); expect(ref.mediaId).toBeUndefined(); }); it('should correctly identify uploaded type', () => { const ref = MediaReference.createUploaded('media-123'); expect(ref.type).toBe('uploaded'); expect(ref.mediaId).toBe('media-123'); expect(ref.generationRequestId).toBeUndefined(); }); it('should correctly identify none type', () => { const ref = MediaReference.createNone(); expect(ref.type).toBe('none'); expect(ref.generationRequestId).toBeUndefined(); expect(ref.mediaId).toBeUndefined(); expect(ref.variant).toBeUndefined(); }); }); describe('Edge Cases', () => { it('should handle whitespace in IDs', () => { const ref = MediaReference.createGenerated(' req-123 '); expect(ref.generationRequestId).toBe('req-123'); }); it('should handle special characters in IDs', () => { const ref = MediaReference.createUploaded('media-abc-123_XYZ'); expect(ref.mediaId).toBe('media-abc-123_XYZ'); }); it('should preserve exact string values', () => { const id = 'CaseSensitive-ID_123'; const ref = MediaReference.createUploaded(id); expect(ref.mediaId).toBe(id); }); it('should handle JSON round-trip', () => { const original = MediaReference.createGenerated('req-999'); const json = original.toJSON(); const restored = MediaReference.fromJSON(json as Record); expect(restored.equals(original)).toBe(true); }); }); });