Why won't unit tests connect to a websocket - unit-testing

UPDATE: I've uploaded a repo - https://github.com/mrpmorris/CannotIntegrationTestWebApp/blob/master/TestProject1/UnitTest1.cs
I have a web server that serves both HTTPS and WebSocket requests. When I run the app I am able to connect and make requests from postman for both HTTPS://localhost:8080 and WSS://localhost:8080/game-server
using Gambit.ApplicationLayer;
using Gambit.GameServer.Configuration;
using Gambit.GameServer.UseCases;
namespace Gambit.GameServer;
public class Program
{
public static async Task Main(string[] args)
{
WebApplication app = BuildApp(args);
await RunAppAsync(app);
}
public static WebApplication BuildApp(string[] args, Action<WebApplicationBuilder>? configure = null)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IServiceCollection services = builder.Services;
IConfiguration configuration = builder.Configuration;
IWebHostEnvironment environment = builder.Environment;
services.AddControllers();
services.AddLogging(opts =>
{
opts.ClearProviders();
opts.AddConfiguration(configuration.GetSection("Logging"));
opts.AddDebug();
opts.AddEventSourceLogger();
#if DEBUG
if (environment.IsDevelopment())
opts.AddConsole();
#endif
});
services.Configure<GameServerOptions>(configuration.GetSection("GameServer"));
services.AddApplicationServices(configuration);
configure?.Invoke(builder);
WebApplication app = builder.Build();
return app;
}
public static async Task RunAppAsync(WebApplication app)
{
app.MapGet("/", () => "Gambit.Server.API is running");
app.AddUserUseCases();
app.AddGameUseCases();
app.MapControllers();
app.UseWebSockets();
await app.RunAsync();
}
}
When I run my unit tests I use the same code to create and run the server (once per test run) my tests are able to make HTTPS requests but not connect via a WebSocket. When I try, I get a 404 error. I experience the same in PostMan.
static IntegrationTestsServer()
{
ConfigureMocks();
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTesting");
var app = Program.BuildApp(Array.Empty<string>(), builder =>
{
builder.WebHost.UseSetting("urls", "https://localhost:8080");
});
Configuration = app.Services.GetRequiredService<IConfiguration>();
GameServerOptions = app.Services.GetRequiredService<IOptions<GameServerOptions>>();
var dbContextOptions = app.Services.GetRequiredService<DbContextOptions<ApplicationDbContext>>();
using var dbContext = new ApplicationDbContext(dbContextOptions);
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
HttpClient = new HttpClient { BaseAddress = new Uri("https://localhost:8080") };
_ = Program.RunAppAsync(app);
}
I can even perform a successful HttpClient.GetAsync("https://localhost:8080") immediately before the ClientWebSocket fails
System.Net.WebSockets.WebSocketException : The server returned status code '404' when status code '101' was expected.
Does anyone have any ideas why this might be?

Set ApplicationName in the WebApplicationOptions sent to WebApplication.CreateBuilder
WebApplication.CreateBuilder
(
new WebApplicationOptions
{
ApplicationName = typeof(Gambit.GameServer.Program).Assembly.GetName().Name // <==
}
);
Now it will be able to find your manifest file when running from a test.
See the following blog post for more of the back story on how I figured it out.
https://thefreezeteam.com/posts/StevenTCramer/2022/08/25/runwebserverintest

Related

AWS .Net Core SDK Simple Email Service Suppression List Not Working

