Message Does not Arrive in gRPC Server Events Integration Test

Hi chaps,

I have a feeling that this is going to leave me feeling blonde - I must be missing something simple. I have a set of GRPC integration tests on a ServiceStack Test host which are all passing, and a set of integration tests using the ServerEvents Feature which are working as well (using the standard ServiceStack ServerEvents client) I am now trying to build out some integration tests which use the grpc streaming features, which looks like this :


the server code looks like this, and appears to be sending the correct message using NotiyChannel :

But for some reason, I get the “onConnect” and “onJoin” commands - but I never receive the actual message which was sent from the Service.

What am I missing?

[RB]

Hi @RyanMBritton,

Just to double check, your fixture host is running http2?

If you can put together a stand-alone project on GitHub we can run locally, I can take a look.

yes - that is correct - and the grpc integration tests which are NOT server-events-related are passing

uploaded the project here :
RealRyanBritton/ServiceStack.GrpcServerEventsTest (github.com)

It’s because you’re not using a selector in your Server Event notifications, you should instead be comparing against the channel since you’re using the channel name, e.g:

await foreach (var msg in stream)
{
    if (msg.Channel == "send-messages")
    {
        break;
    }
    else
    {
        $"EVENT {msg.Selector} [{msg.Channel}]: #{msg.UserId} {msg.DisplayName}".Print();
    }
}

I’m sorry @mythz you’re right, if I change the test as you indicated it passes - but the message is still not being received…I only get the onConnect and onJoin commands. If I change the test to the following, a breakpoint at line #46 is never hit :

during the same integration test run, NotifyChannel is definitely being called, though - the message just never seems to arrive :

I changed the test example on Github to reflect the change…

Not familiar with XUnit but it seems to hinder finding out what’s going on as I can’t see any logs and have to resort to debugging unlike being able to see the Console logs like with NUnit.

If you don’t specify a selector an automatic one is used with the format of cmd.{typeof(Message)} which as you’re sending a string ends up being cmd.String, normally you’d want to send DTOs which your client code is expecting to deserialize but your sending the Message string not the SendMessage DTO your expecting.

So I’d change your server implementation to send the DTO instead:

public object Any(SendMessage request)
{
    if (string.IsNullOrWhiteSpace(request.Channel))
    {
        _serverEvents.NotifyAll(request);
    }
    else
    {
        _serverEvents.NotifyChannel(request.Channel, request);
    }
    
    return new SendMessageResponse();
}

Then change your client implementation so the message is sent after the client is connected to the SSE Stream, e.g. test passes when changing it to:

Task.Factory.StartNew(async () => {
    await Task.Delay(500);
    var client = _fixture.CreateGrpcClient();
    var postTask = await client.PostAsync(new SendMessage {Message = "Hello", Channel = "send-messages"});
});

var client = _fixture.CreateGrpcClient();
var stream = client.StreamAsync(new StreamServerEvents
{
    Channels = new[] {"send-messages"}
});

await foreach (var msg in stream)
{
    $"EVENT({msg.Op}) {msg.Selector} [{msg.Channel}]: #{msg.UserId} {msg.DisplayName}".Print();
    if (msg.Selector == "cmd." + nameof(SendMessage))
    {
        var notification = msg.Json.FromJson<SendMessage>();
        Assert.Equal("Hello", notification.Message);
        break;
    }
}

I’d recommend using and switching on explicit selectors (which you can think of as the Route or handler for the message) rather than relying on implicitly generated ones or you can use the cmd.{TypeName} convention as long as you only send DTO Type messages.

this is awesome - thank you so much, guys. Working as expected - and I understand where I was going wrong.

1 Like