package core import ( "encoding/json" "net/http" "net/http/httptest" "sync/atomic" "testing" ) // `account_test` 必须**真打一次上游**。 // // 真实缺陷:此前它一个网络请求都不发 —— 只 syncAccountSource 后查内核源列表里 // 有没有那条记录,就返回 {ok:true, ms:1}。于是"测试账号"永远绿,包括凭据已失效、 // 被限流、上游宕机的情况。这正是「看着能用、一用就错」的假阳性,也正是用户报 // 「完全不可用」时最容易误导人的那种信号。 // // 判据把三条都钉住:① 真发请求 ② 上游失败时必须 ok=false ③ 失败要区分 // "凭据失效"(需重新登录)与"暂时性失败"(可重试)—— 只说"失败"用户不知道该做什么。 func TestAccountTestReallyProbesUpstream(t *testing.T) { var hits int32 fail := false ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { atomic.AddInt32(&hits, 1) w.Header().Set("Content-Type", "application/json") if fail { w.WriteHeader(200) _, _ = w.Write([]byte(`{"error":"invalid_grant: token expired"}`)) return } body, _ := json.Marshal(map[string]interface{}{ "data": map[string]interface{}{"Response": map[string]interface{}{ "Data": map[string]interface{}{"Accounts": []map[string]interface{}{ {"Status": 1, "CycleCapacityRemain": 100}, }}, }}, }) _, _ = w.Write(body) })) defer ts.Close() _, ps := poolCore(t) if _, err := ps.Call("channels", "set_endpoints", map[string]interface{}{ "platform": "buddy", "balance": map[string]interface{}{"url": ts.URL}, }); err != nil { t.Fatalf("set_endpoints: %v", err) } if _, err := ps.Call("channels", "account_add", map[string]interface{}{ "platform": "buddy", "account_id": "probeacct", "credential": `{"access_token":"TOK"}`, }); err != nil { t.Fatalf("account_add: %v", err) } // ① 正常路径:必须真的打了上游 atomic.StoreInt32(&hits, 0) m, err := ps.Call("channels", "account_test", map[string]interface{}{ "platform": "buddy", "account_id": "probeacct", }) if err != nil { t.Fatalf("account_test: %v", err) } w, _ := m.(map[string]interface{}) j, _ := w["json"].(map[string]interface{}) if j["ok"] != true { t.Fatalf("正常凭据应 ok=true: %#v", j) } if j["probed"] != true { t.Errorf("必须标记 probed=true 并真的发请求;实得 probed=%v", j["probed"]) } if n := atomic.LoadInt32(&hits); n == 0 { t.Fatal("account_test 未向上游发任何请求 —— 这就是「看着能用」的假阳性") } // ② 凭据失效:必须 ok=false,且归类为 auth(给出可操作提示) fail = true m2, _ := ps.Call("channels", "account_test", map[string]interface{}{ "platform": "buddy", "account_id": "probeacct", }) j2, _ := m2.(map[string]interface{})["json"].(map[string]interface{}) if j2["ok"] != false { t.Errorf("凭据失效时必须 ok=false,实得 %v", j2["ok"]) } if j2["kind"] != "auth" { t.Errorf("应归类为 auth(需重新登录),实得 %v", j2["kind"]) } if s, _ := j2["action"].(string); s == "" { t.Error("失败必须给出可操作提示(否则用户不知道要重新登录还是等一会)") } // ③ 无凭据的账号:如实报错,不假装成功 if _, err := ps.Call("channels", "account_add", map[string]interface{}{ "platform": "buddy", "account_id": "naked", }); err != nil { t.Fatalf("account_add(无凭据): %v", err) } m3, _ := ps.Call("channels", "account_test", map[string]interface{}{ "platform": "buddy", "account_id": "naked", }) j3, _ := m3.(map[string]interface{})["json"].(map[string]interface{}) if j3["ok"] != false { t.Errorf("无凭据时必须 ok=false,实得 %v", j3["ok"]) } }