Total rebrand across all Go API source files: - Go module path: casera-api -> honeydue-api - All imports updated (130+ files) - Docker: containers, images, networks renamed - Email templates: support email, noreply, icon URL - Domains: casera.app/mycrib.treytartt.com -> honeyDue.treytartt.com - Bundle IDs: com.tt.casera -> com.tt.honeyDue - IAP product IDs updated - Landing page, admin panel, config defaults - Seeds, CI workflows, Makefile, docs - Database table names preserved (no migration needed) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
127 lines
4.3 KiB
Go
127 lines
4.3 KiB
Go
package services
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/treytartt/honeydue-api/internal/models"
|
|
"github.com/treytartt/honeydue-api/internal/push"
|
|
"github.com/treytartt/honeydue-api/internal/repositories"
|
|
"github.com/treytartt/honeydue-api/internal/testutil"
|
|
)
|
|
|
|
func setupNotificationService(t *testing.T) (*NotificationService, *repositories.NotificationRepository) {
|
|
db := testutil.SetupTestDB(t)
|
|
notifRepo := repositories.NewNotificationRepository(db)
|
|
// pushClient is nil for testing (no actual push sends)
|
|
service := NewNotificationService(notifRepo, nil)
|
|
return service, notifRepo
|
|
}
|
|
|
|
func TestDeleteDevice_WrongUser_Returns403(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
notifRepo := repositories.NewNotificationRepository(db)
|
|
service := NewNotificationService(notifRepo, nil)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
attacker := testutil.CreateTestUser(t, db, "attacker", "attacker@test.com", "password")
|
|
|
|
// Register an iOS device for the owner
|
|
device := &models.APNSDevice{
|
|
UserID: &owner.ID,
|
|
Name: "Owner iPhone",
|
|
DeviceID: "device-123",
|
|
RegistrationID: "token-abc",
|
|
Active: true,
|
|
}
|
|
err := db.Create(device).Error
|
|
require.NoError(t, err)
|
|
|
|
// Attacker tries to deactivate the owner's device
|
|
err = service.DeleteDevice(device.ID, push.PlatformIOS, attacker.ID)
|
|
require.Error(t, err, "should not allow deleting another user's device")
|
|
testutil.AssertAppErrorCode(t, err, http.StatusForbidden)
|
|
|
|
// Verify the device is still active
|
|
var found models.APNSDevice
|
|
err = db.First(&found, device.ID).Error
|
|
require.NoError(t, err)
|
|
assert.True(t, found.Active, "device should still be active after failed deletion")
|
|
}
|
|
|
|
func TestDeleteDevice_CorrectUser_Succeeds(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
notifRepo := repositories.NewNotificationRepository(db)
|
|
service := NewNotificationService(notifRepo, nil)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
|
|
// Register an iOS device for the owner
|
|
device := &models.APNSDevice{
|
|
UserID: &owner.ID,
|
|
Name: "Owner iPhone",
|
|
DeviceID: "device-123",
|
|
RegistrationID: "token-abc",
|
|
Active: true,
|
|
}
|
|
err := db.Create(device).Error
|
|
require.NoError(t, err)
|
|
|
|
// Owner deactivates their own device
|
|
err = service.DeleteDevice(device.ID, push.PlatformIOS, owner.ID)
|
|
require.NoError(t, err, "owner should be able to deactivate their own device")
|
|
|
|
// Verify the device is now inactive
|
|
var found models.APNSDevice
|
|
err = db.First(&found, device.ID).Error
|
|
require.NoError(t, err)
|
|
assert.False(t, found.Active, "device should be deactivated")
|
|
}
|
|
|
|
func TestDeleteDevice_WrongUser_Android_Returns403(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
notifRepo := repositories.NewNotificationRepository(db)
|
|
service := NewNotificationService(notifRepo, nil)
|
|
|
|
owner := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
attacker := testutil.CreateTestUser(t, db, "attacker", "attacker@test.com", "password")
|
|
|
|
// Register an Android device for the owner
|
|
device := &models.GCMDevice{
|
|
UserID: &owner.ID,
|
|
Name: "Owner Pixel",
|
|
DeviceID: "device-456",
|
|
RegistrationID: "token-def",
|
|
CloudMessageType: "FCM",
|
|
Active: true,
|
|
}
|
|
err := db.Create(device).Error
|
|
require.NoError(t, err)
|
|
|
|
// Attacker tries to deactivate the owner's Android device
|
|
err = service.DeleteDevice(device.ID, push.PlatformAndroid, attacker.ID)
|
|
require.Error(t, err, "should not allow deleting another user's Android device")
|
|
testutil.AssertAppErrorCode(t, err, http.StatusForbidden)
|
|
|
|
// Verify the device is still active
|
|
var found models.GCMDevice
|
|
err = db.First(&found, device.ID).Error
|
|
require.NoError(t, err)
|
|
assert.True(t, found.Active, "Android device should still be active after failed deletion")
|
|
}
|
|
|
|
func TestDeleteDevice_NonExistent_Returns404(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
notifRepo := repositories.NewNotificationRepository(db)
|
|
service := NewNotificationService(notifRepo, nil)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "password")
|
|
|
|
err := service.DeleteDevice(99999, push.PlatformIOS, user.ID)
|
|
require.Error(t, err, "should return error for non-existent device")
|
|
testutil.AssertAppErrorCode(t, err, http.StatusNotFound)
|
|
}
|