diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Implementation/BlobService.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Implementation/BlobService.cs
new file mode 100644
index 000000000000..6b436c45f9ff
--- /dev/null
+++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Implementation/BlobService.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface;
+using Azure.Storage.Blobs;
+using System.Threading.Tasks;
+using System.IO;
+
+namespace Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Implementation
+{
+ internal class BlobService:IBlobService
+ {
+ private readonly ILogger _logger;
+
+ public BlobService(ILogger? logger)
+ {
+ _logger = logger ?? new Logger();
+ }
+
+ public async Task UploadBufferAsync(string uri, string buffer, string fileRelativePath)
+ {
+ try
+ {
+ string cloudFilePath = GetCloudFilePath(uri, fileRelativePath);
+ BlobClient blobClient = new(new Uri(cloudFilePath));
+ byte[] bufferBytes = Encoding.UTF8.GetBytes(buffer);
+ await blobClient.UploadAsync(new BinaryData(bufferBytes), overwrite: true).ConfigureAwait(false);
+ _logger.Info($"Uploaded buffer to {fileRelativePath}");
+ }
+ catch (Exception ex)
+ {
+ _logger.Error($"Failed to upload buffer: {ex}");
+ }
+ }
+ public void UploadBlobFile(string uri, string fileRelativePath, string filePath)
+ {
+ string cloudFilePath = GetCloudFilePath(uri, fileRelativePath);
+ BlobClient blobClient = new(new Uri(cloudFilePath));
+ blobClient.Upload(filePath, overwrite: true);
+ _logger.Info($"Uploaded file {filePath} to {fileRelativePath}");
+ }
+ public string GetCloudFilePath(string uri, string fileRelativePath)
+ {
+ string[] parts = uri.Split(new string[] { ReporterConstants.s_sASUriSeparator }, StringSplitOptions.None);
+ string containerUri = parts[0];
+ string sasToken = parts.Length > 1 ? parts[1] : string.Empty;
+
+ return $"{containerUri}/{fileRelativePath}?{sasToken}";
+ }
+ public string? GetCloudFileName(string filePath, string testExecutionId)
+ {
+ var fileName = Path.GetFileName(filePath);
+ if (fileName == null)
+ {
+ return null;
+ }
+ return $"{testExecutionId}/{fileName}"; // TODO check if we need to add {Guid.NewGuid()} for file with same name
+ }
+ }
+}
diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Interface/IBlobService.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Interface/IBlobService.cs
new file mode 100644
index 000000000000..587903f1418f
--- /dev/null
+++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Interface/IBlobService.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+namespace Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface
+{
+ internal interface IBlobService
+ {
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// A representing the result of the asynchronous operation.
+ Task UploadBufferAsync(string uri, string buffer, string fileRelativePath);
+ string GetCloudFilePath(string uri, string fileRelativePath);
+ void UploadBlobFile(string uri, string fileRelativePath, string filePath);
+ public string? GetCloudFileName(string filePath, string testExecutionId);
+ }
+}
diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs
index d90affe987a6..6bf1c260b2a1 100644
--- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs
+++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs
@@ -7,6 +7,7 @@
using System.IO;
using System.Text;
using System.Text.Json;
+using System.Threading.Tasks;
using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Implementation;
using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface;
using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Model;
@@ -28,6 +29,7 @@ internal class TestProcessor : ITestProcessor
private readonly IConsoleWriter _consoleWriter;
private readonly CIInfo _cIInfo;
private readonly CloudRunMetadata _cloudRunMetadata;
+ private readonly IBlobService _blobService;
// Test Metadata
internal int TotalTestCount { get; set; } = 0;
@@ -42,7 +44,7 @@ internal class TestProcessor : ITestProcessor
internal TestRunShardDto? _testRunShard;
internal TestResultsUri? _testResultsSasUri;
- public TestProcessor(CloudRunMetadata cloudRunMetadata, CIInfo cIInfo, ILogger? logger = null, IDataProcessor? dataProcessor = null, ICloudRunErrorParser? cloudRunErrorParser = null, IServiceClient? serviceClient = null, IConsoleWriter? consoleWriter = null)
+ public TestProcessor(CloudRunMetadata cloudRunMetadata, CIInfo cIInfo, ILogger? logger = null, IDataProcessor? dataProcessor = null, ICloudRunErrorParser? cloudRunErrorParser = null, IServiceClient? serviceClient = null, IConsoleWriter? consoleWriter = null, IBlobService? blobService = null)
{
_cloudRunMetadata = cloudRunMetadata;
_cIInfo = cIInfo;
@@ -51,6 +53,7 @@ public TestProcessor(CloudRunMetadata cloudRunMetadata, CIInfo cIInfo, ILogger?
_cloudRunErrorParser = cloudRunErrorParser ?? new CloudRunErrorParser(_logger);
_serviceClient = serviceClient ?? new ServiceClient(_cloudRunMetadata, _cloudRunErrorParser);
_consoleWriter = consoleWriter ?? new ConsoleWriter();
+ _blobService = blobService ?? new BlobService(_logger);
}
public void TestRunStartHandler(object? sender, TestRunStartEventArgs e)
@@ -171,7 +174,7 @@ public void TestRunCompleteHandler(object? sender, TestRunCompleteEventArgs e)
// Upload rawResult to blob storage using sasUri
var rawTestResultJson = JsonSerializer.Serialize(rawResult);
var filePath = $"{testResult.TestExecutionId}/rawTestResult.json";
- UploadBuffer(sasUri!.Uri!, rawTestResultJson, filePath);
+ _blobService.UploadBufferAsync(sasUri!.Uri!, rawTestResultJson, filePath);
}
else
{
@@ -215,9 +218,9 @@ private void UploadAttachment(TestResultEventArgs e, string testExecutionId)
{
// get file size
var fileSize = new FileInfo(filePath).Length;
- var cloudFileName = ReporterUtils.GetCloudFileName(filePath, testExecutionId);
+ var cloudFileName = _blobService.GetCloudFileName(filePath, testExecutionId);
if (cloudFileName != null) {
- UploadBlobFile(_testResultsSasUri!.Uri!, cloudFileName, filePath);
+ _blobService.UploadBlobFile(_testResultsSasUri!.Uri!, cloudFileName, filePath);
TotalArtifactCount++;
TotalArtifactSizeInBytes = TotalArtifactSizeInBytes + (int)fileSize;
}
@@ -237,7 +240,7 @@ private void UploadAttachment(TestResultEventArgs e, string testExecutionId)
}
}
- private TestResultsUri? CheckAndRenewSasUri()
+ internal TestResultsUri? CheckAndRenewSasUri()
{
var reporterUtils = new ReporterUtils();
if (_testResultsSasUri == null || !reporterUtils.IsTimeGreaterThanCurrentPlus10Minutes(_testResultsSasUri.Uri))
@@ -270,31 +273,6 @@ private void EndTestRun(TestRunCompleteEventArgs e)
}
_cloudRunErrorParser.DisplayMessages();
}
- private static string GetCloudFilePath(string uri, string fileRelativePath)
- {
- string[] parts = uri.Split(new string[] { ReporterConstants.s_sASUriSeparator }, StringSplitOptions.None);
- string containerUri = parts[0];
- string sasToken = parts.Length > 1 ? parts[1] : string.Empty;
-
- return $"{containerUri}/{fileRelativePath}?{sasToken}";
- }
- private void UploadBuffer(string uri, string buffer, string fileRelativePath)
- {
- string cloudFilePath = GetCloudFilePath(uri, fileRelativePath);
- BlobClient blobClient = new(new Uri(cloudFilePath));
- byte[] bufferBytes = Encoding.UTF8.GetBytes(buffer);
- blobClient.Upload(new BinaryData(bufferBytes), overwrite: true);
- _logger.Info($"Uploaded buffer to {fileRelativePath}");
- }
-
- private void UploadBlobFile(string uri, string fileRelativePath, string filePath)
- {
- string cloudFilePath = GetCloudFilePath(uri, fileRelativePath);
- BlobClient blobClient = new(new Uri(cloudFilePath));
- // Upload filePath to Blob
- blobClient.Upload(filePath, overwrite: true);
- _logger.Info($"Uploaded file {filePath} to {fileRelativePath}");
- }
private TestRunShardDto GetTestRunEndShard(TestRunCompleteEventArgs e)
{
diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs
index d17aa019b488..40390871d682 100644
--- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs
+++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs
@@ -115,15 +115,6 @@ internal static string GetCurrentOS()
else
return OSConstants.s_wINDOWS;
}
- internal static string? GetCloudFileName(string filePath, string testExecutionId)
- {
- var fileName = Path.GetFileName(filePath);
- if (fileName == null)
- {
- return null;
- }
- return $"{testExecutionId}/{fileName}"; // TODO check if we need to add {Guid.NewGuid()} for file with same name
- }
internal TokenDetails ParseWorkspaceIdFromAccessToken(JsonWebTokenHandler? jsonWebTokenHandler, string? accessToken)
{
diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Implementation/BlobServiceTests.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Implementation/BlobServiceTests.cs
new file mode 100644
index 000000000000..eaa6006b8459
--- /dev/null
+++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Implementation/BlobServiceTests.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Text;
+using System.Threading.Tasks;
+using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Implementation;
+using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface;
+using Moq;
+using NUnit.Framework;
+
+namespace Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Tests.Implementation
+{
+ [TestFixture]
+ [Parallelizable(ParallelScope.Self)]
+ public class BlobServiceTests
+ {
+ private Mock? _loggerMock;
+ private BlobService? _blobService;
+
+ [SetUp]
+ public void Setup()
+ {
+ _loggerMock = new Mock();
+ _blobService = new BlobService(_loggerMock.Object);
+ }
+ [Test]
+ public async Task UploadBufferAsync_WithException_LogsError()
+ {
+ string uri = "invalid_uri";
+ string buffer = "Test buffer";
+ string fileRelativePath = "test/path";
+
+ await _blobService!.UploadBufferAsync(uri, buffer, fileRelativePath);
+
+ _loggerMock!.Verify(logger => logger.Error(It.IsAny()), Times.Once);
+ }
+
+ [Test]
+ public void GetCloudFilePath_WithValidParameters_ReturnsCorrectPath()
+ {
+ string uri = "https://example.com/container";
+ string fileRelativePath = "test/path";
+ string expectedPath = "https://example.com/container/test/path?";
+
+ string? result = _blobService?.GetCloudFilePath(uri, fileRelativePath);
+
+ Assert.AreEqual(expectedPath, result);
+ }
+
+ [Test]
+ public void GetCloudFilePath_WithSasUri_ReturnsCorrectPath()
+ {
+ string uri = "https://example.com/container?sasToken";
+ string fileRelativePath = "test/path";
+ string expectedPath = "https://example.com/container/test/path?sasToken";
+
+ string? result = _blobService?.GetCloudFilePath(uri, fileRelativePath);
+
+ Assert.AreEqual(expectedPath, result);
+ }
+ }
+}
diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs
index 652f73195b3e..2778c673da27 100644
--- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs
+++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs
@@ -580,5 +580,38 @@ public void TestRunCompleteHandler_EnableResultPublishSetToFalse_DisplaysMessage
serviceClientMock.Verify(c => c.PostTestRunShardInfo(It.IsAny()), Times.Never);
cloudRunErrorParserMock.Verify(c => c.DisplayMessages(), Times.Exactly(1));
}
+ [Test]
+ public void CheckAndRenewSasUri_WhenUriExpired_FetchesNewSasUri()
+ {
+ var loggerMock = new Mock();
+ var dataProcessorMock = new Mock();
+ var consoleWriterMock = new Mock();
+ var cloudRunErrorParserMock = new Mock();
+ var serviceClientMock = new Mock();
+ var testProcessor = new TestProcessor(_cloudRunMetadata, _cIInfo, loggerMock.Object, dataProcessorMock.Object, cloudRunErrorParserMock.Object, serviceClientMock.Object, consoleWriterMock.Object);
+ var expiredTestResultsSasUri = new TestResultsUri { Uri = "http://example.com", ExpiresAt = DateTime.UtcNow.AddMinutes(-5).ToString(), AccessLevel = AccessLevel.Read };
+ var newTestResultsSasUri = new TestResultsUri { Uri = "http://newexample.com", ExpiresAt = DateTime.UtcNow.AddHours(1).ToString(), AccessLevel = AccessLevel.Read };
+ testProcessor._testResultsSasUri = expiredTestResultsSasUri;
+ serviceClientMock.Setup(sc => sc.GetTestRunResultsUri()).Returns(newTestResultsSasUri);
+ TestResultsUri? result = testProcessor.CheckAndRenewSasUri();
+ Assert.AreEqual(newTestResultsSasUri, result);
+ loggerMock.Verify(l => l.Info(It.IsAny()), Times.AtLeastOnce);
+ }
+ [Test]
+ public void CheckAndRenewSasUri_WhenUriNotExpired_DoesNotFetchNewSasUri()
+ {
+ var loggerMock = new Mock();
+ var dataProcessorMock = new Mock();
+ var consoleWriterMock = new Mock();
+ var cloudRunErrorParserMock = new Mock();
+ var serviceClientMock = new Mock();
+ var testProcessor = new TestProcessor(_cloudRunMetadata, _cIInfo, loggerMock.Object, dataProcessorMock.Object, cloudRunErrorParserMock.Object, serviceClientMock.Object, consoleWriterMock.Object);
+ var validTestResultsSasUri = new TestResultsUri { Uri = "http://example.com?se=" + DateTime.UtcNow.AddMinutes(15).ToString("o"), ExpiresAt = DateTime.UtcNow.AddMinutes(15).ToString(), AccessLevel = AccessLevel.Read };
+ testProcessor._testResultsSasUri = validTestResultsSasUri;
+ TestResultsUri? result = testProcessor.CheckAndRenewSasUri();
+ Assert.AreEqual(validTestResultsSasUri, result);
+ serviceClientMock.Verify(sc => sc.GetTestRunResultsUri(), Times.Never);
+ loggerMock.Verify(l => l.Info(It.IsAny()), Times.Never);
+ }
}
}