package fabricproto import ( "bytes" "errors" "io" "testing" ) func TestMarshalUnmarshalFrameRoundTrip(t *testing.T) { frame := Frame{ Type: FrameData, Flags: 3, TrafficClass: TrafficClassInteractive, StreamID: 42, Sequence: 7, Payload: []byte("hello"), } encoded, err := MarshalFrame(frame) if err != nil { t.Fatalf("marshal frame: %v", err) } decoded, err := UnmarshalFrame(encoded, DefaultMaxPayload) if err != nil { t.Fatalf("unmarshal frame: %v", err) } if decoded.Type != frame.Type || decoded.Flags != frame.Flags || decoded.TrafficClass != frame.TrafficClass || decoded.StreamID != frame.StreamID || decoded.Sequence != frame.Sequence || !bytes.Equal(decoded.Payload, frame.Payload) { t.Fatalf("decoded frame = %+v, want %+v", decoded, frame) } } func TestReadWriteFrameRoundTrip(t *testing.T) { var buf bytes.Buffer frame := Frame{ Type: FrameOpenStream, TrafficClass: TrafficClassReliable, StreamID: 9, Payload: []byte(`{"service":"vpn"}`), } if err := WriteFrame(&buf, frame); err != nil { t.Fatalf("write frame: %v", err) } decoded, err := ReadFrame(&buf, DefaultMaxPayload) if err != nil { t.Fatalf("read frame: %v", err) } if decoded.Type != frame.Type || decoded.StreamID != frame.StreamID || !bytes.Equal(decoded.Payload, frame.Payload) { t.Fatalf("decoded frame = %+v, want %+v", decoded, frame) } } func TestRejectsInvalidMagic(t *testing.T) { encoded, err := MarshalFrame(Frame{Type: FrameHello}) if err != nil { t.Fatalf("marshal frame: %v", err) } encoded[0] = 0 _, err = UnmarshalFrame(encoded, DefaultMaxPayload) if !errors.Is(err, ErrInvalidMagic) { t.Fatalf("error = %v, want %v", err, ErrInvalidMagic) } } func TestRejectsUnsupportedVersion(t *testing.T) { encoded, err := MarshalFrame(Frame{Type: FrameHello}) if err != nil { t.Fatalf("marshal frame: %v", err) } encoded[4] = Version + 1 _, err = UnmarshalFrame(encoded, DefaultMaxPayload) if !errors.Is(err, ErrUnsupportedVer) { t.Fatalf("error = %v, want %v", err, ErrUnsupportedVer) } } func TestRejectsUnknownFrameType(t *testing.T) { if err := ValidateFrame(Frame{Type: FrameType(255)}, DefaultMaxPayload); !errors.Is(err, ErrUnknownFrameType) { t.Fatalf("error = %v, want %v", err, ErrUnknownFrameType) } } func TestRejectsStreamFrameWithoutStreamID(t *testing.T) { if err := ValidateFrame(Frame{Type: FrameData}, DefaultMaxPayload); !errors.Is(err, ErrInvalidStreamID) { t.Fatalf("error = %v, want %v", err, ErrInvalidStreamID) } } func TestRejectsOversizedPayload(t *testing.T) { frame := Frame{ Type: FrameData, StreamID: 1, Payload: bytes.Repeat([]byte("x"), 5), } if err := ValidateFrame(frame, 4); !errors.Is(err, ErrInvalidPayloadLen) { t.Fatalf("error = %v, want %v", err, ErrInvalidPayloadLen) } } func TestRejectsUnknownTrafficClass(t *testing.T) { frame := Frame{ Type: FrameOpenStream, TrafficClass: TrafficClass(99), StreamID: 1, } if err := ValidateFrame(frame, DefaultMaxPayload); !errors.Is(err, ErrUnknownTraffic) { t.Fatalf("error = %v, want %v", err, ErrUnknownTraffic) } } func TestUnmarshalRequiresExactPayloadLength(t *testing.T) { encoded, err := MarshalFrame(Frame{Type: FrameData, StreamID: 1, Payload: []byte("hello")}) if err != nil { t.Fatalf("marshal frame: %v", err) } encoded = encoded[:len(encoded)-1] _, err = UnmarshalFrame(encoded, DefaultMaxPayload) if !errors.Is(err, ErrInvalidPayloadLen) { t.Fatalf("error = %v, want %v", err, ErrInvalidPayloadLen) } } func TestReadFrameReturnsUnexpectedEOFOnShortPayload(t *testing.T) { encoded, err := MarshalFrame(Frame{Type: FrameData, StreamID: 1, Payload: []byte("hello")}) if err != nil { t.Fatalf("marshal frame: %v", err) } _, err = ReadFrame(bytes.NewReader(encoded[:len(encoded)-1]), DefaultMaxPayload) if !errors.Is(err, io.ErrUnexpectedEOF) { t.Fatalf("error = %v, want %v", err, io.ErrUnexpectedEOF) } }