topfans/backend/services/notificationService/service/user_device_service_test.go

118 lines
3.4 KiB
Go

package service
import (
"context"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
notifPb "github.com/topfans/backend/pkg/proto/notification"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// setupUserDeviceDB 测试 DB 入口;无 DB 时返回 false 由 caller skip。
func setupUserDeviceDB(t *testing.T) (*gorm.DB, bool) {
t.Helper()
dsn := os.Getenv("TEST_DB_DSN")
if dsn == "" {
dsn = "postgres://postgres:postgres@localhost:5432/top_fans_test?sslmode=disable"
}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, false
}
sqlDB, err := db.DB()
if err != nil {
return nil, false
}
if err := sqlDB.Ping(); err != nil {
return nil, false
}
return db, true
}
// TestUserDeviceService_RegisterDevice_Validation 覆盖校验路径(无需 DB)。
// 仅覆盖 service 层校验分支;真 DB upsert 见 TestUserDeviceService_UpsertDeactivate。
func TestUserDeviceService_RegisterDevice_Validation(t *testing.T) {
svc := NewUserDeviceService(nil)
tests := []struct {
name string
userID int64
req *notifPb.RegisterDeviceRequest
}{
{"missing user", 0, &notifPb.RegisterDeviceRequest{Cid: "c"}},
{"missing cid", 1, &notifPb.RegisterDeviceRequest{}},
{"nil req", 1, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := svc.RegisterDevice(context.Background(), tt.userID, tt.req)
assert.Error(t, err)
})
}
}
// TestUserDeviceService_UnregisterDevice_Validation 覆盖校验路径。
func TestUserDeviceService_UnregisterDevice_Validation(t *testing.T) {
svc := NewUserDeviceService(nil)
_, err := svc.UnregisterDevice(context.Background(), 0, &notifPb.UnregisterDeviceRequest{})
require.Error(t, err)
_, err = svc.UnregisterDevice(context.Background(), 1, nil)
require.Error(t, err)
}
// TestUserDeviceService_UpsertDeactivate 需要真实 DB;缺 DB 时 skip。
func TestUserDeviceService_UpsertDeactivate(t *testing.T) {
db, ok := setupUserDeviceDB(t)
if !ok {
t.Skip("skipping: test DB not available")
}
// 确保表存在(与 main.go AutoMigrate 同源)
require.NoError(t, db.AutoMigrate(struct{}{})) // noop
svc := NewUserDeviceService(db)
ctx := context.Background()
userID := int64(880100)
cid := "test-cid-001"
// cleanup
t.Cleanup(func() {
_ = db.WithContext(ctx).Exec(`DELETE FROM public.user_devices WHERE cid = $1`, cid).Error
})
// 1) 首次注册
resp1, err := svc.RegisterDevice(ctx, userID, &notifPb.RegisterDeviceRequest{
Cid: cid, Platform: "ios", AppVersion: "1.0.0", DeviceModel: "iPhone14,2",
})
require.NoError(t, err)
require.NotNil(t, resp1)
assert.Greater(t, resp1.Id, int64(0))
// 2) 重复注册同 cid:应更新(归属/版本变化)
resp2, err := svc.RegisterDevice(ctx, userID, &notifPb.RegisterDeviceRequest{
Cid: cid, Platform: "android", AppVersion: "1.0.1", DeviceModel: "Pixel7",
})
require.NoError(t, err)
assert.Equal(t, resp1.Id, resp2.Id, "同 cid upsert 后 id 应不变")
// 3) 拉取活跃 cid 应包含该 cid
cids, err := svc.ListActiveCIDs(ctx, userID)
require.NoError(t, err)
assert.Contains(t, cids, cid)
// 4) 注销
unreg, err := svc.UnregisterDevice(ctx, userID, &notifPb.UnregisterDeviceRequest{Cid: cid})
require.NoError(t, err)
assert.GreaterOrEqual(t, unreg.Affected, int32(1))
// 5) 注销后不再出现在活跃列表
cids2, err := svc.ListActiveCIDs(ctx, userID)
require.NoError(t, err)
assert.NotContains(t, cids2, cid)
}