package tools import ( "testing" "time" "github.com/mark3labs/mcp-go/mcp" "go.mongodb.org/mongo-driver/bson/primitive" "fwh-toolbox-server/domain" ) func syncRecord(p domain.Progress, created, completed time.Time) *domain.SiteInfoUpdateRecord { id := primitive.NewObjectID() return &domain.SiteInfoUpdateRecord{Id: &id, Progress: p, CreateTime: created, UpdateTime: completed, Total: 100} } func TestSiteInfoSyncDecide(t *testing.T) { now := time.Date(2026, 10, 29, 12, 0, 0, 0, time.UTC) ago := func(h float64) time.Time { return now.Add(-time.Duration(h * float64(time.Hour))) } freshDone := syncRecord(domain.ProgressComplete, ago(3), ago(1)) oldDone := syncRecord(domain.ProgressComplete, ago(50), ago(48)) cases := []struct { name string latest, last *domain.SiteInfoUpdateRecord force bool state string action string }{ {"从未同步", nil, nil, false, syncStateNever, "triggered"}, {"pending 刚请求", syncRecord(domain.ProgressPending, ago(0.1), ago(0.1)), oldDone, false, syncStateSyncing, "tracking"}, {"running 2 小时仍在同步", syncRecord(domain.ProgressRunning, ago(2), ago(2)), oldDone, false, syncStateSyncing, "tracking"}, {"同步中 force 也不重复触发", syncRecord(domain.ProgressRunning, ago(2), ago(2)), oldDone, true, syncStateSyncing, "tracking"}, {"running 超过 6 小时疑似中断", syncRecord(domain.ProgressRunning, ago(7), ago(7)), oldDone, false, syncStateStale, "skipped_stale"}, {"疑似中断 force 重新触发", syncRecord(domain.ProgressRunning, ago(7), ago(7)), oldDone, true, syncStateStale, "triggered"}, {"24 小时内已成功", freshDone, freshDone, false, syncStateIdle, "skipped_fresh"}, {"24 小时内已成功 force", freshDone, freshDone, true, syncStateIdle, "triggered"}, {"成功已超过 24 小时", oldDone, oldDone, false, syncStateIdle, "triggered"}, {"上次失败", syncRecord(domain.ProgressError, ago(1), ago(1)), freshDone, false, syncStateIdle, "triggered"}, } for _, c := range cases { s := evalSiteInfoSync(c.latest, c.last, now) if s.State != c.state { t.Errorf("%s: state = %s,期望 %s", c.name, s.State, c.state) } if action, _ := s.decideRefresh(c.force); action != c.action { t.Errorf("%s: action = %s,期望 %s", c.name, action, c.action) } if _, ok := refreshMessages[c.action]; !ok { t.Errorf("%s: action %s 缺少提示文案", c.name, c.action) } if v := s.view(); v["advice"] == "" { t.Errorf("%s: advice 为空", c.name) } } } func TestSiteInfoTaskState(t *testing.T) { now := time.Date(2026, 10, 29, 12, 0, 0, 0, time.UTC) ago := func(h float64) time.Time { return now.Add(-time.Duration(h * float64(time.Hour))) } cases := []struct { name string flag string rec *domain.SiteInfoUpdateRecord status mcp.TaskStatus isError bool }{ {"排队中", "triggered", syncRecord(domain.ProgressPending, ago(0.1), ago(0.1)), mcp.TaskStatusWorking, false}, {"同步中", "tracking", syncRecord(domain.ProgressRunning, ago(1), ago(1)), mcp.TaskStatusWorking, false}, {"超过 6 小时疑似中断", "triggered", syncRecord(domain.ProgressRunning, ago(7), ago(7)), mcp.TaskStatusFailed, true}, {"完成", "triggered", syncRecord(domain.ProgressComplete, ago(2), ago(1)), mcp.TaskStatusCompleted, false}, {"失败", "triggered", syncRecord(domain.ProgressError, ago(2), ago(2)), mcp.TaskStatusFailed, true}, {"取消", "triggered", syncRecord(domain.ProgressCancelled, ago(2), ago(2)), mcp.TaskStatusCancelled, true}, {"未触发:数据较新,立即完成", "skipped_fresh", syncRecord(domain.ProgressComplete, ago(3), ago(2)), mcp.TaskStatusCompleted, false}, {"未触发:疑似中断,立即完成", "skipped_stale", syncRecord(domain.ProgressRunning, ago(7), ago(7)), mcp.TaskStatusCompleted, false}, } for _, c := range cases { st := siteInfoTaskState(c.flag, c.rec, now) if st.Status != c.status { t.Errorf("%s: status = %s,期望 %s", c.name, st.Status, c.status) } if st.Status.IsTerminal() && (st.Result == nil || st.Result.IsError != c.isError) { t.Errorf("%s: 终态结果不符合预期(isError 期望 %v):%+v", c.name, c.isError, st.Result) } if !st.Status.IsTerminal() && st.Message == "" { t.Errorf("%s: 进行中应有进度说明", c.name) } } }