197 lines
5.0 KiB
Go
197 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/cookiejar"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.asio.asia/nixevol/NixMsg/internal/config"
|
|
)
|
|
|
|
// TestUplinkDisableFatalAndRevoked 验证停用在线端收到 fatal,已推送投递收到 revoked。
|
|
func TestUplinkDisableFatalAndRevoked(t *testing.T) {
|
|
dataDir := t.TempDir()
|
|
cfgPath := writeTestConfig(t, dataDir)
|
|
initAdminForTest(t, dataDir)
|
|
enableRegistration(t, dataDir, "uplink-code")
|
|
|
|
cfg, err := config.Load(cfgPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if vErr := cfg.Validate(); vErr != nil {
|
|
t.Fatal(vErr)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
errCh := make(chan error, 1)
|
|
go func() { errCh <- runServe(ctx, cfg) }()
|
|
defer func() {
|
|
cancel()
|
|
select {
|
|
case err := <-errCh:
|
|
if err != nil {
|
|
t.Errorf("serve exit: %v", err)
|
|
}
|
|
case <-time.After(15 * time.Second):
|
|
t.Error("serve did not stop")
|
|
}
|
|
}()
|
|
|
|
addr := waitListenAddr(t, dataDir, 15*time.Second)
|
|
base := "http://" + addr
|
|
|
|
registerEP(t, base, "alice", "password12", "Alice")
|
|
registerEP(t, base, "bob", "password12", "Bob")
|
|
|
|
alice := mqttSessionLogin(t, base, "alice", "password12")
|
|
defer alice.Close()
|
|
bob := mqttSessionLogin(t, base, "bob", "password12")
|
|
defer bob.Close()
|
|
|
|
delay0 := int64(0)
|
|
sendResp := alice.Request(t, map[string]any{
|
|
"v": 1, "type": "send", "rid": "s1", "id": "dm-fatal-1",
|
|
"to": map[string]any{"kind": "endpoint", "id": "bob"},
|
|
"body": map[string]any{"enc": "utf8", "data": "to-void"},
|
|
"delay_ms": delay0,
|
|
})
|
|
if !sendResp.OK {
|
|
t.Fatalf("send: %+v", sendResp)
|
|
}
|
|
msg := bob.WaitType(t, "msg", 8*time.Second)
|
|
if msg["id"] != "dm-fatal-1" {
|
|
t.Fatalf("bob msg=%v", msg)
|
|
}
|
|
|
|
admin := adminHTTPClient(t, base)
|
|
disableEP(t, admin, base, "bob")
|
|
|
|
fatal := bob.WaitType(t, "fatal", 8*time.Second)
|
|
if fatal["reason"] != "disabled" {
|
|
t.Fatalf("fatal=%v", fatal)
|
|
}
|
|
revoked := bob.WaitType(t, "revoked", 8*time.Second)
|
|
if revoked["id"] != "dm-fatal-1" || revoked["reason"] != "endpoint_disabled" {
|
|
t.Fatalf("revoked=%v", revoked)
|
|
}
|
|
}
|
|
|
|
// TestUplinkResetPasswordFatal 验证重置登录密码后在线端收到 fatal(password_reset)。
|
|
func TestUplinkResetPasswordFatal(t *testing.T) {
|
|
dataDir := t.TempDir()
|
|
cfgPath := writeTestConfig(t, dataDir)
|
|
initAdminForTest(t, dataDir)
|
|
enableRegistration(t, dataDir, "uplink-code")
|
|
|
|
cfg, err := config.Load(cfgPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if vErr := cfg.Validate(); vErr != nil {
|
|
t.Fatal(vErr)
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
errCh := make(chan error, 1)
|
|
go func() { errCh <- runServe(ctx, cfg) }()
|
|
defer func() {
|
|
cancel()
|
|
select {
|
|
case err := <-errCh:
|
|
if err != nil {
|
|
t.Errorf("serve exit: %v", err)
|
|
}
|
|
case <-time.After(15 * time.Second):
|
|
t.Error("serve did not stop")
|
|
}
|
|
}()
|
|
|
|
addr := waitListenAddr(t, dataDir, 15*time.Second)
|
|
base := "http://" + addr
|
|
|
|
registerEP(t, base, "carol", "password12", "Carol")
|
|
carol := mqttSessionLogin(t, base, "carol", "password12")
|
|
defer carol.Close()
|
|
|
|
admin := adminHTTPClient(t, base)
|
|
resetLoginPassword(t, admin, base, "carol", "password99xx")
|
|
|
|
fatal := carol.WaitType(t, "fatal", 8*time.Second)
|
|
if fatal["reason"] != "password_reset" {
|
|
t.Fatalf("fatal=%v", fatal)
|
|
}
|
|
}
|
|
|
|
func adminHTTPClient(t *testing.T, base string) *http.Client {
|
|
t.Helper()
|
|
jar, err := cookiejar.New(nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client := &http.Client{Jar: jar, Timeout: 10 * time.Second}
|
|
loginBody, _ := json.Marshal(map[string]string{
|
|
"username": "admin",
|
|
"password": "test-admin-password-xx",
|
|
})
|
|
resp, err := client.Post(base+"/api/admin/login", "application/json", bytes.NewReader(loginBody))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := io.ReadAll(resp.Body)
|
|
_ = resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("admin login: %d %s", resp.StatusCode, raw)
|
|
}
|
|
return client
|
|
}
|
|
|
|
func disableEP(t *testing.T, client *http.Client, base, id string) {
|
|
t.Helper()
|
|
req, err := http.NewRequest(http.MethodPatch, base+"/api/admin/endpoints/"+id,
|
|
strings.NewReader(`{"enabled":false}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("X-Nixmsg-Request", "1")
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := io.ReadAll(resp.Body)
|
|
_ = resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("disable %s: %d %s", id, resp.StatusCode, raw)
|
|
}
|
|
}
|
|
|
|
func resetLoginPassword(t *testing.T, client *http.Client, base, id, password string) {
|
|
t.Helper()
|
|
body := `{"login_password":"` + password + `"}`
|
|
req, err := http.NewRequest(http.MethodPost, base+"/api/admin/endpoints/"+id+"/reset-login-password",
|
|
strings.NewReader(body))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("X-Nixmsg-Request", "1")
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := io.ReadAll(resp.Body)
|
|
_ = resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("reset password %s: %d %s", id, resp.StatusCode, raw)
|
|
}
|
|
}
|