- GUI 接线 Exchange 绑定:新增「直接兑换」按钮,结果带 category/message 并进入结果面板 - 补 -max-price/-min-left(CLI 与 GUI 设置),Target 的价格与库存过滤不再悬空 - 删除死代码 sendUpdateUserInfo 及其请求结构体(浏览器流程不含该帧) - ClassifyExchange 改为多路判定:合并 msg/actPreCondition/exchangeInfo 文案,status=200 且有 orderId 视为成功,新增 condition 分类(precondition 门禁) - WSS 建连改为锁外 dial、锁内注册,同 uid 并发调用共享一次握手 - 默认传输层统一为 http(CLI/GUI/README),.gitignore 忽略 .DS_Store - 同步 Wails 依赖升级(2.12.0 -> 2.15.0)并重新生成前端绑定
133 lines
3.2 KiB
Go
133 lines
3.2 KiB
Go
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])
|
|
}
|
|
}
|