1+ package api
2+
3+ import (
4+ "context"
5+ "encoding/json"
6+ "net/http"
7+ "net/http/httptest"
8+ "testing"
9+ "time"
10+
11+ "github.com/JohanDevl/Export_Trakt_4_Letterboxd/pkg/config"
12+ "github.com/JohanDevl/Export_Trakt_4_Letterboxd/pkg/logger"
13+ "github.com/JohanDevl/Export_Trakt_4_Letterboxd/pkg/performance/cache"
14+ "github.com/stretchr/testify/assert"
15+ "github.com/stretchr/testify/require"
16+ )
17+
18+ // Mock logger for testing
19+ type mockLogger struct {
20+ logs []string
21+ }
22+
23+ func (m * mockLogger ) Debug (msg string , fields ... map [string ]interface {}) {
24+ m .logs = append (m .logs , "DEBUG: " + msg )
25+ }
26+
27+ func (m * mockLogger ) Info (msg string , fields ... map [string ]interface {}) {
28+ m .logs = append (m .logs , "INFO: " + msg )
29+ }
30+
31+ func (m * mockLogger ) Warn (msg string , fields ... map [string ]interface {}) {
32+ m .logs = append (m .logs , "WARN: " + msg )
33+ }
34+
35+ func (m * mockLogger ) Error (msg string , fields ... map [string ]interface {}) {
36+ m .logs = append (m .logs , "ERROR: " + msg )
37+ }
38+
39+ func (m * mockLogger ) Fatal (msg string , fields ... map [string ]interface {}) {
40+ m .logs = append (m .logs , "FATAL: " + msg )
41+ }
42+
43+ func (m * mockLogger ) Debugf (msg string , data map [string ]interface {}) {
44+ m .logs = append (m .logs , "DEBUGF: " + msg )
45+ }
46+
47+ func (m * mockLogger ) Infof (msg string , data map [string ]interface {}) {
48+ m .logs = append (m .logs , "INFOF: " + msg )
49+ }
50+
51+ func (m * mockLogger ) Warnf (msg string , data map [string ]interface {}) {
52+ m .logs = append (m .logs , "WARNF: " + msg )
53+ }
54+
55+ func (m * mockLogger ) Errorf (msg string , data map [string ]interface {}) {
56+ m .logs = append (m .logs , "ERRORF: " + msg )
57+ }
58+
59+ func (m * mockLogger ) SetLogLevel (level string ) {}
60+ func (m * mockLogger ) SetLogFile (file string ) error { return nil }
61+ func (m * mockLogger ) SetTranslator (t logger.Translator ) {}
62+
63+ // Test configuration helper
64+ func createTestConfig () * config.Config {
65+ return & config.Config {
66+ Trakt : config.TraktConfig {
67+ ClientID : "test_client_id" ,
68+ ClientSecret : "test_client_secret" ,
69+ APIBaseURL : "https://api.trakt.tv" ,
70+ ExtendedInfo : "full" ,
71+ },
72+ Export : config.ExportConfig {
73+ Format : "csv" ,
74+ DateFormat : "2006-01-02" ,
75+ HistoryMode : "aggregated" ,
76+ },
77+ Logging : config.LoggingConfig {
78+ Level : "info" ,
79+ },
80+ }
81+ }
82+
83+ // Test Basic Client Creation
84+ func TestBasicClient_Creation (t * testing.T ) {
85+ cfg := createTestConfig ()
86+ log := & mockLogger {}
87+
88+ client := NewClient (cfg , log )
89+ assert .NotNil (t , client )
90+
91+ // Test configuration access
92+ assert .Equal (t , cfg , client .GetConfig ())
93+ }
94+
95+ // Test ClientFactory with Basic Client
96+ func TestClientFactory_Basic (t * testing.T ) {
97+ cfg := createTestConfig ()
98+ log := & mockLogger {}
99+
100+ factory := NewClientFactory (ClientFactoryConfig {
101+ Logger : log ,
102+ })
103+
104+ client , err := factory .CreateBasicClient (cfg )
105+ require .NoError (t , err )
106+ assert .NotNil (t , client )
107+ }
108+
109+ // Test Optimized Client Creation
110+ func TestOptimizedClient_Creation (t * testing.T ) {
111+ cfg := createTestConfig ()
112+ log := & mockLogger {}
113+
114+ optimizedCfg := OptimizedClientConfig {
115+ Config : cfg ,
116+ Logger : log ,
117+ WorkerPoolSize : 5 ,
118+ CacheConfig : cache.CacheConfig {
119+ Capacity : 100 ,
120+ TTL : time .Hour ,
121+ },
122+ }
123+
124+ client := NewOptimizedClient (optimizedCfg )
125+ assert .NotNil (t , client )
126+ defer client .Close ()
127+ }
128+
129+ // Test ClientFactory with Optimized Client
130+ func TestClientFactory_Optimized (t * testing.T ) {
131+ cfg := createTestConfig ()
132+ log := & mockLogger {}
133+
134+ factory := NewClientFactory (ClientFactoryConfig {
135+ Logger : log ,
136+ })
137+
138+ optimizedCfg := OptimizedClientConfig {
139+ Config : cfg ,
140+ Logger : log ,
141+ WorkerPoolSize : 3 ,
142+ CacheConfig : cache.CacheConfig {
143+ Capacity : 50 ,
144+ TTL : 30 * time .Minute ,
145+ },
146+ }
147+
148+ client , err := factory .CreateOptimizedClient (optimizedCfg )
149+ require .NoError (t , err )
150+ assert .NotNil (t , client )
151+
152+ // Test OptimizedTraktAPIClient interface
153+ optimizedClient , ok := client .(OptimizedTraktAPIClient )
154+ assert .True (t , ok )
155+ assert .NotNil (t , optimizedClient .GetCacheStats ())
156+ }
157+
158+ // Test API endpoints with mock server
159+ func TestClient_APIEndpoints (t * testing.T ) {
160+ // Create mock server
161+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
162+ w .Header ().Set ("Content-Type" , "application/json" )
163+
164+ switch r .URL .Path {
165+ case "/sync/watched/movies" :
166+ movies := []Movie {
167+ {
168+ Movie : MovieInfo {
169+ Title : "Test Movie" ,
170+ Year : 2023 ,
171+ IDs : MovieIDs {
172+ TMDB : 12345 ,
173+ IMDB : "tt1234567" ,
174+ },
175+ },
176+ Plays : 1 ,
177+ LastWatchedAt : "2023-12-01T10:00:00.000Z" ,
178+ },
179+ }
180+ json .NewEncoder (w ).Encode (movies )
181+ case "/sync/ratings/movies" :
182+ ratings := []Rating {
183+ {
184+ Movie : MovieInfo {
185+ Title : "Rated Movie" ,
186+ Year : 2023 ,
187+ IDs : MovieIDs {
188+ TMDB : 67890 ,
189+ },
190+ },
191+ Rating : 8 ,
192+ RatedAt : "2023-12-01T12:00:00.000Z" ,
193+ },
194+ }
195+ json .NewEncoder (w ).Encode (ratings )
196+ default :
197+ http .NotFound (w , r )
198+ }
199+ }))
200+ defer server .Close ()
201+
202+ // Create client with test server URL
203+ cfg := createTestConfig ()
204+ cfg .Trakt .APIBaseURL = server .URL
205+ log := & mockLogger {}
206+
207+ client := NewClient (cfg , log )
208+
209+ // Test GetWatchedMovies
210+ movies , err := client .GetWatchedMovies ()
211+ require .NoError (t , err )
212+ assert .Len (t , movies , 1 )
213+ assert .Equal (t , "Test Movie" , movies [0 ].Movie .Title )
214+ assert .Equal (t , 2023 , movies [0 ].Movie .Year )
215+
216+ // Test GetRatings
217+ ratings , err := client .GetRatings ()
218+ require .NoError (t , err )
219+ assert .Len (t , ratings , 1 )
220+ assert .Equal (t , "Rated Movie" , ratings [0 ].Movie .Title )
221+ assert .Equal (t , 8 , ratings [0 ].Rating )
222+ }
223+
224+ // Test data structure JSON serialization
225+ func TestMovieInfo_JSON (t * testing.T ) {
226+ movie := MovieInfo {
227+ Title : "Test JSON Movie" ,
228+ Year : 2023 ,
229+ IDs : MovieIDs {
230+ TMDB : 98765 ,
231+ IMDB : "tt9876543" ,
232+ },
233+ }
234+
235+ jsonData , err := json .Marshal (movie )
236+ require .NoError (t , err )
237+ assert .Contains (t , string (jsonData ), "Test JSON Movie" )
238+ assert .Contains (t , string (jsonData ), "98765" )
239+ }
240+
241+ // Test error handling
242+ func TestClient_ErrorHandling (t * testing.T ) {
243+ // Create server that returns errors
244+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
245+ http .Error (w , "API Error" , http .StatusInternalServerError )
246+ }))
247+ defer server .Close ()
248+
249+ cfg := createTestConfig ()
250+ cfg .Trakt .APIBaseURL = server .URL
251+ log := & mockLogger {}
252+
253+ client := NewClient (cfg , log )
254+
255+ // Should handle API errors gracefully
256+ _ , err := client .GetWatchedMovies ()
257+ assert .Error (t , err )
258+ }
259+
260+ // Test OptimizedClient batch processing
261+ func TestOptimizedClient_BatchRequests (t * testing.T ) {
262+ server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
263+ w .Header ().Set ("Content-Type" , "application/json" )
264+ w .Write ([]byte (`[{"movie":{"title":"Batch Movie","year":2023},"plays":1}]` ))
265+ }))
266+ defer server .Close ()
267+
268+ cfg := createTestConfig ()
269+ cfg .Trakt .APIBaseURL = server .URL
270+ log := & mockLogger {}
271+
272+ optimizedCfg := OptimizedClientConfig {
273+ Config : cfg ,
274+ Logger : log ,
275+ WorkerPoolSize : 3 ,
276+ CacheConfig : cache.CacheConfig {
277+ Capacity : 50 ,
278+ TTL : time .Minute ,
279+ },
280+ }
281+
282+ client := NewOptimizedClient (optimizedCfg )
283+ defer client .Close ()
284+
285+ ctx := context .Background ()
286+ requests := []BatchRequest {
287+ {Endpoint : "/sync/watched/movies" },
288+ {Endpoint : "/sync/collection/movies" },
289+ }
290+
291+ results , err := client .ProcessBatchRequests (ctx , requests )
292+ require .NoError (t , err )
293+ assert .Len (t , results , 2 )
294+
295+ for _ , result := range results {
296+ assert .NotNil (t , result .Request )
297+ assert .True (t , result .Index >= 0 )
298+ }
299+ }
0 commit comments