package services import ( "context" "database/sql" "encoding/json" "testing" "github.com/DATA-DOG/go-sqlmock" ) func TestNewSettingsService(t *testing.T) { db, _, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewSettingsService(db) if service == nil { t.Error("Expected service, got nil") } if service.db != db { t.Error("Expected db to be set") } } func TestSettingsService_GetSettings(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewSettingsService(db) ctx := context.Background() t.Run("returns setting value", func(t *testing.T) { expectedValue := map[string]interface{}{"theme": "dark", "lang": "pt-BR"} jsonBytes, _ := json.Marshal(expectedValue) mock.ExpectQuery("SELECT value FROM system_settings WHERE key"). WithArgs("ui_config"). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow(jsonBytes)) result, err := service.GetSettings(ctx, "ui_config") if err != nil { t.Fatalf("Unexpected error: %v", err) } if result == nil { t.Fatal("Expected result, got nil") } var parsed map[string]interface{} if err := json.Unmarshal(result, &parsed); err != nil { t.Fatalf("Failed to parse result: %v", err) } if parsed["theme"] != "dark" { t.Errorf("Expected theme='dark', got '%v'", parsed["theme"]) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("returns nil when not found", func(t *testing.T) { mock.ExpectQuery("SELECT value FROM system_settings WHERE key"). WithArgs("non_existent"). WillReturnError(sql.ErrNoRows) result, err := service.GetSettings(ctx, "non_existent") if err != nil { t.Fatalf("Unexpected error: %v", err) } if result != nil { t.Errorf("Expected nil, got %v", result) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("returns error on db failure", func(t *testing.T) { mock.ExpectQuery("SELECT value FROM system_settings WHERE key"). WithArgs("test_key"). WillReturnError(sql.ErrConnDone) _, err := service.GetSettings(ctx, "test_key") if err == nil { t.Error("Expected error, got nil") } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) } func TestSettingsService_SaveSettings(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewSettingsService(db) ctx := context.Background() t.Run("saves setting successfully", func(t *testing.T) { value := map[string]interface{}{"enabled": true, "timeout": 30} mock.ExpectExec("INSERT INTO system_settings"). WithArgs("feature_flags", sqlmock.AnyArg()). WillReturnResult(sqlmock.NewResult(1, 1)) err := service.SaveSettings(ctx, "feature_flags", value) if err != nil { t.Fatalf("Unexpected error: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("upserts existing setting", func(t *testing.T) { value := map[string]string{"version": "2.0"} mock.ExpectExec("INSERT INTO system_settings"). WithArgs("app_config", sqlmock.AnyArg()). WillReturnResult(sqlmock.NewResult(0, 1)) err := service.SaveSettings(ctx, "app_config", value) if err != nil { t.Fatalf("Unexpected error: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("returns error on db failure", func(t *testing.T) { mock.ExpectExec("INSERT INTO system_settings"). WithArgs("bad_key", sqlmock.AnyArg()). WillReturnError(sql.ErrConnDone) err := service.SaveSettings(ctx, "bad_key", "value") if err == nil { t.Error("Expected error, got nil") } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("handles unmarshalable value", func(t *testing.T) { // Channels cannot be marshaled to JSON badValue := make(chan int) err := service.SaveSettings(ctx, "bad_marshal", badValue) if err == nil { t.Error("Expected marshal error, got nil") } }) }