package wechat import ( "crypto/sha1" "encoding/hex" "sort" "strings" "testing" ) func TestConfig_Enabled(t *testing.T) { // 三者缺一不可:appid+secret 建二维码,token 验签 cases := []struct { c Config want bool }{ {Config{AppID: "a", AppSecret: "s", Token: "t"}, true}, {Config{AppID: "a", AppSecret: "s"}, false}, {Config{AppID: "a", Token: "t"}, false}, {Config{}, false}, } for _, tc := range cases { if got := tc.c.Enabled(); got != tc.want { t.Fatalf("%+v Enabled=%v want %v", tc.c, got, tc.want) } } } // 验签必须与微信算法一致:sha1(sort(token,ts,nonce))。 func TestCheckSignature(t *testing.T) { c := Config{Token: "mytoken"} ts, nonce := "1700000000", "abc123" arr := []string{c.Token, ts, nonce} sort.Strings(arr) sum := sha1.Sum([]byte(strings.Join(arr, ""))) good := hex.EncodeToString(sum[:]) if !c.CheckSignature(good, ts, nonce) { t.Fatal("正确签名应通过") } if c.CheckSignature("deadbeef", ts, nonce) { t.Fatal("错误签名不该通过") } if (Config{}).CheckSignature(good, ts, nonce) { t.Fatal("无 token 一律不通过(防未配置时被绕过)") } } // 事件解析 + scene 提取:subscribe 带 qrscene_ 前缀,SCAN 不带;两者都要能登录。 func TestParseEvent_SubscribeAndScan(t *testing.T) { subscribe := ` ` ev, err := ParseEvent([]byte(subscribe)) if err != nil { t.Fatal(err) } if !ev.IsLoginScan() { t.Fatal("subscribe 带 qrscene 应识别为登录扫码") } if ev.Scene() != "tkt-abc" { t.Fatalf("subscribe 应剥掉 qrscene_ 前缀,得 %q", ev.Scene()) } if ev.FromUserName != "openid_new" { t.Fatalf("openid 取错:%q", ev.FromUserName) } scan := ` ` ev2, _ := ParseEvent([]byte(scan)) if !ev2.IsLoginScan() || ev2.Scene() != "tkt-def" { t.Fatalf("SCAN 事件应识别,scene=%q", ev2.Scene()) } } // 非登录事件(取关、普通消息)不能被当成登录。 func TestParseEvent_IgnoresNonLogin(t *testing.T) { unsub := `` ev, _ := ParseEvent([]byte(unsub)) if ev.IsLoginScan() { t.Fatal("取关事件不该被当成登录") } // 无 scene 的 subscribe(用户直接搜号关注,不是扫登录码)也不登录 plainSub := `` ev2, _ := ParseEvent([]byte(plainSub)) if ev2.IsLoginScan() { t.Fatal("无 scene 的关注不该触发登录") } text := `` ev3, _ := ParseEvent([]byte(text)) if ev3.IsLoginScan() { t.Fatal("普通文本消息不该触发登录") } } func TestConfig_SecretRoundTrip(t *testing.T) { c := Config{AppID: "x", AppSecret: "plain-secret", Token: "t"} stored, err := c.EncryptedForStore() if err != nil { t.Fatal(err) } if stored.AppSecret == "plain-secret" { t.Fatal("落库不该是明文") } if back := stored.DecryptFromStore(); back.AppSecret != "plain-secret" { t.Fatalf("还原失败:%q", back.AppSecret) } }