Testcontainers exception on startup - c#

Does anyone know why I sometimes get exception when I use Selenium together with Testcontainers. See below:
Exception has occurred: CLR/OpenQA.Selenium.WebDriverException
An exception of type 'OpenQA.Selenium.WebDriverException' occurred in WebDriver.dll but was not handled in user code: 'An unknown exception was encountered sending an HTTP request to the remote WebDriver server for URL http://localhost:4444/session. The exception message was: An error occurred while sending the request.'
Inner exceptions found, see $exception in variables window for more details.
Innermost exception System.IO.IOException : The response ended prematurely.
at System.Net.Http.HttpConnection.d__61.MoveNext()
This happens half the time when i run the following test constructor (C# / xUnit.net):
public DockerShould()
{
var gridNetwork = new NetworkBuilder()
.WithName("gridNetwork")
.Build();
const int SessionPort = 4444;
var containerHub = new ContainerBuilder()
.WithImage("selenium/hub:4.8")
.WithName("selenium-hub")
.WithPortBinding(4442, 4442)
.WithPortBinding(4443, 4443)
.WithPortBinding(SessionPort, SessionPort)
.WithNetwork(gridNetwork)
.Build();
var firefoxEnvironment = new Dictionary<string, string>()
{
{"SE_EVENT_BUS_HOST", "selenium-hub"},
{"SE_EVENT_BUS_PUBLISH_PORT", "4442"},
{"SE_EVENT_BUS_SUBSCRIBE_PORT", "4443"}
};
var containerFirefox = new ContainerBuilder()
.WithImage("selenium/node-firefox:4.8")
.WithEnvironment(firefoxEnvironment)
.WithNetwork(gridNetwork)
.Build();
var firefoxVideoEnvironment = new Dictionary<string, string>()
{
{"DISPLAY_CONTAINER_NAME", "firefox"},
{"FILE_NAME", "firefox.mp4"}
};
var containerFirefoxVideo = new ContainerBuilder()
.WithImage("selenium/video:ffmpeg-4.3.1-20230210")
.WithNetwork(gridNetwork)
.WithEnvironment(firefoxVideoEnvironment)
// .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(SessionPort))
.Build();
gridNetwork.CreateAsync().Wait();
containerHub.StartAsync().Wait();
containerFirefox.StartAsync().Wait();
containerFirefoxVideo.StartAsync().Wait();
Thread.Sleep(5000);
_remoteWebDriver = new RemoteWebDriver(new Uri("http://localhost:4444"), new FirefoxOptions());
}
The exception occurs when creating the new RemoteWebDriver. I've added a thread.sleep to give a bit of a delay before the variable is created. I'm not sure it's really helping much. Is there a more elegant way to ensure all containers are started up before attempting to create the web driver (which i'm assuming is the problem)?

