import { DataRelationManager } from '../../core/utils/data-relations' import { vezaFaker } from '../../core/utils/faker-config' import type { User, Audio, Playlist } from '../../core/schemas/database' /** * Web service fixtures - MSW handlers and localStorage data */ export class WebFixtures { private static currentUser: User | null = null private static authToken: string | null = null /** * Initialize web fixtures with authentication */ static async initialize(userId?: string): Promise { console.log('🌐 Initializing web fixtures...') // Get or create current user if (userId) { this.currentUser = DataRelationManager.getAll().users.get(userId) || null } if (!this.currentUser) { // Create a test user const { UserGenerator } = await import('../../core/generators/users') this.currentUser = UserGenerator.generateTestUser() DataRelationManager.registerUser(this.currentUser) } // Generate auth token this.authToken = this.generateAuthToken(this.currentUser) // Seed localStorage this.seedLocalStorage() console.log(`✅ Web fixtures initialized for user: ${this.currentUser.username}`) } /** * Seed localStorage with authentication data */ private static seedLocalStorage(): void { if (typeof window === 'undefined') return // Skip in Node.js environment const authData = { access_token: this.authToken, refresh_token: this.generateRefreshToken(), user: this.currentUser, expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 24 hours } // Zustand auth store format const authStore = { state: { user: this.currentUser, isAuthenticated: true, isLoading: false, error: null, tokens: { access: this.authToken, refresh: authData.refresh_token } }, version: 0 } localStorage.setItem('auth-storage', JSON.stringify(authStore)) localStorage.setItem('access_token', this.authToken!) localStorage.setItem('refresh_token', authData.refresh_token) localStorage.setItem('user', JSON.stringify(this.currentUser)) // User preferences const preferences = { theme: this.currentUser!.preferences.theme, language: this.currentUser!.preferences.language, notifications: this.currentUser!.preferences.notifications } localStorage.setItem('user-preferences', JSON.stringify(preferences)) // Recent activity const recentActivity = this.generateRecentActivity() localStorage.setItem('recent-activity', JSON.stringify(recentActivity)) } /** * Generate MSW API handlers */ static getMSWHandlers(): any[] { const { http, HttpResponse } = require('msw') return [ // Authentication endpoints http.post('/api/auth/login', async ({ request }: any) => { const body = await request.json() if (body.email === this.currentUser?.email || body.email === 'test@example.com') { return HttpResponse.json({ access_token: this.authToken, refresh_token: this.generateRefreshToken(), user: this.currentUser, expires_in: 86400 }) } return HttpResponse.json( { error: 'Invalid credentials' }, { status: 401 } ) }), http.post('/api/auth/register', async ({ request }: any) => { const body = await request.json() // Create new user const { UserGenerator } = await import('../../core/generators/users') const newUser = UserGenerator.generate({ role: 'user', status: 'active', emailVerified: true }) // Override with provided data newUser.username = body.username newUser.email = body.email newUser.firstName = body.firstName || body.first_name newUser.lastName = body.lastName || body.last_name return HttpResponse.json({ access_token: this.generateAuthToken(newUser), refresh_token: this.generateRefreshToken(), user: newUser, expires_in: 86400 }, { status: 201 }) }), http.post('/api/auth/refresh', async ({ request }: any) => { const body = await request.json() if (body.refresh_token) { return HttpResponse.json({ access_token: this.generateAuthToken(this.currentUser!), refresh_token: this.generateRefreshToken(), expires_in: 86400 }) } return HttpResponse.json( { error: 'Invalid refresh token' }, { status: 401 } ) }), http.post('/api/auth/logout', () => { return HttpResponse.json({ message: 'Logged out successfully' }) }), // User endpoints http.get('/api/users/me', ({ request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } return HttpResponse.json(this.currentUser) }), http.put('/api/users/me', async ({ request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const body = await request.json() // Update current user Object.assign(this.currentUser!, body) this.currentUser!.updatedAt = new Date() return HttpResponse.json(this.currentUser) }), // Dashboard data http.get('/api/dashboard', ({ request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } return HttpResponse.json(this.generateDashboardData()) }), // Audio endpoints http.get('/api/tracks', ({ request }: any) => { const url = new URL(request.url) const limit = parseInt(url.searchParams.get('limit') || '20') const offset = parseInt(url.searchParams.get('offset') || '0') const genre = url.searchParams.get('genre') let tracks = Array.from(DataRelationManager.getAll().tracks.values()) if (genre) { tracks = tracks.filter(track => track.genre === genre) } const paginatedTracks = tracks.slice(offset, offset + limit) return HttpResponse.json({ tracks: paginatedTracks, total: tracks.length, limit, offset, hasMore: offset + limit < tracks.length }) }), http.get('/api/tracks/:id', ({ params }: any) => { const track = DataRelationManager.getAll().tracks.get(params.id) if (!track) { return HttpResponse.json({ error: 'Track not found' }, { status: 404 }) } return HttpResponse.json(track) }), // Playlist endpoints http.get('/api/playlists', ({ request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userPlaylists = DataRelationManager.getUserPlaylists(this.currentUser!.id) return HttpResponse.json(userPlaylists) }), http.post('/api/playlists', async ({ request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const body = await request.json() const { AudioGenerator } = await import('../../core/generators/audio') const playlist = AudioGenerator.generatePlaylist({ createdById: this.currentUser!.id, trackCount: 0, visibility: body.visibility || 'private' }) // Override with provided data playlist.title = body.title playlist.description = body.description DataRelationManager.registerPlaylist(playlist) return HttpResponse.json(playlist, { status: 201 }) }), // Chat endpoints http.get('/api/conversations', ({ request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userConversations = DataRelationManager.getUserConversations(this.currentUser!.id) return HttpResponse.json(userConversations) }), http.get('/api/conversations/:id/messages', ({ params, request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const messages = DataRelationManager.getConversationMessages(params.id) return HttpResponse.json(messages) }), // Search endpoint http.get('/api/search', ({ request }: any) => { const url = new URL(request.url) const query = url.searchParams.get('q') const type = url.searchParams.get('type') // 'tracks', 'users', 'playlists', 'all' if (!query) { return HttpResponse.json({ results: [] }) } const results = this.performSearch(query, type || undefined) return HttpResponse.json(results) }), // Analytics endpoints http.get('/api/analytics/overview', ({ request }: any) => { const authHeader = request.headers.get('authorization') if (!authHeader || !authHeader.includes(this.authToken!)) { return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 }) } return HttpResponse.json(this.generateAnalyticsData()) }) ] } /** * Generate dashboard data */ private static generateDashboardData(): any { const userTracks = DataRelationManager.getUserTracks(this.currentUser!.id) const userPlaylists = DataRelationManager.getUserPlaylists(this.currentUser!.id) const userConversations = DataRelationManager.getUserConversations(this.currentUser!.id) return { user: this.currentUser, stats: { tracks: userTracks.length, playlists: userPlaylists.length, conversations: userConversations.length, totalPlays: userTracks.reduce((sum, track) => sum + track.stats.plays, 0), totalLikes: userTracks.reduce((sum, track) => sum + track.stats.likes, 0) }, recentTracks: userTracks.slice(0, 5), recentPlaylists: userPlaylists.slice(0, 3), recentActivity: this.generateRecentActivity(), recommendations: this.generateRecommendations() } } /** * Generate analytics data */ private static generateAnalyticsData(): any { const userTracks = DataRelationManager.getUserTracks(this.currentUser!.id) const stats = DataRelationManager.getStatistics() return { overview: { totalPlays: userTracks.reduce((sum, track) => sum + track.stats.plays, 0), totalLikes: userTracks.reduce((sum, track) => sum + track.stats.likes, 0), totalShares: userTracks.reduce((sum, track) => sum + track.stats.shares, 0), totalDownloads: userTracks.reduce((sum, track) => sum + track.stats.downloads, 0) }, trends: { daily: this.generateTrendData(7), weekly: this.generateTrendData(4), monthly: this.generateTrendData(12) }, topTracks: userTracks .sort((a, b) => b.stats.plays - a.stats.plays) .slice(0, 10), demographics: { byGenre: this.generateGenreStats(userTracks), byLocation: this.generateLocationStats(), byAge: this.generateAgeStats() }, platform: { totalUsers: stats.users, totalTracks: stats.tracks, totalPlaylists: stats.playlists } } } /** * Perform search across different content types */ private static performSearch(query: string, type?: string): any { const allData = DataRelationManager.getAll() const results: any = { tracks: [], users: [], playlists: [], total: 0 } const searchTerm = query.toLowerCase() if (!type || type === 'tracks' || type === 'all') { results.tracks = Array.from(allData.tracks.values()) .filter(track => track.title.toLowerCase().includes(searchTerm) || track.artist.toLowerCase().includes(searchTerm) || track.genre.toLowerCase().includes(searchTerm) || track.tags.some(tag => tag.toLowerCase().includes(searchTerm)) ) .slice(0, 20) } if (!type || type === 'users' || type === 'all') { results.users = Array.from(allData.users.values()) .filter(user => user.username.toLowerCase().includes(searchTerm) || user.firstName.toLowerCase().includes(searchTerm) || user.lastName.toLowerCase().includes(searchTerm) || (user.displayName && user.displayName.toLowerCase().includes(searchTerm)) ) .slice(0, 20) } if (!type || type === 'playlists' || type === 'all') { results.playlists = Array.from(allData.playlists.values()) .filter(playlist => playlist.title.toLowerCase().includes(searchTerm) || (playlist.description && playlist.description.toLowerCase().includes(searchTerm)) || playlist.tags.some(tag => tag.toLowerCase().includes(searchTerm)) ) .slice(0, 20) } results.total = results.tracks.length + results.users.length + results.playlists.length return results } /** * Helper methods */ private static generateAuthToken(user: User): string { const header = { alg: 'HS256', typ: 'JWT' } const payload = { sub: user.id, username: user.username, email: user.email, role: user.role, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 86400 // 24 hours } // Simple base64 encoding for mock JWT const encodedHeader = btoa(JSON.stringify(header)) const encodedPayload = btoa(JSON.stringify(payload)) const signature = btoa(`mock-signature-${user.id}`) return `${encodedHeader}.${encodedPayload}.${signature}` } private static generateRefreshToken(): string { return `refresh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` } private static generateRecentActivity(): any[] { return Array.from({ length: 10 }, () => ({ id: vezaFaker.string.uuid(), type: vezaFaker.helpers.arrayElement(['play', 'like', 'share', 'comment', 'follow']), description: vezaFaker.helpers.arrayElement([ 'a écouté un nouveau morceau', 'a aimé une chanson', 'a partagé une playlist', 'a commenté un titre', 'suit maintenant un artiste' ]), timestamp: vezaFaker.date.recent({ days: 7 }), metadata: { trackId: vezaFaker.string.uuid(), trackTitle: vezaFaker.music.songTitle() } })) } private static generateRecommendations(): any[] { const allTracks = Array.from(DataRelationManager.getAll().tracks.values()) const randomTracks = vezaFaker.helpers.arrayElements(allTracks, 5) return randomTracks.map(track => ({ ...track, reason: vezaFaker.helpers.arrayElement([ 'Basé sur vos goûts musicaux', 'Populaire dans votre région', 'Recommandé par des amis', 'Tendance cette semaine', 'Artiste similaire' ]) })) } private static generateTrendData(periods: number): any[] { return Array.from({ length: periods }, (_, i) => ({ period: i + 1, plays: vezaFaker.number.int({ min: 100, max: 1000 }), likes: vezaFaker.number.int({ min: 10, max: 100 }), shares: vezaFaker.number.int({ min: 5, max: 50 }) })) } private static generateGenreStats(tracks: Audio[]): any { const genreCounts: Record = {} tracks.forEach(track => { genreCounts[track.genre] = (genreCounts[track.genre] || 0) + 1 }) return Object.entries(genreCounts).map(([genre, count]) => ({ genre, count, percentage: Math.round((count / tracks.length) * 100) })) } private static generateLocationStats(): any[] { const locations = ['Paris', 'Lyon', 'Marseille', 'Toulouse', 'Nice'] return locations.map(location => ({ location, count: vezaFaker.number.int({ min: 50, max: 500 }), percentage: vezaFaker.number.int({ min: 5, max: 30 }) })) } private static generateAgeStats(): any[] { const ageGroups = ['18-24', '25-34', '35-44', '45-54', '55+'] return ageGroups.map(ageGroup => ({ ageGroup, count: vezaFaker.number.int({ min: 100, max: 1000 }), percentage: vezaFaker.number.int({ min: 10, max: 35 }) })) } /** * Cleanup methods */ static reset(): void { this.currentUser = null this.authToken = null if (typeof window !== 'undefined') { localStorage.removeItem('auth-storage') localStorage.removeItem('access_token') localStorage.removeItem('refresh_token') localStorage.removeItem('user') localStorage.removeItem('user-preferences') localStorage.removeItem('recent-activity') } } /** * Getters */ static getCurrentUser(): User | null { return this.currentUser } static getAuthToken(): string | null { return this.authToken } }