Skip to content
This repository has been archived by the owner on Jul 5, 2020. It is now read-only.

Make local debug of dep collector functional tests easier #738

Merged
merged 10 commits into from
Nov 10, 2017
19 changes: 11 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,21 @@ If all or most of the Dependency Collector functional tests fail with messages l
Please make sure you can run docker run hello-world successfully to confirm that your machine is Docker ready.
Also, the very first time DependencyCollector tests are run, all Docker images are downloaded from web and this could potentially take an hour or so. This is only one time per machine.

The test code intentionally does not clean up the containers it spun up. This is to enable fast re-runs of the tests. If the WebApp code is changed, then Docker-Compose will detect it, and re-build the container.
If you want to do clean up all the containers created by the test, execute the ```dockercleanup.ps1``` from repository root.

## Debugging the functional tests
It is important to note that since the test application is deployed as a separate process/container, debugging the tests itself will not help debug the application code. A debugger need to be attached
to either IISExpress or IIS after deploying the application.
The test apps refers to the Web SDK assemblies from your local build. After making the changes to product code, build locally (from Visual Studio or using ```buildDebug.cmd```). Then start the test application
in either IISExpress or IIS, and attach debugger to it. Open the .cs file you want your breakpoint in and set it. Now triggering a request to the application will hit the breakpoint.
to the process hosting the Application. (IISExpress or IIS)
The test apps refers to the Web SDK assemblies from your local build. After making the changes to product code, build locally (from Visual Studio or using ```buildDebug.cmd```). Then start the test application from its publish
folder in either IISExpress or IIS, and attach debugger to it. Open the .cs file you want your breakpoint in and set it. Now triggering a request to the application will hit the breakpoint.
The exact request to be triggered depends on what you are doing. If investigating functional test failures locally, then the tests logs should contain the url it hit to trigger scenarios.

<TODO>
Dependency Collector tests deploy the test apps, along with dependencies (Fake Ingestion, SQL etc) to Docker containers inside same network, so that apps can access the dependencies with their names. However, if
the test apps are deployed to IIS or IISExpress, it won't be able to access dependencies without using their IP Address.
(Todo is to write a small PS script, which can find ip addresses and replace them in Web.config or applicationinsights.config)
</TODO>
Dependency Collector tests deploy the test apps, along with dependencies (Fake Ingestion, SQL etc) to Docker containers inside same Docker virtual network, so that apps can access the dependencies with their names. However, if
the test apps are deployed to IIS or IISExpress, then they are outside the Docker virtual network of dependencies, and so it won't be able to access dependencies without using their IP Address. This is a Docker for windows limitation, and could be fixed in future.
Until then, the test app need to address the dependencies using their IP Address. Instead of manually finding IP Addresses and replacing containers names with IP Address, its easy to just run the following script.
This uses Docker commands to determine the IP Addresses, and replaces them in the necessary configs.
"<repo-root>\bin\Debug\Test\E2ETests\E2ETests\replacecontainernamewithip.ps1"

Following pre-requisite is needed to deploy to IIS locally.
* IIS (Make sure Internet Information Services > World Wide Web Services > Application Development Features > ASP.NET 4.6 is enabled)
Expand Down
3 changes: 3 additions & 0 deletions Test/E2ETests/E2ETests/DependencyCollectionTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="replacecontainernamewithip.ps1">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="net47\" />
Expand Down
21 changes: 21 additions & 0 deletions Test/E2ETests/E2ETests/replacecontainernamewithip.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
$ErrorActionPreference = "silentlycontinue"

$ingestionIP = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" e2etests_ingestionservice_1
$ingestionContainerName = "e2etests_ingestionservice_1"
$webapiIp = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" e2etests_e2etestwebapi_1
$webapiContainerName = "e2etestwebapi"
$sqlIp = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" e2etests_sql-server_1
$sqlContainerName = "sql-server"
$azureIP = docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" e2etests_azureemulator_1
$azureContainerName = "e2etests_azureemulator_1"

# NetCore App
(Get-Content ..\\TestApps\\NetCore20\\E2ETestAppCore20\\netcoreapp2.0\\Publish\\appsettings.json).replace($ingestionContainerName, $ingestionIP) | Set-Content ..\\TestApps\\NetCore20\\E2ETestAppCore20\\netcoreapp2.0\\Publish\\appsettings.json
(Get-Content ..\\TestApps\\NetCore20\\E2ETestAppCore20\\netcoreapp2.0\\Publish\\appsettings.json).replace($sqlContainerName, $sqlIp) | Set-Content ..\\TestApps\\NetCore20\\E2ETestAppCore20\\netcoreapp2.0\\Publish\\appsettings.json

