feat: validate Worker protocol and verify process crash recovery

This commit is contained in:
2026-09-17 15:17:09 +08:00
parent 4d4b2e9f7d
commit 9e3d4be5cc
23 changed files with 1878 additions and 28 deletions

View File

@@ -10,6 +10,7 @@ import (
"testing"
"time"
"git.yuntuo.hk/k2safe/orbit/internal/testcontract"
"git.yuntuo.hk/k2safe/orbit/internal/transport/httpserver"
core "git.yuntuo.hk/k2safe/orbit/model"
pulse "git.yuntuo.hk/k2safe/orbit/pulse/model"
@@ -75,6 +76,7 @@ func (c *testWorkerSocket) send(kind string, body any, id core.ID) core.ID {
if err != nil {
c.t.Fatal(err)
}
testcontract.WorkerFrame(c.t, "client", frame)
ctx, cancel := context.WithTimeout(c.ctx, 5*time.Second)
defer cancel()
if err := c.conn.Write(ctx, websocket.MessageText, frame); err != nil {
@@ -90,6 +92,7 @@ func (c *testWorkerSocket) read() testWorkerFrame {
if err != nil {
c.t.Fatal(err)
}
testcontract.WorkerFrame(c.t, "server", raw)
var frame testWorkerFrame
if kind != websocket.MessageText || json.Unmarshal(raw, &frame) != nil || frame.Version != "1" || frame.MessageID.Validate() != nil {
c.t.Fatal("invalid server frame")
@@ -191,6 +194,10 @@ func TestIntegrationWorkerWebSocket(t *testing.T) {
if err := json.Unmarshal(first.ack(startMessage), &begun); err != nil || !begun.Lease.Active || begun.Lease.State != pulse.AttemptRunning {
t.Fatal("missing durable start")
}
var renewed worker.CommandResult
if err := json.Unmarshal(first.ack(first.send("task.renew", ref, "")), &renewed); err != nil || !renewed.Lease.Active {
t.Fatal("missing renewal confirmation")
}
// 模拟旧节点连接断开;新的 WSS 连接进入不同节点并原子迁移有效尝试。
_ = first.conn.CloseNow()
second := connectTestWorker(t, ctx, serverB, fixture)
@@ -220,13 +227,22 @@ func TestIntegrationWorkerWebSocket(t *testing.T) {
})
t.Run("strict_worker_frame_and_hub_shutdown", func(t *testing.T) {
conn := connectTestWorker(t, ctx, serverB, fixture)
secret, _, err := worker.NewSecret()
if err != nil {
t.Fatal(err)
}
message := conn.send("worker.resume", worker.AttemptRef{TaskID: executionID(t), AttemptID: executionID(t), Epoch: 1, LeaseSecret: secret}, "")
failure := conn.read()
if failure.Type != "error" || failure.ReplyTo != message || failure.Error == nil || failure.Error.Code != "NOT_FOUND" {
t.Fatal("missing structured error frame")
}
raw := []byte(`{"protocol_version":"1","protocol_version":"1","type":"worker.capacity"}`)
if err := conn.conn.Write(ctx, websocket.MessageText, raw); err != nil {
t.Fatal(err)
}
readCtx, stop := context.WithTimeout(ctx, 3*time.Second)
defer stop()
_, _, err := conn.conn.Read(readCtx)
_, _, err = conn.conn.Read(readCtx)
if websocket.CloseStatus(err) != websocket.StatusPolicyViolation {
t.Fatalf("duplicate frame not rejected: %v", err)
}