185 lines
4.8 KiB
Go
185 lines
4.8 KiB
Go
package localcontrol
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
iop "iop/proto/gen/iop"
|
|
|
|
"google.golang.org/protobuf/encoding/protowire"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func TestProtocolValidationMatrix(t *testing.T) {
|
|
t.Parallel()
|
|
valid := runtimeStatusRequest("message-1")
|
|
unknownOperation := runtimeStatusRequest("message-unknown")
|
|
unknownOperation.Operation = "runtime.future"
|
|
readWithCommand := runtimeStatusRequest("message-command")
|
|
readWithCommand.GetRequest().CommandId = "unexpected"
|
|
mutationWithoutCommand := projectRequest(
|
|
"message-mutation",
|
|
"",
|
|
OperationProjectStart,
|
|
"project-a",
|
|
)
|
|
|
|
tests := []struct {
|
|
name string
|
|
input *iop.AgentLocalEnvelope
|
|
code string
|
|
}{
|
|
{name: "valid", input: valid},
|
|
{
|
|
name: "unsupported version",
|
|
input: func() *iop.AgentLocalEnvelope {
|
|
value := cloneEnvelope(valid)
|
|
value.ProtocolVersion++
|
|
return value
|
|
}(),
|
|
code: ErrorUnsupportedVersion,
|
|
},
|
|
{
|
|
name: "wrong kind",
|
|
input: func() *iop.AgentLocalEnvelope {
|
|
value := cloneEnvelope(valid)
|
|
value.Kind = iop.AgentLocalKind_AGENT_LOCAL_KIND_EVENT
|
|
return value
|
|
}(),
|
|
code: ErrorMalformedFrame,
|
|
},
|
|
{
|
|
name: "missing message",
|
|
input: func() *iop.AgentLocalEnvelope {
|
|
value := cloneEnvelope(valid)
|
|
value.MessageId = ""
|
|
return value
|
|
}(),
|
|
code: ErrorMalformedFrame,
|
|
},
|
|
{
|
|
name: "unknown operation",
|
|
input: unknownOperation,
|
|
code: ErrorUnsupportedOperation,
|
|
},
|
|
{
|
|
name: "read command id",
|
|
input: readWithCommand,
|
|
code: ErrorMalformedFrame,
|
|
},
|
|
{
|
|
name: "mutation missing command id",
|
|
input: mutationWithoutCommand,
|
|
code: ErrorMalformedFrame,
|
|
},
|
|
{
|
|
name: "replay daemon missing",
|
|
input: func() *iop.AgentLocalEnvelope {
|
|
value := cloneEnvelope(valid)
|
|
cursor := uint64(0)
|
|
value.GetRequest().ReplayAfterSequence = &cursor
|
|
return value
|
|
}(),
|
|
code: ErrorMalformedFrame,
|
|
},
|
|
{
|
|
name: "oversize",
|
|
input: func() *iop.AgentLocalEnvelope {
|
|
value := cloneEnvelope(valid)
|
|
value.MessageId = strings.Repeat("x", MaxFrameBytes)
|
|
return value
|
|
}(),
|
|
code: ErrorMalformedFrame,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
failure := ValidateRequest(test.input)
|
|
if test.code == "" {
|
|
if failure != nil {
|
|
t.Fatalf("ValidateRequest: %v", failure)
|
|
}
|
|
return
|
|
}
|
|
if failure == nil || failure.Code != test.code {
|
|
t.Fatalf("failure = %#v, want code %q", failure, test.code)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProtocolRejectsUnknownFields(t *testing.T) {
|
|
t.Parallel()
|
|
payload, err := proto.Marshal(runtimeStatusRequest("message-unknown-field"))
|
|
if err != nil {
|
|
t.Fatalf("Marshal: %v", err)
|
|
}
|
|
payload = protowire.AppendTag(payload, 100, protowire.VarintType)
|
|
payload = protowire.AppendVarint(payload, 1)
|
|
envelope, err := ParseEnvelope(payload)
|
|
if err != nil {
|
|
t.Fatalf("ParseEnvelope: %v", err)
|
|
}
|
|
failure := ValidateRequest(envelope)
|
|
if failure == nil || failure.Code != ErrorMalformedFrame {
|
|
t.Fatalf("failure = %#v, want malformed_frame", failure)
|
|
}
|
|
}
|
|
|
|
func TestProtocolCommandHashUsesImmutableArguments(t *testing.T) {
|
|
t.Parallel()
|
|
first := projectRequest(
|
|
"message-a",
|
|
"command-a",
|
|
OperationProjectStart,
|
|
"project-a",
|
|
)
|
|
second := cloneEnvelope(first)
|
|
second.MessageId = "message-b"
|
|
second.GetRequest().CommandId = "command-b"
|
|
cursor := uint64(9)
|
|
second.GetRequest().ReplayAfterSequence = &cursor
|
|
second.GetRequest().ReplayDaemonId = "daemon-a"
|
|
|
|
firstHash, err := immutableCommandHash(first.GetOperation(), first.GetRequest())
|
|
if err != nil {
|
|
t.Fatalf("immutableCommandHash first: %v", err)
|
|
}
|
|
secondHash, err := immutableCommandHash(second.GetOperation(), second.GetRequest())
|
|
if err != nil {
|
|
t.Fatalf("immutableCommandHash second: %v", err)
|
|
}
|
|
if firstHash != secondHash {
|
|
t.Fatalf("hash changed for transport-only fields: %q != %q", firstHash, secondHash)
|
|
}
|
|
second.GetRequest().GetProject().MilestoneId = "milestone-b"
|
|
changedHash, err := immutableCommandHash(second.GetOperation(), second.GetRequest())
|
|
if err != nil {
|
|
t.Fatalf("immutableCommandHash changed: %v", err)
|
|
}
|
|
if changedHash == firstHash {
|
|
t.Fatal("immutable argument change did not change command hash")
|
|
}
|
|
}
|
|
|
|
func TestProtocolErrorsAreBoundedAndSafe(t *testing.T) {
|
|
t.Parallel()
|
|
request := runtimeStatusRequest("message-safe")
|
|
response := errorEnvelope(
|
|
request,
|
|
protocolError(ErrorInternal, strings.Repeat("safe", MaxFrameBytes)),
|
|
)
|
|
if response.GetError() == nil {
|
|
t.Fatal("expected typed error payload")
|
|
}
|
|
if len(response.GetError().GetSafeMessage()) > maxSafeTextBytes {
|
|
t.Fatalf("safe message length = %d", len(response.GetError().GetSafeMessage()))
|
|
}
|
|
if response.GetCorrelationId() != request.GetMessageId() ||
|
|
response.GetError().GetCorrelationId() != request.GetMessageId() {
|
|
t.Fatal("error correlation was not preserved")
|
|
}
|
|
}
|