package main import ( "bookhoard/internal/database" "bookhoard/internal/handlers" "bytes" "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestWithStructs demonstrates struct-based testing patterns // BEFORE: map[string]interface{} -> AFTER: handlers.* structs // BEFORE: No DB verification -> AFTER: Database verification func TestWithStructs(t *testing.T) { deviceSetup := setupDeviceTest(t) token := deviceSetup.UserToken // Create a device to test listing device := deviceSetup.CreateDevice(t, "Test Device", "koreader", "test-device-struct-123") t.Run("ListDevices with struct", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/devices", nil) req.Header.Set("Authorization", "Bearer "+token) rr := httptest.NewRecorder() deviceSetup.Server.Config.Handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code, "Should list devices") var response handlers.DeviceListResponse err := json.Unmarshal(rr.Body.Bytes(), &response) require.NoError(t, err, "Response should match DeviceListResponse schema") assert.GreaterOrEqual(t, len(response.Devices), 1, "Should have at least one device") firstDevice := response.Devices[0] assert.Equal(t, device.Name, firstDevice.DeviceName, "Should match device name") assert.Equal(t, device.Type, firstDevice.DeviceType, "Should match device type") }) t.Run("UpdateDevice with struct and DB verification", func(t *testing.T) { syncEnabled := false syncFreq := int32(15) updateRequest := map[string]interface{}{ "sync_enabled": &syncEnabled, "sync_frequency_minutes": &syncFreq, } updateBody, _ := json.Marshal(updateRequest) req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s", device.ID), bytes.NewBuffer(updateBody)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) rr := httptest.NewRecorder() deviceSetup.Server.Config.Handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code, "Should update device") var response map[string]interface{} err := json.Unmarshal(rr.Body.Bytes(), &response) require.NoError(t, err, "Response should unmarshal") assert.True(t, response["device_updated"].(bool), "Device should be updated") // NEW: Database verification pgDeviceID := pgtype.UUID{Bytes: [16]byte(device.ID), Valid: true} updatedDevice, err := deviceSetup.DB.GetDevice(context.Background(), pgDeviceID) require.NoError(t, err, "Device should exist in database after update") assert.Equal(t, syncEnabled, updatedDevice.SyncEnabled.Bool, "DB: Sync should be disabled") assert.Equal(t, syncFreq, updatedDevice.SyncFrequencyMinutes.Int32, "DB: Sync frequency should be updated") }) } // verifyDeviceUpdated is a helper function for database verification after device updates func verifyDeviceUpdated(t *testing.T, db *database.Queries, deviceID uuid.UUID, syncEnabled bool, syncFreq int32) { pgDeviceID := pgtype.UUID{Bytes: [16]byte(deviceID), Valid: true} device, err := db.GetDevice(context.Background(), pgDeviceID) require.NoError(t, err, "Device should exist in database") assert.Equal(t, syncEnabled, device.SyncEnabled.Bool, "DB: Sync enabled should match") assert.Equal(t, syncFreq, device.SyncFrequencyMinutes.Int32, "DB: Sync frequency should match") }