| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- package service
- import (
- "context"
- "testing"
- "time"
- infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
- "github.com/stretchr/testify/require"
- )
- type openAITokenStatsRepoStub struct {
- OpsRepository
- resp *OpsOpenAITokenStatsResponse
- err error
- captured *OpsOpenAITokenStatsFilter
- }
- func (s *openAITokenStatsRepoStub) GetOpenAITokenStats(ctx context.Context, filter *OpsOpenAITokenStatsFilter) (*OpsOpenAITokenStatsResponse, error) {
- s.captured = filter
- if s.err != nil {
- return nil, s.err
- }
- if s.resp != nil {
- return s.resp, nil
- }
- return &OpsOpenAITokenStatsResponse{}, nil
- }
- func TestOpsServiceGetOpenAITokenStats_Validation(t *testing.T) {
- now := time.Now().UTC()
- tests := []struct {
- name string
- filter *OpsOpenAITokenStatsFilter
- wantCode int
- wantReason string
- }{
- {
- name: "filter 不能为空",
- filter: nil,
- wantCode: 400,
- wantReason: "OPS_FILTER_REQUIRED",
- },
- {
- name: "start_time/end_time 必填",
- filter: &OpsOpenAITokenStatsFilter{
- StartTime: time.Time{},
- EndTime: now,
- },
- wantCode: 400,
- wantReason: "OPS_TIME_RANGE_REQUIRED",
- },
- {
- name: "start_time 不能晚于 end_time",
- filter: &OpsOpenAITokenStatsFilter{
- StartTime: now,
- EndTime: now.Add(-1 * time.Minute),
- },
- wantCode: 400,
- wantReason: "OPS_TIME_RANGE_INVALID",
- },
- {
- name: "group_id 必须大于 0",
- filter: &OpsOpenAITokenStatsFilter{
- StartTime: now.Add(-time.Hour),
- EndTime: now,
- GroupID: int64Ptr(0),
- },
- wantCode: 400,
- wantReason: "OPS_GROUP_ID_INVALID",
- },
- {
- name: "top_n 与分页参数互斥",
- filter: &OpsOpenAITokenStatsFilter{
- StartTime: now.Add(-time.Hour),
- EndTime: now,
- TopN: 10,
- Page: 1,
- },
- wantCode: 400,
- wantReason: "OPS_PAGINATION_CONFLICT",
- },
- {
- name: "top_n 参数越界",
- filter: &OpsOpenAITokenStatsFilter{
- StartTime: now.Add(-time.Hour),
- EndTime: now,
- TopN: 101,
- },
- wantCode: 400,
- wantReason: "OPS_TOPN_INVALID",
- },
- {
- name: "page_size 参数越界",
- filter: &OpsOpenAITokenStatsFilter{
- StartTime: now.Add(-time.Hour),
- EndTime: now,
- Page: 1,
- PageSize: 101,
- },
- wantCode: 400,
- wantReason: "OPS_PAGE_SIZE_INVALID",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- svc := &OpsService{
- opsRepo: &openAITokenStatsRepoStub{},
- }
- _, err := svc.GetOpenAITokenStats(context.Background(), tt.filter)
- require.Error(t, err)
- require.Equal(t, tt.wantCode, infraerrors.Code(err))
- require.Equal(t, tt.wantReason, infraerrors.Reason(err))
- })
- }
- }
- func TestOpsServiceGetOpenAITokenStats_DefaultPagination(t *testing.T) {
- now := time.Now().UTC()
- repo := &openAITokenStatsRepoStub{
- resp: &OpsOpenAITokenStatsResponse{
- Items: []*OpsOpenAITokenStatsItem{
- {Model: "gpt-4o-mini", RequestCount: 10},
- },
- Total: 1,
- },
- }
- svc := &OpsService{opsRepo: repo}
- filter := &OpsOpenAITokenStatsFilter{
- TimeRange: "30d",
- StartTime: now.Add(-30 * 24 * time.Hour),
- EndTime: now,
- }
- resp, err := svc.GetOpenAITokenStats(context.Background(), filter)
- require.NoError(t, err)
- require.NotNil(t, resp)
- require.NotNil(t, repo.captured)
- require.Equal(t, 1, repo.captured.Page)
- require.Equal(t, 20, repo.captured.PageSize)
- require.Equal(t, 0, repo.captured.TopN)
- }
- func TestOpsServiceGetOpenAITokenStats_RepoUnavailable(t *testing.T) {
- now := time.Now().UTC()
- svc := &OpsService{}
- _, err := svc.GetOpenAITokenStats(context.Background(), &OpsOpenAITokenStatsFilter{
- TimeRange: "1h",
- StartTime: now.Add(-time.Hour),
- EndTime: now,
- TopN: 10,
- })
- require.Error(t, err)
- require.Equal(t, 503, infraerrors.Code(err))
- require.Equal(t, "OPS_REPO_UNAVAILABLE", infraerrors.Reason(err))
- }
- func int64Ptr(v int64) *int64 { return &v }
|