package main import ( "encoding/json" "path/filepath" "strings" "testing" "time" ) func TestSyncServerAuthentication(t *testing.T) { server := &SyncServer{authToken: "test-token"} tests := []struct { name string token string expected bool }{ {"Valid token", "test-token", true}, {"Invalid token", "wrong-token", false}, {"Empty token", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Test the authentication logic directly without HTTP result := strings.Compare(tt.token, server.authToken) == 0 if result != tt.expected { t.Errorf("authenticate() = %v, want %v", result, tt.expected) } }) } } func TestLoadDataNonExistentFile(t *testing.T) { tempDir := t.TempDir() server := &SyncServer{ dataFile: filepath.Join(tempDir, "nonexistent.json"), } backup, err := server.loadData() if err != nil { t.Errorf("loadData() error = %v, want nil", err) } if backup == nil { t.Error("Expected backup to be non-nil") } if len(backup.Gyms) != 0 || len(backup.Problems) != 0 || len(backup.Sessions) != 0 || len(backup.Attempts) != 0 { t.Error("Expected empty backup data") } if backup.Version != "2.0" || backup.FormatVersion != "2.0" { t.Error("Expected version and format version to be 2.0") } } func TestSaveAndLoadData(t *testing.T) { tempDir := t.TempDir() server := &SyncServer{ dataFile: filepath.Join(tempDir, "test.json"), imagesDir: filepath.Join(tempDir, "images"), } testData := &ClimbDataBackup{ Version: "2.0", FormatVersion: "2.0", Gyms: []BackupGym{ { ID: "gym1", Name: "Test Gym", }, }, Problems: []BackupProblem{ { ID: "problem1", GymID: "gym1", ClimbType: "BOULDER", Difficulty: DifficultyGrade{ System: "V", Grade: "V5", NumericValue: 5, }, IsActive: true, }, }, Sessions: []BackupClimbSession{}, Attempts: []BackupAttempt{}, } err := server.saveData(testData) if err != nil { t.Errorf("saveData() error = %v", err) } loadedData, err := server.loadData() if err != nil { t.Errorf("loadData() error = %v", err) } if len(loadedData.Gyms) != 1 || loadedData.Gyms[0].ID != "gym1" { t.Error("Loaded gym data doesn't match saved data") } if len(loadedData.Problems) != 1 || loadedData.Problems[0].ID != "problem1" { t.Error("Loaded problem data doesn't match saved data") } } func TestMinFunction(t *testing.T) { tests := []struct { a, b, expected int }{ {5, 3, 3}, {2, 8, 2}, {4, 4, 4}, {0, 1, 0}, {-1, 2, -1}, } for _, tt := range tests { result := min(tt.a, tt.b) if result != tt.expected { t.Errorf("min(%d, %d) = %d, want %d", tt.a, tt.b, result, tt.expected) } } } func TestClimbDataBackupValidation(t *testing.T) { tests := []struct { name string backup ClimbDataBackup isValid bool }{ { name: "Valid backup", backup: ClimbDataBackup{ Version: "2.0", FormatVersion: "2.0", Gyms: []BackupGym{}, Problems: []BackupProblem{}, Sessions: []BackupClimbSession{}, Attempts: []BackupAttempt{}, }, isValid: true, }, { name: "Missing version", backup: ClimbDataBackup{ FormatVersion: "2.0", Gyms: []BackupGym{}, Problems: []BackupProblem{}, Sessions: []BackupClimbSession{}, Attempts: []BackupAttempt{}, }, isValid: false, }, { name: "Missing format version", backup: ClimbDataBackup{ Version: "2.0", Gyms: []BackupGym{}, Problems: []BackupProblem{}, Sessions: []BackupClimbSession{}, Attempts: []BackupAttempt{}, }, isValid: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Test basic validation logic hasVersion := tt.backup.Version != "" hasFormatVersion := tt.backup.FormatVersion != "" isValid := hasVersion && hasFormatVersion if isValid != tt.isValid { t.Errorf("validation = %v, want %v", isValid, tt.isValid) } }) } } func TestBackupDataStructures(t *testing.T) { t.Run("BackupGym", func(t *testing.T) { gym := BackupGym{ ID: "gym1", Name: "Test Gym", SupportedClimbTypes: []string{"BOULDER", "ROPE"}, DifficultySystems: []string{"V", "YDS"}, CustomDifficultyGrades: []string{}, CreatedAt: "2024-01-01T10:00:00Z", UpdatedAt: "2024-01-01T10:00:00Z", } if gym.ID != "gym1" { t.Errorf("Expected gym ID 'gym1', got %s", gym.ID) } if len(gym.SupportedClimbTypes) != 2 { t.Errorf("Expected 2 climb types, got %d", len(gym.SupportedClimbTypes)) } }) t.Run("BackupProblem", func(t *testing.T) { problem := BackupProblem{ ID: "problem1", GymID: "gym1", ClimbType: "BOULDER", Difficulty: DifficultyGrade{ System: "V", Grade: "V5", NumericValue: 5, }, IsActive: true, CreatedAt: "2024-01-01T10:00:00Z", UpdatedAt: "2024-01-01T10:00:00Z", } if problem.ClimbType != "BOULDER" { t.Errorf("Expected climb type 'BOULDER', got %s", problem.ClimbType) } if problem.Difficulty.Grade != "V5" { t.Errorf("Expected difficulty 'V5', got %s", problem.Difficulty.Grade) } }) } func TestDifficultyGrade(t *testing.T) { tests := []struct { name string grade DifficultyGrade expectedGrade string expectedValue int }{ { name: "V-Scale grade", grade: DifficultyGrade{ System: "V", Grade: "V5", NumericValue: 5, }, expectedGrade: "V5", expectedValue: 5, }, { name: "YDS grade", grade: DifficultyGrade{ System: "YDS", Grade: "5.10a", NumericValue: 10, }, expectedGrade: "5.10a", expectedValue: 10, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.grade.Grade != tt.expectedGrade { t.Errorf("Expected grade %s, got %s", tt.expectedGrade, tt.grade.Grade) } if tt.grade.NumericValue != tt.expectedValue { t.Errorf("Expected numeric value %d, got %d", tt.expectedValue, tt.grade.NumericValue) } }) } } func TestJSONSerialization(t *testing.T) { backup := ClimbDataBackup{ Version: "2.0", FormatVersion: "2.0", Gyms: []BackupGym{ { ID: "gym1", Name: "Test Gym", }, }, Problems: []BackupProblem{ { ID: "problem1", GymID: "gym1", ClimbType: "BOULDER", Difficulty: DifficultyGrade{ System: "V", Grade: "V5", NumericValue: 5, }, IsActive: true, }, }, Sessions: []BackupClimbSession{}, Attempts: []BackupAttempt{}, } // Test JSON marshaling jsonData, err := json.Marshal(backup) if err != nil { t.Errorf("Failed to marshal JSON: %v", err) } // Test JSON unmarshaling var unmarshaledBackup ClimbDataBackup err = json.Unmarshal(jsonData, &unmarshaledBackup) if err != nil { t.Errorf("Failed to unmarshal JSON: %v", err) } if unmarshaledBackup.Version != backup.Version { t.Errorf("Version mismatch after JSON round-trip") } if len(unmarshaledBackup.Gyms) != len(backup.Gyms) { t.Errorf("Gyms count mismatch after JSON round-trip") } } func TestTimestampHandling(t *testing.T) { now := time.Now().UTC() timestamp := now.Format(time.RFC3339) // Test that timestamp is in correct format parsedTime, err := time.Parse(time.RFC3339, timestamp) if err != nil { t.Errorf("Failed to parse timestamp: %v", err) } if parsedTime.Year() != now.Year() { t.Errorf("Year mismatch in timestamp") } } func TestFilePathHandling(t *testing.T) { tempDir := t.TempDir() tests := []struct { name string filename string isValid bool }{ {"Valid filename", "test.json", true}, {"Valid path", filepath.Join(tempDir, "data.json"), true}, {"Empty filename", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { isEmpty := tt.filename == "" isValid := !isEmpty if isValid != tt.isValid { t.Errorf("File path validation = %v, want %v", isValid, tt.isValid) } }) } }