105 lines
4.1 KiB
Go
105 lines
4.1 KiB
Go
package scout
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
|
|
"apps/backend/internal/middleware"
|
|
jobDomain "apps/backend/internal/module/job/domain"
|
|
jobRepo "apps/backend/internal/module/job/repository"
|
|
jobUC "apps/backend/internal/module/job/usecase"
|
|
scoutDomain "apps/backend/internal/module/scout/domain"
|
|
scoutRepo "apps/backend/internal/module/scout/repository"
|
|
scoutUC "apps/backend/internal/module/scout/usecase"
|
|
"apps/backend/internal/svc"
|
|
"apps/backend/internal/types"
|
|
)
|
|
|
|
func TestRunScanCreatesIndependentRunAndJobLink(t *testing.T) {
|
|
ctx := middleware.WithUID(context.Background(), 7)
|
|
scoutStore := scoutRepo.NewMemory()
|
|
jobs := jobUC.New(jobRepo.NewMemory())
|
|
svcCtx := &svc.ServiceContext{Scout: scoutUC.New(scoutStore), Jobs: jobs}
|
|
data, err := NewRunScanLogic(ctx, svcCtx).RunScan(&types.ScoutScanReq{Brief: types.ScoutBriefPublic{
|
|
Intent: "市集", Mode: domainModeActivity, ThemeKey: "same-theme", ThemeLabel: "週末市集",
|
|
ScanTerms: []string{"市集"}, TargetCount: 12,
|
|
}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if data.Run.Id == "" || data.Job.Id == "" || data.Run.Id == data.Job.Id {
|
|
t.Fatalf("missing independent ids: %+v", data)
|
|
}
|
|
if data.Job.RefId != data.Run.Id || data.Run.JobId != data.Job.Id || data.Run.Status != scoutDomain.RunQueued {
|
|
t.Fatalf("job/run link mismatch: job=%+v run=%+v", data.Job, data.Run)
|
|
}
|
|
var raw map[string]json.RawMessage
|
|
if err := json.Unmarshal([]byte(data.Job.Payload), &raw); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var payloadRunID string
|
|
if err := json.Unmarshal(raw["run_id"], &payloadRunID); err != nil || payloadRunID != data.Run.Id {
|
|
t.Fatalf("payload run_id=%q err=%v payload=%s", payloadRunID, err, data.Job.Payload)
|
|
}
|
|
var brief scoutDomain.RunBrief
|
|
if err := json.Unmarshal([]byte(data.Job.Payload), &brief); err != nil || brief.Intent != "市集" || len(brief.ScanTerms) != 1 {
|
|
t.Fatalf("flat worker brief=%+v err=%v", brief, err)
|
|
}
|
|
if data.Run.TargetCount != 12 {
|
|
t.Fatalf("target count=%d", data.Run.TargetCount)
|
|
}
|
|
runs, err := scoutStore.ListRuns(context.Background(), scoutDomain.RunFilter{OwnerUID: 7, Page: 1, PageSize: 10})
|
|
if err != nil || len(runs.Items) != 1 || runs.Items[0].ID != data.Run.Id {
|
|
t.Fatalf("stored runs=%+v err=%v", runs, err)
|
|
}
|
|
}
|
|
|
|
func TestRunScanCreatesFreshRunForSameTheme(t *testing.T) {
|
|
ctx := middleware.WithUID(context.Background(), 7)
|
|
scoutStore := scoutRepo.NewMemory()
|
|
svcCtx := &svc.ServiceContext{Scout: scoutUC.New(scoutStore), Jobs: jobUC.New(jobRepo.NewMemory())}
|
|
logic := NewRunScanLogic(ctx, svcCtx)
|
|
request := &types.ScoutScanReq{Brief: types.ScoutBriefPublic{Intent: "市集", Mode: domainModeActivity, ThemeKey: "repeat", ScanTerms: []string{"市集"}}}
|
|
first, err := logic.RunScan(request)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, err := logic.RunScan(request)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if first.Run.Id == second.Run.Id || first.Job.Id == second.Job.Id {
|
|
t.Fatalf("same theme reused id: first=%+v second=%+v", first, second)
|
|
}
|
|
}
|
|
|
|
type failingInsertJobRepo struct{ jobDomain.Repository }
|
|
|
|
func (failingInsertJobRepo) Insert(context.Context, *jobDomain.Job) error {
|
|
return errors.New("insert unavailable")
|
|
}
|
|
|
|
func TestRunScanSchedulingFailureFailsQueuedRun(t *testing.T) {
|
|
ctx := middleware.WithUID(context.Background(), 7)
|
|
scoutStore := scoutRepo.NewMemory()
|
|
failingJobs := jobUC.New(failingInsertJobRepo{Repository: jobRepo.NewMemory()})
|
|
svcCtx := &svc.ServiceContext{Scout: scoutUC.New(scoutStore), Jobs: failingJobs}
|
|
_, err := NewRunScanLogic(ctx, svcCtx).RunScan(&types.ScoutScanReq{Brief: types.ScoutBriefPublic{
|
|
Intent: "市集", Mode: domainModeActivity, ScanTerms: []string{"市集"},
|
|
}})
|
|
if err == nil {
|
|
t.Fatal("expected scheduling failure")
|
|
}
|
|
runs, listErr := scoutStore.ListRuns(context.Background(), scoutDomain.RunFilter{OwnerUID: 7, Page: 1, PageSize: 10})
|
|
if listErr != nil || len(runs.Items) != 1 || runs.Items[0].Status != scoutDomain.RunFailed {
|
|
t.Fatalf("failed run convergence: runs=%+v err=%v listErr=%v", runs, err, listErr)
|
|
}
|
|
if runs.Items[0].Error == "" {
|
|
t.Fatal("failed run missing safe error")
|
|
}
|
|
}
|
|
|
|
const domainModeActivity = "activity"
|