From 14678b986b343ea6069c52e2e2dba7ee883717f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Thu, 3 Apr 2025 15:32:14 +0800 Subject: [PATCH] feat: add kyc test --- pkg/repository/kyc.go | 16 +- pkg/repository/kyc_test.go | 477 +++++++++++++++++++++++++++++++++++++ 2 files changed, 491 insertions(+), 2 deletions(-) create mode 100644 pkg/repository/kyc_test.go diff --git a/pkg/repository/kyc.go b/pkg/repository/kyc.go index 178a8cf..063b42d 100644 --- a/pkg/repository/kyc.go +++ b/pkg/repository/kyc.go @@ -97,11 +97,23 @@ func (repo *KYCRepository) List(ctx context.Context, params repository.KYCQueryP sort = append(sort, bson.E{Key: "created_at", Value: -1}) } - var results []*entity.KYC - total, err := repo.DB.GetClient().FindManyWithTotal(ctx, &results, filter, params.PageIndex, params.PageSize, WithSort(sort)) + // 設置排序選項 + opts := options.Find().SetSkip((params.PageIndex - 1) * params.PageSize).SetLimit(params.PageSize) + opts.SetSort(bson.D{{Key: "updated_at", Value: -1}}) + + // 查詢符合條件的總數 + total, err := repo.DB.GetClient().CountDocuments(ctx, filter) if err != nil { return nil, 0, err } + + // 執行查詢並獲取結果 + var results []*entity.KYC + err = repo.DB.GetClient().Find(ctx, &results, filter, opts) + if err != nil { + return nil, 0, err + } + return results, total, nil } diff --git a/pkg/repository/kyc_test.go b/pkg/repository/kyc_test.go new file mode 100644 index 0000000..4a07939 --- /dev/null +++ b/pkg/repository/kyc_test.go @@ -0,0 +1,477 @@ +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) + } + }) + } +}