thread-master/apps/backend/internal/module/job/usecase/service_test.go

237 lines
8.1 KiB
Go

package usecase_test
import (
"context"
"testing"
"apps/backend/internal/module/appnotif/repository"
appUC "apps/backend/internal/module/appnotif/usecase"
"apps/backend/internal/module/job/domain"
jobRepo "apps/backend/internal/module/job/repository"
"apps/backend/internal/module/job/usecase"
"github.com/stretchr/testify/require"
)
func newJobSvc() (*usecase.Service, *appUC.Service) {
jr := jobRepo.NewMemory()
nr := repository.NewMemory()
notif := appUC.New(nr)
svc := usecase.New(jr)
svc.Notifier = notif
return svc, notif
}
func TestJB_01_CreateDemo(t *testing.T) {
svc, _ := newJobSvc()
j, err := svc.StartDemo(context.Background(), 1_000_100)
require.NoError(t, err)
require.Equal(t, domain.StatusQueued, j.Status)
require.Equal(t, domain.TemplateDemo, j.TemplateType)
list, err := svc.List(context.Background(), 1_000_100)
require.NoError(t, err)
require.NotEmpty(t, list)
}
func TestJB_02_ProgressMonotonic(t *testing.T) {
svc, _ := newJobSvc()
j, err := svc.StartDemo(context.Background(), 1_000_101)
require.NoError(t, err)
claimed, err := svc.ClaimNext(context.Background(), "worker-test")
require.NoError(t, err)
require.Equal(t, j.ID, claimed.ID)
require.Equal(t, domain.StatusRunning, claimed.Status)
out, err := svc.RunDemoToSuccess(context.Background(), claimed.ID)
require.NoError(t, err)
require.Equal(t, 100, out.ProgressPercent)
require.Equal(t, domain.StatusSucceeded, out.Status)
// cannot go backwards
err = domain.ApplyProgress(out, 50, "nope")
require.ErrorIs(t, err, domain.ErrIllegalStatus)
}
func TestJB_03_JobSucceeded(t *testing.T) {
svc, _ := newJobSvc()
j, _ := svc.StartDemo(context.Background(), 1_000_102)
_, _ = svc.ClaimNext(context.Background(), "w1")
out, err := svc.RunDemoToSuccess(context.Background(), j.ID)
require.NoError(t, err)
require.Equal(t, domain.StatusSucceeded, out.Status)
require.True(t, out.CompletedAt > 0)
}
func TestJB_04_JobFailed(t *testing.T) {
svc, _ := newJobSvc()
j, _ := svc.StartDemo(context.Background(), 1_000_103)
_, _ = svc.ClaimNext(context.Background(), "w1")
out, err := svc.FailJob(context.Background(), j.ID, "boom")
require.NoError(t, err)
require.Equal(t, domain.StatusFailed, out.Status)
require.NotEmpty(t, out.Error)
require.True(t, out.CompletedAt > 0)
}
func TestJB_05_JobTerminalCreatesNotification(t *testing.T) {
svc, notif := newJobSvc()
uid := int64(1_000_104)
j, _ := svc.StartDemo(context.Background(), uid)
_, _ = svc.ClaimNext(context.Background(), "w1")
_, err := svc.RunDemoToSuccess(context.Background(), j.ID)
require.NoError(t, err)
list, err := notif.List(context.Background(), uid)
require.NoError(t, err)
require.NotEmpty(t, list)
}
func TestJB_06_UnreadCount(t *testing.T) {
svc, notif := newJobSvc()
uid := int64(1_000_105)
j, _ := svc.StartDemo(context.Background(), uid)
_, _ = svc.ClaimNext(context.Background(), "w1")
_, _ = svc.FailJob(context.Background(), j.ID, "x")
c, err := notif.UnreadCount(context.Background(), uid)
require.NoError(t, err)
require.Equal(t, int64(1), c)
}
func TestJB_07_MarkRead(t *testing.T) {
svc, notif := newJobSvc()
uid := int64(1_000_106)
j, _ := svc.StartDemo(context.Background(), uid)
_, _ = svc.ClaimNext(context.Background(), "w1")
_, _ = svc.RunDemoToSuccess(context.Background(), j.ID)
list, _ := notif.List(context.Background(), uid)
require.NotEmpty(t, list)
require.NoError(t, notif.MarkRead(context.Background(), uid, list[0].ID))
c, _ := notif.UnreadCount(context.Background(), uid)
require.Equal(t, int64(0), c)
require.True(t, list[0].ReadAt == 0) // old snapshot
}
func TestJB_08_MarkAllRead(t *testing.T) {
svc, notif := newJobSvc()
uid := int64(1_000_107)
for i := 0; i < 2; i++ {
j, _ := svc.StartDemo(context.Background(), uid)
_, _ = svc.ClaimNext(context.Background(), "w1")
_, _ = svc.RunDemoToSuccess(context.Background(), j.ID)
}
c, _ := notif.UnreadCount(context.Background(), uid)
require.GreaterOrEqual(t, c, int64(2))
require.NoError(t, notif.MarkAllRead(context.Background(), uid))
c2, _ := notif.UnreadCount(context.Background(), uid)
require.Equal(t, int64(0), c2)
}
func TestJB_09_GetOtherUIDDenied(t *testing.T) {
svc, _ := newJobSvc()
j, _ := svc.StartDemo(context.Background(), 1_000_108)
_, err := svc.Get(context.Background(), 1_000_109, j.ID)
require.ErrorIs(t, err, domain.ErrForbidden)
}
func TestJB_10_GuardedReject(t *testing.T) {
svc, _ := newJobSvc()
j, _ := svc.StartDemo(context.Background(), 1_000_110)
_, _ = svc.ClaimNext(context.Background(), "w1")
_, _ = svc.RunDemoToSuccess(context.Background(), j.ID)
_, err := svc.Transition(context.Background(), j.ID, domain.StatusRunning, "nope", 10)
require.ErrorIs(t, err, domain.ErrIllegalStatus)
}
func TestJB_11_ManualDeleteTerminal(t *testing.T) {
svc, _ := newJobSvc()
uid := int64(1_000_111)
j, _ := svc.StartDemo(context.Background(), uid)
_, _ = svc.ClaimNext(context.Background(), "w1")
out, err := svc.RunDemoToSuccess(context.Background(), j.ID)
require.NoError(t, err)
require.Equal(t, domain.StatusSucceeded, out.Status)
require.NoError(t, svc.Delete(context.Background(), uid, j.ID))
_, err = svc.Get(context.Background(), uid, j.ID)
require.ErrorIs(t, err, domain.ErrNotFound)
}
func TestJB_12_DeleteRunningRefused(t *testing.T) {
svc, _ := newJobSvc()
uid := int64(1_000_112)
j, _ := svc.StartDemo(context.Background(), uid)
claimed, err := svc.ClaimNext(context.Background(), "w1")
require.NoError(t, err)
require.Equal(t, domain.StatusRunning, claimed.Status)
err = svc.Delete(context.Background(), uid, j.ID)
require.ErrorIs(t, err, domain.ErrIllegalStatus)
}
func TestJB_13_PurgeExpiredTerminal(t *testing.T) {
svc, _ := newJobSvc()
uid := int64(1_000_113)
j, _ := svc.StartDemo(context.Background(), uid)
_, _ = svc.ClaimNext(context.Background(), "w1")
out, err := svc.RunDemoToSuccess(context.Background(), j.ID)
require.NoError(t, err)
// 模擬完成時間在 2 天前
out.CompletedAt = domain.NowNano() - domain.TerminalRetentionNs - 1
out.UpdatedAt = out.CompletedAt
require.NoError(t, svc.Repo.Update(context.Background(), out))
n, err := svc.PurgeExpiredTerminal(context.Background())
require.NoError(t, err)
require.Equal(t, int64(1), n)
_, err = svc.Get(context.Background(), uid, j.ID)
require.ErrorIs(t, err, domain.ErrNotFound)
}
func TestJB_14_PurgeKeepsRecentTerminal(t *testing.T) {
svc, _ := newJobSvc()
uid := int64(1_000_114)
j, _ := svc.StartDemo(context.Background(), uid)
_, _ = svc.ClaimNext(context.Background(), "w1")
_, err := svc.RunDemoToSuccess(context.Background(), j.ID)
require.NoError(t, err)
n, err := svc.PurgeExpiredTerminal(context.Background())
require.NoError(t, err)
require.Equal(t, int64(0), n)
got, err := svc.Get(context.Background(), uid, j.ID)
require.NoError(t, err)
require.Equal(t, j.ID, got.ID)
}
func TestJB_15_SchedulePersonaAnalyzeAccount(t *testing.T) {
svc, _ := newJobSvc()
uid := int64(1_000_115)
j, err := svc.SchedulePersonaAnalyzeAccount(context.Background(), uid, "pe_abc", "ultralab_tw", "zh-TW")
require.NoError(t, err)
require.Equal(t, domain.TemplatePersonaAnalyzeAccount, j.TemplateType)
require.Equal(t, domain.StatusQueued, j.Status)
require.Equal(t, "pe_abc", j.RefID)
require.Contains(t, j.Payload, "ultralab_tw")
require.Contains(t, j.ProgressSummary, "@ultralab_tw")
// re-schedule cancels previous pending
j2, err := svc.SchedulePersonaAnalyzeAccount(context.Background(), uid, "pe_abc", "other", "en")
require.NoError(t, err)
require.NotEqual(t, j.ID, j2.ID)
old, err := svc.Get(context.Background(), uid, j.ID)
require.NoError(t, err)
require.Equal(t, domain.StatusCancelled, old.Status)
claimed, err := svc.ClaimNext(context.Background(), "w1")
require.NoError(t, err)
require.Equal(t, j2.ID, claimed.ID)
require.Equal(t, domain.StatusRunning, claimed.Status)
}
func TestJB_16_SchedulePersonaAnalyzeText(t *testing.T) {
svc, _ := newJobSvc()
uid := int64(1_000_116)
j, err := svc.SchedulePersonaAnalyzeText(context.Background(), uid, "pe_txt", "hello world\n---\nsecond sample text here", "手動", "zh-TW")
require.NoError(t, err)
require.Equal(t, domain.TemplatePersonaAnalyzeText, j.TemplateType)
require.Equal(t, "pe_txt", j.RefID)
require.Contains(t, j.Payload, "raw_text")
}