145 lines
6.5 KiB
Go
145 lines
6.5 KiB
Go
package creator
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCreatorPureCoverageGuards(t *testing.T) {
|
|
if got, err := selectCandidate(nil); err != nil || got != "" {
|
|
t.Fatalf("empty candidate: %q %v", got, err)
|
|
}
|
|
if got, err := selectCandidate([]string{"one"}); err != nil || got != "one" {
|
|
t.Fatalf("single candidate: %q %v", got, err)
|
|
}
|
|
got, err := selectCandidate([]string{"one", "two"})
|
|
if err != nil || (got != "one" && got != "two") {
|
|
t.Fatalf("multiple candidates: %q %v", got, err)
|
|
}
|
|
for _, input := range []StrategyInput{
|
|
{},
|
|
{Position: 1, Action: "invalid", EventTypes: []string{"comment"}},
|
|
{Position: 1, Action: ActionLikeComment, TargetType: "work", EventTypes: []string{"comment"}},
|
|
{Position: 1, Action: ActionLikeWork, TargetType: "comment", EventTypes: []string{"comment"}},
|
|
{Position: 1, Action: ActionReplyComment, EventTypes: []string{"dm"}},
|
|
{Position: 1, Action: ActionReplyComment, EventTypes: []string{"comment"}, CandidateTexts: []string{""}},
|
|
} {
|
|
if _, err := validateStrategyInput(input); !errors.Is(err, ErrInvalid) {
|
|
t.Fatalf("invalid strategy input accepted: %+v -> %v", input, err)
|
|
}
|
|
}
|
|
valid, err := validateStrategyInput(StrategyInput{Position: 1, Action: ActionReplyComment, EventTypes: []string{" comment "}, CandidateTexts: []string{" reply "}})
|
|
if err != nil || valid.TargetType != "user" || valid.EventTypes[0] != "comment" || valid.CandidateTexts[0] != "reply" {
|
|
t.Fatalf("valid strategy normalization: %+v %v", valid, err)
|
|
}
|
|
if result := actionPreconditionResult(ErrConflict, "blocked"); result.State != "blocked" || result.Reason != "blocked" {
|
|
t.Fatalf("blocked precondition: %+v", result)
|
|
}
|
|
if result := actionPreconditionResult(errors.New("database down"), "ignored"); result.State != "uncertain" {
|
|
t.Fatalf("uncertain precondition: %+v", result)
|
|
}
|
|
for _, result := range []ActionResult{{State: "succeeded"}, {State: "failed"}, {State: "blocked"}, {State: "uncertain"}, {}} {
|
|
normalized := normalizeActionResult(result, nil)
|
|
if normalized.State == "" {
|
|
t.Fatalf("empty action result was not normalized: %+v", normalized)
|
|
}
|
|
}
|
|
if result := normalizeActionResult(ActionResult{}, errors.New("executor failed")); result.State != "uncertain" || result.Reason != "executor failed" {
|
|
t.Fatalf("executor error normalization: %+v", result)
|
|
}
|
|
if title, script, err := parseGeneratedRewrite(`{"title":" title ","script":" script "}`); err != nil || title != "title" || script != "script" {
|
|
t.Fatalf("generated rewrite parse: %q %q %v", title, script, err)
|
|
}
|
|
for _, value := range []string{"not-json", `{"title":"","script":"script"}`, `{"title":"title","script":""}`} {
|
|
if _, _, err := parseGeneratedRewrite(value); err == nil {
|
|
t.Fatalf("invalid generated rewrite accepted: %s", value)
|
|
}
|
|
}
|
|
if _, err := decodeCommentCheckpoint(`{"cursor":"missing-work-key"}`); !errors.Is(err, ErrInvalid) {
|
|
t.Fatalf("invalid empty checkpoint: %v", err)
|
|
}
|
|
if value, err := decodeCommentCheckpoint(`{"work_key":"work","cursor":"cursor"}`); err != nil || value.WorkKey != "work" || value.Cursor != "cursor" {
|
|
t.Fatalf("valid checkpoint: %+v %v", value, err)
|
|
}
|
|
}
|
|
|
|
func TestCreatorSchedulingAndActionPredicates(t *testing.T) {
|
|
now := time.Now().UTC()
|
|
if next, reason := NextMetricAtValue(nil, now, SettingsUpdate{}); !next.IsZero() || reason != "published_at_pending_verification" {
|
|
t.Fatalf("pending metric schedule: %v %q", next, reason)
|
|
}
|
|
published := now.Add(-time.Hour)
|
|
input := SettingsUpdate{MetricInitialIntervalSeconds: 60, MetricMaxIntervalSeconds: 3600, MetricMultiplier: 2, MetricAgeSeconds: 7200}
|
|
if next, reason := NextMetricAtValue(&published, now, input); next.IsZero() || reason != "" {
|
|
t.Fatalf("metric schedule: %v %q", next, reason)
|
|
}
|
|
if coalesceReason("value", "fallback") != "value" || coalesceReason("", "fallback") != "fallback" {
|
|
t.Fatal("coalesceReason did not choose the expected value")
|
|
}
|
|
for _, test := range []struct {
|
|
action, interactor, comment, work string
|
|
valid bool
|
|
}{
|
|
{ActionDM, "peer", "", "", true},
|
|
{ActionFollow, "peer", "", "", true},
|
|
{ActionReplyComment, "peer", "comment", "", true},
|
|
{ActionLikeComment, "peer", "", "", false},
|
|
{ActionLikeWork, "peer", "", "work", true},
|
|
{ActionRepost, "peer", "", "", false},
|
|
{ActionDM, "", "comment", "work", false},
|
|
{"unknown", "peer", "comment", "work", false},
|
|
} {
|
|
if got := ActionTargetValid(test.action, test.interactor, test.comment, test.work, ""); got != test.valid {
|
|
t.Fatalf("ActionTargetValid(%+v) = %v", test, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCreatorPaginationGuards(t *testing.T) {
|
|
items, err := collectPagesFromCursor(context.Background(), "", func(_ context.Context, cursor string) ([]string, string, bool, error) {
|
|
if cursor != "" {
|
|
t.Fatalf("unexpected cursor %q", cursor)
|
|
}
|
|
return []string{"one"}, "", false, nil
|
|
}, nil)
|
|
if err != nil || len(items) != 1 {
|
|
t.Fatalf("single page: %v %v", items, err)
|
|
}
|
|
for name, fetch := range map[string]func(context.Context, string) ([]string, string, bool, error){
|
|
"missing cursor": func(context.Context, string) ([]string, string, bool, error) { return nil, "", true, nil },
|
|
"repeated cursor": func(context.Context, string) ([]string, string, bool, error) { return nil, "same", true, nil },
|
|
} {
|
|
_, err := collectPagesFromCursor(context.Background(), "same", fetch, nil)
|
|
if !errors.Is(err, ErrInvalid) {
|
|
t.Fatalf("%s: %v", name, err)
|
|
}
|
|
}
|
|
_, err = collectPagesFromCursor(context.Background(), "", func(context.Context, string) ([]string, string, bool, error) {
|
|
return nil, "next", true, errors.New("fetch failed")
|
|
}, nil)
|
|
if err == nil || err.Error() != "fetch failed" {
|
|
t.Fatalf("fetch failure: %v", err)
|
|
}
|
|
_, err = collectPagesFromCursor(context.Background(), "", func(context.Context, string) ([]string, string, bool, error) {
|
|
return []string{"one"}, "next", true, nil
|
|
}, func([]string, string, bool) error { return errors.New("save failed") })
|
|
if err == nil || err.Error() != "save failed" {
|
|
t.Fatalf("after-page failure: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConfiguredBailianWithoutStore(t *testing.T) {
|
|
var client *ConfiguredBailian
|
|
if _, err := client.Generate(context.Background(), "instruction", "input"); !errors.Is(err, ErrUnavailable) {
|
|
t.Fatalf("nil Generate: %v", err)
|
|
}
|
|
if _, _, err := client.MatchTheme(context.Background(), "title", "body", "topic"); !errors.Is(err, ErrUnavailable) {
|
|
t.Fatalf("nil MatchTheme: %v", err)
|
|
}
|
|
if _, _, err := client.MatchLead(context.Background(), "work", "comment", "requirement"); !errors.Is(err, ErrUnavailable) {
|
|
t.Fatalf("nil MatchLead: %v", err)
|
|
}
|
|
}
|