app-cloudep-product-service/pkg/repository/kyc_test.go

478 lines
11 KiB
Go
Raw Normal View History

2025-04-03 07:32:14 +00:00
package repository
import (
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
mgo "code.30cm.net/digimon/library-go/mongo"
"context"
"fmt"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
"github.com/zeromicro/go-zero/core/stores/redis"
"go.mongodb.org/mongo-driver/bson/primitive"
"testing"
"time"
)
func SetupTestKYCRepository(db string) (repository.KYCRepository, func(), error) {
h, p, tearDown, err := startMongoContainer()
if err != nil {
return nil, nil, err
}
s, _ := miniredis.Run()
conf := &mgo.Conf{
Schema: Schema,
Host: fmt.Sprintf("%s:%s", h, p),
Database: db,
MaxStaleness: 300,
MaxPoolSize: 100,
MinPoolSize: 100,
MaxConnIdleTime: 300,
Compressors: []string{},
EnableStandardReadWriteSplitMode: false,
ConnectTimeoutMs: 3000,
}
cacheConf := cache.CacheConf{
cache.NodeConf{
RedisConf: redis.RedisConf{
Host: s.Addr(),
Type: redis.NodeType,
},
Weight: 100,
},
}
cacheOpts := []cache.Option{
cache.WithExpiry(1000 * time.Microsecond),
cache.WithNotFoundExpiry(1000 * time.Microsecond),
}
param := KYCRepositoryParam{
Conf: conf,
CacheConf: cacheConf,
CacheOpts: cacheOpts,
DBOpts: []mon.Option{
mgo.SetCustomDecimalType(),
mgo.InitMongoOptions(*conf),
},
}
repo := NewKYCRepository(param)
//_, _ = repo.Index20250317001UP(context.Background())
return repo, tearDown, nil
}
func TestInsertKYC(t *testing.T) {
model, tearDown, err := SetupTestKYCRepository("testDB")
assert.NoError(t, err)
defer tearDown()
ctx := context.Background()
// 建立多筆 KYC 資料
items := []entity.KYC{
{
UID: "user-a",
CountryRegion: "TW",
Name: "王小明",
Identification: "A123456789",
IdentificationType: "ID_CARD",
Address: "台北市信義區",
PostalCode: "110",
Status: "PENDING",
},
{
UID: "user-b",
CountryRegion: "US",
Name: "John Smith",
Identification: "P987654321",
IdentificationType: "PASSPORT",
Address: "123 Main St, NY",
PostalCode: "10001",
Status: "PENDING",
},
}
// 插入資料
for i := range items {
err := model.Create(ctx, &items[i])
assert.NoError(t, err)
assert.False(t, items[i].ID.IsZero(), "ID 應自動產生")
assert.NotZero(t, items[i].CreatedAt)
assert.NotZero(t, items[i].UpdatedAt)
}
// 驗證插入資料是否正確
for _, item := range items {
kyc, err := model.FindByID(ctx, item.ID.Hex())
assert.NoError(t, err)
assert.Equal(t, item.UID, kyc.UID)
assert.Equal(t, item.Name, kyc.Name)
assert.Equal(t, item.CountryRegion, kyc.CountryRegion)
}
}
func TestFindLatestByUID(t *testing.T) {
model, tearDown, err := SetupTestKYCRepository("testDB")
assert.NoError(t, err)
defer tearDown()
ctx := context.Background()
now := time.Now().UnixNano()
// 測試資料準備
testData := []entity.KYC{
{
UID: "user-multi",
Name: "早期資料",
Status: "PENDING",
CreatedAt: now - 1000,
UpdatedAt: now - 1000,
},
{
UID: "user-multi",
Name: "最新資料",
Status: "APPROVED",
CreatedAt: now + 1000,
UpdatedAt: now + 1000,
},
{
UID: "user-single",
Name: "唯一資料",
Status: "PENDING",
CreatedAt: now,
UpdatedAt: now,
},
}
for i := range testData {
err := model.Create(ctx, &testData[i])
assert.NoError(t, err)
}
// table-driven 測試表
tests := []struct {
name string
uid string
expectNil bool
expect string // 預期的 Name
}{
{
name: "多筆資料,取最新",
uid: "user-multi",
expectNil: false,
expect: "最新資料",
},
{
name: "單筆資料",
uid: "user-single",
expectNil: false,
expect: "唯一資料",
},
{
name: "查無資料",
uid: "user-none",
expectNil: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := model.FindLatestByUID(ctx, tt.uid)
if tt.expectNil {
assert.Error(t, err)
assert.Nil(t, result)
} else {
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, tt.expect, result.Name)
}
})
}
}
func TestKYCRepository_List(t *testing.T) {
model, tearDown, err := SetupTestKYCRepository("testDB")
assert.NoError(t, err)
defer tearDown()
ctx := context.Background()
now := time.Now().UnixNano()
seedData := []entity.KYC{
{
UID: "user-1",
CountryRegion: "TW",
Status: "PENDING",
Name: "Alice",
CreatedAt: now,
UpdatedAt: now,
},
{
UID: "user-1",
CountryRegion: "JP",
Status: "APPROVED",
Name: "Bob",
CreatedAt: now + 1,
UpdatedAt: now + 1,
},
{
UID: "user-2",
CountryRegion: "US",
Status: "REJECTED",
Name: "Charlie",
CreatedAt: now + 2,
UpdatedAt: now + 2,
},
}
for i := range seedData {
err := model.Create(ctx, &seedData[i])
assert.NoError(t, err)
}
tests := []struct {
name string
params repository.KYCQueryParams
expectedCount int
expectedTotal int64
expectedFirst string
}{
{
name: "Query by UID",
params: repository.KYCQueryParams{
UID: ptr("user-1"),
PageSize: 10,
PageIndex: 1,
},
expectedCount: 2,
expectedTotal: 2,
},
{
name: "Query by CountryRegion",
params: repository.KYCQueryParams{
Country: ptr("JP"),
PageSize: 10,
PageIndex: 1,
},
expectedCount: 1,
expectedTotal: 1,
expectedFirst: "Bob",
},
{
name: "Query by Status",
params: repository.KYCQueryParams{
Status: ptr("REJECTED"),
PageSize: 10,
PageIndex: 1,
},
expectedCount: 1,
expectedTotal: 1,
expectedFirst: "Charlie",
},
{
name: "Pagination test",
params: repository.KYCQueryParams{
PageSize: 1,
PageIndex: 1,
},
expectedCount: 1,
expectedTotal: 3,
},
{
name: "No match",
params: repository.KYCQueryParams{
UID: ptr("non-existent"),
PageSize: 10,
PageIndex: 1,
},
expectedCount: 0,
expectedTotal: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
list, total, err := model.List(ctx, tt.params)
assert.NoError(t, err)
assert.Equal(t, tt.expectedCount, len(list))
assert.Equal(t, tt.expectedTotal, total)
if tt.expectedFirst != "" && len(list) > 0 {
assert.Equal(t, tt.expectedFirst, list[0].Name)
}
})
}
}
func TestKYCRepository_UpdateStatus(t *testing.T) {
model, tearDown, err := SetupTestKYCRepository("testDB")
assert.NoError(t, err)
defer tearDown()
ctx := context.Background()
// 建立一筆 KYC 資料
kyc := &entity.KYC{
UID: "user-status",
Name: "測試用戶",
Status: "PENDING",
CreatedAt: time.Now().UnixNano(),
UpdatedAt: time.Now().UnixNano(),
}
err = model.Create(ctx, kyc)
assert.NoError(t, err)
tests := []struct {
name string
id string
newStatus string
reason string
expectErr bool
expectStatus string
}{
{
name: "成功更新為 REJECTED",
id: kyc.ID.Hex(),
newStatus: "REJECTED",
reason: "文件不符",
expectErr: false,
expectStatus: "REJECTED",
},
{
name: "失敗 - 無效 ID 格式",
id: "not-a-valid-id",
newStatus: "APPROVED",
reason: "",
expectErr: true,
},
{
name: "失敗 - 找不到資料",
id: primitive.NewObjectID().Hex(), // 合法但不存在
newStatus: "APPROVED",
reason: "",
expectErr: false, // Mongo 不會報錯,但更新筆數是 0後面可以補強這邊驗證
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := model.UpdateStatus(ctx, tt.id, tt.newStatus, tt.reason)
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
// 如果是第一個 case檢查資料是否真的被更新
if tt.expectStatus != "" {
result, err := model.FindByID(ctx, tt.id)
assert.NoError(t, err)
assert.Equal(t, tt.expectStatus, result.Status)
assert.Equal(t, tt.reason, result.RejectReason)
assert.NotZero(t, result.UpdatedAt)
}
}
})
}
}
func TestKYCRepository_UpdateKYCInfo(t *testing.T) {
model, tearDown, err := SetupTestKYCRepository("testDB")
assert.NoError(t, err)
defer tearDown()
ctx := context.Background()
now := time.Now().UnixNano()
// 建立兩筆資料:一筆 PENDING、一筆 APPROVED
pendingKYC := &entity.KYC{
UID: "user-pending",
Name: "原名",
Address: "原地址",
Status: "PENDING",
CreatedAt: now,
UpdatedAt: now,
}
err = model.Create(ctx, pendingKYC)
assert.NoError(t, err)
approvedKYC := &entity.KYC{
UID: "user-approved",
Name: "原名",
Address: "原地址",
Status: "APPROVED",
CreatedAt: now,
UpdatedAt: now,
}
err = model.Create(ctx, approvedKYC)
assert.NoError(t, err)
name := "新名字"
address := "新地址"
tests := []struct {
name string
id string
update *repository.KYCUpdateParams
expectErr bool
expectApply bool // 預期更新是否應成功套用
}{
{
name: "成功更新 PENDING 狀態資料",
id: pendingKYC.ID.Hex(),
update: &repository.KYCUpdateParams{
Name: &name,
Address: &address,
},
expectErr: false,
expectApply: true,
},
{
name: "更新失敗 - 非 PENDING 狀態",
id: approvedKYC.ID.Hex(),
update: &repository.KYCUpdateParams{
Name: &name,
Address: &address,
},
expectErr: false,
expectApply: false,
},
{
name: "更新失敗 - 非法 ID 格式",
id: "not-an-id",
update: &repository.KYCUpdateParams{
Name: &name,
},
expectErr: true,
expectApply: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := model.UpdateKYCInfo(ctx, tt.id, tt.update)
if tt.expectErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
// 若預期資料已被套用,驗證欄位是否更新
if tt.expectApply {
got, err := model.FindByID(ctx, tt.id)
assert.NoError(t, err)
assert.Equal(t, name, got.Name)
assert.Equal(t, address, got.Address)
} else {
got, err := model.FindByID(ctx, tt.id)
assert.NoError(t, err)
assert.NotEqual(t, name, got.Name)
}
})
}
}