package app import ( "bytes" "context" "encoding/json" "errors" "net/http" "net/http/httptest" "testing" "time" "bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/logger" "bindbox-game/internal/proposal" "bindbox-game/internal/repository/mysql" "bindbox-game/internal/repository/mysql/model" douyinsvc "bindbox-game/internal/service/douyin" ) type fakeDouyinService struct { syncUserOrdersFn func(ctx context.Context, localUserID int64) (*douyinsvc.SyncResult, error) } func (f *fakeDouyinService) FetchAndSyncOrders(ctx context.Context, opts *douyinsvc.FetchOptions) (*douyinsvc.SyncResult, error) { return nil, nil } func (f *fakeDouyinService) SyncUserOrders(ctx context.Context, localUserID int64) (*douyinsvc.SyncResult, error) { if f.syncUserOrdersFn == nil { return nil, nil } return f.syncUserOrdersFn(ctx, localUserID) } func (f *fakeDouyinService) SyncAllOrders(ctx context.Context, duration time.Duration, useProxy bool) (*douyinsvc.SyncResult, error) { return nil, nil } func (f *fakeDouyinService) ListOrders(ctx context.Context, page, pageSize int, filter *douyinsvc.ListOrdersFilter) ([]*model.DouyinOrders, int64, error) { return nil, 0, nil } func (f *fakeDouyinService) GetConfig(ctx context.Context) (*douyinsvc.DouyinConfig, error) { return nil, nil } func (f *fakeDouyinService) SaveConfig(ctx context.Context, cookie, proxy string, intervalMinutes int) error { return nil } func (f *fakeDouyinService) SyncOrder(ctx context.Context, item *douyinsvc.DouyinOrderItem, suggestUserID int64, productID string) (bool, bool) { return false, false } func (f *fakeDouyinService) GrantMinesweeperQualifications(ctx context.Context) error { return nil } func (f *fakeDouyinService) GrantLivestreamPrizes(ctx context.Context) error { return nil } func (f *fakeDouyinService) SyncRefundStatus(ctx context.Context) error { return nil } func (f *fakeDouyinService) GrantOrderReward(ctx context.Context, shopOrderID string) (*douyinsvc.GrantOrderRewardResult, error) { return nil, nil } func TestSyncMyDouyinOrders_AppSuccess(t *testing.T) { repo, err := mysql.NewSQLiteRepoForTest() if err != nil { t.Fatal(err) } if err := repo.GetDbW().Exec(`CREATE TABLE users ( id INTEGER PRIMARY KEY, nickname TEXT, douyin_user_id TEXT, deleted_at DATETIME )`).Error; err != nil { t.Fatal(err) } if err := repo.GetDbW().Exec(`INSERT INTO users (id, nickname, douyin_user_id) VALUES (1, 'tester', 'dy_user_001')`).Error; err != nil { t.Fatal(err) } lg, err := logger.NewCustomLogger(logger.WithOutputInConsole()) if err != nil { t.Fatal(err) } mux, err := core.New(lg) if err != nil { t.Fatal(err) } h := New(lg, repo, nil) h.douyin = &fakeDouyinService{ syncUserOrdersFn: func(ctx context.Context, localUserID int64) (*douyinsvc.SyncResult, error) { if localUserID != 1 { t.Fatalf("unexpected localUserID: %d", localUserID) } return &douyinsvc.SyncResult{ TotalFetched: 4, NewOrders: 2, MatchedUsers: 3, ElapsedMS: 123, }, nil }, } dummyAuth := func(ctx core.Context) (proposal.SessionUserInfo, core.BusinessError) { return proposal.SessionUserInfo{Id: 1}, nil } app := mux.Group("/api/app", core.WrapAuthHandler(dummyAuth)) app.POST("/users/douyin/orders/sync", h.SyncMyDouyinOrders()) rr := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/app/users/douyin/orders/sync", bytes.NewBufferString("")) mux.ServeHTTP(rr, req) if rr.Code != http.StatusOK { t.Fatalf("code=%d body=%s", rr.Code, rr.Body.String()) } var rsp syncMyDouyinOrdersResponse if err := json.Unmarshal(rr.Body.Bytes(), &rsp); err != nil { t.Fatal(err) } if rsp.DouyinUserID != "dy_user_001" { t.Fatalf("unexpected douyin_user_id: %s", rsp.DouyinUserID) } if rsp.TotalFetched != 4 || rsp.NewOrders != 2 || rsp.MatchedUsers != 3 || rsp.ElapsedMS != 123 { t.Fatalf("unexpected response: %+v", rsp) } } func TestSyncMyDouyinOrders_AppError(t *testing.T) { repo, err := mysql.NewSQLiteRepoForTest() if err != nil { t.Fatal(err) } if err := repo.GetDbW().Exec(`CREATE TABLE users ( id INTEGER PRIMARY KEY, nickname TEXT, douyin_user_id TEXT, deleted_at DATETIME )`).Error; err != nil { t.Fatal(err) } if err := repo.GetDbW().Exec(`INSERT INTO users (id, nickname, douyin_user_id) VALUES (1, 'tester', '')`).Error; err != nil { t.Fatal(err) } lg, err := logger.NewCustomLogger(logger.WithOutputInConsole()) if err != nil { t.Fatal(err) } mux, err := core.New(lg) if err != nil { t.Fatal(err) } h := New(lg, repo, nil) h.douyin = &fakeDouyinService{ syncUserOrdersFn: func(ctx context.Context, localUserID int64) (*douyinsvc.SyncResult, error) { return nil, errors.New("当前用户未绑定抖音号") }, } dummyAuth := func(ctx core.Context) (proposal.SessionUserInfo, core.BusinessError) { return proposal.SessionUserInfo{Id: 1}, nil } app := mux.Group("/api/app", core.WrapAuthHandler(dummyAuth)) app.POST("/users/douyin/orders/sync", h.SyncMyDouyinOrders()) rr := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/app/users/douyin/orders/sync", bytes.NewBufferString("")) mux.ServeHTTP(rr, req) if rr.Code != http.StatusBadRequest { t.Fatalf("code=%d body=%s", rr.Code, rr.Body.String()) } if !bytes.Contains(rr.Body.Bytes(), []byte("当前用户未绑定抖音号")) { t.Fatalf("unexpected body=%s", rr.Body.String()) } }