Files
creator-hub/internal/creator/logic_test.go
T

92 lines
3.7 KiB
Go

package creator
import (
"math"
"testing"
"time"
)
func TestMetricScheduleUsesUTCAndCumulativeIntervals(t *testing.T) {
published := time.Date(2026, 1, 1, 0, 0, 0, 0, time.FixedZone("CST", 8*60*60))
points := MetricSchedule(published, time.Hour, 24*time.Hour, 2, 6)
want := []time.Duration{time.Hour, 3 * time.Hour, 7 * time.Hour, 15 * time.Hour, 31 * time.Hour, 55 * time.Hour}
for index, duration := range want {
if got := points[index].Sub(published.UTC()); got != duration {
t.Fatalf("point %d: got %s, want %s", index, got, duration)
}
}
if next, reason := NextMetricAt(published, published.Add(3*time.Hour), time.Hour, 24*time.Hour, 2, 30*24*time.Hour); next != published.UTC().Add(7*time.Hour) || reason != "" {
t.Fatalf("strict next point: got %s/%q", next, reason)
}
if next, reason := NextMetricAt(published, published.Add(30*24*time.Hour), time.Hour, 24*time.Hour, 2, 30*24*time.Hour); !next.IsZero() || reason != "monitoring_age_reached" {
t.Fatalf("age boundary: got %s/%q", next, reason)
}
}
func TestKeywordMatchingDoesNotNormalizeText(t *testing.T) {
if got := TrimKeyword(" \tAI \r\n"); got != "AI" {
t.Fatalf("trimmed keyword = %q", got)
}
if ok, _ := MatchKeywords("AI 买 车", []string{"AI"}, nil); !ok {
t.Fatal("expected exact substring match")
}
if ok, _ := MatchKeywords("ai 买车", []string{"AI"}, nil); ok {
t.Fatal("case should remain significant")
}
if ok, _ := MatchKeywords("AI 买车", []string{"买 车"}, nil); ok {
t.Fatal("internal whitespace should remain significant")
}
if ok, _ := MatchKeywords("AI 买 车", []string{"AI"}, []string{"买 车"}); ok {
t.Fatal("exclude keyword should veto the match")
}
}
func TestValidateSettingsRejectsUnsafeNumbers(t *testing.T) {
base := SettingsUpdate{LookbackDays: 30, NewWorkIntervalSeconds: 1800, MetricInitialIntervalSeconds: 3600, MetricMultiplier: 2, MetricMaxIntervalSeconds: 86400, MetricAgeSeconds: 30 * 24 * 60 * 60}
for _, mutate := range []func(*SettingsUpdate){
func(value *SettingsUpdate) { value.MetricMultiplier = math.NaN() },
func(value *SettingsUpdate) { value.MetricAgeSeconds = maxDurationSeconds + 1 },
func(value *SettingsUpdate) { value.LookbackDays = int(maxDurationSeconds/(24*60*60) + 1) },
} {
value := base
mutate(&value)
if err := ValidateSettings(value); err != ErrInvalid {
t.Fatalf("expected invalid settings, got %v", err)
}
}
}
func TestNextMetricIntervalRejectsNonFiniteMultiplier(t *testing.T) {
if _, err := nextMetricInterval(3600, math.NaN(), 86400); err != ErrInvalid {
t.Fatalf("expected NaN multiplier to be invalid, got %v", err)
}
if next, err := nextMetricInterval(3600, math.Inf(1), 86400); err != nil || next != 86400 {
t.Fatalf("expected infinite growth to clamp at maximum, got %d/%v", next, err)
}
}
func TestActionTargetAndBusinessStatusGates(t *testing.T) {
if ActionTargetValid(ActionReplyComment, "uid-1", "", "", "comment") {
t.Fatal("reply without a comment target must be blocked")
}
profile := AccountProfile{AuthorizationStatus: "authorized", LoginStatus: "logged_in", BusinessStatus: "muted"}
if err := CanWrite(profile, false, ActionDM); err == nil {
t.Fatal("muted accounts cannot manually send DMs")
}
profile.BusinessStatus = "normal"
if err := CanWrite(profile, false, ActionDM); err != nil {
t.Fatalf("normal logged-in account should be writable: %v", err)
}
}
func TestValidMessageTypeKeepsNonTextMessagesExplicit(t *testing.T) {
for _, messageType := range []string{MessageTypeText, MessageTypeImage, MessageTypeVoice, MessageTypeVideo, MessageTypeSticker, MessageTypeUnknown} {
if !ValidMessageType(messageType) {
t.Fatalf("message type %q should be accepted", messageType)
}
}
if ValidMessageType("text-like") {
t.Fatal("unknown message type must not be accepted")
}
}