package bridge import ( "encoding/json" "strings" "testing" ) func messageID(id uint64) *uint64 { return &id } func TestMessageValidate(t *testing.T) { tests := []struct { name string message Message wantErr string }{ { name: "request", message: Message{ Version: ProtocolVersion, Type: Request, RequestId: messageID(1), Name: "locals/get", }, }, { name: "successful response", message: Message{ Version: ProtocolVersion, Type: Response, RequestId: messageID(1), Name: "locals/get", Body: json.RawMessage(`{"variables":{}}`), }, }, { name: "error response", message: Message{ Version: ProtocolVersion, Type: Response, RequestId: messageID(1), Name: "locals/get", Error: &ProtocolError{ Code: "not_paused", Message: "execution is not paused", }, }, }, { name: "event", message: Message{ Version: ProtocolVersion, Type: Event, Name: "execution/stopped", Body: json.RawMessage(`{"reason":"step","file":"test.py","line":1}`), }, }, { name: "unsupported version", message: Message{ Version: ProtocolVersion + 1, Type: Request, RequestId: messageID(1), Name: "locals/get", }, wantErr: "unsupported protocol version", }, { name: "missing name", message: Message{ Version: ProtocolVersion, Type: Request, RequestId: messageID(1), }, wantErr: "message name is required", }, { name: "request without ID", message: Message{ Version: ProtocolVersion, Type: Request, Name: "locals/get", }, wantErr: "request ID is required", }, { name: "request with error", message: Message{ Version: ProtocolVersion, Type: Request, RequestId: messageID(1), Name: "locals/get", Error: &ProtocolError{Code: "invalid"}, }, wantErr: "request cannot contain an error", }, { name: "response without ID", message: Message{ Version: ProtocolVersion, Type: Response, Name: "locals/get", }, wantErr: "response ID is required", }, { name: "response with body and error", message: Message{ Version: ProtocolVersion, Type: Response, RequestId: messageID(1), Name: "locals/get", Body: json.RawMessage(`{}`), Error: &ProtocolError{Code: "invalid"}, }, wantErr: "response cannot contain both body and error", }, { name: "event with ID", message: Message{ Version: ProtocolVersion, Type: Event, RequestId: messageID(1), Name: "execution/stopped", }, wantErr: "event cannot contain a request ID", }, { name: "event with error", message: Message{ Version: ProtocolVersion, Type: Event, Name: "execution/stopped", Error: &ProtocolError{Code: "invalid"}, }, wantErr: "event cannot contain an error", }, { name: "unknown type", message: Message{ Version: ProtocolVersion, Type: MessageType("notification"), RequestId: messageID(1), Name: "locals/get", }, wantErr: "unknown message type", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.message.Validate() if tt.wantErr == "" { if err != nil { t.Fatalf("Validate() error = %v", err) } return } if err == nil { t.Fatalf("Validate() error = nil, want error containing %q", tt.wantErr) } if !strings.Contains(err.Error(), tt.wantErr) { t.Fatalf("Validate() error = %q, want error containing %q", err, tt.wantErr) } }) } } func TestNewMessageEncodesTypedBody(t *testing.T) { id := uint64(7) msg, err := newMessage(Request, &id, "breakpoint/set", SetBreakpointRequest{ File: "example.py", Line: 42, }) if err != nil { t.Fatalf("newMessage() error = %v", err) } if err := msg.Validate(); err != nil { t.Fatalf("Validate() error = %v", err) } var body SetBreakpointRequest if err := json.Unmarshal(msg.Body, &body); err != nil { t.Fatalf("unmarshal body: %v", err) } if body.File != "example.py" || body.Line != 42 { t.Fatalf("decoded body = %+v", body) } } func TestProtocolErrorString(t *testing.T) { tests := []struct { name string err *ProtocolError want string }{ {name: "code and message", err: &ProtocolError{Code: "not_paused", Message: "execution is not paused"}, want: "not_paused: execution is not paused"}, {name: "message only", err: &ProtocolError{Message: "execution is not paused"}, want: "execution is not paused"}, {name: "nil", err: nil, want: ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.err.Error(); got != tt.want { t.Fatalf("Error() = %q, want %q", got, tt.want) } }) } }