client: Windows 客户端(WinUI3 壳 + agent-core;调试用 Electron 壳存档);localserver token 改为常数时间比较
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"everypublish/shared/proto"
|
||||
)
|
||||
|
||||
type fakeState struct {
|
||||
online bool
|
||||
chs []proto.Challenge
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeState) Online() bool { return f.online }
|
||||
func (f *fakeState) SessionID() string { return "s1" }
|
||||
func (f *fakeState) Challenges() []proto.Challenge { return f.chs }
|
||||
func (f *fakeState) SolveChallenge(id, value string) error { return f.err }
|
||||
|
||||
func startTestServer(t *testing.T, state CoreState, pair PairFunc) (*Server, string) {
|
||||
t.Helper()
|
||||
token := RandToken()
|
||||
s := New(state, pair, token, t.TempDir(), nil)
|
||||
go func() { _ = s.Listen() }()
|
||||
for i := 0; i < 50; i++ {
|
||||
if s.Addr() != "" {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
if s.Addr() == "" {
|
||||
t.Fatal("server did not start")
|
||||
}
|
||||
return s, token
|
||||
}
|
||||
|
||||
func get(t *testing.T, base, path, token string) (int, map[string]interface{}) {
|
||||
t.Helper()
|
||||
req, _ := http.NewRequest("GET", "http://"+base+path, nil)
|
||||
if token != "" {
|
||||
req.Header.Set("X-Agent-Token", token)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("get: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var m map[string]interface{}
|
||||
_ = json.NewDecoder(resp.Body).Decode(&m)
|
||||
return resp.StatusCode, m
|
||||
}
|
||||
|
||||
func TestStatusAuth(t *testing.T) {
|
||||
s, token := startTestServer(t, &fakeState{online: true}, nil)
|
||||
code, _ := get(t, s.Addr(), "/status", "")
|
||||
if code != 401 {
|
||||
t.Fatalf("no token should be 401, got %d", code)
|
||||
}
|
||||
code, m := get(t, s.Addr(), "/status", token)
|
||||
if code != 200 || m["online"] != true {
|
||||
t.Fatalf("status wrong: %d %v", code, m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPairCallback(t *testing.T) {
|
||||
var gotCode, gotName string
|
||||
s, token := startTestServer(t, &fakeState{}, func(code, name string) error {
|
||||
gotCode, gotName = code, name
|
||||
return nil
|
||||
})
|
||||
// 未带 token → 401
|
||||
req, _ := http.NewRequest("POST", "http://"+s.Addr()+"/pair", bytes.NewBufferString(`{"code":"AB12CD","name":"PC"}`))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("pair: %v", err)
|
||||
}
|
||||
if resp.StatusCode != 401 {
|
||||
t.Fatalf("pair without token should 401, got %d", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
// 带 token → 回调被调用
|
||||
req2, _ := http.NewRequest("POST", "http://"+s.Addr()+"/pair", bytes.NewBufferString(`{"code":"AB12CD","name":"PC"}`))
|
||||
req2.Header.Set("X-Agent-Token", token)
|
||||
resp2, err := http.DefaultClient.Do(req2)
|
||||
if err != nil {
|
||||
t.Fatalf("pair: %v", err)
|
||||
}
|
||||
resp2.Body.Close()
|
||||
if gotCode != "AB12CD" || gotName != "PC" {
|
||||
t.Fatalf("callback not invoked: %q %q", gotCode, gotName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPairErrorPropagates(t *testing.T) {
|
||||
s, token := startTestServer(t, &fakeState{}, func(code, name string) error {
|
||||
return errors.New("配对码无效")
|
||||
})
|
||||
req, _ := http.NewRequest("POST", "http://"+s.Addr()+"/pair", bytes.NewBufferString(`{"code":"X"}`))
|
||||
req.Header.Set("X-Agent-Token", token)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("pair: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var m map[string]interface{}
|
||||
_ = json.NewDecoder(resp.Body).Decode(&m)
|
||||
if m["code"] != float64(1) {
|
||||
t.Fatalf("expect error code 1, got %v", m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSolve(t *testing.T) {
|
||||
s, token := startTestServer(t, &fakeState{err: nil}, nil)
|
||||
code, m := get(t, s.Addr(), "/challenges", token)
|
||||
if code != 200 {
|
||||
t.Fatalf("challenges: %d", code)
|
||||
}
|
||||
_ = m
|
||||
req, _ := http.NewRequest("POST", "http://"+s.Addr()+"/challenges/9", bytes.NewBufferString(`{"value":"123456"}`))
|
||||
req.Header.Set("X-Agent-Token", token)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("solve: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var m2 map[string]interface{}
|
||||
_ = json.NewDecoder(resp.Body).Decode(&m2)
|
||||
if m2["code"] != float64(0) {
|
||||
t.Fatalf("solve should succeed, got %v", m2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user