Your configuration has a few shortcomings. I am uncertain as to which one is ultimately causing the issue, but I have provided a working example below. The crucial parts have been commented. Please note that the example does not incorporate a wait strategy to determine the readiness of the container or the service inside it. That is an aspect that you will still need to address. But first lets take a look at some basics.
Please consider reading the article Consuming the Task-based Asynchronous Pattern. Testcontainers for .NET utilizes the Task-based Asynchronous Pattern (TAP). I noticed that you tend to block the asynchronous context frequently.
You do not need to bind ports for container-to-container communication.
Avoid using fixed port bindings such as WithPortBinding(4444, 4444). To prevent port conflicts, assign a random host port by using WithPortBinding(4444, true) and retrieve it from the container instance using GetMappedPublicPort(4444).
Do not use fixed container names for the same reason mentioned in 3. Use WithNetworkAliases(string) instead.
Do not use localhost to access services running inside containers. The endpoint varies according to the Docker environment. Use the Hostname property instead.
public sealed class StackOverflow : IAsyncLifetime
{
private const ushort WebDriverPort = 4444;
private readonly INetwork _network;
private readonly IContainer _selenium;
private readonly IContainer _firefox;
private readonly IContainer _ffmpg;
public StackOverflow()
{
_network = new NetworkBuilder()
.Build();
_selenium = new ContainerBuilder()
.WithImage("selenium/hub:4.8")
// Use random assigned host ports to access the service running inside the containers.
.WithPortBinding(WebDriverPort, true)
.WithNetwork(_network)
// Use a network-alias to communication between containers.
.WithNetworkAliases(nameof(_selenium))
.Build();
_firefox = new ContainerBuilder()
.WithImage("selenium/node-firefox:4.8")
.WithEnvironment("SE_EVENT_BUS_HOST", nameof(_selenium))
.WithEnvironment("SE_EVENT_BUS_PUBLISH_PORT", "4442")
.WithEnvironment("SE_EVENT_BUS_SUBSCRIBE_PORT", "4443")
.WithNetwork(_network)
.WithNetworkAliases(nameof(_firefox))
.Build();
_ffmpg = new ContainerBuilder()
.WithImage("selenium/video:ffmpeg-4.3.1-20230210")
.WithEnvironment("DISPLAY_CONTAINER_NAME", nameof(_firefox))
.WithEnvironment("FILE_NAME", nameof(_firefox) + ".mp4")
.WithNetwork(_network)
.WithNetworkAliases(nameof(_ffmpg))
.Build();
}
public async Task InitializeAsync()
{
await _network.CreateAsync()
.ConfigureAwait(false);
// You can await Task.WhenAll(params Task[]) too.
await _selenium.StartAsync()
.ConfigureAwait(false);
await _firefox.StartAsync()
.ConfigureAwait(false);
await _ffmpg.StartAsync()
.ConfigureAwait(false);
}
public async Task DisposeAsync()
{
await _selenium.DisposeAsync()
.ConfigureAwait(false);
await _firefox.DisposeAsync()
.ConfigureAwait(false);
await _ffmpg.DisposeAsync()
.ConfigureAwait(false);
await _network.DeleteAsync()
.ConfigureAwait(false);
}
[Fact]
public async Task Question()
{
// TODO: The container configurations mentioned above lack a wait strategy. It is crucial that you add a wait strategy to each of them to determine readiness (afterwards remove this line).
await Task.Delay(TimeSpan.FromSeconds(15))
.ConfigureAwait(false);
// Use the Hostname property instead of localhost. Get the random assigned host port with GetMappedPublicPort(ushort).
var webDriver = new RemoteWebDriver(new UriBuilder(Uri.UriSchemeHttp, _selenium.Hostname, _selenium.GetMappedPublicPort(WebDriverPort)).Uri, new FirefoxOptions());
Assert.NotNull(webDriver.SessionId);
}
}

I will like to ask you please: What do you use hub and node selenium mode?
I recommended using in this case standalone mode - and why?
Because the webDriver testcoaniner in my opinion works like a dynamic grid in s: docker-selenium github
I am also asking because I just working on that: Feature- WebDriver container
So I would like your opinion and how I can map between the testcotanienr and the RemoteWebDriver capabilities

Related

Blazor SignalR Create a List for all clients but only update it once

