Skip to content

Commit

Permalink
Adding CIAM E2E tests and improvements to build infrastructure (#2753) (
Browse files Browse the repository at this point in the history
#2769)

* Adding web app and web app call web api CIAM non CUD tests

* Adding CIAM deamon test app for non CUD domain

* Integrating lab client apps
Refactoring

* Refactoring

* Setting ui test to headless

* Update WebAppCallsApiCallsGraphLocally.cs

* Adding CIAM CUD integration tests (#2737)

* Adding CIAM CUD tests

* Updating comments

* Updating test

---------



* Enabling test runs with VSTest

* Test updates

* test

* Testing

* Disabling test

* Test

* Updating the CIAM tests to use the lab API (manual testing)

* Revert "Test"

This reverts commit c3f3d3a.

* Revert "Disabling test"

This reverts commit 5f2a384.

* Using hosted agents

* Always publish traces

* Revert "Using hosted agents"

This reverts commit 7484c58.

* Updating process error

* Publish screenshots

* Updating tests

* Adding additional statements

* Updating screenshot staging

* Fix typo

* Updating endpoint to use Http

* Reconfiguring ports

* hiding UI

* Enabling web app tests

* Resolving test issue

* Fixing run time and hiding ui

* Adding asp auth

* Enabling test

* Refactoring test YAML

* Disabling restore

* Fixing build task

* Test

* Test

* Fixing parameters

* Unit tests

* Install test platform

* Changing test runner

* Updating test directories

* Updating test run

* Fix typo

* Updating test run

* Updating check

* Refactoring process restart logic

* Clean up YAML

* Revert "Adding asp auth"

This reverts commit b7622cd.

* Refactoring

* Enabling code coverage settings

* Fix indentation

---------

Co-authored-by: Bogdan Gavril <[email protected]>
Co-authored-by: trwalke <[email protected]>
Co-authored-by: Jean-Marc Prieur <[email protected]>
  • Loading branch information
4 people authored Apr 17, 2024
1 parent 3083a3c commit 54afc21
Show file tree
Hide file tree
Showing 16 changed files with 468 additions and 84 deletions.
7 changes: 7 additions & 0 deletions build/pipeline-releasebuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ steps:
BuildPlatform: '$(BuildPlatform)'
BuildConfiguration: '$(BuildConfiguration)'
MsIdentityWebSemVer: $(MsIdentityWebSemVer)

# Run all tests
- template: template-run-unit-tests.yaml
parameters:
BuildPlatform: '$(BuildPlatform)'
BuildConfiguration: '$(BuildConfiguration)'
MsIdentityWebSemVer: $(MsIdentityWebSemVer)

# Run Post-build code analysis (e.g. Roslyn)
- template: template-postbuild-code-analysis.yaml
Expand Down
9 changes: 5 additions & 4 deletions build/template-restore-build-MSIdentityWeb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ steps:
- script: |
dotnet workload restore tests\DevApps\blazorwasm-b2c\blazorwasm2-b2c.csproj
displayName: 'Install wasm-tools'

- task: DotNetCoreCLI@2
displayName: 'Build solution Microsoft.Identity.Web.sln and run tests'
displayName: 'Build solution Microsoft.Identity.Web.sln'
inputs:
command: test
command: 'custom'
custom: 'build'
projects: 'Microsoft.Identity.Web.sln'
arguments: '--collect "Code coverage" --settings "build\CodeCoverage.runsettings" --configuration ${{ parameters.BuildConfiguration }} -p:RunCodeAnalysis=true -p:MsIdentityWebSemVer=${{ parameters.MsIdentityWebSemVer }} -p:SourceLinkCreate=true'
arguments: '-p:configuration=${{ parameters.BuildConfiguration }} -p:RunCodeAnalysis=true -p:MsIdentityWebSemVer=${{ parameters.MsIdentityWebSemVer }} -p:SourceLinkCreate=true'

# This task is needed so that the 1CS Rolsyn analyzers task works.
# The previous task does the restore
Expand Down
65 changes: 58 additions & 7 deletions build/template-run-unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,69 @@

steps:

- task: DotNetCoreCLI@2
- task: VSTest@2
displayName: 'Run unit tests'
inputs:
command: 'test'
projects: |
Tests/**/*.csproj
!Tests/DevApps/**/*.csproj
arguments: '--collect "Code coverage" --settings "build\CodeCoverage.runsettings"'
continueOnError: 'true'
testSelector: 'testAssemblies'
testAssemblyVer2: 'tests\Microsoft.Identity.Web.Test\bin\**\Microsoft.Identity.Web.Test.dll'
searchFolder: '$(System.DefaultWorkingDirectory)'
runInParallel: true
codeCoverageEnabled: true
failOnMinTestsNotRun: true
minimumExpectedTests: '1'
runSettingsFile: 'build\CodeCoverage.runsettings'

- task: VSTest@2
displayName: 'Run integration tests'
inputs:
testSelector: 'testAssemblies'
testAssemblyVer2: 'tests\Microsoft.Identity.Web.Test.Integration\bin\**\Microsoft.Identity.Web.Test.Integration.dll'
searchFolder: '$(System.DefaultWorkingDirectory)'
rerunFailedTests: true
rerunMaxAttempts: '3'
runInParallel: false
codeCoverageEnabled: true
failOnMinTestsNotRun: false
minimumExpectedTests: '1'
runSettingsFile: 'build\CodeCoverage.runsettings'

- task: VSTest@2
displayName: 'Run E2E tests'
inputs:
testSelector: 'testAssemblies'
testAssemblyVer2: |
tests\E2E Tests\WebAppUiTests\bin\**\WebAppUiTests.dll
tests\E2E Tests\TokenAcquirerTests\bin\**\TokenAcquirerTests.dll
tests\E2E Tests\NET 7 tests\IntegrationTests\bin\**\IntegrationTests.dll
searchFolder: '$(System.DefaultWorkingDirectory)'
rerunFailedTests: true
rerunMaxAttempts: '3'
runInParallel: false
codeCoverageEnabled: true
failOnMinTestsNotRun: false
minimumExpectedTests: '1'
runSettingsFile: 'build\CodeCoverage.runsettings'

- task: PublishBuildArtifacts@1
displayName: 'Publish traces after test'
inputs:
PathtoPublish: '$(Build.SourcesDirectory)/tests/E2E Tests/PlaywrightTraces/'
ArtifactName: 'traces-after-tests-$(Build.BuildNumber)'
condition: failed()

# Copy all packages out to staging
- task: CopyFiles@2
displayName: 'Copy screenshots to staging directory'
inputs:
SourceFolder: '$(Build.SourcesDirectory)/tests/E2E Tests/'
Contents: '**/*screenshotFail.png'
TargetFolder: '$(Build.ArtifactStagingDirectory)\screenshots'
flattenFolders: true
condition: failed()

- task: PublishBuildArtifacts@1
displayName: 'Publish Screenshot after test'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)\screenshots'
ArtifactName: 'ScreenshotFail'
condition: failed()
8 changes: 4 additions & 4 deletions tests/DevApps/WebAppCallsMicrosoftGraph/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
"KeyVaultUrl": "https://webappsapistests.vault.azure.net",
"KeyVaultCertificateName": "Self-Signed-5-5-22"
}
// {
// "SourceType": "ClientSecret",
// "ClientSecret": ""
// }
//{
// "SourceType": "ClientSecret",
// "ClientSecret": ""
//}
]

