186 lines
4.3 KiB
Go
186 lines
4.3 KiB
Go
package load
|
||
|
||
import (
|
||
"bytes"
|
||
"io"
|
||
"net"
|
||
"os"
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"git.asio.asia/nixevol/NixMsg/test/harness"
|
||
"github.com/mochi-mqtt/server/v2/packets"
|
||
)
|
||
|
||
func TestEncodeConnectMQTT5(t *testing.T) {
|
||
raw, err := encodeConnect("ep-1", "password1234")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if raw[0]>>4 != packets.Connect {
|
||
t.Fatalf("type=0x%02x", raw[0])
|
||
}
|
||
rem, n, err := decodeRemainingLength(raw[1:])
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
body := raw[1+n:]
|
||
pk := packets.Packet{
|
||
ProtocolVersion: mqttProtocolLevel5,
|
||
FixedHeader: packets.FixedHeader{Type: packets.Connect, Remaining: rem},
|
||
}
|
||
if err := pk.ConnectDecode(body); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if pk.ProtocolVersion != mqttProtocolLevel5 {
|
||
t.Fatalf("protocol version %d", pk.ProtocolVersion)
|
||
}
|
||
if !pk.Connect.Clean {
|
||
t.Fatal("CleanStart=false")
|
||
}
|
||
if pk.Connect.Keepalive != mqttKeepaliveSec {
|
||
t.Fatalf("keepalive=%d", pk.Connect.Keepalive)
|
||
}
|
||
if pk.Connect.ClientIdentifier != "ep-1" {
|
||
t.Fatalf("client id %q", pk.Connect.ClientIdentifier)
|
||
}
|
||
if string(pk.Connect.Username) != "ep-1" {
|
||
t.Fatalf("username %q", pk.Connect.Username)
|
||
}
|
||
if string(pk.Connect.Password) != "password1234" {
|
||
t.Fatalf("password mismatch")
|
||
}
|
||
if !pk.Connect.UsernameFlag || !pk.Connect.PasswordFlag {
|
||
t.Fatal("username/password flag")
|
||
}
|
||
if !bytes.Contains(raw, []byte{0, 4, 'M', 'Q', 'T', 'T', mqttProtocolLevel5}) {
|
||
t.Fatal("CONNECT 不是 MQTT 5")
|
||
}
|
||
}
|
||
|
||
func TestConnackReasonMQTT5(t *testing.T) {
|
||
reason, err := connackReason([]byte{0x20, 0x03, 0x00, 0x00, 0x00})
|
||
if err != nil || reason != 0 {
|
||
t.Fatalf("mqtt5 connack: %d %v", reason, err)
|
||
}
|
||
reason, err = connackReason([]byte{0x20, 0x02, 0x00, 0x87})
|
||
if err != nil || reason != 0x87 {
|
||
t.Fatalf("mqtt311-style: %d %v", reason, err)
|
||
}
|
||
}
|
||
|
||
func TestPercentiles(t *testing.T) {
|
||
empty := calcPercentiles(nil)
|
||
if empty.N != 0 {
|
||
t.Fatalf("empty n=%d", empty.N)
|
||
}
|
||
one := calcPercentiles([]float64{7})
|
||
if one.P50 != 7 || one.P95 != 7 || one.P99 != 7 {
|
||
t.Fatalf("%+v", one)
|
||
}
|
||
vals := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||
p := calcPercentiles(vals)
|
||
if p.P50 != 5 || p.P95 != 10 || p.P99 != 10 {
|
||
t.Fatalf("%+v", p)
|
||
}
|
||
}
|
||
|
||
func TestEndpointIDs(t *testing.T) {
|
||
ids, err := EndpointIDs("qb", 3)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if strings.Join(ids, ",") != "qb0000,qb0001,qb0002" {
|
||
t.Fatalf("%v", ids)
|
||
}
|
||
}
|
||
|
||
func TestMQTT5ConnectFakeBroker(t *testing.T) {
|
||
addr, stop := startFakeMQTT5(t)
|
||
defer stop()
|
||
mc, err := harness.DialMQTTTCP(addr, 2*time.Second)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
defer func() { _ = mc.Close() }()
|
||
pkt, err := encodeConnect("ep1", "password1234")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if sendErr := mc.Send(pkt); sendErr != nil {
|
||
t.Fatal(sendErr)
|
||
}
|
||
ack, err := mc.Recv()
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
reason, err := connackReason(ack)
|
||
if err != nil || reason != 0 {
|
||
t.Fatalf("reason=%d err=%v ack=%x", reason, err, ack)
|
||
}
|
||
}
|
||
|
||
func startFakeMQTT5(t *testing.T) (addr string, closeFn func()) {
|
||
t.Helper()
|
||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||
if err != nil {
|
||
t.Fatalf("listen: %v", err)
|
||
}
|
||
go func() {
|
||
for {
|
||
conn, err := ln.Accept()
|
||
if err != nil {
|
||
return
|
||
}
|
||
go func(c net.Conn) {
|
||
defer func() { _ = c.Close() }()
|
||
_ = c.SetDeadline(time.Now().Add(5 * time.Second))
|
||
buf := make([]byte, 2048)
|
||
n, err := c.Read(buf)
|
||
if err != nil || n < 10 || buf[0] != 0x10 {
|
||
return
|
||
}
|
||
if !bytes.Contains(buf[:n], []byte{0, 4, 'M', 'Q', 'T', 'T', mqttProtocolLevel5}) {
|
||
return
|
||
}
|
||
_, _ = c.Write([]byte{0x20, 0x03, 0x00, 0x00, 0x00})
|
||
_ = c.SetDeadline(time.Time{})
|
||
_, _ = io.Copy(io.Discard, c)
|
||
}(conn)
|
||
}
|
||
}()
|
||
return ln.Addr().String(), func() { _ = ln.Close() }
|
||
}
|
||
|
||
func TestLocalLoadReport(t *testing.T) {
|
||
if os.Getenv("NIXMSG_LOAD_VERIFY") == "" {
|
||
t.Skip("本地 50×20/s×1m:设置 NIXMSG_LOAD_VERIFY=1")
|
||
}
|
||
srv, err := harness.Start(harness.Options{})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
defer func() { _ = srv.Stop() }()
|
||
|
||
cfg := Config{
|
||
HTTPBase: srv.HTTPBase,
|
||
AdminPass: srv.AdminPassword,
|
||
Password: "password1234",
|
||
Prefix: "vr",
|
||
N: 50,
|
||
Rate: 20,
|
||
Duration: time.Minute,
|
||
GroupSize: 50,
|
||
Scenario: "all",
|
||
}
|
||
rep, err := Run(cfg)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if rep.SubmitMs.N == 0 {
|
||
t.Fatalf("empty report:\n%s", rep.Format())
|
||
}
|
||
t.Log(rep.Format())
|
||
}
|