|
|
|
|
@ -13,6 +13,7 @@ import (
|
|
|
|
|
"net"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"net/netip"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
@ -1738,3 +1739,692 @@ func TestLocalClient_ErrorHandling(t *testing.T) {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== Additional comprehensive tests for remaining uncovered methods =====
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_Ping(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/ping") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"Success": true,
|
|
|
|
|
"Latency": 0.025,
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.Ping(context.Background(), netip.Addr{}, "")
|
|
|
|
|
// May error due to invalid IP, but tests the HTTP path
|
|
|
|
|
_ = err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_QueryDNS(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/query-dns") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"Bytes": []byte{0, 0, 0, 0},
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, err := lc.QueryDNS(context.Background(), "example.com", "A")
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Allow errors, testing HTTP path
|
|
|
|
|
t.Logf("QueryDNS returned error (may be expected): %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_CurrentDERPMap(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/derpmap") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"Regions": map[string]interface{}{
|
|
|
|
|
"1": map[string]interface{}{"RegionID": 1, "RegionName": "test"},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.CurrentDERPMap(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("CurrentDERPMap returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_ProfileStatus(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/profiles") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode([]map[string]interface{}{
|
|
|
|
|
{"ID": "prof1", "Name": "profile1"},
|
|
|
|
|
{"ID": "prof2", "Name": "profile2"},
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, err := lc.ProfileStatus(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("ProfileStatus failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_SwitchProfile(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.Method != "PUT" {
|
|
|
|
|
t.Errorf("Method = %s, want PUT", r.Method)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/profiles/") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := lc.SwitchProfile(context.Background(), ipn.ProfileID("test-profile"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("SwitchProfile returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_DeleteProfile(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.Method != "DELETE" {
|
|
|
|
|
t.Errorf("Method = %s, want DELETE", r.Method)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/profiles/") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := lc.DeleteProfile(context.Background(), ipn.ProfileID("test-profile"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("DeleteProfile returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_QueryFeature(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/query-feature") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"Complete": true,
|
|
|
|
|
"Text": "feature is supported",
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.QueryFeature(context.Background(), "some-feature")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("QueryFeature returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_SetUseExitNode(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.Method != "POST" {
|
|
|
|
|
t.Errorf("Method = %s, want POST", r.Method)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/exit-node") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := lc.SetUseExitNode(context.Background(), true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("SetUseExitNode returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_DebugPacketFilterRules(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/packet-filter-rules") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write([]byte("[]"))
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.DebugPacketFilterRules(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("DebugPacketFilterRules returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_GetServeConfig(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/serve-config") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write([]byte("{}"))
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.GetServeConfig(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("GetServeConfig returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_SetServeConfig(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.Method != "POST" {
|
|
|
|
|
t.Errorf("Method = %s, want POST", r.Method)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/serve-config") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := lc.SetServeConfig(context.Background(), nil)
|
|
|
|
|
// Allow error for nil config
|
|
|
|
|
_ = err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_CheckUpdate(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/update/check") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"CurrentVersion": "1.0.0",
|
|
|
|
|
"LatestVersion": "1.1.0",
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.CheckUpdate(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("CheckUpdate returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_ReloadConfig(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.Method != "POST" {
|
|
|
|
|
t.Errorf("Method = %s, want POST", r.Method)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/reload-config") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.ReloadConfig(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("ReloadConfig failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_AwaitWaitingFiles(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/files") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
// Check for wait parameter
|
|
|
|
|
if r.URL.Query().Get("wait") == "" {
|
|
|
|
|
t.Error("AwaitWaitingFiles should set wait parameter")
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode([]map[string]interface{}{
|
|
|
|
|
{"Name": "file.txt", "Size": 100},
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
files, err := lc.AwaitWaitingFiles(context.Background(), 1*time.Second)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("AwaitWaitingFiles returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
_ = files // May be nil or have files
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_ExpandSNIName(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/expand-sni-name") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write([]byte("expanded.example.com"))
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result, ok := lc.ExpandSNIName(context.Background(), "example")
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatal("ExpandSNIName failed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !strings.Contains(result, "expanded") {
|
|
|
|
|
t.Errorf("result = %q, want to contain 'expanded'", result)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_CertPair(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/cert/") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"CertPEM": "-----BEGIN CERTIFICATE-----\ntest\n-----END CERTIFICATE-----",
|
|
|
|
|
"KeyPEM": "-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----",
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, err := lc.CertPair(context.Background(), "example.com")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("CertPair returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_NetworkLockStatus(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/tka/status") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"Enabled": true,
|
|
|
|
|
"Head": "abc123",
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.NetworkLockStatus(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("NetworkLockStatus returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_NetworkLockLog(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/tka/log") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode([]map[string]interface{}{
|
|
|
|
|
{"AUM": "test-aum", "MessageHash": "hash123"},
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.NetworkLockLog(context.Background(), 10)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("NetworkLockLog returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_NetworkLockDisable(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.Method != "POST" {
|
|
|
|
|
t.Errorf("Method = %s, want POST", r.Method)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/tka/disable") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := lc.NetworkLockDisable(context.Background(), []byte{})
|
|
|
|
|
// May error with empty secret
|
|
|
|
|
_ = err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLocalClient_SuggestExitNode(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if !strings.Contains(r.URL.Path, "/suggest-exit-node") {
|
|
|
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
|
|
|
"ID": "node123",
|
|
|
|
|
"Name": "exit-node-1",
|
|
|
|
|
})
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.SuggestExitNode(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Logf("SuggestExitNode returned error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test HTTP method variations
|
|
|
|
|
func TestLocalClient_HTTPMethods(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
fn func(*LocalClient) error
|
|
|
|
|
expectedMethod string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "POST_methods",
|
|
|
|
|
fn: func(lc *LocalClient) error {
|
|
|
|
|
return lc.DebugAction(context.Background(), "test")
|
|
|
|
|
},
|
|
|
|
|
expectedMethod: "POST",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "DELETE_methods",
|
|
|
|
|
fn: func(lc *LocalClient) error {
|
|
|
|
|
return lc.DeleteWaitingFile(context.Background(), "test.txt")
|
|
|
|
|
},
|
|
|
|
|
expectedMethod: "DELETE",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
methodReceived := ""
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
methodReceived = r.Method
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_ = tt.fn(lc)
|
|
|
|
|
|
|
|
|
|
if methodReceived != tt.expectedMethod {
|
|
|
|
|
t.Errorf("HTTP method = %s, want %s", methodReceived, tt.expectedMethod)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test timeout and cancellation behavior
|
|
|
|
|
func TestLocalClient_TimeoutBehavior(t *testing.T) {
|
|
|
|
|
slowServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
defer slowServer.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", slowServer.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
_, err := lc.get200(ctx, "/test")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Error("expected timeout error")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test response body limits
|
|
|
|
|
func TestLocalClient_ResponseSizeLimits(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
size int
|
|
|
|
|
wantErr bool
|
|
|
|
|
}{
|
|
|
|
|
{"small_response", 1024, false},
|
|
|
|
|
{"medium_response", 1024 * 1024, false},
|
|
|
|
|
{"large_acceptable", 5 * 1024 * 1024, false},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
data := make([]byte, tt.size)
|
|
|
|
|
for i := range data {
|
|
|
|
|
data[i] = 'A'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write(data)
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resp, err := lc.get200(context.Background(), "/test")
|
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
|
|
|
t.Errorf("get200() error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
|
}
|
|
|
|
|
if err == nil && len(resp) != tt.size {
|
|
|
|
|
t.Errorf("response size = %d, want %d", len(resp), tt.size)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test JSON parsing edge cases
|
|
|
|
|
func TestLocalClient_JSONParsing(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
response string
|
|
|
|
|
wantErr bool
|
|
|
|
|
}{
|
|
|
|
|
{"valid_json", `{"key": "value"}`, false},
|
|
|
|
|
{"empty_json", `{}`, false},
|
|
|
|
|
{"json_array", `[]`, false},
|
|
|
|
|
{"invalid_json", `{invalid}`, true},
|
|
|
|
|
{"truncated_json", `{"key": "val`, true},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write([]byte(tt.response))
|
|
|
|
|
}))
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
lc := &LocalClient{
|
|
|
|
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
|
var d net.Dialer
|
|
|
|
|
return d.DialContext(ctx, "tcp", server.Listener.Addr().String())
|
|
|
|
|
},
|
|
|
|
|
OmitAuth: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := lc.Status(context.Background())
|
|
|
|
|
hasErr := err != nil
|
|
|
|
|
if hasErr != tt.wantErr {
|
|
|
|
|
t.Errorf("JSON parsing error = %v, wantErr %v", err, tt.wantErr)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|