262 lines
8.9 KiB
TypeScript
262 lines
8.9 KiB
TypeScript
/**
|
|
* TDD Tests for MediaResolverPort interface contract
|
|
*
|
|
* Tests cover:
|
|
* - Interface contract compliance
|
|
* - Method signatures
|
|
* - Return types
|
|
* - Error handling behavior
|
|
*/
|
|
|
|
import { MediaReference } from '@core/domain/media/MediaReference';
|
|
|
|
// Mock interface for testing
|
|
interface MediaResolverPort {
|
|
resolve(ref: MediaReference, baseUrl: string): Promise<string | null>;
|
|
}
|
|
|
|
describe('MediaResolverPort', () => {
|
|
let mockResolver: MediaResolverPort;
|
|
|
|
beforeEach(() => {
|
|
// Create a mock implementation for testing
|
|
mockResolver = {
|
|
resolve: jest.fn(async (ref: MediaReference, baseUrl: string): Promise<string | null> => {
|
|
// Mock implementation that returns different URLs based on type
|
|
switch (ref.type) {
|
|
case 'system-default':
|
|
return `${baseUrl}/defaults/${ref.variant}`;
|
|
case 'generated':
|
|
return `${baseUrl}/generated/${ref.generationRequestId}`;
|
|
case 'uploaded':
|
|
return `${baseUrl}/media/${ref.mediaId}`;
|
|
case 'none':
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
})
|
|
};
|
|
});
|
|
|
|
describe('Interface Contract', () => {
|
|
it('should have a resolve method', () => {
|
|
expect(typeof mockResolver.resolve).toBe('function');
|
|
});
|
|
|
|
it('should accept MediaReference and string parameters', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
await expect(mockResolver.resolve(ref, baseUrl)).resolves.toBeDefined();
|
|
});
|
|
|
|
it('should return Promise<string | null>', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
expect(result === null || typeof result === 'string').toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('System Default Resolution', () => {
|
|
it('should resolve system-default avatar to correct URL', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/defaults/avatar');
|
|
});
|
|
|
|
it('should resolve system-default logo to correct URL', async () => {
|
|
const ref = MediaReference.createSystemDefault('logo');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/defaults/logo');
|
|
});
|
|
});
|
|
|
|
describe('Generated Resolution', () => {
|
|
it('should resolve generated reference to correct URL', async () => {
|
|
const ref = MediaReference.createGenerated('req-123');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/generated/req-123');
|
|
});
|
|
|
|
it('should handle generated reference with special characters', async () => {
|
|
const ref = MediaReference.createGenerated('req-abc-123_XYZ');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/generated/req-abc-123_XYZ');
|
|
});
|
|
});
|
|
|
|
describe('Uploaded Resolution', () => {
|
|
it('should resolve uploaded reference to correct URL', async () => {
|
|
const ref = MediaReference.createUploaded('media-456');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/media/media-456');
|
|
});
|
|
|
|
it('should handle uploaded reference with special characters', async () => {
|
|
const ref = MediaReference.createUploaded('media-abc-456_XYZ');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/media/media-abc-456_XYZ');
|
|
});
|
|
});
|
|
|
|
describe('None Resolution', () => {
|
|
it('should resolve none reference to null', async () => {
|
|
const ref = MediaReference.createNone();
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Base URL Handling', () => {
|
|
it('should handle base URL without trailing slash', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/defaults/avatar');
|
|
});
|
|
|
|
it('should handle base URL with trailing slash', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = 'https://api.example.com/';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
// Implementation should handle this consistently
|
|
expect(result).toBeTruthy();
|
|
});
|
|
|
|
it('should handle localhost URLs', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = 'http://localhost:3000';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('http://localhost:3000/defaults/avatar');
|
|
});
|
|
|
|
it('should handle relative URLs', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = '/api';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('/api/defaults/avatar');
|
|
});
|
|
});
|
|
|
|
describe('Error Handling', () => {
|
|
it('should handle null baseUrl gracefully', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
|
|
// This should not throw but handle gracefully
|
|
await expect(mockResolver.resolve(ref, null as any)).resolves.toBeDefined();
|
|
});
|
|
|
|
it('should handle empty baseUrl gracefully', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
|
|
// This should not throw but handle gracefully
|
|
await expect(mockResolver.resolve(ref, '')).resolves.toBeDefined();
|
|
});
|
|
|
|
it('should handle undefined baseUrl gracefully', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
|
|
// This should not throw but handle gracefully
|
|
await expect(mockResolver.resolve(ref, undefined as any)).resolves.toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Edge Cases', () => {
|
|
it('should handle very long media IDs', async () => {
|
|
const longId = 'a'.repeat(1000);
|
|
const ref = MediaReference.createUploaded(longId);
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe(`https://api.example.com/media/${longId}`);
|
|
});
|
|
|
|
it('should handle Unicode characters in IDs', async () => {
|
|
const ref = MediaReference.createUploaded('media-日本語-123');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const result = await mockResolver.resolve(ref, baseUrl);
|
|
|
|
expect(result).toBe('https://api.example.com/media/media-日本語-123');
|
|
});
|
|
|
|
it('should handle multiple calls with different references', async () => {
|
|
const refs = [
|
|
MediaReference.createSystemDefault('avatar'),
|
|
MediaReference.createGenerated('req-123'),
|
|
MediaReference.createUploaded('media-456'),
|
|
MediaReference.createNone()
|
|
];
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const results = await Promise.all(refs.map(ref => mockResolver.resolve(ref, baseUrl)));
|
|
|
|
expect(results).toEqual([
|
|
'https://api.example.com/defaults/avatar',
|
|
'https://api.example.com/generated/req-123',
|
|
'https://api.example.com/media/media-456',
|
|
null
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('Performance Considerations', () => {
|
|
it('should resolve quickly for simple cases', async () => {
|
|
const ref = MediaReference.createSystemDefault('avatar');
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const start = Date.now();
|
|
await mockResolver.resolve(ref, baseUrl);
|
|
const duration = Date.now() - start;
|
|
|
|
expect(duration).toBeLessThan(100); // Should be very fast
|
|
});
|
|
|
|
it('should handle concurrent resolutions', async () => {
|
|
const refs = Array.from({ length: 100 }, (_, i) =>
|
|
MediaReference.createUploaded(`media-${i}`)
|
|
);
|
|
const baseUrl = 'https://api.example.com';
|
|
|
|
const start = Date.now();
|
|
const results = await Promise.all(refs.map(ref => mockResolver.resolve(ref, baseUrl)));
|
|
const duration = Date.now() - start;
|
|
|
|
expect(results.length).toBe(100);
|
|
expect(duration).toBeLessThan(1000); // Should handle 100 concurrent calls quickly
|
|
});
|
|
});
|
|
}); |