package protocol import ( "encoding/json" "testing" "time" ) func TestEnvelopeValidate_AgentHello(t *testing.T) { payload := AgentHelloPayload{ NodeID: "node-01", AgentVersion: "0.1.0", ProtocolVersion: CurrentProtocolVersion, } raw, err := json.Marshal(payload) if err != nil { t.Fatal(err) } env := &Envelope{ ProtocolVersion: CurrentProtocolVersion, MessageID: "msg-001", Type: TypeAgentHello, Timestamp: time.Now().UTC(), Payload: raw, } if err := env.Validate(); err != nil { t.Fatalf("expected valid envelope, got %v", err) } if !env.Type.IsAgentToControl() { t.Fatal("agent.hello should be agent-to-control") } } func TestEnvelopeValidate_ServerStart(t *testing.T) { payload := ServerStartPayload{ OperationMeta: OperationMeta{ ServerID: "srv-01", Generation: 4, }, DesiredGeneration: 4, } raw, err := json.Marshal(payload) if err != nil { t.Fatal(err) } env := &Envelope{ ProtocolVersion: CurrentProtocolVersion, MessageID: "msg-002", CorrelationID: "corr-002", Type: TypeServerStart, Timestamp: time.Now().UTC(), Payload: raw, } if err := env.Validate(); err != nil { t.Fatalf("expected valid envelope, got %v", err) } if !env.Type.IsControlToAgent() { t.Fatal("server.start should be control-to-agent") } } func TestEnvelopeValidate_RejectsInvalid(t *testing.T) { tests := []struct { name string env Envelope }{ { name: "missing message id", env: Envelope{ ProtocolVersion: 1, Type: TypeAgentHeartbeat, Timestamp: time.Now().UTC(), Payload: json.RawMessage(`{"nodeId":"n1"}`), }, }, { name: "unknown type", env: Envelope{ ProtocolVersion: 1, MessageID: "m1", Type: "agent.unknown", Timestamp: time.Now().UTC(), Payload: json.RawMessage(`{}`), }, }, { name: "non-utc timestamp", env: Envelope{ ProtocolVersion: 1, MessageID: "m1", Type: TypeAgentHeartbeat, Timestamp: time.Now().Local(), Payload: json.RawMessage(`{"nodeId":"n1"}`), }, }, { name: "invalid payload json", env: Envelope{ ProtocolVersion: 1, MessageID: "m1", Type: TypeAgentHeartbeat, Timestamp: time.Now().UTC(), Payload: json.RawMessage(`{not-json`), }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if err := tc.env.Validate(); err == nil { t.Fatal("expected validation error") } }) } } func TestDecodeEnvelope(t *testing.T) { data := []byte(`{ "protocolVersion": 1, "messageId": "01HXYZ", "type": "agent.heartbeat", "timestamp": "2026-01-01T00:00:00Z", "payload": {"nodeId":"node-01","runningServers":0} }`) env, err := DecodeEnvelope(data) if err != nil { t.Fatalf("decode failed: %v", err) } if env.Type != TypeAgentHeartbeat { t.Fatalf("unexpected type %s", env.Type) } } func TestNewEnvelope(t *testing.T) { env, err := NewEnvelope(TypeAgentHello, "msg-new", AgentHelloPayload{ NodeID: "n1", AgentVersion: "0.1.0", ProtocolVersion: 1, }) if err != nil { t.Fatalf("new envelope: %v", err) } if env.ProtocolVersion != CurrentProtocolVersion { t.Fatalf("protocol version %d", env.ProtocolVersion) } }