diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/api/Azure.Developer.MicrosoftPlaywrightTesting.NUnit.netstandard2.0.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/api/Azure.Developer.MicrosoftPlaywrightTesting.NUnit.netstandard2.0.cs index 52f0189d991e8..64cd0a6c655dc 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/api/Azure.Developer.MicrosoftPlaywrightTesting.NUnit.netstandard2.0.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/api/Azure.Developer.MicrosoftPlaywrightTesting.NUnit.netstandard2.0.cs @@ -3,7 +3,7 @@ namespace Azure.Developer.MicrosoftPlaywrightTesting.NUnit [NUnit.Framework.SetUpFixtureAttribute] public partial class PlaywrightServiceNUnit : Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.PlaywrightService { - public PlaywrightServiceNUnit(Azure.Core.TokenCredential? credential = null) : base (default(Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.PlaywrightServiceOptions), default(Azure.Core.TokenCredential)) { } + public PlaywrightServiceNUnit(Azure.Core.TokenCredential? credential = null) : base (default(Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.PlaywrightServiceOptions), default(Azure.Core.TokenCredential), default(Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface.IFrameworkLogger)) { } public static Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.PlaywrightServiceOptions playwrightServiceOptions { get { throw null; } } [NUnit.Framework.OneTimeSetUpAttribute] public System.Threading.Tasks.Task SetupAsync() { throw null; } diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/src/NUnitFrameworkLogger.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/src/NUnitFrameworkLogger.cs new file mode 100644 index 0000000000000..806da17197119 --- /dev/null +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/src/NUnitFrameworkLogger.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface; +using NUnit.Framework; + +namespace Azure.Developer.MicrosoftPlaywrightTesting.NUnit +{ + internal class NUnitFrameworkLogger : IFrameworkLogger + { + public void Debug(string message) + { + TestContext.WriteLine($"[MPT-NUnit]: {message}"); + } + + public void Error(string message) + { + TestContext.Error.WriteLine($"[MPT-NUnit]: {message}"); + } + + public void Info(string message) + { + TestContext.Progress.WriteLine($"[MPT-NUnit]: {message}"); + } + + public void Warning(string message) + { + TestContext.Progress.WriteLine($"[MPT-NUnit]: {message}"); + } + } +} diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/src/PlaywrightServiceNUnit.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/src/PlaywrightServiceNUnit.cs index ddcee9697806b..547be80ea7777 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/src/PlaywrightServiceNUnit.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.NUnit/src/PlaywrightServiceNUnit.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System; using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger; +using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface; namespace Azure.Developer.MicrosoftPlaywrightTesting.NUnit; @@ -16,12 +17,13 @@ namespace Azure.Developer.MicrosoftPlaywrightTesting.NUnit; [SetUpFixture] public class PlaywrightServiceNUnit : PlaywrightService { + private static NUnitFrameworkLogger nunitFrameworkLogger { get; } = new(); /// /// Initializes a new instance of the class. /// /// The azure token credential to use for authentication. public PlaywrightServiceNUnit(TokenCredential? credential = null) - : base(playwrightServiceOptions, credential: credential) + : base(playwrightServiceOptions, credential: credential, frameworkLogger: nunitFrameworkLogger) { } @@ -47,7 +49,7 @@ public async Task SetupAsync() { if (!UseCloudHostedBrowsers) return; - TestContext.Progress.WriteLine("\nRunning tests using Microsoft Playwright Testing service.\n"); + nunitFrameworkLogger.Info("\nRunning tests using Microsoft Playwright Testing service.\n"); await InitializeAsync().ConfigureAwait(false); } diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/api/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.netstandard2.0.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/api/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.netstandard2.0.cs index 92f645d91b4cf..e9db853b9e0d4 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/api/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.netstandard2.0.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/api/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.netstandard2.0.cs @@ -8,8 +8,8 @@ public ConnectOptions() { } } public partial class PlaywrightService { - public PlaywrightService(Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.PlaywrightServiceOptions playwrightServiceOptions, Azure.Core.TokenCredential? credential = null) { } - public PlaywrightService(System.Runtime.InteropServices.OSPlatform? os = default(System.Runtime.InteropServices.OSPlatform?), string? runId = null, string? exposeNetwork = null, string? serviceAuth = null, bool? useCloudHostedBrowsers = default(bool?), Azure.Core.TokenCredential? credential = null) { } + public PlaywrightService(Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.PlaywrightServiceOptions playwrightServiceOptions, Azure.Core.TokenCredential? credential = null, Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface.IFrameworkLogger? frameworkLogger = null) { } + public PlaywrightService(System.Runtime.InteropServices.OSPlatform? os = default(System.Runtime.InteropServices.OSPlatform?), string? runId = null, string? exposeNetwork = null, string? serviceAuth = null, bool? useCloudHostedBrowsers = default(bool?), Azure.Core.TokenCredential? credential = null, Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface.IFrameworkLogger? frameworkLogger = null) { } public string? ExposeNetwork { get { throw null; } set { } } public System.Runtime.InteropServices.OSPlatform? Os { get { throw null; } set { } } public System.Threading.Timer? RotationTimer { get { throw null; } set { } } @@ -67,3 +67,13 @@ public enum ServiceVersion } } } +namespace Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface +{ + public partial interface IFrameworkLogger + { + void Debug(string message); + void Error(string message); + void Info(string message); + void Warning(string message); + } +} diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/EntraLifecycle.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/EntraLifecycle.cs index a4f33add343be..f32c73990e2fa 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/EntraLifecycle.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/EntraLifecycle.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Azure.Core; +using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface; using Azure.Identity; using Microsoft.IdentityModel.JsonWebTokens; @@ -16,9 +17,11 @@ internal class EntraLifecycle internal long? _entraIdAccessTokenExpiry; private readonly TokenCredential _tokenCredential; private readonly JsonWebTokenHandler _jsonWebTokenHandler; + private readonly IFrameworkLogger? _frameworkLogger; - public EntraLifecycle(TokenCredential? tokenCredential = null, JsonWebTokenHandler? jsonWebTokenHandler = null) + public EntraLifecycle(TokenCredential? tokenCredential = null, JsonWebTokenHandler? jsonWebTokenHandler = null, IFrameworkLogger? frameworkLogger = null) { + _frameworkLogger = frameworkLogger; _tokenCredential = tokenCredential ?? new DefaultAzureCredential(); _jsonWebTokenHandler = jsonWebTokenHandler ?? new JsonWebTokenHandler(); SetEntraIdAccessTokenFromEnvironment(); @@ -37,7 +40,7 @@ internal async Task FetchEntraIdAccessTokenAsync(CancellationToken cancellationT } catch (Exception ex) { - Console.Error.WriteLine(ex); + _frameworkLogger?.Error(ex.ToString()); throw new Exception(Constants.s_no_auth_error); } } diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Implementation/VSTestFrameworkLogger.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Implementation/VSTestFrameworkLogger.cs new file mode 100644 index 0000000000000..4a0d364643288 --- /dev/null +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Implementation/VSTestFrameworkLogger.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface; + +namespace Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Implementation +{ + internal class VSTestFrameworkLogger : IFrameworkLogger + { + private readonly ILogger _logger; + public VSTestFrameworkLogger(ILogger? logger = null) + { + _logger = logger ?? new Logger(); + } + + public void Debug(string message) + { + _logger.Debug(message); + } + + public void Error(string message) + { + _logger.Error(message); + } + + public void Info(string message) + { + _logger.Info(message); + } + + public void Warning(string message) + { + _logger.Warning(message); + } + } +} diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Interface/IFrameworkLogger.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Interface/IFrameworkLogger.cs new file mode 100644 index 0000000000000..63c41c5d3b753 --- /dev/null +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Interface/IFrameworkLogger.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface +{ + /// + /// Sets up logging for the TestLogger package. + /// + public interface IFrameworkLogger + { + /// + /// Log informational message. + /// + /// + void Info(string message); + /// + /// Log debug messages. + /// + /// + void Debug(string message); + /// + /// Log warnming messages. + /// + /// + void Warning(string message); + /// + /// Log error messages. + /// + /// + void Error(string message); + } +} diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightReporter.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightReporter.cs index ac187e9e32a51..63c6168a382cb 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightReporter.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightReporter.cs @@ -121,10 +121,19 @@ internal void InitializePlaywrightReporter(string xmlSettings) return; } // setup entra rotation handlers - _playwrightService = new PlaywrightService(null, playwrightServiceSettings!.RunId, null, playwrightServiceSettings.ServiceAuth, null, entraLifecycle: null, jsonWebTokenHandler: _jsonWebTokenHandler, credential: playwrightServiceSettings.AzureTokenCredential); + IFrameworkLogger frameworkLogger = new VSTestFrameworkLogger(_logger); + try + { + _playwrightService = new PlaywrightService(null, playwrightServiceSettings!.RunId, null, playwrightServiceSettings.ServiceAuth, null, entraLifecycle: null, jsonWebTokenHandler: _jsonWebTokenHandler, credential: playwrightServiceSettings.AzureTokenCredential, frameworkLogger: frameworkLogger); #pragma warning disable AZC0102 // Do not use GetAwaiter().GetResult(). Use the TaskExtensions.EnsureCompleted() extension method instead. - _playwrightService.InitializeAsync().GetAwaiter().GetResult(); + _playwrightService.InitializeAsync().GetAwaiter().GetResult(); #pragma warning restore AZC0102 // Do not use GetAwaiter().GetResult(). Use the TaskExtensions.EnsureCompleted() extension method instead. + } + catch (Exception ex) + { + // We have checks for access token and base url in the next block, so we can ignore the exception here. + _logger.Error("Failed to initialize PlaywrightService: " + ex); + } var cloudRunId = _environment.GetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceRunId); string baseUrl = _environment.GetEnvironmentVariable(ReporterConstants.s_pLAYWRIGHT_SERVICE_REPORTING_URL); diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightService.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightService.cs index d87082998823e..ed65c0b531aa3 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightService.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/PlaywrightService.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using Azure.Core; +using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface; using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Model; using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Utility; using Microsoft.IdentityModel.JsonWebTokens; @@ -136,19 +137,22 @@ public string? ExposeNetwork private readonly EntraLifecycle? _entraLifecycle; private readonly JsonWebTokenHandler? _jsonWebTokenHandler; + private IFrameworkLogger? _frameworkLogger; /// /// Initializes a new instance of the class. /// /// /// - public PlaywrightService(PlaywrightServiceOptions playwrightServiceOptions, TokenCredential? credential = null) : this( + /// + public PlaywrightService(PlaywrightServiceOptions playwrightServiceOptions, TokenCredential? credential = null, IFrameworkLogger? frameworkLogger = null) : this( os: playwrightServiceOptions.Os, runId: playwrightServiceOptions.RunId, exposeNetwork: playwrightServiceOptions.ExposeNetwork, serviceAuth: playwrightServiceOptions.ServiceAuth, useCloudHostedBrowsers: playwrightServiceOptions.UseCloudHostedBrowsers, - credential: credential ?? playwrightServiceOptions.AzureTokenCredential + credential: credential ?? playwrightServiceOptions.AzureTokenCredential, + frameworkLogger: frameworkLogger ) { // No-op @@ -163,21 +167,25 @@ public PlaywrightService(PlaywrightServiceOptions playwrightServiceOptions, Toke /// The service authentication mechanism. /// Whether to use cloud-hosted browsers. /// The token credential. - public PlaywrightService(OSPlatform? os = null, string? runId = null, string? exposeNetwork = null, string? serviceAuth = null, bool? useCloudHostedBrowsers = null, TokenCredential? credential = null) + /// Logger + public PlaywrightService(OSPlatform? os = null, string? runId = null, string? exposeNetwork = null, string? serviceAuth = null, bool? useCloudHostedBrowsers = null, TokenCredential? credential = null, IFrameworkLogger? frameworkLogger = null) { if (string.IsNullOrEmpty(ServiceEndpoint)) return; - _entraLifecycle = new EntraLifecycle(tokenCredential: credential); + _frameworkLogger = frameworkLogger; + _entraLifecycle = new EntraLifecycle(tokenCredential: credential, frameworkLogger: _frameworkLogger); _jsonWebTokenHandler = new JsonWebTokenHandler(); InitializePlaywrightServiceEnvironmentVariables(GetServiceCompatibleOs(os), runId, exposeNetwork, serviceAuth, useCloudHostedBrowsers); } - internal PlaywrightService(OSPlatform? os = null, string? runId = null, string? exposeNetwork = null, string? serviceAuth = null, bool? useCloudHostedBrowsers = null, EntraLifecycle? entraLifecycle = null, JsonWebTokenHandler? jsonWebTokenHandler = null, TokenCredential? credential = null) + internal PlaywrightService(OSPlatform? os = null, string? runId = null, string? exposeNetwork = null, string? serviceAuth = null, bool? useCloudHostedBrowsers = null, EntraLifecycle? entraLifecycle = null, JsonWebTokenHandler? jsonWebTokenHandler = null, TokenCredential? credential = null, IFrameworkLogger? frameworkLogger = null) { if (string.IsNullOrEmpty(ServiceEndpoint)) return; + _frameworkLogger = frameworkLogger; _jsonWebTokenHandler = jsonWebTokenHandler ?? new JsonWebTokenHandler(); - _entraLifecycle = entraLifecycle ?? new EntraLifecycle(credential, _jsonWebTokenHandler); + _entraLifecycle = entraLifecycle ?? new EntraLifecycle(credential, _jsonWebTokenHandler, _frameworkLogger); + _frameworkLogger = frameworkLogger; InitializePlaywrightServiceEnvironmentVariables(GetServiceCompatibleOs(os), runId, exposeNetwork, serviceAuth, useCloudHostedBrowsers); } @@ -211,6 +219,7 @@ internal PlaywrightService(OSPlatform? os = null, string? runId = null, string? } if (string.IsNullOrEmpty(GetAuthToken())) { + _frameworkLogger?.Error("Access token not found when trying to call GetConnectOptionsAsync."); throw new Exception(Constants.s_no_auth_error); } @@ -236,20 +245,26 @@ internal PlaywrightService(OSPlatform? os = null, string? runId = null, string? public async Task InitializeAsync(CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(ServiceEndpoint)) + { + _frameworkLogger?.Info("Exiting initialization as service endpoint is not set."); return; + } if (!UseCloudHostedBrowsers) { // Since playwright-dotnet checks PLAYWRIGHT_SERVICE_ACCESS_TOKEN and PLAYWRIGHT_SERVICE_URL to be set, remove PLAYWRIGHT_SERVICE_URL so that tests are run locally. // If customers use GetConnectOptionsAsync, after setting disableScalableExecution, an error will be thrown. + _frameworkLogger?.Info("Disabling scalable execution since UseCloudHostedBrowsers is set to false."); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceUri, null); return; } // If default auth mechanism is Access token and token is available in the environment variable, no need to setup rotation handler if (ServiceAuth == ServiceAuthType.AccessToken) { + _frameworkLogger?.Info("Auth mechanism is Access Token."); ValidateMptPAT(); return; } + _frameworkLogger?.Info("Auth mechanism is Entra Id."); await _entraLifecycle!.FetchEntraIdAccessTokenAsync(cancellationToken).ConfigureAwait(false); RotationTimer = new Timer(RotationHandlerAsync, null, TimeSpan.FromMinutes(Constants.s_entra_access_token_rotation_interval_period_in_minutes), TimeSpan.FromMinutes(Constants.s_entra_access_token_rotation_interval_period_in_minutes)); } @@ -259,6 +274,7 @@ public async Task InitializeAsync(CancellationToken cancellationToken = default) /// public void Cleanup() { + _frameworkLogger?.Info("Cleaning up Playwright service resources."); RotationTimer?.Dispose(); } @@ -266,6 +282,7 @@ internal async void RotationHandlerAsync(object? _) { if (_entraLifecycle!.DoesEntraIdAccessTokenRequireRotation()) { + _frameworkLogger?.Info("Rotating Entra Id access token."); await _entraLifecycle.FetchEntraIdAccessTokenAsync().ConfigureAwait(false); } } @@ -360,20 +377,28 @@ internal static void SetReportingUrlAndWorkspaceId() private void ValidateMptPAT() { - string authToken = GetAuthToken()!; - if (string.IsNullOrEmpty(authToken)) - throw new Exception(Constants.s_no_auth_error); - JsonWebToken jsonWebToken = _jsonWebTokenHandler!.ReadJsonWebToken(authToken) ?? throw new Exception(Constants.s_invalid_mpt_pat_error); - var tokenWorkspaceId = jsonWebToken.Claims.FirstOrDefault(c => c.Type == "aid")?.Value; - Match match = Regex.Match(ServiceEndpoint, @"wss://(?[\w-]+)\.api\.(?playwright(?:-test|-int)?\.io|playwright\.microsoft\.com)/accounts/(?[\w-]+)/"); - if (!match.Success) - throw new Exception(Constants.s_invalid_service_endpoint_error_message); - var serviceEndpointWorkspaceId = match.Groups["workspaceId"].Value; - if (tokenWorkspaceId != serviceEndpointWorkspaceId) - throw new Exception(Constants.s_workspace_mismatch_error); - var expiry = (long)(jsonWebToken.ValidTo - new DateTime(1970, 1, 1)).TotalSeconds; - if (expiry <= DateTimeOffset.UtcNow.ToUnixTimeSeconds()) - throw new Exception(Constants.s_expired_mpt_pat_error); + try + { + string authToken = GetAuthToken()!; + if (string.IsNullOrEmpty(authToken)) + throw new Exception(Constants.s_no_auth_error); + JsonWebToken jsonWebToken = _jsonWebTokenHandler!.ReadJsonWebToken(authToken) ?? throw new Exception(Constants.s_invalid_mpt_pat_error); + var tokenWorkspaceId = jsonWebToken.Claims.FirstOrDefault(c => c.Type == "aid")?.Value; + Match match = Regex.Match(ServiceEndpoint, @"wss://(?[\w-]+)\.api\.(?playwright(?:-test|-int)?\.io|playwright\.microsoft\.com)/accounts/(?[\w-]+)/"); + if (!match.Success) + throw new Exception(Constants.s_invalid_service_endpoint_error_message); + var serviceEndpointWorkspaceId = match.Groups["workspaceId"].Value; + if (tokenWorkspaceId != serviceEndpointWorkspaceId) + throw new Exception(Constants.s_workspace_mismatch_error); + var expiry = (long)(jsonWebToken.ValidTo - new DateTime(1970, 1, 1)).TotalSeconds; + if (expiry <= DateTimeOffset.UtcNow.ToUnixTimeSeconds()) + throw new Exception(Constants.s_expired_mpt_pat_error); + } + catch (Exception ex) + { + _frameworkLogger?.Error(ex.ToString()); + throw; + } } internal static string? GetServiceCompatibleOs(OSPlatform? oSPlatform) diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/PlaywrightServiceTests.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/PlaywrightServiceTests.cs index 6f6cfbf88916e..0e610789b60a9 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/PlaywrightServiceTests.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/PlaywrightServiceTests.cs @@ -198,7 +198,7 @@ public void Initialize_WhenServiceEnpointIsNotSet_NoOP() Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceUri, null); var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object); service.InitializeAsync().Wait(); defaultAzureCredentialMock.Verify(x => x.GetTokenAsync(It.IsAny(), It.IsAny()), Times.Never); @@ -213,7 +213,7 @@ public void Initialize_WhenDefaultAuthIsEntraIdAccessTokenAndAccessTokenEnvironm defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AccessToken(token, DateTimeOffset.UtcNow.AddMinutes(10))); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "access_token"); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object); service.InitializeAsync().Wait(); @@ -252,7 +252,7 @@ public void Initialize_WhenDefaultAuthIsEntraIdAccessTokenAndAccessTokenEnvironm defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AccessToken(token, DateTimeOffset.UtcNow.AddMinutes(10))); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "access_token"); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, useCloudHostedBrowsers: false); @@ -275,7 +275,7 @@ public void Initialize_WhenDefaultAuthIsEntraIdAccessTokenAndAccessTokenEnvironm defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AccessToken(token, DateTimeOffset.UtcNow.AddMinutes(10))); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object); service.InitializeAsync().Wait(); defaultAzureCredentialMock.Verify(x => x.GetTokenAsync(It.IsAny(), It.IsAny()), Times.Once); @@ -292,7 +292,7 @@ public void Initialize_WhenFetchesEntraIdAccessToken_SetsUpRotationHandler() defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AccessToken(token, DateTimeOffset.UtcNow.AddMinutes(10))); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object); service.InitializeAsync().Wait(); defaultAzureCredentialMock.Verify(x => x.GetTokenAsync(It.IsAny(), It.IsAny()), Times.Once); @@ -309,7 +309,7 @@ public void Initialize_WhenFailsToFetchEntraIdAccessToken_ThrowsException() defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception()); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object); Exception? ex = Assert.ThrowsAsync(async () => await service.InitializeAsync()); Assert.That(ex!.Message, Is.EqualTo(Constants.s_no_auth_error)); @@ -327,7 +327,7 @@ public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsSet_ThrowsException defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception()); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler()); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler(), null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler()); Exception? ex = Assert.ThrowsAsync(async () => await service.InitializeAsync()); Assert.That(ex!.Message, Is.EqualTo(Constants.s_no_auth_error)); @@ -344,7 +344,7 @@ public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsNotSet_ThrowsExcept defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception()); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler()); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler(), null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler()); Exception? ex = Assert.ThrowsAsync(async () => await service.InitializeAsync()); Assert.That(ex!.Message, Is.EqualTo(Constants.s_no_auth_error)); @@ -359,7 +359,7 @@ public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsNotValid_ThrowsErro defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception()); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler()); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler(), null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler()); Assert.That(() => service.InitializeAsync().Wait(), Throws.Exception); } @@ -379,7 +379,7 @@ public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatTokenParsingReturnsNu defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception()); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler()); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler(), null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: jsonWebTokenHandlerMock.Object); Assert.That(() => service.InitializeAsync().Wait(), Throws.Exception); } @@ -396,7 +396,7 @@ public void Initialize_WhenEntraIdAccessTokenFailsAndMptPatIsExpired_ThrowsError defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception()); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler()); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler(), null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler()); Assert.That(() => service.InitializeAsync().Wait(), Throws.Exception); @@ -419,7 +419,7 @@ public void Initialize_WhenDefaultAuthIsMptPATAndPATIsSet_DoesNotSetUpRotationHa }; Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceUri, $"{testRubric["url"]}"); var defaultAzureCredentialMock = new Mock(); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler()); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, new JsonWebTokenHandler(), null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object, jsonWebTokenHandler: new JsonWebTokenHandler(), serviceAuth: ServiceAuthType.AccessToken); service.InitializeAsync().Wait(); Assert.That(service.RotationTimer, Is.Null); @@ -434,7 +434,7 @@ public void RotationHandler_WhenEntraIdAccessTokenRequiresRotation_FetchesEntraI defaultAzureCredentialMock .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AccessToken(token, DateTimeOffset.UtcNow.AddMinutes(5))); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object); service.RotationHandlerAsync(null); @@ -446,7 +446,7 @@ public void RotationHandler_WhenEntraIdAccessTokenDoesNotRequireRotation_NoOp() { var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object._entraIdAccessTokenExpiry = (int)DateTimeOffset.UtcNow.AddMinutes(22).ToUnixTimeSeconds(); PlaywrightService service = new(entraLifecycle: entraLifecycleMock.Object); @@ -482,7 +482,7 @@ public async Task GetConnectOptionsAsync_WhenServiceEndpointIsSet_ReturnsConnect var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "valid_token"); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object._entraIdAccessTokenExpiry = (int)DateTimeOffset.UtcNow.AddMinutes(22).ToUnixTimeSeconds(); var runId = "run-id"; @@ -507,7 +507,7 @@ public async Task GetConnectOptionsAsync_WhenTokenRequiresRotation_RotatesEntraT .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AccessToken("valid_token", DateTimeOffset.UtcNow.AddMinutes(5))); var jsonWebTokenHandlerMock = new Mock(); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object._entraIdAccessTokenExpiry = (int)DateTimeOffset.UtcNow.AddMinutes(-1).ToUnixTimeSeconds(); @@ -525,7 +525,7 @@ public async Task GetConnectOptionsAsync_WhenTokenDoesNotRequireRotation_DoesNot .Setup(x => x.GetTokenAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AccessToken("valid_token", DateTimeOffset.UtcNow.AddMinutes(5))); var jsonWebTokenHandlerMock = new Mock(); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object._entraIdAccessTokenExpiry = (int)DateTimeOffset.UtcNow.AddMinutes(22).ToUnixTimeSeconds(); @@ -540,7 +540,7 @@ public async Task GetConnectOptionsAsync_WhenDefaultParametersAreProvided_SetsSe var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "valid_token"); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object ._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object @@ -563,7 +563,7 @@ public async Task GetConnectOptionsAsync_WhenDefaultParametersAreNotProvided_Set var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "valid_token"); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object ._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object @@ -586,7 +586,7 @@ public async Task GetConnectOptionsAsync_WhenParametersAreSetInTheObject_UsesObj var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "valid_token"); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object ._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object @@ -612,7 +612,7 @@ public async Task GetConnectOptionsAsync_WhenParametersAreSetInTheObjectButAlsoP var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "valid_token"); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object ._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object @@ -635,7 +635,7 @@ public async Task GetConnectOptionsAsync_WhenParametersAreSetViaEnvironmentButAl var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "valid_token"); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object ._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object @@ -661,7 +661,7 @@ public async Task GetConnectOptionsAsync_WhenServiceParametersAreSetViaEnvironme var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, "valid_token"); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); entraLifecycleMock.Object ._entraIdAccessToken = "valid_token"; entraLifecycleMock.Object @@ -686,7 +686,7 @@ public void GetConnectOptionsAsync_WhenNoAuthTokenIsSet_ThrowsException() { var defaultAzureCredentialMock = new Mock(); var jsonWebTokenHandlerMock = new Mock(); - var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object); + var entraLifecycleMock = new Mock(defaultAzureCredentialMock.Object, jsonWebTokenHandlerMock.Object, null); var service = new PlaywrightService(entraLifecycle: entraLifecycleMock.Object); Environment.SetEnvironmentVariable(ServiceEnvironmentVariable.PlaywrightServiceAccessToken, null);