package sync import ( "bookmann/internal/database" "context" "testing" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestOfflineDetector_DeviceStatusDetection(t *testing.T) { ctx := context.Background() db := setupTestDB(t) defer teardownTestDB(t, db) userID := createTestUser(t, db) deviceID := createTestDevice(t, db, userID) detector := NewOfflineDetector(db, nil) device, err := db.GetDevice(ctx, deviceID) require.NoError(t, err) status := detector.getDeviceStatus(device) assert.True(t, status.IsOnline, "device should be online initially") assert.Equal(t, device.DeviceName, status.DeviceName) assert.Equal(t, device.DeviceType, status.DeviceType) } func TestOfflineDetector_OfflineThreshold(t *testing.T) { ctx := context.Background() db := setupTestDB(t) defer teardownTestDB(t, db) userID := createTestUser(t, db) deviceID := createTestDevice(t, db, userID) _, err := db.UpdateDeviceLastSeen(ctx, deviceID) require.NoError(t, err) detector := NewOfflineDetector(db, nil) device, err := db.GetDevice(ctx, deviceID) require.NoError(t, err) // Simulate device being offline by manipulating the timestamp directly status := detector.getDeviceStatus(device) assert.True(t, status.IsOnline, "device should be online initially") } func TestOfflineDetector_ConstantValues(t *testing.T) { assert.Equal(t, 5*time.Minute, OnlineThreshold) assert.Equal(t, 2*time.Minute, OfflineCheckInterval) } func createTestUserForOffline(t *testing.T, db *database.Queries) pgtype.UUID { ctx := context.Background() userID := uuid.New() hashedPassword := "$2a$10$rKvZ.HZx3lLJ6IQCpH1lOukQ/xU8j5cH8mYhPY5YGfXllq5hG8y0Ou" _, err := db.CreateUser(ctx, database.CreateUserParams{ Email: "test-offline@example.com", Username: "testoffline", PasswordHash: hashedPassword, FirstName: pgtype.Text{String: "Test", Valid: true}, LastName: pgtype.Text{String: "Offline", Valid: true}, Role: "user", }) require.NoError(t, err) return pgtype.UUID{Bytes: userID, Valid: true} } func createTestDeviceForOffline(t *testing.T, db *database.Queries, userID pgtype.UUID) pgtype.UUID { ctx := context.Background() deviceID := uuid.New() authToken := "test-offline-token-" + deviceID.String() _, err := db.CreateDevice(ctx, database.CreateDeviceParams{ UserID: userID, DeviceName: "Test Offline Device", DeviceType: "koreader", DeviceIdentifier: deviceID.String(), AuthToken: authToken, SyncEnabled: pgtype.Bool{Bool: true, Valid: true}, }) require.NoError(t, err) return pgtype.UUID{Bytes: deviceID, Valid: true} } func TestOfflineDetector_GetDeviceStatus(t *testing.T) { ctx := context.Background() db := setupTestDB(t) defer teardownTestDB(t, db) userID := createTestUserForOffline(t, db) deviceID := createTestDeviceForOffline(t, db, userID) detector := NewOfflineDetector(db, nil) status, err := detector.GetDeviceStatus(ctx, deviceID) require.NoError(t, err) assert.NotNil(t, status) assert.Equal(t, "Test Offline Device", status.DeviceName) assert.Equal(t, "koreader", status.DeviceType) } func TestOfflineDetector_ForceReconnectDevice(t *testing.T) { ctx := context.Background() db := setupTestDB(t) defer teardownTestDB(t, db) userID := createTestUserForOffline(t, db) deviceID := createTestDeviceForOffline(t, db, userID) _, err := db.UpdateDeviceLastSeen(ctx, deviceID) require.NoError(t, err) detector := NewOfflineDetector(db, nil) err = detector.ForceReconnectDevice(ctx, deviceID) require.NoError(t, err) device, err := db.GetDevice(ctx, deviceID) require.NoError(t, err) assert.True(t, device.SyncEnabled.Bool, "device should be re-enabled after force reconnect") } func createTestUser(t *testing.T, db *database.Queries) pgtype.UUID { ctx := context.Background() userID := uuid.New() hashedPassword := "$2a$10$rKvZ.HZx3lLJ6IQCpH1lOukQ/xU8j5cH8mYhPY5YGfXllq5hG8y0Ou" _, err := db.CreateUser(ctx, database.CreateUserParams{ Email: "test@example.com", Username: "testuser", PasswordHash: hashedPassword, FirstName: pgtype.Text{String: "Test", Valid: true}, LastName: pgtype.Text{String: "User", Valid: true}, Role: "user", }) require.NoError(t, err) return pgtype.UUID{Bytes: userID, Valid: true} } func createTestDevice(t *testing.T, db *database.Queries, userID pgtype.UUID) pgtype.UUID { ctx := context.Background() deviceID := uuid.New() authToken := "test-auth-token-" + deviceID.String() _, err := db.CreateDevice(ctx, database.CreateDeviceParams{ UserID: userID, DeviceName: "Test Device", DeviceType: "koreader", DeviceIdentifier: deviceID.String(), AuthToken: authToken, }) require.NoError(t, err) return pgtype.UUID{Bytes: deviceID, Valid: true} }