package gui import ( "testing" "time" ) func TestSplitCookieLinesNormalizesLineBreaks(t *testing.T) { tests := []struct { name string input string want []string }{ { name: "crlf_and_cr", input: "ck-a\r\nck-b\rck-c", want: []string{"ck-a", "ck-b", "ck-c"}, }, { name: "escaped_line_breaks", input: `ck-a\r\nck-b\rck-c\nck-d`, want: []string{"ck-a", "ck-b", "ck-c", "ck-d"}, }, { name: "slash_r_line_breaks", input: "ck-a/r/nck-b/rck-c/nck-d", want: []string{"ck-a", "ck-b", "ck-c", "ck-d"}, }, { name: "slash_n_only_is_preserved", input: "ck-a/nck-b", want: []string{"ck-a/nck-b"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { lines := splitCookieLines(tt.input) if len(lines) != len(tt.want) { t.Fatalf("len(splitCookieLines()) = %d, want %d", len(lines), len(tt.want)) } for i, line := range lines { if line.text != tt.want[i] { t.Fatalf("line[%d] = %q, want %q", i, line.text, tt.want[i]) } } }) } } func TestParseStartTimeAcceptsClockOnly(t *testing.T) { t.Run("future_today", func(t *testing.T) { now := time.Date(2026, 7, 6, 10, 20, 30, 0, time.Local) got := nextClockTime(now, 12, 5, 6) want := time.Date(2026, 7, 6, 12, 5, 6, 0, time.Local) if !got.Equal(want) { t.Fatalf("nextClockTime() = %s, want %s", got, want) } }) t.Run("past_tomorrow", func(t *testing.T) { now := time.Date(2026, 7, 6, 10, 20, 30, 0, time.Local) got := nextClockTime(now, 9, 5, 6) want := time.Date(2026, 7, 7, 9, 5, 6, 0, time.Local) if !got.Equal(want) { t.Fatalf("nextClockTime() = %s, want %s", got, want) } }) t.Run("parse_hms", func(t *testing.T) { got, err := parseStartTime("23:59:58") if err != nil { t.Fatalf("parseStartTime() error = %v", err) } if got.Hour() != 23 || got.Minute() != 59 || got.Second() != 58 { t.Fatalf("parseStartTime() = %s, want 23:59:58", got) } }) } func TestAddLogDoesNotBlockWhenQueueIsFull(t *testing.T) { app := &App{ logCh: make(chan LogEntry), logStop: make(chan struct{}), } done := make(chan struct{}) go func() { app.addLog("debug", "test") close(done) }() select { case <-done: case <-time.After(100 * time.Millisecond): t.Fatal("addLog blocked when log queue was full") } if dropped := app.droppedLogs.Load(); dropped != 1 { t.Fatalf("droppedLogs = %d, want 1", dropped) } } func TestParseTargetsAppliesPriceAndInventoryFilters(t *testing.T) { targets := parseTargets(Config{ PID: 12889, Name: "小黄鸭", TargetText: "13001\nauto", AutoSelect: true, MaxPrice: 1000, MinLeft: 2, }) if len(targets) != 4 { t.Fatalf("targets=%d, want 4", len(targets)) } for _, target := range targets { if target.MaxPrice != 1000 { t.Fatalf("target %+v maxPrice=%d, want 1000", target, target.MaxPrice) } if target.MinLeft != 2 { t.Fatalf("target %+v minLeft=%d, want 2", target, target.MinLeft) } } } func TestParseTargetsWithoutFiltersKeepsZeroValues(t *testing.T) { targets := parseTargets(Config{PID: 12889}) if len(targets) != 1 { t.Fatalf("targets=%d, want 1", len(targets)) } if targets[0].MaxPrice != 0 || targets[0].MinLeft != 0 { t.Fatalf("unexpected filters: %+v", targets[0]) } }