fix(protocol): fence gateway work recovery

This commit is contained in:
sechmachine
2026-08-13 07:43:42 +07:00
parent 8eacc4fda9
commit 6e18bc9ee6
21 changed files with 572 additions and 146 deletions
+120
View File
@@ -89,11 +89,125 @@ func TestRC6BitratePreferenceTaggedBounds(t *testing.T) {
}
}
func TestRC6GatewayQualityDiscoveryAndLeaseFencing(t *testing.T) {
operationID := "12345678-1234-1234-1234-123456789abc"
revision := int64(7)
leaseGeneration := int64(3)
currentRevision := int64(6)
poll := protocol.GatewayQualityWorkRequest{Version: "1", SessionID: "session-1", GatewayID: "gateway-1", ReconnectSequence: 2, Acquisition: "poll"}
if err := poll.Validate(); err != nil {
t.Fatalf("session-bound lost-prompt poll rejected: %v", err)
}
prompt := poll
prompt.Acquisition = "prompt"
prompt.OperationID = operationID
prompt.Revision = &revision
if err := prompt.Validate(); err != nil {
t.Fatalf("prompt-bound acquisition rejected: %v", err)
}
observation := prompt
observation.Acquisition = "observation"
observation.LeaseGeneration = &leaseGeneration
observation.CurrentAppliedRevision = &currentRevision
if err := observation.Validate(); err != nil {
t.Fatalf("lease-bound applied-revision observation rejected: %v", err)
}
for name, invalid := range map[string]protocol.GatewayQualityWorkRequest{
"poll with unknown coordinates": prompt,
"prompt missing revision": {Version: "1", SessionID: "session-1", GatewayID: "gateway-1", Acquisition: "prompt", OperationID: operationID},
"observation missing lease": {Version: "1", SessionID: "session-1", GatewayID: "gateway-1", Acquisition: "observation", OperationID: operationID, Revision: &revision, CurrentAppliedRevision: &currentRevision},
} {
if name == "poll with unknown coordinates" {
invalid.Acquisition = "poll"
}
if invalid.Validate() == nil {
t.Fatalf("accepted invalid %s: %+v", name, invalid)
}
}
stopPoll := protocol.GatewayStopWorkRequest{Version: "1", SessionID: "session-1", GatewayID: "gateway-1", ReconnectSequence: 2, Acquisition: "poll"}
if err := stopPoll.Validate(); err != nil {
t.Fatalf("session-bound Stop poll rejected: %v", err)
}
stopPrompt := stopPoll
stopPrompt.Acquisition = "prompt"
stopPrompt.OperationID = operationID
if err := stopPrompt.Validate(); err != nil {
t.Fatalf("prompt-bound Stop acquisition rejected: %v", err)
}
stopPoll.OperationID = operationID
if stopPoll.Validate() == nil {
t.Fatal("Stop poll accepted unknown operation coordinates")
}
}
func TestRC6GatewayQualityAckHasUnambiguousObservation(t *testing.T) {
revision := int64(7)
prior := int64(6)
base := protocol.GatewayQualityAck{
Version: "1", SessionID: "session-1", GatewayID: "gateway-1", ReconnectSequence: 2,
OperationID: "12345678-1234-1234-1234-123456789abc", Revision: revision, LeaseGeneration: 3,
}
for _, valid := range []protocol.GatewayQualityAck{
func() protocol.GatewayQualityAck {
value := base
value.Outcome = "applied"
value.CurrentAppliedRevision = &revision
return value
}(),
func() protocol.GatewayQualityAck {
value := base
value.Outcome = "proven_prior"
value.CurrentAppliedRevision = &prior
return value
}(),
func() protocol.GatewayQualityAck { value := base; value.Outcome = "unknown"; return value }(),
} {
if err := valid.Validate(); err != nil {
t.Fatalf("valid quality acknowledgement rejected: %+v: %v", valid, err)
}
}
for _, invalid := range []protocol.GatewayQualityAck{
func() protocol.GatewayQualityAck {
value := base
value.Outcome = "applied"
value.CurrentAppliedRevision = &prior
return value
}(),
func() protocol.GatewayQualityAck {
value := base
value.Outcome = "proven_prior"
value.CurrentAppliedRevision = &revision
return value
}(),
func() protocol.GatewayQualityAck {
value := base
value.Outcome = "unknown"
value.CurrentAppliedRevision = &prior
return value
}(),
func() protocol.GatewayQualityAck {
value := base
value.Outcome = "unknown"
value.LeaseGeneration = 0
return value
}(),
} {
if invalid.Validate() == nil {
t.Fatalf("contradictory or unfenced quality acknowledgement accepted: %+v", invalid)
}
}
}
func TestRC6JSONFixturesAndCanonicalOperationFields(t *testing.T) {
validFixtures := map[string]func([]byte) error{
"../../fixtures/valid/session-request.json": func(raw []byte) error { _, err := protocol.DecodeSessionRequest(raw); return err },
"../../fixtures/valid/selected-session-descriptor.json": func(raw []byte) error { _, err := protocol.DecodeSelectedSessionDescriptor(raw); return err },
"../../fixtures/valid/session-quality-limits.json": func(raw []byte) error { _, err := protocol.DecodeSessionQualityLimits(raw); return err },
"../../fixtures/valid/gateway-quality-poll.json": func(raw []byte) error { _, err := protocol.DecodeGatewayQualityWorkRequest(raw); return err },
"../../fixtures/valid/gateway-quality-ack-applied.json": func(raw []byte) error { _, err := protocol.DecodeGatewayQualityAck(raw); return err },
}
for path, decode := range validFixtures {
raw, err := os.ReadFile(path)
@@ -111,6 +225,12 @@ func TestRC6JSONFixturesAndCanonicalOperationFields(t *testing.T) {
"../../fixtures/invalid/video-profile-unknown.json": func(raw []byte) error { _, err := protocol.DecodeVideoProfile(raw); return err },
"../../fixtures/invalid/bitrate-preference-auto-target.json": func(raw []byte) error { _, err := protocol.DecodeBitratePreference(raw); return err },
"../../fixtures/invalid/selected-session-descriptor-provider-field.json": func(raw []byte) error { _, err := protocol.DecodeSelectedSessionDescriptor(raw); return err },
"../../fixtures/invalid/gateway-quality-poll-coordinates.json": func(raw []byte) error { _, err := protocol.DecodeGatewayQualityWorkRequest(raw); return err },
"../../fixtures/invalid/gateway-quality-ack-contradictory.json": func(raw []byte) error { _, err := protocol.DecodeGatewayQualityAck(raw); return err },
"../../fixtures/invalid/gateway-quality-ack-uppercase-uuid.json": func(raw []byte) error { _, err := protocol.DecodeGatewayQualityAck(raw); return err },
"../../fixtures/invalid/gateway-quality-ack-zero-uuid.json": func(raw []byte) error { _, err := protocol.DecodeGatewayQualityAck(raw); return err },
"../../fixtures/invalid/quality-operation-offset-time.json": func(raw []byte) error { _, err := protocol.DecodeQualityChangeOperation(raw); return err },
"../../fixtures/invalid/quality-operation-noncanonical-fraction.json": func(raw []byte) error { _, err := protocol.DecodeQualityChangeOperation(raw); return err },
}
for path, decode := range invalidFixtures {
raw, err := os.ReadFile(path)