@@ -2,9 +2,11 @@ package handlers
22
33import (
44 "html/template"
5+ "io/ioutil"
56 "net/http"
67 "net/http/httptest"
78 "os"
9+ "path/filepath"
810 "testing"
911 "time"
1012
@@ -634,4 +636,213 @@ func TestCountCSVRecords(t *testing.T) {
634636 if count != 0 {
635637 t .Errorf ("countCSVRecords() for non-existent file = %d, expected 0" , count )
636638 }
639+
640+ // Test optimized CSV record counting
641+ countOpt := handler .countCSVRecordsOptimized (testFile )
642+ if countOpt != expectedCount {
643+ t .Errorf ("countCSVRecordsOptimized() = %d, expected %d" , countOpt , expectedCount )
644+ }
645+
646+ // Test optimized counting with non-existent file
647+ countOptNonExistent := handler .countCSVRecordsOptimized (nonExistentFile )
648+ if countOptNonExistent != 0 {
649+ t .Errorf ("countCSVRecordsOptimized() for non-existent file = %d, expected 0" , countOptNonExistent )
650+ }
651+ }
652+
653+ func TestCacheSystem (t * testing.T ) {
654+ // Create test configuration
655+ cfg := & config.Config {}
656+ log := logger .NewLogger ()
657+ keyringMgr , _ := keyring .NewManager (keyring .MemoryBackend )
658+ tokenManager := auth .NewTokenManager (cfg , log , keyringMgr )
659+ templates := template .New ("" )
660+
661+ // Create exports handler
662+ handler := NewExportsHandler (cfg , log , tokenManager , templates )
663+
664+ // Test cache initialization
665+ if handler .cache == nil {
666+ t .Error ("Cache should be initialized" )
667+ }
668+
669+ if handler .cache .cacheTTL != 5 * time .Minute {
670+ t .Errorf ("Expected cache TTL of 5 minutes, got %v" , handler .cache .cacheTTL )
671+ }
672+
673+ // Test cache with empty exports dir
674+ exports := handler .getExportsWithCache (1 , 10 )
675+ if len (exports ) != 0 {
676+ t .Errorf ("Expected 0 exports for non-existent dir, got %d" , len (exports ))
677+ }
678+ }
679+
680+ func TestLazyLoading (t * testing.T ) {
681+ // Create test configuration
682+ cfg := & config.Config {}
683+ log := logger .NewLogger ()
684+ keyringMgr , _ := keyring .NewManager (keyring .MemoryBackend )
685+ tokenManager := auth .NewTokenManager (cfg , log , keyringMgr )
686+ templates := template .New ("" )
687+
688+ // Create temporary exports directory
689+ tempDir , err := ioutil .TempDir ("" , "test_exports_*" )
690+ if err != nil {
691+ t .Fatalf ("Failed to create temp dir: %v" , err )
692+ }
693+ defer os .RemoveAll (tempDir )
694+
695+ // Set exports directory
696+ cfg .Letterboxd .ExportDir = tempDir
697+ handler := NewExportsHandler (cfg , log , tokenManager , templates )
698+
699+ // Create test export directories with different dates
700+ recentDir := filepath .Join (tempDir , "export_2025-07-29_10-00" )
701+ olderDir := filepath .Join (tempDir , "export_2024-01-01_10-00" )
702+
703+ if err := os .MkdirAll (recentDir , 0755 ); err != nil {
704+ t .Fatalf ("Failed to create recent dir: %v" , err )
705+ }
706+ if err := os .MkdirAll (olderDir , 0755 ); err != nil {
707+ t .Fatalf ("Failed to create older dir: %v" , err )
708+ }
709+
710+ // Create test CSV files
711+ recentCSV := filepath .Join (recentDir , "watched.csv" )
712+
713+ if err := ioutil .WriteFile (recentCSV , []byte ("Title,Year\n Movie1,2025\n " ), 0644 ); err != nil {
714+ t .Fatalf ("Failed to write recent CSV: %v" , err )
715+ }
716+
717+ // Test date parsing
718+ parsedTime := handler .parseDirTime ("export_2025-07-29_10-00" )
719+ if parsedTime .IsZero () {
720+ t .Error ("Failed to parse directory time" )
721+ }
722+
723+ // Test optimized directory processing
724+ exportItem := handler .processExportDirectoryOptimized (recentDir , "export_2025-07-29_10-00" )
725+ if exportItem == nil {
726+ t .Error ("Expected export item from optimized processing" )
727+ } else {
728+ if exportItem .Type != "watched" {
729+ t .Errorf ("Expected type 'watched', got '%s'" , exportItem .Type )
730+ }
731+ if exportItem .Status != "completed" {
732+ t .Errorf ("Expected status 'completed', got '%s'" , exportItem .Status )
733+ }
734+ }
735+
736+ // Test optimized CSV file processing
737+ csvItem := handler .processCSVFileOptimized (recentCSV , "watched.csv" )
738+ if csvItem == nil {
739+ t .Error ("Expected CSV item from optimized processing" )
740+ } else {
741+ if csvItem .Type != "watched" {
742+ t .Errorf ("Expected type 'watched', got '%s'" , csvItem .Type )
743+ }
744+ }
745+ }
746+
747+ func TestExportUtilityFunctions (t * testing.T ) {
748+ // Create test configuration
749+ cfg := & config.Config {}
750+ log := logger .NewLogger ()
751+ keyringMgr , _ := keyring .NewManager (keyring .MemoryBackend )
752+ tokenManager := auth .NewTokenManager (cfg , log , keyringMgr )
753+ templates := template .New ("" )
754+ handler := NewExportsHandler (cfg , log , tokenManager , templates )
755+
756+ // Test parse export type
757+ tests := []struct {
758+ filename string
759+ expected string
760+ }{
761+ {"watched.csv" , "watched" },
762+ {"collection.csv" , "collection" },
763+ {"shows.csv" , "shows" },
764+ {"ratings.csv" , "ratings" },
765+ {"watchlist.csv" , "watchlist" },
766+ {"unknown.csv" , "" },
767+ }
768+
769+ for _ , test := range tests {
770+ result := handler .parseExportType (test .filename )
771+ if result != test .expected {
772+ t .Errorf ("parseExportType(%s): expected %s, got %s" , test .filename , test .expected , result )
773+ }
774+ }
775+
776+ // Test file size formatting
777+ testSizes := []struct {
778+ size int64
779+ expected string
780+ }{
781+ {0 , "0 B" },
782+ {500 , "500 B" },
783+ {1024 , "1.0 KB" },
784+ {1536 , "1.5 KB" },
785+ {1048576 , "1.0 MB" },
786+ }
787+
788+ for _ , test := range testSizes {
789+ result := handler .formatFileSize (test .size )
790+ if result != test .expected {
791+ t .Errorf ("formatFileSize(%d): expected %s, got %s" , test .size , test .expected , result )
792+ }
793+ }
794+
795+ // Test getIntParam
796+ req := httptest .NewRequest ("GET" , "/?page=5&limit=20&invalid=abc" , nil )
797+
798+ page := handler .getIntParam (req , "page" , 1 )
799+ if page != 5 {
800+ t .Errorf ("getIntParam(page): expected 5, got %d" , page )
801+ }
802+
803+ limit := handler .getIntParam (req , "limit" , 10 )
804+ if limit != 20 {
805+ t .Errorf ("getIntParam(limit): expected 20, got %d" , limit )
806+ }
807+
808+ defaultVal := handler .getIntParam (req , "missing" , 42 )
809+ if defaultVal != 42 {
810+ t .Errorf ("getIntParam(missing): expected 42, got %d" , defaultVal )
811+ }
812+
813+ invalidVal := handler .getIntParam (req , "invalid" , 99 )
814+ if invalidVal != 99 {
815+ t .Errorf ("getIntParam(invalid): expected 99, got %d" , invalidVal )
816+ }
817+
818+ // Test apply filters
819+ exports := []ExportItem {
820+ {Type : "watched" , Status : "completed" },
821+ {Type : "ratings" , Status : "completed" },
822+ {Type : "watched" , Status : "failed" },
823+ }
824+
825+ // Filter by type
826+ watchedOnly := handler .applyFilters (exports , "watched" , "" )
827+ if len (watchedOnly ) != 2 {
828+ t .Errorf ("applyFilters type watched: expected 2, got %d" , len (watchedOnly ))
829+ }
830+
831+ // Filter by status
832+ completedOnly := handler .applyFilters (exports , "" , "completed" )
833+ if len (completedOnly ) != 2 {
834+ t .Errorf ("applyFilters status completed: expected 2, got %d" , len (completedOnly ))
835+ }
836+
837+ // Filter by both
838+ watchedCompleted := handler .applyFilters (exports , "watched" , "completed" )
839+ if len (watchedCompleted ) != 1 {
840+ t .Errorf ("applyFilters watched+completed: expected 1, got %d" , len (watchedCompleted ))
841+ }
842+
843+ // No filters
844+ allExports := handler .applyFilters (exports , "" , "" )
845+ if len (allExports ) != 3 {
846+ t .Errorf ("applyFilters no filter: expected 3, got %d" , len (allExports ))
847+ }
637848}
0 commit comments