I am trying to retrieve the SES account-level suppression list using AWS SDK in .Net Core:
Below is my code:
public class SimpleEmailServiceUtility : ISimpleEmailServiceUtility
{
private readonly IAmazonSimpleEmailServiceV2 _client;
public SimpleEmailServiceUtility(IAmazonSimpleEmailServiceV2 client)
{
_client = client;
}
public async Task<ListSuppressedDestinationsResponse> GetSuppressionList()
{
ListSuppressedDestinationsRequest request = new ListSuppressedDestinationsRequest();
request.PageSize = 10;
ListSuppressedDestinationsResponse response = new ListSuppressedDestinationsResponse();
try
{
response = await _client.ListSuppressedDestinationsAsync(request);
}
catch (Exception ex)
{
Console.WriteLine("ListSuppressedDestinationsAsync failed with exception: " + ex.Message);
}
return response;
}
}
But it doesn't seem to be working. The request takes too long and then returns empty response or below error if I remove try/catch:
An unhandled exception occurred while processing the request.
TaskCanceledException: A task was canceled.
System.Threading.Tasks.TaskCompletionSourceWithCancellation<T>.WaitWithCancellationAsync(CancellationToken cancellationToken)
TimeoutException: A task was canceled.
Amazon.Runtime.HttpWebRequestMessage.GetResponseAsync(CancellationToken cancellationToken)
Can anyone please guide if I am missing something?
Thank you!
I have tested your code and everything works correctly.
using Amazon;
using Amazon.SimpleEmailV2;
using Amazon.SimpleEmailV2.Model;
internal class Program
{
private async static Task Main(string[] args)
{
var client = new AmazonSimpleEmailServiceV2Client("accessKeyId", "secrectAccessKey", RegionEndpoint.USEast1);
var utility = new SimpleEmailServiceUtility(client);
var result = await utility.GetSuppressionList();
}
}
<PackageReference Include="AWSSDK.SimpleEmailV2" Version="3.7.1.127" />
Things that you can check:
Try again, maybe it was a temporary problem.
Try with the latest version that I am using(if not already)
How far are you from the region that you try to get the list? Try making the same request from an EC2 instance in that region.
Finally found the issue, I was using awsConfig.DefaultClientConfig.UseHttp = true;' in startup` which was causing the issue. Removing it fixed the issue and everything seems to be working fine now.

Is Xamarin Master-Detail Template Broken When used with Azure Backing?

The Master-Detail template in Xamarin has typically been a great starting point for many of my apps that work with .Net Core Backing Service. For review it has a Dependency service in the Client that allows Mocking of the Azure Backing Service or connection to a real or local service while in development.
The control variable generated by the template is public static bool UseMockDataStore = true;
All Code discussed is completely found here: https://github.com/BicycleMark/SignalRGB
I created a Xamarin Master-Detail project naming it SignalRGB and did the following:
The solution project structure looks like:
I start Instance one Visual Studio 2019 And Run SignalRGB.Web
3) Made Note of url: [https://localhost:44300]
4) Opened another Instance of Visual Studio (2) to run client with UseMockDataSource=false / The default it displayed results in Client using MockDataSource
5)went to these lines and updated Client for talking to my local server waiting for an http request :
public static string AzureBackendUrl =
//DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000" : "http://localhost:44300";
DeviceInfo.Platform == DevicePlatform.Android ? "http://localhost:44300" : "http://localhost:44300";
public static bool UseMockDataStore = false;
public App()
{
InitializeComponent();
if (UseMockDataStore)
{
DependencyService.Register<MockDataStore>();
}
else
{
DependencyService.Register<AzureDataStore>();
}
MainPage = new MainPage();
}
Went and ran SignalRGB.UWP from VS Instance(2) and client application hung on the line GetStringAsync():
bool IsConnected => Connectivity.NetworkAccess == NetworkAccess.Internet;
public async Task<IEnumerable<Item>> GetItemsAsync(bool forceRefresh = false)
{
if (forceRefresh && IsConnected)
{
var json = await client.GetStringAsync ($"api/item");
items = await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<Item>>(json));
}
return items;
}
I have tried other platforms iOS and get same result:
What Am I doing wrong here?
Oversight? in the templated code perhaps. 44300 is the port but notice the protocol.
DeviceInfo.Platform == DevicePlatform.Android ? "https://localhost:44300" :

AWS Load Balancer with HTTPS redirections for Server-side event (eventSource)

