Deploy MQTT test backend with private viewer and Gitea CI/CD
Test and deploy MQTT backend / test (push) Successful in 2m8s
Test and deploy MQTT backend / deploy (push) Successful in 18s

This commit is contained in:
2026-10-08 14:48:53 +03:30
commit 71ef622b4f
70 changed files with 7123 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
package simulator
import (
"context"
"io"
"log/slog"
"testing"
"time"
"github.com/eclipse/paho.golang/paho"
"robot-market/test-mqtt-5/internal/backend"
"robot-market/test-mqtt-5/internal/config"
"robot-market/test-mqtt-5/internal/events"
"robot-market/test-mqtt-5/internal/mqtt"
)
func testSimulator() *Simulator {
cfg := config.Defaults()
return New(mqtt.New(cfg, events.NewStore(64, slog.New(slog.NewTextHandler(io.Discard, nil)))))
}
func commandPacket(s *Simulator, cmdName, id string) *paho.Publish {
cmd := backend.NewCommand("sim-001", cmdName, id, 15*time.Second)
p := mqtt.Packet(mqtt.Topic(s.Client.Config, "commands"), 1, false, cmd)
p.Properties.ResponseTopic = mqtt.Topic(s.Client.Config, "acks")
p.Properties.CorrelationData = []byte(id)
p.Properties.MessageExpiry = paho.Uint32(15)
return p
}
func TestDuplicateAndConflict(t *testing.T) {
s := testSimulator()
ctx := context.Background()
id := backend.ID()
p := commandPacket(s, "ping", id)
s.Handle(ctx, p)
s.Handle(ctx, p)
if len(s.cache) != 1 {
t.Fatal("duplicate not bounded")
}
s.CompleteDue(ctx, time.Now())
s.Handle(ctx, p)
s.CompleteDue(ctx, time.Now())
if s.Executions != 1 || !s.cache[id].complete {
t.Fatal("duplicate executed again")
}
conflict := commandPacket(s, "get_version", id)
s.Handle(ctx, conflict)
s.CompleteDue(ctx, time.Now())
if s.Executions != 1 || s.cache[id].command.Command != "ping" {
t.Fatal("conflicting reuse executed")
}
}
func TestInvalidMetadataAndCapacity(t *testing.T) {
for _, bad := range []string{"retained", "topic", "correlation", "expiry", "qos"} {
t.Run(bad, func(t *testing.T) {
s := testSimulator()
p := commandPacket(s, "ping", backend.ID())
switch bad {
case "retained":
p.Retain = true
case "topic":
p.Properties.ResponseTopic = "other/acks"
case "correlation":
p.Properties.CorrelationData = nil
case "expiry":
p.Properties.MessageExpiry = nil
case "qos":
p.QoS = 0
}
s.Handle(context.Background(), p)
if len(s.cache) != 0 {
t.Fatal("invalid command admitted")
}
})
}
s := testSimulator()
s.Client.Config.Limits.DedupEntries = 1
s.Client.Config.Simulator.AckDelay = time.Second
s.Handle(context.Background(), commandPacket(s, "ping", backend.ID()))
s.Handle(context.Background(), commandPacket(s, "ping", backend.ID()))
if len(s.cache) != 1 {
t.Fatal("cache capacity ignored")
}
s.CompleteDue(context.Background(), time.Now().Add(time.Minute))
if s.Executions != 0 {
t.Fatal("expired command executed")
}
s.purge(time.Now().Add(time.Hour))
if len(s.cache) != 0 {
t.Fatal("completed cache not purged")
}
}
func TestCancellationStopsAdmissionAndExecution(t *testing.T) {
s := testSimulator()
s.Handle(context.Background(), commandPacket(s, "ping", backend.ID()))
ctx, cancel := context.WithCancel(context.Background())
cancel()
s.Handle(ctx, commandPacket(s, "ping", backend.ID()))
s.CompleteDue(ctx, time.Now().Add(time.Second))
if len(s.cache) != 1 || s.Executions != 0 {
t.Fatal("work admitted/executed after cancellation")
}
}