From e3c100df94f3bc9bf1c9a29c3075bbb0f71b7342 Mon Sep 17 00:00:00 2001 From: ycc Date: Wed, 4 Feb 2026 20:04:19 +0100 Subject: [PATCH] adding server test --- client/server.go | 2 +- client/server_test.go | 640 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 641 insertions(+), 1 deletion(-) create mode 100644 client/server_test.go diff --git a/client/server.go b/client/server.go index 8d444ec..effcf10 100644 --- a/client/server.go +++ b/client/server.go @@ -195,7 +195,7 @@ func (ints *Server) BuildVideoRoomRequestMessage(users []string, expiry uint64) Username: users[idx], } } - msg.VideoData.Credentials = videocreds + msg.VideoData = &meowlib.VideoData{Credentials: videocreds} return &msg, nil } diff --git a/client/server_test.go b/client/server_test.go new file mode 100644 index 0000000..4f20a8f --- /dev/null +++ b/client/server_test.go @@ -0,0 +1,640 @@ +package client + +import ( + "testing" + + "forge.redroom.link/yves/meowlib" + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" +) + +// makeServerPair creates two Server structs with cross-wired keypairs, +// simulating a client and a server. clientSrv encrypts for the server; +// serverSrv encrypts for the client. +func makeServerPair(t *testing.T) (clientSrv *Server, serverSrv *Server) { + t.Helper() + clientKp, err := meowlib.NewKeyPair() + if err != nil { + t.Fatal(err) + } + serverKp, err := meowlib.NewKeyPair() + if err != nil { + t.Fatal(err) + } + clientSrv = &Server{ + Name: "client-side", + Url: "https://server.example.com/meow", + PublicKey: serverKp.Public, + UserKp: clientKp, + } + serverSrv = &Server{ + Name: "server-side", + Url: "https://server.example.com/meow", + PublicKey: clientKp.Public, + UserKp: serverKp, + } + return +} + +// --------------------------------------------------------------------------- +// CreateServerFromUrl +// --------------------------------------------------------------------------- + +func TestCreateServerFromUrl(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + assert.Equal(t, "https://example.com/meow", srv.Name) + assert.Equal(t, "https://example.com/meow", srv.Url) + assert.NotNil(t, srv.UserKp) + assert.NotEmpty(t, srv.UserKp.Public) + assert.NotEmpty(t, srv.UserKp.Private) +} + +func TestCreateServerFromUrl_UniqueUserKps(t *testing.T) { + srv1, _ := CreateServerFromUrl("https://a.example.com/meow") + srv2, _ := CreateServerFromUrl("https://a.example.com/meow") + assert.NotEqual(t, srv1.UserKp.Public, srv2.UserKp.Public) +} + +// --------------------------------------------------------------------------- +// CreateServerFromUid +// --------------------------------------------------------------------------- + +func TestCreateServerFromUid_WithCredentials(t *testing.T) { + srv, err := CreateServerFromUid("user:pass@https://example.com/meow") + assert.NoError(t, err) + assert.Equal(t, "user", srv.Login) + assert.Equal(t, "pass", srv.Password) + assert.Equal(t, "https://example.com/meow", srv.Url) + assert.NotNil(t, srv.UserKp) +} + +func TestCreateServerFromUid_WithoutCredentials(t *testing.T) { + srv, err := CreateServerFromUid("https://example.com/meow") + assert.NoError(t, err) + assert.Empty(t, srv.Login) + assert.Empty(t, srv.Password) + assert.Equal(t, "https://example.com/meow", srv.Url) +} + +func TestCreateServerFromUid_NameIsFullUid(t *testing.T) { + uid := "admin:secret@https://example.com/meow" + srv, err := CreateServerFromUid(uid) + assert.NoError(t, err) + assert.Equal(t, uid, srv.Name) +} + +func TestCreateServerFromUid_PasswordOnly(t *testing.T) { + srv, err := CreateServerFromUid(":secret@https://example.com/meow") + assert.NoError(t, err) + assert.Empty(t, srv.Login) + assert.Equal(t, "secret", srv.Password) + assert.Equal(t, "https://example.com/meow", srv.Url) +} + +// --------------------------------------------------------------------------- +// CreateServerFromMeowUrl +// --------------------------------------------------------------------------- + +func TestCreateServerFromMeowUrl_WithCredentials(t *testing.T) { + srv, err := CreateServerFromMeowUrl("meow://user:pass@server.example.com/meow") + assert.NoError(t, err) + assert.Equal(t, "user", srv.Login) + assert.Equal(t, "pass", srv.Password) + assert.Equal(t, "server.example.com/meow", srv.Url) +} + +func TestCreateServerFromMeowUrl_NoCredentials(t *testing.T) { + srv, err := CreateServerFromMeowUrl("meow://server.example.com/meow") + assert.NoError(t, err) + assert.Empty(t, srv.Login) + assert.Equal(t, "server.example.com/meow", srv.Url) +} + +// --------------------------------------------------------------------------- +// CreateServerFromInvitationLink +// --------------------------------------------------------------------------- + +func TestCreateServerFromInvitationLink(t *testing.T) { + srv, err := CreateServerFromInvitationLink("meow://user:pass@server.example.com/meow?abc123") + assert.NoError(t, err) + assert.Equal(t, "user", srv.Login) + assert.Equal(t, "pass", srv.Password) + assert.Equal(t, "server.example.com/meow", srv.Url) +} + +func TestCreateServerFromInvitationLink_NoQueryParam(t *testing.T) { + srv, err := CreateServerFromInvitationLink("meow://server.example.com/meow") + assert.NoError(t, err) + assert.Equal(t, "server.example.com/meow", srv.Url) +} + +func TestCreateServerFromInvitationLink_MultipleQuestionMarks(t *testing.T) { + // Only the first ? splits; everything before it is the server URL + srv, err := CreateServerFromInvitationLink("meow://server.example.com/meow?code?extra") + assert.NoError(t, err) + assert.Equal(t, "server.example.com/meow", srv.Url) +} + +// --------------------------------------------------------------------------- +// CreateServerFromServerCard +// --------------------------------------------------------------------------- + +func TestCreateServerFromServerCard(t *testing.T) { + card := &meowlib.ServerCard{ + Name: "MyServer", + PublicKey: "server-pub-key", + Description: "A test server", + Url: "https://example.com/meow", + Login: "admin", + Password: "secret", + } + srv, err := CreateServerFromServerCard(card) + assert.NoError(t, err) + assert.Equal(t, "MyServer", srv.Name) + assert.Equal(t, "server-pub-key", srv.PublicKey) + assert.Equal(t, "A test server", srv.Description) + assert.Equal(t, "https://example.com/meow", srv.Url) + assert.Equal(t, "admin", srv.Login) + assert.Equal(t, "secret", srv.Password) + assert.NotNil(t, srv.UserKp) +} + +func TestCreateServerFromServerCard_MinimalCard(t *testing.T) { + card := &meowlib.ServerCard{Url: "https://minimal.example.com"} + srv, err := CreateServerFromServerCard(card) + assert.NoError(t, err) + assert.Equal(t, "https://minimal.example.com", srv.Url) + assert.NotNil(t, srv.UserKp) +} + +// --------------------------------------------------------------------------- +// GetServerCard +// --------------------------------------------------------------------------- + +func TestGetServerCard(t *testing.T) { + srv := &Server{ + Name: "MyServer", + PublicKey: "pub123", + Description: "desc", + Url: "https://example.com/meow", + Login: "user", + Password: "pw", + } + card := srv.GetServerCard() + assert.Equal(t, srv.Name, card.Name) + assert.Equal(t, srv.PublicKey, card.PublicKey) + assert.Equal(t, srv.Description, card.Description) + assert.Equal(t, srv.Url, card.Url) + assert.Equal(t, srv.Login, card.Login) + assert.Equal(t, srv.Password, card.Password) +} + +func TestGetServerCard_RoundTrip(t *testing.T) { + card := &meowlib.ServerCard{ + Name: "RT", + PublicKey: "pk", + Description: "roundtrip", + Url: "https://rt.example.com", + Login: "l", + Password: "p", + } + srv, err := CreateServerFromServerCard(card) + assert.NoError(t, err) + restored := srv.GetServerCard() + assert.Equal(t, card.Name, restored.Name) + assert.Equal(t, card.PublicKey, restored.PublicKey) + assert.Equal(t, card.Description, restored.Description) + assert.Equal(t, card.Url, restored.Url) + assert.Equal(t, card.Login, restored.Login) + assert.Equal(t, card.Password, restored.Password) +} + +// --------------------------------------------------------------------------- +// GetUid / GetMeowUrl +// --------------------------------------------------------------------------- + +func TestGetUid_WithCredentials(t *testing.T) { + srv := &Server{Login: "user", Password: "pass", Url: "https://example.com/meow"} + assert.Equal(t, "user:pass@https://example.com/meow", srv.GetUid()) +} + +func TestGetUid_NoCredentials(t *testing.T) { + srv := &Server{Url: "https://example.com/meow"} + assert.Equal(t, "https://example.com/meow", srv.GetUid()) +} + +func TestGetUid_PasswordOnly(t *testing.T) { + srv := &Server{Password: "pass", Url: "https://example.com/meow"} + assert.Equal(t, ":pass@https://example.com/meow", srv.GetUid()) +} + +func TestGetMeowUrl_NoCredentials(t *testing.T) { + srv := &Server{Url: "https://example.com/meow"} + assert.Equal(t, "meow://https://example.com/meow", srv.GetMeowUrl()) +} + +func TestGetMeowUrl_WithCredentials(t *testing.T) { + srv := &Server{Login: "user", Password: "pass", Url: "https://example.com/meow"} + // With credentials the meow:// prefix is not added — matches GetUid behaviour + assert.Equal(t, srv.GetUid(), srv.GetMeowUrl()) +} + +// --------------------------------------------------------------------------- +// AsymEncryptMessage / AsymDecryptMessage +// --------------------------------------------------------------------------- + +func TestServer_AsymEncryptDecrypt_RoundTrip(t *testing.T) { + clientSrv, serverSrv := makeServerPair(t) + plaintext := []byte("hello from client to server") + + enc, err := clientSrv.AsymEncryptMessage(plaintext) + assert.NoError(t, err) + assert.NotEmpty(t, enc.Data) + assert.NotEmpty(t, enc.Signature) + + decrypted, err := serverSrv.AsymDecryptMessage(enc.Data, enc.Signature) + assert.NoError(t, err) + assert.Equal(t, plaintext, decrypted) +} + +func TestServer_AsymEncryptDecrypt_Bidirectional(t *testing.T) { + clientSrv, serverSrv := makeServerPair(t) + + // Client → Server + enc1, err := clientSrv.AsymEncryptMessage([]byte("client msg")) + assert.NoError(t, err) + dec1, err := serverSrv.AsymDecryptMessage(enc1.Data, enc1.Signature) + assert.NoError(t, err) + assert.Equal(t, []byte("client msg"), dec1) + + // Server → Client + enc2, err := serverSrv.AsymEncryptMessage([]byte("server msg")) + assert.NoError(t, err) + dec2, err := clientSrv.AsymDecryptMessage(enc2.Data, enc2.Signature) + assert.NoError(t, err) + assert.Equal(t, []byte("server msg"), dec2) +} + +func TestServer_AsymEncryptMessage_InvalidKey(t *testing.T) { + srv := &Server{ + PublicKey: "not-a-valid-key", + UserKp: &meowlib.KeyPair{Private: "also-invalid"}, + } + _, err := srv.AsymEncryptMessage([]byte("test")) + assert.Error(t, err) +} + +func TestServer_AsymDecryptMessage_WrongSignatureKey(t *testing.T) { + clientSrv, serverSrv := makeServerPair(t) + + enc, err := clientSrv.AsymEncryptMessage([]byte("hello")) + assert.NoError(t, err) + + // Replace expected sender key with a random one + eve, err := meowlib.NewKeyPair() + if err != nil { + t.Fatal(err) + } + serverSrv.PublicKey = eve.Public + + _, err = serverSrv.AsymDecryptMessage(enc.Data, enc.Signature) + assert.Error(t, err) +} + +// --------------------------------------------------------------------------- +// PackServerMessage / UnPackServerMessage +// --------------------------------------------------------------------------- + +func TestServer_PackUnPack_RoundTrip(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + payload := []byte("test payload") + signature := []byte("test sig") + + packed, err := srv.PackServerMessage(payload, signature) + assert.NoError(t, err) + assert.NotEmpty(t, packed) + + gotPayload, gotSig, err := srv.UnPackServerMessage(packed) + assert.NoError(t, err) + assert.Equal(t, payload, gotPayload) + assert.Equal(t, signature, gotSig) +} + +func TestServer_PackServerMessage_SetsFrom(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + packed, err := srv.PackServerMessage([]byte("p"), []byte("s")) + assert.NoError(t, err) + + msg := &meowlib.PackedServerMessage{} + err = proto.Unmarshal(packed, msg) + assert.NoError(t, err) + assert.Equal(t, srv.UserKp.Public, msg.From) +} + +func TestServer_UnPackServerMessage_InvalidData(t *testing.T) { + srv := &Server{} + _, _, err := srv.UnPackServerMessage([]byte{0xff, 0xff, 0xff, 0xff, 0xff}) + assert.Error(t, err) +} + +// --------------------------------------------------------------------------- +// BuildToServerMessageFromUserMessage +// --------------------------------------------------------------------------- + +func TestServer_BuildToServerMessageFromUserMessage(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + pum := &meowlib.PackedUserMessage{ + Destination: "dest-key", + Payload: []byte("encrypted"), + Signature: []byte("sig"), + } + + msg := srv.BuildToServerMessageFromUserMessage(pum) + assert.NotEmpty(t, msg.Uuid) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.Len(t, msg.Messages, 1) + assert.Equal(t, pum, msg.Messages[0]) +} + +func TestServer_BuildToServerMessageFromUserMessage_UniqueUuids(t *testing.T) { + srv, _ := CreateServerFromUrl("https://example.com/meow") + pum := &meowlib.PackedUserMessage{Destination: "d"} + + msg1 := srv.BuildToServerMessageFromUserMessage(pum) + msg2 := srv.BuildToServerMessageFromUserMessage(pum) + assert.NotEqual(t, msg1.Uuid, msg2.Uuid) +} + +// --------------------------------------------------------------------------- +// BuildMessageSendingMessage +// --------------------------------------------------------------------------- + +func TestServer_BuildMessageSendingMessage(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + pum := &meowlib.PackedUserMessage{ + Destination: "dest", + Payload: []byte("payload"), + } + + data, err := srv.BuildMessageSendingMessage(pum) + assert.NoError(t, err) + assert.NotEmpty(t, data) + + var msg meowlib.ToServerMessage + err = proto.Unmarshal(data, &msg) + assert.NoError(t, err) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.Len(t, msg.Messages, 1) + assert.Equal(t, []byte("payload"), msg.Messages[0].Payload) +} + +// --------------------------------------------------------------------------- +// BuildMessageRequestMessage +// --------------------------------------------------------------------------- + +func TestServer_BuildMessageRequestMessage(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + data, err := srv.BuildMessageRequestMessage([]string{"key1", "key2"}) + assert.NoError(t, err) + assert.NotEmpty(t, data) + + var msg meowlib.ToServerMessage + err = proto.Unmarshal(data, &msg) + assert.NoError(t, err) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.NotEmpty(t, msg.Uuid) + // Note: lookupKeys parameter is currently unused in the message body + assert.Empty(t, msg.PullRequest) +} + +// --------------------------------------------------------------------------- +// BuildVideoRoomRequestMessage +// --------------------------------------------------------------------------- + +func TestServer_BuildVideoRoomRequestMessage(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + users := []string{"alice", "bob", "charlie"} + msg, err := srv.BuildVideoRoomRequestMessage(users, 3600) + assert.NoError(t, err) + assert.NotNil(t, msg) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.NotNil(t, msg.VideoData) + assert.Len(t, msg.VideoData.Credentials, 3) + assert.Equal(t, "alice", msg.VideoData.Credentials[0].Username) + assert.Equal(t, "bob", msg.VideoData.Credentials[1].Username) + assert.Equal(t, "charlie", msg.VideoData.Credentials[2].Username) +} + +func TestServer_BuildVideoRoomRequestMessage_SingleUser(t *testing.T) { + srv, _ := CreateServerFromUrl("https://example.com/meow") + msg, err := srv.BuildVideoRoomRequestMessage([]string{"solo"}, 60) + assert.NoError(t, err) + assert.Len(t, msg.VideoData.Credentials, 1) + assert.Equal(t, "solo", msg.VideoData.Credentials[0].Username) +} + +// --------------------------------------------------------------------------- +// BuildToServerMessageInvitationCreation +// --------------------------------------------------------------------------- + +func TestServer_BuildToServerMessageInvitationCreation(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + cc := &meowlib.ContactCard{ + Name: "Alice", + ContactPublicKey: "alice-pub", + } + + msg, err := srv.BuildToServerMessageInvitationCreation(cc, "secret", 300, 8) + assert.NoError(t, err) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.NotNil(t, msg.Invitation) + assert.Equal(t, int32(1), msg.Invitation.Step) + assert.Equal(t, "secret", msg.Invitation.Password) + assert.Equal(t, int32(300), msg.Invitation.Timeout) + assert.Equal(t, int32(8), msg.Invitation.ShortcodeLen) + assert.NotEmpty(t, msg.Invitation.Payload) + + // Payload is a compressed ContactCard — decompress and verify + restored, err := meowlib.NewContactCardFromCompressed(msg.Invitation.Payload) + assert.NoError(t, err) + assert.Equal(t, "Alice", restored.Name) + assert.Equal(t, "alice-pub", restored.ContactPublicKey) +} + +func TestServer_BuildToServerMessageInvitationCreation_NoPassword(t *testing.T) { + srv, _ := CreateServerFromUrl("https://example.com/meow") + cc := &meowlib.ContactCard{Name: "Bob"} + + msg, err := srv.BuildToServerMessageInvitationCreation(cc, "", 60, 6) + assert.NoError(t, err) + assert.Empty(t, msg.Invitation.Password) + assert.Equal(t, int32(1), msg.Invitation.Step) +} + +// --------------------------------------------------------------------------- +// BuildToServerMessageInvitationRequest +// --------------------------------------------------------------------------- + +func TestServer_BuildToServerMessageInvitationRequest(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + msg, err := srv.BuildToServerMessageInvitationRequest("SC1234", "mypassword") + assert.NoError(t, err) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.NotNil(t, msg.Invitation) + assert.Equal(t, int32(2), msg.Invitation.Step) + assert.Equal(t, "SC1234", msg.Invitation.Shortcode) + assert.Equal(t, "mypassword", msg.Invitation.Password) +} + +func TestServer_BuildToServerMessageInvitationRequest_NoPassword(t *testing.T) { + srv, _ := CreateServerFromUrl("https://example.com/meow") + msg, err := srv.BuildToServerMessageInvitationRequest("CODE", "") + assert.NoError(t, err) + assert.Equal(t, "CODE", msg.Invitation.Shortcode) + assert.Empty(t, msg.Invitation.Password) +} + +// --------------------------------------------------------------------------- +// BuildToServerMessageInvitationAnswer +// --------------------------------------------------------------------------- + +func TestServer_BuildToServerMessageInvitationAnswer(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + pum := &meowlib.PackedUserMessage{ + Destination: "dest", + Payload: []byte("answer-payload"), + Signature: []byte("answer-sig"), + } + + msg, err := srv.BuildToServerMessageInvitationAnswer(pum, "my-pub-key", "inv-uuid-42", 600) + assert.NoError(t, err) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.NotNil(t, msg.Invitation) + assert.Equal(t, int32(3), msg.Invitation.Step) + assert.Equal(t, "inv-uuid-42", msg.Invitation.Uuid) + assert.Equal(t, "my-pub-key", msg.Invitation.From) + assert.NotEmpty(t, msg.Invitation.Payload) + + // Payload is proto-serialized PackedUserMessage + var decoded meowlib.PackedUserMessage + err = proto.Unmarshal(msg.Invitation.Payload, &decoded) + assert.NoError(t, err) + assert.Equal(t, "dest", decoded.Destination) + assert.Equal(t, []byte("answer-payload"), decoded.Payload) +} + +// --------------------------------------------------------------------------- +// BuildToServerMessageInvitationAnswerRequest +// --------------------------------------------------------------------------- + +func TestServer_BuildToServerMessageInvitationAnswerRequest(t *testing.T) { + srv, err := CreateServerFromUrl("https://example.com/meow") + assert.NoError(t, err) + + msg, err := srv.BuildToServerMessageInvitationAnswerRequest("inv-uuid-99") + assert.NoError(t, err) + assert.Equal(t, "1", msg.Type) + assert.Equal(t, srv.UserKp.Public, msg.From) + assert.NotNil(t, msg.Invitation) + assert.Equal(t, int32(4), msg.Invitation.Step) + assert.Equal(t, "inv-uuid-99", msg.Invitation.Uuid) +} + +// --------------------------------------------------------------------------- +// ProcessOutboundMessage / ProcessInboundServerResponse (full pipeline) +// --------------------------------------------------------------------------- + +func TestServer_ProcessOutboundMessage(t *testing.T) { + clientSrv, serverSrv := makeServerPair(t) + + original := &meowlib.ToServerMessage{ + Uuid: "out-uuid", + Type: "1", + From: clientSrv.UserKp.Public, + } + + packed, err := clientSrv.ProcessOutboundMessage(original) + assert.NoError(t, err) + assert.NotEmpty(t, packed) + + // Verify the server side can unpack and decrypt back to the original + payload, sig, err := serverSrv.UnPackServerMessage(packed) + assert.NoError(t, err) + decrypted, err := serverSrv.AsymDecryptMessage(payload, sig) + assert.NoError(t, err) + + var restored meowlib.ToServerMessage + err = proto.Unmarshal(decrypted, &restored) + assert.NoError(t, err) + assert.Equal(t, "out-uuid", restored.Uuid) +} + +func TestServer_ProcessOutboundMessage_InvalidServerKey(t *testing.T) { + srv := &Server{ + PublicKey: "bad-key", + UserKp: &meowlib.KeyPair{Public: "pub", Private: "bad-priv"}, + } + msg := &meowlib.ToServerMessage{Type: "1"} + _, err := srv.ProcessOutboundMessage(msg) + assert.Error(t, err) +} + +func TestServer_ProcessInboundServerResponse(t *testing.T) { + clientSrv, serverSrv := makeServerPair(t) + + original := &meowlib.FromServerMessage{ + Chat: []*meowlib.PackedUserMessage{ + {Destination: "chat-dest", Payload: []byte("chat-payload")}, + }, + } + originalBytes, err := proto.Marshal(original) + assert.NoError(t, err) + + // Simulate server packing: encrypt for the client, then pack + enc, err := serverSrv.AsymEncryptMessage(originalBytes) + assert.NoError(t, err) + packedMsg, err := serverSrv.PackServerMessage(enc.Data, enc.Signature) + assert.NoError(t, err) + + // Client processes the inbound message + received, err := clientSrv.ProcessInboundServerResponse(packedMsg) + assert.NoError(t, err) + assert.NotNil(t, received) + assert.Len(t, received.Chat, 1) + assert.Equal(t, "chat-dest", received.Chat[0].Destination) + assert.Equal(t, []byte("chat-payload"), received.Chat[0].Payload) +} + +func TestServer_ProcessInboundServerResponse_InvalidData(t *testing.T) { + srv := &Server{ + UserKp: &meowlib.KeyPair{Private: "invalid"}, + } + _, err := srv.ProcessInboundServerResponse([]byte{0xff, 0xff}) + assert.Error(t, err) +}