guys,
I am building a website with JAVA Spring on AWS Elastic beanstalk with Load Balancer. In short, I have a page which receives Server-side Event (SSE) from server using eventsource on client-side and SseEmitter on my Java Spring back-end.
I want my website to work with HTTPS so I followed official suggestion to set the NGINX configuration:
https://aws.amazon.com/premiumsupport/knowledge-center/redirect-http-https-elb/?nc1=f_ls
It works well...when I connect to my website with HTTP, it automatically redirect to HTTPS.
However, my server-side event stops working.
I tried solutions from another post:
EventSource / Server-Sent Events through Nginx
But it didn't help.
On my server side, I also add httpHeaders following other suggestions.
Server-side:
public static final class CustomSseEmitter extends SseEmitter {
static final MediaType UTF8_TEXT_EVENTSTREAM = new MediaType("text", "event-stream", Charset.forName("UTF-8"));
#Override
protected void extendResponse(ServerHttpResponse outputMessage) {
HttpHeaders headers = outputMessage.getHeaders();
headers.set("X-Accel-Buffering", "no");
headers.set("Cache-Control", "no-cache");
headers.set("Connection", "keep-alive");
headers.set("Content-Type", "text/event-stream");
if (headers.getContentType() == null) {
headers.setContentType(UTF8_TEXT_EVENTSTREAM);
}
}
}
Client-side:
var registerSSE = function (companyId, retryCount) {
source = new EventSource("/middle/registerSSE/" + companyId);
source.onopen = function (e) {
console.log("Build SSE successful");
};
source.onmessage = function (e) {
console.log("receive SSE");
};
source.onerror = function (e) {
console.error("SSE broken [" + e.data + "], retry " + (
++retryCount) + " times!");
}
}
Sorry I am kinda a newbie in this context, how can I combine both required features?
Any suggestions would be appreciated.
Thanks.

Unit Test Web API - How to get auth token

I use token auth for my WebApi application.
I have the following ConfigureAuth method in Startup class:
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
and ApplicationOAuthProvider:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await userManager.FindAsync(context.UserName, context.Password);
//ApplicationUser user = new ApplicationUser() { UserName ="a" };
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
so, I should call /Token and pass credentials to get token. It works, but I want to create Unit Test for it. Is it possible?
The only way to do that is by make an integration test, which asserts the full pipeline testing - from request to response. Before the actual test on the server, you can call the token endpoint to get it, and then use it in the actual unit test by attaching it to the response. I have a sample, which uses MyTested.WebApi here:
Sample
You can do the same without the testing library, this is just how to do it.
I like the idea of pluggable configuration.
For Unit Test project, I want to use specific identity and get predictable data fro LDAP. So, i use the following line in my unit test method when setting http configuration:
config.Filters.Add(new WebApiSetIdentityFilter(config, identityName));
where the filter just "hacks" the identity, replacing the fields I need:
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
//This principal flows throughout the request.
context.Principal = new GenericPrincipal(new GenericIdentity(this.IdentityName, "LdapAuthentication"), new string[0]);
}

Cannot call web api 2 post method with int parameter in URL in Unit Test using Http server

Please ignore the spelling mistake, I cannot copy code so I have typed the whole thing and changed name of controller and method.
WEB API 2
Controller:
// Controller name is Test
public HttpResponseMessage Method1(int param1) // Post method
{
// return string
}
If I create an object of controller in test case then it is working fine. But if I want to test in localhost using following code:
Unit Test:
public void Method1Test()
{
HttpResponseMessage response;
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
HttpServer server = new HttpServer(config);
using(var client = new HttpClient(server))
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5022/api/test?param1=1");
request.Content = new ObjectContent<int>(param1, new JsonMediaTypeFormatter());
response = client.SendAsync(request, CancellationToken.None).Result;
};
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
Now, my test case is failing. I used the same code in different project and it worked. May be it is the way I am trying to call Post method. Is this the right way to call post method with Int parameter in URL?
In help page, under API column it shows:
POST api/test/param1={param1}
Also I have put some stop point in actual service I am cursor is not stopping at that point. Why?
If I want to call the same service from browser, what URL should I pass? Is it -
http://localhost:5022/api/test?param1=1
Or something else?
I figured it out. Following is the correct unit test method but this has some extra information which I have not provided earlier i.e., passing object as an input for the service.
private void Method1Test(ObjectClass obj)
{
HttpResponseMessage response = null;
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
HttpServer server = new HttpServer(config);
using (var client = new HttpClient(server))
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5022/api/test/1");
request.Content = new ObjectContent<ObjectClass>(obj, new JsonMediaTypeFormatter());
response = client.SendAsync(request, CancellationToken.None).Result;
};
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
So the correct URL that I was looking for was
http://localhost:5022/api/test/1
Sorry, It took long to post this answer. This method is working like a charm for more then 2 years.