How do I launch my ASP.NET application from integration test code? - asp.net

I have written a basic suite of Selenium integration tests for an application we are building. They work great. The only issue is that the application needs to be pre-deployed and running before the tests can run.
How do I launch an ASP.NET MVC application from integration test code?

I figured out how to launch my app my running MS build and then basically passing it the path to the sign and starting a new IISExpress process to host it:
ProcessStartInfo processStartInfo = new ProcessStartInfo()
{
ErrorDialog = false,
CreateNoWindow = true,
UseShellExecute = false,
Arguments = string.Format("/path:\"{0}\" /port:{1}", this.pathToSite, this.portNumber)
};
string path = (!string.IsNullOrEmpty(processStartInfo.EnvironmentVariables["programfiles(x86)"]) ? processStartInfo.EnvironmentVariables["programfiles(x86)"] : processStartInfo.EnvironmentVariables["programfiles"]) + "\\IIS Express\\iisexpress.exe";
processStartInfo.FileName = path;
this.iisProcess = new Process
{
StartInfo = processStartInfo
};
this.iisProcess.Start();
Hope this helps the next guy. Otherwise I will just leave this here for my own reference. I wrapped all this in a method called when starting TestFixtureSetup. Of course I run
public void Shutdown()
{
if(this.IisExpressProcess == null)
{
return;
}
this.IisExpressProcess.Stop();
}
on TestFixtureTearDown.

Related

Quartz.NET Runtime Scheduler will only get db connection string from app.config

I've created an asp.net core web app with Quartz for my job scheduling. I'm writing the job blueprints into a db at startup and then adding triggers and scheduling the job once I have all the info I need(user input).
Yesterday I had this issue, that Quartz couldn't find the job in the jobstore when I tried to schedule it. Eventually I figured out that it was looking in the RAM jobstore for some reason.
I tried a number of different things, did some googling to make sure I had everything setup correctly and in the end, on a whim, I figured I'd try adding the connection strings in an app.config. Suddenly quartz happily fetched the connection strings and scheduled my jobs. I added the app.config because I initially used the app.config for my connection strings when setting up the project before figuring out that using the appsettings.json is the preferred method for a .net core project. So maybe there is some setting somewhere that still prompts quartz to look for an app.config? I just have no clue where that could be.
Also, as I said, when setting up the jobs in my program.cs it gets the strings from appsettings.json just as it is supposed to.
program.cs with a most .AddJob omitted for brevity.
Log.Logger = new LoggerConfiguration().Enrich.FromLogContext().WriteTo.Console().CreateLogger();
builder.Services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
var jobKey = new JobKey("HR First Contact", "DEFAULT");
q.AddJob<MailHRNewEmployee>(jobKey, j => j
.StoreDurably()
.WithDescription("job blueprint"));
q.UsePersistentStore(s =>
{
// s.PerformSchemaValidation = true;
s.UseProperties = true;
s.RetryInterval = TimeSpan.FromSeconds(15);
s.UseMySqlConnector(MySql =>
{
MySql.ConnectionString = builder.Configuration.GetConnectionString("quartz.dataSource.default.connectionString");
MySql.TablePrefix = "QRTZ_";
});
s.UseJsonSerializer();
});
});
builder.Services.Configure<QuartzOptions>(builder.Configuration.GetSection("Quartz"));
builder.Services.Configure<QuartzOptions>(options =>
{
options.Scheduling.IgnoreDuplicates = true;
options.Scheduling.OverWriteExistingData = true;
});
builder.Services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
my Scheduler - most of it omitted for brevity, I can add the rest but it's just fetching data I need for the jobmap.
public async Task StartAsync(CancellationToken cancellationToken)
{
ISchedulerFactory schedFact = new StdSchedulerFactory();
this.scheduler = await schedFact.GetScheduler();
ITrigger jtriggz = CreateTriggerFirstContact();
await scheduler.ScheduleJob(jtriggz, cancellationToken);
await scheduler.Start(cancellationToken);
}
private ITrigger CreateTriggerFirstContact()
{
return Quartz.TriggerBuilder.Create().WithIdentity(_employee.FName + _employee.LName, "HR Contact").UsingJobData("FName", _employee.FName).UsingJobData("LName", _employee.LName).UsingJobData("Gender", _employee.Gender).UsingJobData("Id", Convert.ToString(_employee.Id)).UsingJobData("Empfaenger", recipient).UsingJobData("EmpfaengerName", HRManager.Name).UsingJobData("EmpfaengerGeschlecht", HRManager.Gender).StartAt(DateTime.Now.AddSeconds(10)).ForJob("HR First Contact").Build();
}

Cefsharp Winforms app shows blank browser