#WebApp
(Get-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config).replace($ingestionContainerName, $ingestionIP) | Set-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config
(Get-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config).replace($sqlContainerName, $sqlIp) | Set-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config
(Get-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config).replace($azureContainerName, $azureIP) | Set-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config
(Get-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config).replace($webapiContainerName, $webapiIp) | Set-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\Web.config
(Get-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\ApplicationInsights.config).replace($azureContainerName, $azureIP) | Set-Content ..\\TestApps\\Net452\\E2ETestApp\\Publish\ApplicationInsights.config
12 changes: 10 additions & 2 deletions Test/E2ETests/TestApps/Net452/E2ETestApp/Dependencies.aspx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
namespace E2ETestApp
{
public partial class Dependencies : System.Web.UI.Page
{
public const string LocalDbConnectionString = @"Server =sql-server;Initial Catalog=dependencytest;User Id = sa; Password=MSDNm4g4z!n4";
{
public static string ConnectionStringFormat = "Server = {0};Initial Catalog=dependencytest;User Id = sa; Password=MSDNm4g4z!n4";
public static string LocalDbConnectionString;
public const string InvalidAccountConnectionString = @"Server =sql-server;User Id = sa; Password=thisiswrong";
public const string InvalidServerConnectionString = @"Server =sql-server-dontexist;User Id = sa; Password=MSDNm4g4z!n4";
private string SqlQuerySuccess = "WAITFOR DELAY '00:00:00:007';select * from dbo.Messages";
private string SqlQueryError = "WAITFOR DELAY '00:00:00:007';SELECT name FROM master.dbo.sysdatabasesunknown";
private string SqlStoredProcedureName = "WAITFOR DELAY '00:00:00:007';SELECT name FROM master.dbo.sysdatabases";
public static string EndPointAddressFormat = "http://{0}/api/Data/PushItem";

private const string UrlTestWebApiGetCallTemplate = "http://{0}:80/api/values";
private const string UrlGoogle = "http://google.com";
Expand All @@ -42,6 +44,12 @@ protected void Page_Load(object sender, EventArgs e)
var webApiHostName = Microsoft.Azure.CloudConfigurationManager.GetSetting("webapihostname");
string UrlTestWebApiGetCall = string.Format(UrlTestWebApiGetCallTemplate, webApiHostName);

var ingestionhostname = Microsoft.Azure.CloudConfigurationManager.GetSetting("ingestionhostname");
TelemetryConfiguration.Active.TelemetryChannel.EndpointAddress = string.Format(EndPointAddressFormat, ingestionhostname);

var sqlhostname = Microsoft.Azure.CloudConfigurationManager.GetSetting("sqlhostname");
LocalDbConnectionString = string.Format(ConnectionStringFormat, sqlhostname);

try
{
switch (type)
Expand Down
5 changes: 4 additions & 1 deletion Test/E2ETests/TestApps/Net452/E2ETestApp/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
-->
<configuration>
<appSettings>
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://e2etests_azureemulator_1:10000/devstoreaccount1;TableEndpoint=http://e2etests_azureemulator_1:10002/devstoreaccount1;QueueEndpoint=http://e2etests_azureemulator_1:10001/devstoreaccount1;" />
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://{0}:10000/devstoreaccount1;TableEndpoint=http://{0}:10002/devstoreaccount1;QueueEndpoint=http://{0}:10001/devstoreaccount1;" />
<add key="azureemulatorhostname" value="e2etests_azureemulator_1" />
<add key="webapihostname" value="e2etestwebapi" />
<add key="ingestionhostname" value="e2etests_ingestionservice_1" />
<add key="sqlhostname" value="sql-server" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
<HintPath>..\..\..\..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.3\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
Expand Down
8 changes: 7 additions & 1 deletion Test/E2ETests/TestApps/Net452/E2ETestWebApi/Global.asax.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Microsoft.ApplicationInsights.Extensibility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
Expand All @@ -11,13 +12,18 @@ namespace E2ETestWebApi
{
public class WebApiApplication : System.Web.HttpApplication
{
public static string EndPointAddressFormat = "http://{0}/api/Data/PushItem";

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);

var ingestionhostname = Microsoft.Azure.CloudConfigurationManager.GetSetting("ingestionhostname");
TelemetryConfiguration.Active.TelemetryChannel.EndpointAddress = string.Format(EndPointAddressFormat, ingestionhostname);
}
}
}
1 change: 1 addition & 0 deletions Test/E2ETests/TestApps/Net452/E2ETestWebApi/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="ingestionhostname" value="e2etests_ingestionservice_1" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.3" targetFramework="net452" />
<package id="Microsoft.Net.Compilers" version="1.3.2" targetFramework="net452" developmentDependency="true" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" />
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net452" />
<package id="Modernizr" version="2.6.2" targetFramework="net452" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net452" />
<package id="Respond" version="1.2.0" targetFramework="net452" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace E2ETestAppCore20
{
public class AppInsightsOptions
{
public AppInsightsOptions()
{
}
public string EndPoint { get; set; }
public string SqlServerInstance { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using HttpSQLHelpers;
using Microsoft.ApplicationInsights.Extensibility;
using System.Threading;
using Microsoft.Extensions.Options;

namespace E2ETestAppCore20.Controllers
{
Expand All @@ -17,10 +18,15 @@ public class ExternalCallsController : Controller
/// </summary>
private const string InvalidHostName = "http://www.zzkaodkoakdahdjghejajdnad.com";

/// <summary>
/// Connection string format.
/// </summary>
public string ConnectionStringFormat = "Server = {0};Initial Catalog=dependencytest;User Id = sa; Password=MSDNm4g4z!n4";

/// <summary>
/// Connection string to local database.
/// </summary>
public const string ConnectionString = @"Server =sql-server;Initial Catalog=dependencytest;User Id = sa; Password=MSDNm4g4z!n4";
public static string ConnectionString;

/// <summary>
/// Valid SQL Query. The wait for delay of 6 ms is used to prevent access time of less than 1 ms. SQL is not accurate below 3, so used 6 ms delay.
Expand All @@ -47,6 +53,14 @@ private string GetQueryValue(string valueKey)
return Request.Query[valueKey].ToString();
}

public ExternalCallsController(IOptions<AppInsightsOptions> options)
{
if (string.IsNullOrEmpty(ConnectionString))
{
ConnectionString = string.Format(ConnectionStringFormat, options.Value.SqlServerInstance);
}
}

// GET external/calls
[HttpGet]
public string Get()
Expand All @@ -72,6 +86,33 @@ public string Get()
title = response = "Flushed telemetry channel";
TelemetryConfiguration.Active.TelemetryChannel.Flush();
break;
case "setsqlserverinstance":
string sqlServerInstance = GetQueryValue("server");
if(!string.IsNullOrEmpty(sqlServerInstance))
{
ConnectionString = string.Format(ConnectionStringFormat, sqlServerInstance);
title = response = "Update SQL Server Instance to: " + sqlServerInstance;
}
else
{
title = response = "SQL Server Instance not updated. ";
}

break;
case "setchannelendpoint":
string endPoint = GetQueryValue("endpoint");
if (!string.IsNullOrEmpty(endPoint))
{
TelemetryConfiguration.Active.TelemetryChannel.EndpointAddress = string.Format(Program.EndPointAddressFormat, endPoint);
title = response = "Update Endpoint to: " + TelemetryConfiguration.Active.TelemetryChannel.EndpointAddress;
}
else
{
title = response = "Endpoint not updated. ";
}

break;

case "http":
title = "Made Sync GET HTTP call to bing";
MakeHttpGetCallSync(count, "bing");
Expand Down
3 changes: 2 additions & 1 deletion Test/E2ETests/TestApps/NetCore20/E2ETestAppCore20/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
using Microsoft.Extensions.Logging;

namespace E2ETestAppCore20
{
{
public class Program
{
public static string EndPointAddressFormat = "http://{0}/api/Data/PushItem";
public static void Main(string[] args)
{
IWebHostBuilder builder = new WebHostBuilder()
Expand Down
16 changes: 11 additions & 5 deletions Test/E2ETests/TestApps/NetCore20/E2ETestAppCore20/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,32 @@
using Microsoft.Extensions.Options;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.DependencyCollector;
using System.IO;

namespace E2ETestAppCore20
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
public Startup(IHostingEnvironment env)
{
var configBuilder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json");

Configuration = configBuilder.Build();
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppInsightsOptions>(Configuration);
services.AddMvc();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<AppInsightsOptions> options)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
Expand All @@ -40,7 +46,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
teleConfig.TelemetryChannel.DeveloperMode = true;

// Fake endpoint.
teleConfig.TelemetryChannel.EndpointAddress = "http://e2etests_ingestionservice_1/api/Data/PushItem";
teleConfig.TelemetryChannel.EndpointAddress = string.Format(Program.EndPointAddressFormat, options.Value.EndPoint);
teleConfig.InstrumentationKey = "fafa4b10-03d3-4bb0-98f4-364f0bdf5df8";

new DependencyTrackingTelemetryModule().Initialize(TelemetryConfiguration.Active);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@
"Default": "Warning"
}
}
}
}
},
"EndPoint": "e2etests_ingestionservice_1",
"SqlServerInstance": "sql-server"
}
Loading