I just worked my way through this MS Learn Tutorial regarding SignalR in Blazor.
At the end of the tutorial, you get a program that can have multiple clients hooked up to a "ChatHub" to send and receive messages, like a "Townsquare-Chatroom"
While testing I realized, that if you send some messages and afterward create a new client, the new client does not display the previously send messages. This is because every client stores its received messages locally as shown here:
#code{
// ...
private List<string> messages = new();
// ...
}
I decided to implement such a feature.
To do so, I created ChatLog.cs which is supposed to log the messages for all clients instead of saving them inside of each individual client:
public class ChatLog
{
private List<string> _messages= new List<string>();
public List<string> Messages
{
get { return _messages; }
set
{
_messages = value;
}
}
}
Of course, I also had to make some changes inside of index.razor to make things work:
I added a new service in program.cs as singleton
==> Program.cs
// ...
builder.Services.AddSingleton<ChatLog>();
// ...
and injected ChatLog into my index.razor
==> Index.razor
// ...
#inject ChatLog ChatLogger
// ...
I changed the code in index.razor #code to add the messages to ChatLog.Messages instead of the "local" messages-List
protected override async Task OnInitializedAsync()
{
// Change
if(ChatLogger.Messages is null)
{
ChatLogger.Messages = new();
}
hubConnection = new HubConnectionBuilder()
.WithUrl(NavManager.ToAbsoluteUri("/chathub"))
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
var formattedMessage = $"{user}: {message}";
// Change
ChatLogger.Messages.Add(formattedMessage);
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
}
Now I run into a new problem.
Since the event
hubConnection.On<string, string>...
is called by every client, and all new messages get added into ChatLog.Messages X-times (x == amount of active clients).
I just can't think of a way to avoid this problem and only log every message exactly once.
Can someone help me?
Thanks in advance and sorry for the long explanation. Maybe someone can also help shorten it?
EDIT
To clarify the problem: Since the messages get added to the messages List inside of the event (as shown above), every instance (or every tab of the website) adds the message, resulting in multiple (and unwanted) adds.
E.g.
Two clients
Message "Hello" was sent once but added twice
Message "Ciao" was sent twice but added four times
From what I can gather this is more a learning exercise than something you're actually planning on using in a production environment, so we can ignore the fact that this isn't really a very robust implementation.
In any case, a simply solution would be to have the sender of the message store it in the messagelog, instead of storing it upon reception.
Taking from the tutorial you followed:
using Microsoft.AspNetCore.SignalR;
namespace BlazorServerSignalRApp.Server.Hubs
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
// STORE YOUR MESSAGE IN YOUR MESSAGE LOG HERE
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
You should be able to inject your MessageLog service into the ChatHub in order to access it from there. (If I'm understanding your project structure correctly)

How can I catch an ExtendedSocketException?

This may sound stupid, but I just can't catch an ExtendedSocketException that is getting thrown by SocketTaskExtensions.ConnectAsync().
The full namespace is: System.Net.Internals.SocketExceptionFactory.ExtendedSocketException
The compiler complains with Cannot resolve symbol 'ExtendedSocketException'. Both my class library and test projects are targeting .Net Core 2.1.
Also there is no such reference or Nuget package that could be added. At least I coudn't find anything... Also there seems nothing on https://learn.microsoft.com...
What am I doing wrong here?
[Fact]
[Trait("Category", "UnitTest")]
public async Task Should_Throw_Exception_If_Port_Unreachable()
{
// Arrange
var client = new TcpConnector();
var nonListeningPort = 81;
var endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), nonListeningPort);
// Act & Assert
var connectTask = client.ConnectAsync(endpoint);
Func<Task> func = async () => { await connectTask; };
func.Should().Throw<ExtendedSocketException>();
}
ExtendedSocketException is a private class so you can't reference it outside of .NET internals. You can catch SocketException however and inspect the exception message.
Even though the ExtendedSocketException is meant to be private, it's definitely being exposed:
On the other hand, it inherits from SocketException, so we shouldn't need to care about it too much.
In terms of your test, I assume that you should be able to modify your assertion to something similar to:
var exception = Assert.Catch(func);
Assert.IsInstanceOf<SocketException>(exception);
Assert.That(exception.Message.Contains("actively refused"));
ExtendedSocketException is derived from SocketException (for .NET Core). This can still be handled by using a catch block catching SocketException. We are able to confirm this through System.Type.IsAssignableFrom(Type c):
catch (Exception e)
{
Debug.Write(typeof(SocketException).IsAssignableFrom(e.GetType()));
}
In xUnit, the assertion can be:
var client = new TcpClient();
Task result() => client.ConnectAsync(IPAddress.Loopback, 23000);
var ex = Record.ExceptionAsync(async () => await client.ConnectAsync(IPAddress.Loopback, 23000));
Assert.IsAssignableFrom<SocketException>(ex.Result);

SignalR server --> client call not working