I have a WinForms based cefsharp app that was working. I updated the NuGet packages for CefSharp (to 75.1.142.0) and now the browser only shows blank on start up. The log file says:
[1029/115545.989:WARNING:resource_bundle.cc(952)] unable to find resource: 164
[1029/115546.193:ERROR:viz_main_impl.cc(170)] Exiting GPU process due to errors during initialization
[1029/115546.210:WARNING:gpu_process_host.cc(1205)] The GPU process has crashed 1 time(s)
Is there a way to get more detailed debug info?
EDIT: OK - Stupid me! Updating the packages doesn't update the files in my custom locations (obviously!). Fix that, and everything is sweet!
I found an answer, but I'm not sure why it should be. I commented out the setting of the paths in the CefSharpSettings (as below) and now it works?
private static void LoadForm(string[] args)
{
// Alternate file locations
string browser = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"CEFSharp\CefSharp.BrowserSubprocess.exe");
string locales = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"CEFSharp\locales\");
string res = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"CEFSharp\");
string cache = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"CEFSharp\cache\");
// Initialise CEF
CefSharpSettings.SubprocessExitIfParentProcessClosed = true;
CefSharp.CefSharpSettings.LegacyJavascriptBindingEnabled = true;
CefSettings settings = new CefSettings();
settings.CachePath = cache;
settings.MultiThreadedMessageLoop = true;
settings.ExternalMessagePump = false;
settings.RemoteDebuggingPort = 8088;
settings.CefCommandLineArgs.Add("proxy-auto-detect", "true");
settings.LogFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"CEFSharp\Debug.log");
//settings.BrowserSubprocessPath = browser;
//settings.LocalesDirPath = locales;
//settings.ResourcesDirPath = res;
settings.DisableGpuAcceleration();
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
Application.Run(new ImportForm(args));
}

dotnet core, console app, UDP, why aren't messages being received?

I'm struggling to get a dotnet core console application to receive a UDP message. When I use the same code in a dotnet framework console app, the messages are received, so I feel nothing should be blocking them (ie. firewall, etc.).
I've tried running this project using Visual Studio, and published a release version and run using dotnet in a command window, but nothing is received. No exceptions. It seems so simple. I'm at a loss. Any advice is appreciated. Thanks.
This is the code:
static void Main(string[] args)
{
var client = new UdpClient(10006);
var ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
var msgReceived = client.Receive(ref ipEndPoint);
Console.WriteLine($"received '{Encoding.ASCII.GetString(msgReceived)}'");
}
I see the same/similar question asked here, without an answer:
How to send and receive commands from a UDP network server via .NET Core console application
Are you sure you are actually sending data to that machine and port number.
The code should work ok. I put this in my Main and it receives the data.
I used threads so they can be in the same process, I didnt test it as seperate processes.
var threadReceiver = new Thread(() =>
{
var client = new UdpClient(10006);
var ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
var msgReceived = client.Receive(ref ipEndPoint);
Console.WriteLine($"received '{Encoding.ASCII.GetString(msgReceived)}'");
});
var threadSender = new Thread(() =>
{
var client = new UdpClient(10007);
var ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
var bytes = Encoding.ASCII.GetBytes("this is the data");
Thread.Sleep(1000);
var msgReceived = client.Send(bytes, bytes.Length, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 10006)) ;
Console.WriteLine($"Sent data");
});
threadReceiver.Start();
threadSender.Start();
threadReceiver.Join();
This outputs :
Sent data
received 'this is the data'
When i ran this the first time I was asked by windows firewall to allow the traffic. I checked private and public networks. You need to make sure your firewall is enabled for the network type you're connected to. Check the network settings to see if windows thinks is public or private. You can setup firewall rules manually for the app in the Windows firewall settings.

Using Web.Api to asynchronously log errors for asp.net mvc

