229 lines
6.0 KiB
TypeScript
229 lines
6.0 KiB
TypeScript
/**
|
|
* In-Memory Media Resolver Adapter
|
|
*
|
|
* Stub implementation for testing purposes.
|
|
* Resolves MediaReference objects to fake URLs without external dependencies.
|
|
*
|
|
* Part of the adapters layer, implementing the MediaResolverPort interface.
|
|
*/
|
|
|
|
import { MediaResolverPort } from '@core/ports/media/MediaResolverPort';
|
|
import { MediaReference } from '@core/domain/media/MediaReference';
|
|
|
|
/**
|
|
* Configuration for InMemoryMediaResolverAdapter
|
|
*/
|
|
export interface InMemoryMediaResolverConfig {
|
|
/**
|
|
* Base URL to use for generated URLs
|
|
* @default 'https://fake-media.example.com'
|
|
*/
|
|
baseUrl?: string;
|
|
|
|
/**
|
|
* Whether to simulate network delays
|
|
* @default false
|
|
*/
|
|
simulateDelay?: boolean;
|
|
|
|
/**
|
|
* Delay in milliseconds when simulateDelay is true
|
|
* @default 50
|
|
*/
|
|
delayMs?: number;
|
|
|
|
/**
|
|
* Whether to return null for certain reference types (simulating missing media)
|
|
* @default false
|
|
*/
|
|
simulateMissingMedia?: boolean;
|
|
}
|
|
|
|
/**
|
|
* In-Memory Media Resolver Adapter
|
|
*
|
|
* Stub implementation that resolves media references to fake URLs.
|
|
* Designed for use in tests and development environments.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* const adapter = new InMemoryMediaResolverAdapter({
|
|
* baseUrl: 'https://test.example.com',
|
|
* simulateDelay: true
|
|
* });
|
|
*
|
|
* const ref = MediaReference.createSystemDefault('avatar');
|
|
* const url = await adapter.resolve(ref);
|
|
* // Returns: '/media/default/male-default-avatar.png'
|
|
* ```
|
|
*/
|
|
export class InMemoryMediaResolverAdapter implements MediaResolverPort {
|
|
private readonly config: Required<InMemoryMediaResolverConfig>;
|
|
|
|
constructor(config: InMemoryMediaResolverConfig = {}) {
|
|
this.config = {
|
|
baseUrl: config.baseUrl ?? 'https://fake-media.example.com',
|
|
simulateDelay: config.simulateDelay ?? false,
|
|
delayMs: config.delayMs ?? 50,
|
|
simulateMissingMedia: config.simulateMissingMedia ?? false,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Resolve a media reference to a path-only URL
|
|
*
|
|
* @param ref - The media reference to resolve
|
|
* @returns Promise resolving to path string or null
|
|
*/
|
|
async resolve(ref: MediaReference): Promise<string | null> {
|
|
// Simulate network delay if configured
|
|
if (this.config.simulateDelay) {
|
|
await this.delay(this.config.delayMs);
|
|
}
|
|
|
|
// Simulate missing media for some cases
|
|
if (this.config.simulateMissingMedia && this.shouldReturnNull()) {
|
|
return null;
|
|
}
|
|
|
|
switch (ref.type) {
|
|
case 'system-default':
|
|
let filename: string;
|
|
if (ref.variant === 'avatar' && ref.avatarVariant) {
|
|
filename = `${ref.avatarVariant}-default-avatar.png`;
|
|
} else if (ref.variant === 'avatar') {
|
|
filename = `neutral-default-avatar.png`;
|
|
} else {
|
|
filename = `${ref.variant}.png`;
|
|
}
|
|
return `/media/default/${filename}`;
|
|
|
|
case 'generated':
|
|
// Parse the generationRequestId to extract type and id
|
|
// Format: "{type}-{id}" where id can contain hyphens
|
|
if (ref.generationRequestId) {
|
|
const firstHyphenIndex = ref.generationRequestId.indexOf('-');
|
|
if (firstHyphenIndex !== -1) {
|
|
const type = ref.generationRequestId.substring(0, firstHyphenIndex);
|
|
const id = ref.generationRequestId.substring(firstHyphenIndex + 1);
|
|
|
|
// Use the correct API routes
|
|
if (type === 'team') {
|
|
return `/media/teams/${id}/logo`;
|
|
} else if (type === 'league') {
|
|
return `/media/leagues/${id}/logo`;
|
|
} else if (type === 'driver') {
|
|
return `/media/avatar/${id}`;
|
|
}
|
|
// Fallback for other types
|
|
return `/media/generated/${type}/${id}`;
|
|
}
|
|
}
|
|
// Fallback for unexpected format
|
|
return null;
|
|
|
|
case 'uploaded':
|
|
return `/media/uploaded/${ref.mediaId}`;
|
|
|
|
case 'none':
|
|
return null;
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simulate network delay
|
|
*/
|
|
private delay(ms: number): Promise<void> {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
/**
|
|
* Determine if this reference should return null (simulating missing media)
|
|
*/
|
|
private shouldReturnNull(): boolean {
|
|
// Randomly return null for 20% of cases
|
|
return Math.random() < 0.2;
|
|
}
|
|
|
|
/**
|
|
* Get the configured base URL
|
|
*/
|
|
getBaseUrl(): string {
|
|
return this.config.baseUrl;
|
|
}
|
|
|
|
/**
|
|
* Update configuration
|
|
*/
|
|
updateConfig(config: Partial<InMemoryMediaResolverConfig>): void {
|
|
Object.assign(this.config, config);
|
|
}
|
|
|
|
/**
|
|
* Reset to default configuration
|
|
*/
|
|
reset(): void {
|
|
this.config.baseUrl = 'https://fake-media.example.com';
|
|
this.config.simulateDelay = false;
|
|
this.config.delayMs = 50;
|
|
this.config.simulateMissingMedia = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Factory function to create a configured in-memory resolver
|
|
*/
|
|
export function createInMemoryResolver(
|
|
config: InMemoryMediaResolverConfig = {}
|
|
): MediaResolverPort {
|
|
return new InMemoryMediaResolverAdapter(config);
|
|
}
|
|
|
|
/**
|
|
* Pre-configured resolver for common test scenarios
|
|
*/
|
|
export const TestResolvers = {
|
|
/**
|
|
* Fast resolver with no delays
|
|
*/
|
|
fast: () => new InMemoryMediaResolverAdapter({
|
|
baseUrl: 'https://test.example.com',
|
|
simulateDelay: false,
|
|
}),
|
|
|
|
/**
|
|
* Slow resolver that simulates network latency
|
|
*/
|
|
slow: () => new InMemoryMediaResolverAdapter({
|
|
baseUrl: 'https://test.example.com',
|
|
simulateDelay: true,
|
|
delayMs: 200,
|
|
}),
|
|
|
|
/**
|
|
* Unreliable resolver that sometimes returns null
|
|
*/
|
|
unreliable: () => new InMemoryMediaResolverAdapter({
|
|
baseUrl: 'https://test.example.com',
|
|
simulateMissingMedia: true,
|
|
}),
|
|
|
|
/**
|
|
* Custom base URL resolver
|
|
*/
|
|
withBaseUrl: (baseUrl: string) => new InMemoryMediaResolverAdapter({
|
|
baseUrl,
|
|
simulateDelay: false,
|
|
}),
|
|
|
|
/**
|
|
* Local development resolver
|
|
*/
|
|
local: () => new InMemoryMediaResolverAdapter({
|
|
baseUrl: 'http://localhost:3000/media',
|
|
simulateDelay: false,
|
|
}),
|
|
} as const; |