// Id.Web v1.0 way of declaring client credentials
Expand Down
16 changes: 8 additions & 8 deletions tests/DevApps/ciam/myWebApi/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
},
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7082;http://localhost:5299",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7082;http://localhost:5299;",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
Expand Down
4 changes: 2 additions & 2 deletions tests/DevApps/ciam/myWebApi/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"AzureAd": {
"ClientId": "63e6d091-0e6d-4c8b-be67-d405e02ae3d8",
"ClientId": "634de702-3173-4a71-b336-a4fab786a479",
"Scopes": "access_as_user",
"Authority": "https://TrialTenantJmprieur.ciamlogin.com/"
"Authority": "https://MSIDLABCIAM6.ciamlogin.com"
},
"Logging": {
"LogLevel": {
Expand Down
2 changes: 1 addition & 1 deletion tests/DevApps/ciam/myWebApi/myWebApi.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>aspnet-myWebApi-17be892d-3bb3-4282-92e2-301b02c86ed6</UserSecretsId>
Expand Down
4 changes: 1 addition & 3 deletions tests/DevApps/ciam/myWebApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
// Licensed under the MIT License.

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
var initialScopes = builder.Configuration.GetSection("DownstreamApi:Scopes").Get<string[]>();

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
Expand Down
38 changes: 26 additions & 12 deletions tests/DevApps/ciam/myWebApp/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
{
"AzureAd": {
"ClientId": "4c8068d9-c00f-4908-afd7-44e4b263f66c",
"ClientSecret": "See user secrets",
"ClientCertificates": [],
"CallbackPath": "/signin-oidc",
"Authority": "https://TrialTenantJmprieur.ciamlogin.com/",
"Prompt": "login"
},
"DownstreamApi": {
"BaseUrl": "https://localhost:7082/weatherforecast",
"Scopes": [ "api://63e6d091-0e6d-4c8b-be67-d405e02ae3d8/.default" ]
},
"AzureAd": {
"ClientId": "b244c86f-ed88-45bf-abda-6b37aa482c79",
"ClientCertificates": [],
"CallbackPath": "/signin-oidc",
"Authority": "https://MSIDLABCIAM6.ciamlogin.com",
"Prompt": "login",
"ClientCredentials": [
//{
// "SourceType": "SignedAssertionFromManagedIdentity",
// "ManagedIdentityClientId": ""
//},
{
"SourceType": "KeyVault",
"KeyVaultUrl": "https://webappsapistests.vault.azure.net",
"KeyVaultCertificateName": "Self-Signed-5-5-22"
}
//{
// "SourceType": "ClientSecret",
// "ClientSecret": ""
//}
]
},
"DownstreamApi": {
"BaseUrl": "http://localhost:5299/WeatherForecast",
"Scopes": [ "api://634de702-3173-4a71-b336-a4fab786a479/.default" ]
},
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down
1 change: 1 addition & 0 deletions tests/DevApps/ciam/myWebApp/myWebApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>aspnet-myWebApp-23b8728b-775f-4796-8b63-e85cbaccc27c</UserSecretsId>
<UseWIP>true</UseWIP>
</PropertyGroup>

<ItemGroup Condition="'$(UseWIP)' == 'false' ">
Expand Down
29 changes: 27 additions & 2 deletions tests/E2E Tests/TokenAcquirerTests/TokenAcquirer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public class TokenAcquirer
"Self-Signed-5-5-22")
};

private static readonly CredentialDescription[] s_ciamClientCredentials = new[]
{
CertificateDescription.FromKeyVault(
"https://buildautomation.vault.azure.net",
"AzureADIdentityDivisionTestAgentCert")
};

public TokenAcquirer()
{
TokenAcquirerFactory.ResetDefaultInstance(); // Test only
Expand Down Expand Up @@ -169,6 +176,23 @@ public async Task AcquireToken_WithMicrosoftIdentityApplicationOptions_ClientCre
await CreateGraphClientAndAssert(tokenAcquirerFactory, services);
}

[IgnoreOnAzureDevopsFact(Skip = "https://github.com/AzureAD/microsoft-identity-web/issues/2732")]
//[Fact]
public async Task AcquireToken_WithMicrosoftIdentityApplicationOptions_ClientCredentialsCiamAsync()
{
TokenAcquirerFactory tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
IServiceCollection services = tokenAcquirerFactory.Services;

services.Configure<MicrosoftIdentityApplicationOptions>(s_optionName, option =>
{
option.Authority = "https://MSIDLABCIAM6.ciamlogin.com";
option.ClientId = "b244c86f-ed88-45bf-abda-6b37aa482c79";
option.ClientCredentials = s_clientCredentials;
});

await CreateGraphClientAndAssert(tokenAcquirerFactory, services);
}

[IgnoreOnAzureDevopsFact]
// [Fact]
public async Task AcquireToken_WithFactoryAndMicrosoftIdentityApplicationOptions_ClientCredentialsAsync()
Expand Down Expand Up @@ -374,6 +398,7 @@ private static async Task CreateGraphClientAndAssert(TokenAcquirerFactory tokenA
Assert.True(users!=null && users.Value!=null && users.Value.Count >0);
*/


// Alternatively to calling Microsoft Graph, you can get a token acquirer service
// and get a token, and use it in an SDK.
ITokenAcquirer tokenAcquirer = tokenAcquirerFactory.GetTokenAcquirer(s_optionName);
Expand All @@ -382,7 +407,7 @@ private static async Task CreateGraphClientAndAssert(TokenAcquirerFactory tokenA
}
}

public class AcquireTokenManagedIdentity
public class AcquireTokenManagedIdentity
{
[OnlyOnAzureDevopsFact]
//[Fact]
Expand All @@ -404,7 +429,7 @@ public async Task AcquireTokenWithManagedIdentity_UserAssigned()
Assert.False(string.IsNullOrEmpty(result));
}

private static AuthorizationHeaderProviderOptions GetAuthHeaderOptions_ManagedId(string baseUrl, string? userAssignedClientId=null)
private static AuthorizationHeaderProviderOptions GetAuthHeaderOptions_ManagedId(string baseUrl, string? userAssignedClientId = null)
{
ManagedIdentityOptions managedIdentityOptions = new()
{
Expand Down
48 changes: 39 additions & 9 deletions tests/E2E Tests/WebAppUiTests/TestingWebAppLocally.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Xunit;
using Xunit.Abstractions;
using System.Threading;
using System.Net;

namespace WebAppUiTests;

Expand All @@ -24,6 +25,7 @@ public class TestingWebAppLocally : IClassFixture<InstallPlaywrightBrowserFixtur
{
private const string UrlString = "https://localhost:5001/MicrosoftIdentity/Account/signin";
private const string TraceFileClassName = "TestingWebAppLocally";
private const string TraceFileClassNameCiam = "TestingWebAppLocallyCiam";
private readonly ITestOutputHelper _output;
private readonly string _devAppExecutable = Path.DirectorySeparatorChar.ToString() + "WebAppCallsMicrosoftGraph.exe";
private readonly string _devAppPath = "DevApps" + Path.DirectorySeparatorChar.ToString() + "WebAppCallsMicrosoftGraph";
Expand All @@ -38,20 +40,48 @@ public TestingWebAppLocally(ITestOutputHelper output)
[Fact]
[SupportedOSPlatform("windows")]
public async Task ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailPassword()
{
LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync().ConfigureAwait(false);

var clientEnvVars = new Dictionary<string, string>();

await ExecuteWebAppCallsGraphFlow(labResponse.User.Upn, labResponse.User.GetOrFetchPassword(), clientEnvVars, TraceFileClassName).ConfigureAwait(false);
}

[Theory]
[InlineData("https://MSIDLABCIAM6.ciamlogin.com")] // CIAM authority
[InlineData("https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/")] // CIAM CUD Authority
[SupportedOSPlatform("windows")]
public async Task ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailWithCiamPassword(string authority)
{
var clientEnvVars = new Dictionary<string, string>
{
{"AzureAd__ClientId", "b244c86f-ed88-45bf-abda-6b37aa482c79"},
{"AzureAd__Authority", authority},
{"AzureAd__TenantId", ""},
{"AzureAd__Domain", ""},
{"AzureAd__Instance", "" }
};

await ExecuteWebAppCallsGraphFlow("[email protected]", LabUserHelper.FetchUserPassword("msidlabciam6"), clientEnvVars, TraceFileClassNameCiam).ConfigureAwait(false);
}

private async Task ExecuteWebAppCallsGraphFlow(string upn, string credential, Dictionary<string, string>? clientEnvVars, string traceFileClassName)
{
// Arrange
Process? process = null;
const string TraceFileName = TraceFileClassName + "_ValidEmailPassword";
string TraceFileName = traceFileClassName + "_ValidEmailPassword";
using IPlaywright playwright = await Playwright.CreateAsync();
IBrowser browser = await playwright.Chromium.LaunchAsync(new() { Headless = true });
IBrowserContext context = await browser.NewContextAsync(new BrowserNewContextOptions { IgnoreHTTPSErrors = true });
await context.Tracing.StartAsync(new() { Screenshots = true, Snapshots = true, Sources = true });

try
{
process = UiTestHelpers.StartProcessLocally(_uiTestAssemblyLocation, _devAppPath, _devAppExecutable);
process = UiTestHelpers.StartProcessLocally(_uiTestAssemblyLocation, _devAppPath, _devAppExecutable, clientEnvVars);

if (!UiTestHelpers.ProcessIsAlive(process)) { Assert.Fail(TC.WebAppCrashedString); }
if (!UiTestHelpers.ProcessIsAlive(process))
{ Assert.Fail(TC.WebAppCrashedString); }

IPage page = await browser.NewPageAsync();

Expand All @@ -68,16 +98,15 @@ public async Task ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailPassword()
{
await Task.Delay(1000);
InitialConnectionRetryCount--;
if (InitialConnectionRetryCount == 0) { throw ex; }
if (InitialConnectionRetryCount == 0)
{ throw ex; }
}
}

LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync().ConfigureAwait(false);

// Act
Trace.WriteLine("Starting Playwright automation: web app sign-in & call Graph.");
string email = labResponse.User.Upn;
await UiTestHelpers.FirstLogin_MicrosoftIdFlow_ValidEmailPassword(page, email, labResponse.User.GetOrFetchPassword(), _output);
string email = upn;
await UiTestHelpers.FirstLogin_MicrosoftIdFlow_ValidEmailPassword(page, email, credential, _output);

// Assert
await Assertions.Expect(page.GetByText("Welcome")).ToBeVisibleAsync(_assertVisibleOptions);
Expand All @@ -91,7 +120,8 @@ public async Task ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailPassword()
{
// Cleanup the web app process and any child processes
Queue<Process> processes = new();
if (process != null) { processes.Enqueue(process); }
if (process != null)
{ processes.Enqueue(process); }
UiTestHelpers.KillProcessTrees(processes);

Check warning on line 125 in tests/E2E Tests/WebAppUiTests/TestingWebAppLocally.cs

View workflow job for this annotation

GitHub Actions / Analyse

This call site is reachable on all platforms. 'UiTestHelpers.KillProcessTrees(Queue<Process>)' is only supported on: 'windows'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 125 in tests/E2E Tests/WebAppUiTests/TestingWebAppLocally.cs

View workflow job for this annotation

GitHub Actions / IdWeb GitHub Action Test

This call site is reachable on all platforms. 'UiTestHelpers.KillProcessTrees(Queue<Process>)' is only supported on: 'windows'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

// Cleanup Playwright
Expand Down
Loading

0 comments on commit 54afc21

Please sign in to comment.