I have a legacy logging DLL that logs errors into a database. Instead of consuming the DLL within each application in our environment, we would like to make web calls to log errors.
I have built up a web.api app that will log errors into a database. When tested with POSTMAN it works as advertised.
I have added a class within a demo MVC app and wired up one of my constructors to execute a log command, but the call not only does not make it to my web.api, but fiddler does not show a call even being made.
Any input on making this actually run would be greatly appreciated.
Here's my code:
Logging Utility Called within Web.API
public class Utilities
{
public void LogException(string exceptionMessage, string stackTrace, string appCode, string runMode, int entityId, int itmsUserID, string updateBy, string path, string method)
{
ErrorLog.Entry _error = new ErrorLog.Entry();
_error.ErrorMessage = exceptionMessage;
_error.StackTrace = stackTrace;
_error.AppCode = appCode;
_error.Path = path;
_error.Method = method;
_error.UpdateBy = updateBy;
_error.RunMode = runMode;
_error.EntityID = entityId;
//_error.Server = server; server will have to be changed to accept a setter
_error.ITMSUserID = CommonFunctions.Get_ITMSUserID(updateBy);
_error.Save();
}
}
Web.API
// POST: api/ErrorLog
public void Post([FromBody]ErrorLogEntryDTO item)
{
var utils = new Utilities();
utils.LogException(item.ErrorMessage, item.StackTrace, item.AppCode, item.RunMode, item.EntityID, item.ITMSUserID, item.UpdateBy, item.Path, item.Method);
}
MVC Controller Code
// GET: BillingRules/Create
public virtual ActionResult CauseHandledError()
{
try
{
throw new Exception("Handled exception test");
}
catch (Exception ex)
{
var utils = new Utilities();
utils.LogException(ex, "system", MVC.BillingRules.Name, MVC.BillingRules.ActionNames.CauseHandledError);
}
return RedirectToAction(MVC.BillingRules.ActionNames.Index, MVC.BillingRules.Name);
}
Utilities Code within MVC App
public void LogException(Exception exception, string updateBy, string path, string method)
{
try
{
var itmsUserID = CommonFunctions.Get_ITMSUserID(updateBy);
var errorDTO = new ErrorLogEntryDTO();
errorDTO.ITMSUserID = itmsUserID;
errorDTO.AppCode = _appCode.Value;
errorDTO.ErrorMessage = exception.Message;
errorDTO.StackTrace = exception.StackTrace;
errorDTO.Path = path;
errorDTO.Method = method;
errorDTO.UpdateBy = updateBy;
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:52316");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var result = client.PostAsJsonAsync("api/ErrorLog", errorDTO).Result; //ContinueWith(readTask => client.Dispose()); //
}
catch (Exception ex)
{
var myError = ex;
throw;
}
}
I'm pretty sure calling .Result in this instance does not immediately invoke the PostAsJsonAsync method. Because you're not doing anything with the Result, it never actually executes. Since it doesn't appear you care about the response, you should be able to use:
client.PostAsJsonAsync("api/ErrorLog", errorDTO).Wait();
I think .Result invokes the PostAsJsonAsync call. You are waiting for the respsonse, so the call must be finished after this line. Regardless if you use the Result or not.
You can remove the [FromBody] attribute, because the complex type is per default read from the body.
And I can't reproduce your issue. I've created a new Web API project and a new console project. In the Web API I've changed the post of the valuescontroller to yours.
In the console project I'm yousing your LogException() method from the MVC app.
It hits my web api app.
Are both in the same host or in different hosts?
Edit:
To make your logging async you can use fire-and-forget with Task.Run() but it depends on the application you have. In ASP.Net Task.Run() is an anti-pattern according to Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation.

ASP.NET Process Start PowerShell Script IIS 7.5

All works fine on my dev machine but if deployed to IIS the process doesn't get started. I am starting a powershell script by
private void RunScript()
{
Process process = null;
try
{
int timeout = 1800000;
var startInfo = new ProcessStartInfo
{
FileName = #"powershell.exe",
Arguments = string.Format("{0} {1}", "\path\toscript", "myParam"),
UseShellExecute = false,
CreateNoWindow = true
};
process = Process.Start(startInfo);
process.WaitForExit(timeout);
}
finally
{
if (!process.HasExited)
{
if (process.Responding)
process.CloseMainWindow();
else
process.Kill();
}
if (process != null)
{
process.Close();
process.Dispose();
}
}
}
Here's what's configured for the app pool this is running under.
Process Model
->Identity = domain user who is a Domain Admin.
->Load User Profile = True
Web App
Authentication is Windows
What else do I need to configure to so that I can run the Process?
As Start-Automating suggested I eventually ended up doing this:
using (Runspace runSpace = RunspaceFactory.CreateRunspace())
{
try
{
runSpace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runSpace);
scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
using (Pipeline pipeLine = runSpace.CreatePipeline())
{
var myCommand = new Command(scriptPath);
var myParam1 = new CommandParameter("-paramName", "someValue");
myCommand.Parameters.Add(myParam1);
pipeLine.Commands.Add(myCommand);
pipeLine.Commands.Add("Out-String");
Collection<PSObject> returnObjects = pipeLine.Invoke();
runSpace.Close();
return returnObjects;
}
}
finally
{
runSpace.Close();
}
}
On the IIS server I executed the following powershell command "Set-ExecutionPolicy RemoteSigned"
It's much better to embed the PowerShell APIs the call the .exe
Here's an old link that will get you a PowerShell runspace embedded in ASP.NET per user:
http://powershellpipeworks.com/
Check the permissions of the file system where powershell.exe lives.
Also, check the Security Log in the Event Viewer for authentication errors and access violations.

Resources