I'm currently using SignalR to communicate between a server and multiple separate processes spawned by the server itself.
Both Server & Client are coded in C#. I'm using SignalR 2.2.0.0
On the server side, I use OWIN to run the server.
I am also using LightInject as an IoC container.
Here is my code:
public class AgentManagementStartup
{
public void ConfigurationOwin(IAppBuilder app, IAgentManagerDataStore dataStore)
{
var serializer = new JsonSerializer
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
TypeNameHandling = TypeNameHandling.Auto,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
};
var container = new ServiceContainer();
container.RegisterInstance(dataStore);
container.RegisterInstance(serializer);
container.Register<EventHub>();
container.Register<ManagementHub>();
var config = container.EnableSignalR();
app.MapSignalR("", config);
}
}
On the client side, I register this way:
public async Task Connect()
{
try
{
m_hubConnection = new HubConnection(m_serverUrl, false);
m_hubConnection.Closed += OnConnectionClosed;
m_hubConnection.TraceLevel = TraceLevels.All;
m_hubConnection.TraceWriter = Console.Out;
var serializer = m_hubConnection.JsonSerializer;
serializer.TypeNameHandling = TypeNameHandling.Auto;
serializer.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
m_managementHubProxy = m_hubConnection.CreateHubProxy(AgentConstants.ManagementHub.Name);
m_managementHubProxy.On("closeRequested", CloseRequestedCallback);
await m_hubConnection.Start();
}
catch (Exception e)
{
m_logger.Error("Exception encountered in Connect method", e);
}
}
On the server side I send a close request the following way:
var managementHub = GlobalHost.ConnectionManager.GetHubContext<ManagementHub>();
managementHub.Clients.All.closeRequested();
I never receive any callback in CloseRequestedCallback. Neither on the Client side nor on the server side I get any errors in the logs.
What did I do wrong here ?
EDIT 09/10/15
After some research and modifications, I found out it was linked with the replacement of the IoC container. When I removed everything linked to LightInject and used SignalR as is, everything worked. I was surprised about this since LightInject documented their integration with SignalR.
After I found this, I realised that the GlobalHost.DependencyResolver was not the same as the one I was supplying to the HubConfiguration. Once I added
GlobalHost.DependencyResolver = config.Resolver;
before
app.MapSignalR("", config);
I am now receiving callbacks within CloseRequestedCallback. Unfortunately, I get the following error as soon as I call a method from the Client to the Server:
Microsoft.AspNet.SignalR.Client.Infrastructure.SlowCallbackException
Possible deadlock detected. A callback registered with "HubProxy.On"
or "Connection.Received" has been executing for at least 10 seconds.
I am not sure about the fix I found and what impact it could have on the system. Is it OK to replace the GlobalHost.DependencyResolver with my own without registering all of its default content ?
EDIT 2 09/10/15
According to this, changing the GlobalHost.DependencyResolver is the right thing to do. Still left with no explanation for the SlowCallbackException since I do nothing in all my callbacks (yet).
Issue 1: IoC Container + Dependency Injection
If you want to change the IoC for you HubConfiguration, you also need to change the one from the GlobalHost so that returns the same hub when requesting it ouside of context.
Issue 2: Unexpected SlowCallbackException
This exception was caused by the fact that I was using SignalR within a Console Application. The entry point of the app cannot be an async method so to be able to call my initial configuration asynchronously I did as follow:
private static int Main()
{
var t = InitAsync();
t.Wait();
return t.Result;
}
Unfortunately for me, this causes a lot of issues as described here & more in details here.
By starting my InitAsync as follow:
private static int Main()
{
Task.Factory.StartNew(async ()=> await InitAsync());
m_waitInitCompletedRequest.WaitOne(TimeSpan.FromSeconds(30));
return (int)EndpointErrorCode.Ended;
}
Everything now runs fine and I don't get any deadlocks.
For more details on the issues & answers, you may also refer to the edits in my question.

Tring to get GitHub repo via Octokit

