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

480 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"context"
"fmt"
"testing"
"time"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/kyc"
"code.30cm.net/digimon/app-cloudep-product-service/pkg/domain/repository"
mgo "code.30cm.net/digimon/library-go/mongo"
"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"
)
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 資料
k := &entity.KYC{
UID: "user-status",
Name: "測試用戶",
Status: kyc.StatusPending,
CreatedAt: time.Now().UnixNano(),
UpdatedAt: time.Now().UnixNano(),
}
err = model.Create(ctx, k)
assert.NoError(t, err)
tests := []struct {
name string
id string
newStatus kyc.Status
reason string
expectErr bool
expectStatus kyc.Status
}{
{
name: "成功更新為 REJECTED",
id: k.ID.Hex(),
newStatus: kyc.StatusREJECTED,
reason: "文件不符",
expectErr: false,
expectStatus: kyc.StatusREJECTED,
},
{
name: "失敗 - 無效 ID 格式",
id: "not-a-valid-id",
newStatus: kyc.StatusAPPROVED,
reason: "",
expectErr: true,
},
{
name: "失敗 - 找不到資料",
id: primitive.NewObjectID().Hex(), // 合法但不存在
newStatus: kyc.StatusAPPROVED,
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.ToString(), 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)
}
})
}
}