I'm building simple tool for downloading .lua files from online public GitHub repos via link given by user. I started learning async methods so I wanted to test myself.
It's a console application (for now). The ultimate goal is to get .lua files in a repo and ask the user which ones he wants downloaded, but I'll be happy if I connect to GH for now.
I'm using Octokit (https://github.com/octokit/octokit.net) GitHub API integration to .NET.
This is the reduced code; I removed some of unimportant stuff:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Octokit;
namespace GetThemLuas
{
class Program
{
static readonly GitHubClient Github = new GitHubClient(new ProductHeaderValue ("Testing123"), new Uri("https://www.github.com/"));
static void Main(string[] args)
{
Console.WriteLine("Welcome to GitHub repo downloader");
GetRepoTry4();
}
private static async void GetRepoTry4()
{
try
{
Console.WriteLine("Searching for data"); //returns here... code below is never ran
var searchResults = await Github.Search.SearchRepo(new SearchRepositoriesRequest("octokit"));
if (searchResults != null)
foreach (var result in searchResults.Items)
Console.WriteLine(result.FullName);
Console.WriteLine("Fetching data...."); //testing search
var myrepo = await Github.Repository.Get("Haacked", "octokit.net");
Console.WriteLine("Done! :)");
Console.WriteLine("Repo loaded successfully!");
Console.WriteLine("Repo owner: " + myrepo.Owner);
Console.WriteLine("Repo ID: " + myrepo.Id);
Console.WriteLine("Repo Date: " + myrepo.CreatedAt);
}
catch (Exception e)
{
Console.WriteLine("Ayyyy... troubles"); //never trigged
Console.WriteLine(e.Message);
}
}
}
}
The problem is the await` keyword as it terminates the method and returns.
I'm still learning async methods so it's possible I messed something up, but even my ReSharper says it fine.
I used var to replace task<T> stuff. It seams OK to me plus no warnings nor errors.
I fixed the await issue. Now when I finally connected to GH and tried to get the repo it threw an exeption at both calls to GH (tested with commenting first then second call). e.message was some huge stuff.
I logged it into a file and it looks like an HTML document. Here it is (http://pastebin.com/fxJD1dUb)
Change GetRepoTry4(); to Task.Run(async () => { await GetRepoTry4(); }).Wait(); and private static async void GetRepoTry4() to private static async Task GetRepoTry4().
This should get you at least wired up correctly enough to start debugging the real issue.
Generally speaking all async methods need to return a Task or Task<T> and all methods that return a Task or Task<T> should be async. Additionally, you should get your code into the dispatcher as quickly as possible and start using await.
The constructor with the Uri overload is intended for use with GitHub Enterprise installations, e.g:
static readonly GitHubClient Github = new GitHubClient(new ProductHeaderValue ("Testing123"), new Uri("https://github.enterprise.com/"));
If you're just using it to connect to GitHub, you don't need to specify this:
static readonly GitHubClient Github = new GitHubClient(new ProductHeaderValue ("Testing123"));
You're seeing a HTML page because the base address is incorrect - all of the API-related operations use api.github.com, which is the default.
Install Octokit Nuget Package for Github.Then add below function
public JsonResult GetRepositoryDeatil(long id)
{
var client = new GitHubClient(new ProductHeaderValue("demo"));
var tokenAuth = new Credentials("xxxxxxx"); // NOTE: not real token
client.Credentials = tokenAuth;
var content = client.Repository.Content.GetAllContents(id).Result;
List<RepositoryContent> objRepositoryContentList = content.ToList();
return Json(objRepositoryContentList, JsonRequestBehavior.AllowGet);
}
Due to the use of the async/await you should change the definition of the method GetRepoTry4 to the following:
private static async Task GetRepoTry4()
EDIT:
Then in the Main method call it like so GetRepoTry4().Wait();. This will enable the method GetRepoTry4() to be awaited.

Logging to NLog in Hangfire.io

I am currently using Hangfire to run some jobs in a windows service. The jobs are fired from a WebAPI.
My system is currently working fine when all jobs succeed, but I am getting no logging for when exceptions occur. Has anyone got any experience in using a custom logger to receive messages from Hangfire?
My logger is a basic NLog interface:
public class Logger : ILogger
{
public readonly NLog.Logger logger;
public Logger(string name)
{
if (LogManager.Configuration == null)
{
FallbackInitialisation();
}
logger = LogManager.GetLogger(name);
}
public Trace(string message)
//etc.
}
I am configuring my Hangfire job server in my windows service like so:
SqlServerStorage storage = new SqlServerStorage("myConnectionString");
BackgroundJobServerOptions options = new BackgroundJobServerOptions();
m_server = new BackgroundJobServer(options, storage);
GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 0 });
Based on the hangfire documentation, I simply tried following it by adding the required references, and adding the following after my job server setup, however there were still no logs produced:
var properties = new NameValueCollection();
properties["configType"] = "INLINE";
LogManager.Adapter = new NLogLoggerFactoryAdapter(properties);
I am simply simulating an exception by throw new Exception() in the method called by the job. The job is created in the webApi call like so:
[HttpGet]
public void TestStartJob()
{
m_logger.Trace("TestStartJob");
BackgroundJob.Enqueue(() => m_service.TestStartJob());
}
What I am looking for, is if anyone has any experience in getting logging properly configured for use with Hangfire.
I have managed to solve this issue by using the following versions of nuget packages:
NLog - 3.1
Common.Logging - 2.2.0
JVW.Logging.CommonLoggingNLogAdapter - 1.0.0.1
and then configuring the logging as such
var properties = new NameValueCollection();
properties["configType"] = "INLINE";
Common.Logging.LogManager.Adapter = new NLogFactoryAdapter(properties);

Categories