Skip to content

Commit

Permalink
Update test project to consume framework for recorded tests (#19171)
Browse files Browse the repository at this point in the history
* Update test project to consume framework for recorded tests

* Changes required to get instrumentation working for the new armClient
added support for session recording for onetimesetup and onetimeteardown

* move unit test files

* update tests to not use username for prefix

* update targets to separate existing track 2 libraries from new core until autorest changes are finished

* merge in armbuildertest and update to use new framework

* remove necessity on session files if no onetimesetup

* Address review comments
  • Loading branch information
m-nash authored Mar 8, 2021
1 parent 444967c commit 9f52f9e
Show file tree
Hide file tree
Showing 107 changed files with 6,008 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Azure.ResourceManager.TestFramework
{
public class ResourceGroupCleanupPolicy : HttpPipelineSynchronousPolicy
{
private readonly object _listLock = new object();
private Regex _resourceGroupPattern = new Regex(@"/subscriptions/[^/]+/resourcegroups/([^?/]+)\?api-version");
private readonly IList<string> _resourceGroupCreated = new List<string>();

Expand All @@ -26,7 +27,10 @@ public override void OnSendingRequest(HttpMessage message)
var match = _resourceGroupPattern.Match(message.Request.Uri.ToString());
if (match.Success)
{
_resourceGroupCreated.Add(match.Groups[1].Value);
lock (_listLock)
{
_resourceGroupCreated.Add(match.Groups[1].Value);
}
}
}
}
Expand Down
163 changes: 163 additions & 0 deletions common/ManagementTestShared/Redesign/ManagementRecordedTestBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core;
using Azure.Core.TestFramework;
using Azure.ResourceManager.Core;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Azure.ResourceManager.TestFramework
{
public abstract class ManagementRecordedTestBase<TEnvironment> : RecordedTestBase<TEnvironment>
where TEnvironment: TestEnvironment, new()
{
protected ResourceGroupCleanupPolicy CleanupPolicy = new ResourceGroupCleanupPolicy();

protected ResourceGroupCleanupPolicy OneTimeCleanupPolicy = new ResourceGroupCleanupPolicy();

protected AzureResourceManagerClient GlobalClient { get; private set; }

public TestEnvironment SessionEnvironment { get; private set; }

public TestRecording SessionRecording { get; private set; }

private AzureResourceManagerClient _cleanupClient;

protected ManagementRecordedTestBase(bool isAsync) : base(isAsync)
{
SessionEnvironment = new TEnvironment();
SessionEnvironment.Mode = Mode;
}

protected ManagementRecordedTestBase(bool isAsync, RecordedTestMode mode) : base(isAsync, mode)
{
SessionEnvironment = new TEnvironment();
SessionEnvironment.Mode = Mode;
}

private AzureResourceManagerClient GetCleanupClient()
{
if (Mode != RecordedTestMode.Playback)
{
return new AzureResourceManagerClient(
TestEnvironment.SubscriptionId,
TestEnvironment.Credential,
new AzureResourceManagerClientOptions());
}
return null;
}

protected AzureResourceManagerClient GetArmClient()
{
var options = InstrumentClientOptions(new AzureResourceManagerClientOptions());
options.AddPolicy(CleanupPolicy, HttpPipelinePosition.PerCall);

return CreateClient<AzureResourceManagerClient>(
TestEnvironment.SubscriptionId,
TestEnvironment.Credential,
options);
}

[SetUp]
protected void Setup()
{
_cleanupClient ??= GetCleanupClient();
}

[TearDown]
protected void CleanupResourceGroups()
{
if (Mode != RecordedTestMode.Playback)
{
Parallel.ForEach(CleanupPolicy.ResourceGroupsCreated, resourceGroup =>
{
_cleanupClient.GetResourceGroupOperations(TestEnvironment.SubscriptionId, resourceGroup).StartDelete();
});
}
}

private void StartSessionRecording()
{
// Only create test recordings for the latest version of the service
TestContext.TestAdapter test = TestContext.CurrentContext.Test;
if (Mode != RecordedTestMode.Live &&
test.Properties.ContainsKey("SkipRecordings"))
{
throw new IgnoreException((string)test.Properties.Get("SkipRecordings"));
}
SessionRecording = new TestRecording(Mode, GetSessionFilePath(), Sanitizer, Matcher);
SessionEnvironment.SetRecording(SessionRecording);
ValidateClientInstrumentation = SessionRecording.HasRequests;
}

protected void StopSessionRecording()
{
if (ValidateClientInstrumentation)
{
throw new InvalidOperationException("The test didn't instrument any clients but had recordings. Please call InstrumentClient for the client being recorded.");
}

SessionRecording?.Dispose(true);
GlobalClient = null;
}

[OneTimeSetUp]
public void OneTimeSetUp()
{
if (!HasOneTimeSetup())
return;

StartSessionRecording();

var options = InstrumentClientOptions(new AzureResourceManagerClientOptions(), SessionRecording);
options.AddPolicy(OneTimeCleanupPolicy, HttpPipelinePosition.PerCall);

GlobalClient = CreateClient<AzureResourceManagerClient>(
SessionEnvironment.SubscriptionId,
SessionEnvironment.Credential,
options);
}

private bool HasOneTimeSetup()
{
HashSet<Type> types = new HashSet<Type>();
Type type = GetType();
Type endType = typeof(ManagementRecordedTestBase<TEnvironment>);
while (type != endType)
{
types.Add(type);
type = type.BaseType;
}

var methods = GetType().GetMethods().Where(m => types.Contains(m.DeclaringType));
foreach (var method in methods)
{
foreach(var attr in method.GetCustomAttributes(false))
{
if (attr is OneTimeSetUpAttribute)
return true;
}
}
return false;
}

[OneTimeTearDown]
public void OneTimeCleanupResourceGroups()
{
if (Mode != RecordedTestMode.Playback)
{
Parallel.ForEach(OneTimeCleanupPolicy.ResourceGroupsCreated, resourceGroup =>
{
_cleanupClient.GetResourceGroupOperations(SessionEnvironment.SubscriptionId, resourceGroup).StartDelete();
});
}

if (!(GlobalClient is null))
throw new InvalidOperationException("StopSessionRecording was never called please make sure you call that at the end of your OneTimeSetup");
}
}
}
38 changes: 38 additions & 0 deletions common/ManagementTestShared/Redesign/ResourceGroupCleanupPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Text.RegularExpressions;

using Azure.Core;
using Azure.Core.Pipeline;

namespace Azure.ResourceManager.TestFramework
{
public class ResourceGroupCleanupPolicy : HttpPipelineSynchronousPolicy
{
private readonly object _listLock = new object();
private Regex _resourceGroupPattern = new Regex(@"/subscriptions/[^/]+/resourcegroups/([^?/]+)\?api-version");
private readonly IList<string> _resourceGroupCreated = new List<string>();

public IList<string> ResourceGroupsCreated
{
get { return _resourceGroupCreated; }
}

public override void OnSendingRequest(HttpMessage message)
{
if (message.Request.Method == RequestMethod.Put)
{
var match = _resourceGroupPattern.Match(message.Request.Uri.ToString());
if (match.Success)
{
lock (_listLock)
{
_resourceGroupCreated.Add(match.Groups[1].Value);
}
}
}
}
}
}
9 changes: 7 additions & 2 deletions eng/Azure.Management.Test.targets
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
<RbacSharedSources>$(MSBuildThisFileDirectory)/../sdk/testcommon/Azure.Graph.Rbac/src</RbacSharedSources>
</PropertyGroup>

<ItemGroup Condition="'$(TestHelperProjects)' != ''">
<Compile Include="$(ManagementTestSharedSources)/**/*.cs"
<ItemGroup Condition="'$(TestHelperProjects)' != '' and '$(UseNewMgmtFramework)' != 'true'">
<Compile Include="$(ManagementTestSharedSources)/Current/**/*.cs"
Link="TestShared/%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup Condition="'$(TestHelperProjects)' != '' and '$(UseNewMgmtFramework)' == 'true'">
<Compile Include="$(ManagementTestSharedSources)/Redesign/**/*.cs"
Link="TestShared/%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public void Intercept(IInvocation invocation)
// We don't want to instrument generated rest clients.
if ((type.Name.EndsWith("Client") && !type.Name.EndsWith("RestClient")) ||
// Generated ARM clients will have a property containing the sub-client that ends with Operations.
(invocation.Method.Name.StartsWith("get_") && type.Name.EndsWith("Operations")))
(invocation.Method.Name.StartsWith("get_") && type.Name.EndsWith("Operations")) ||
// Instrument the subscription client that hangs off of the new AzureResouceManagementClient
(type.Name.EndsWith("DefaultSubscription")))
{
invocation.ReturnValue = _testBase.InstrumentClient(type, result, Array.Empty<IInterceptor>());
}
Expand Down
9 changes: 5 additions & 4 deletions sdk/core/Azure.Core.TestFramework/src/RecordedTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ protected RecordedTestBase(bool isAsync, RecordedTestMode mode) : base(isAsync)
Mode = mode;
}

public T InstrumentClientOptions<T>(T clientOptions) where T : ClientOptions
public T InstrumentClientOptions<T>(T clientOptions, TestRecording recording = default) where T : ClientOptions
{
clientOptions.Transport = Recording.CreateTransport(clientOptions.Transport);
recording ??= Recording;
clientOptions.Transport = recording.CreateTransport(clientOptions.Transport);
if (Mode == RecordedTestMode.Playback)
{
// Not making the timeout zero so retry code still goes async
Expand All @@ -80,7 +81,7 @@ public T InstrumentClientOptions<T>(T clientOptions) where T : ClientOptions
return clientOptions;
}

private string GetSessionFilePath()
protected string GetSessionFilePath()
{
TestContext.TestAdapter testAdapter = TestContext.CurrentContext.Test;

Expand Down Expand Up @@ -141,7 +142,7 @@ public virtual void StartTestRecording()
if (Mode != RecordedTestMode.Live &&
test.Properties.ContainsKey("SkipRecordings"))
{
throw new IgnoreException((string) test.Properties.Get("SkipRecordings"));
throw new IgnoreException((string)test.Properties.Get("SkipRecordings"));
}
Recording = new TestRecording(Mode, GetSessionFilePath(), Sanitizer, Matcher);
ValidateClientInstrumentation = Recording.HasRequests;
Expand Down
5 changes: 5 additions & 0 deletions sdk/core/Azure.Core.TestFramework/src/TestEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ protected string GetOptionalVariable(string name)
value = Environment.GetEnvironmentVariable(name);
}

if (value == null)
{
value = Environment.GetEnvironmentVariable($"AZURE_{name}");
}

if (value == null)
{
_environmentFile.TryGetValue(name, out value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public class AzureResourceManagerClient
/// Initializes a new instance of the <see cref="AzureResourceManagerClient"/> class for mocking.
/// </summary>
protected AzureResourceManagerClient()
: this(null, null, new DefaultAzureCredential(), null)
{
}

Expand Down Expand Up @@ -113,17 +112,17 @@ private AzureResourceManagerClient(
/// <summary>
/// Gets the Api version overrides.
/// </summary>
public Dictionary<string, string> ApiVersionOverrides { get; private set; }
public virtual Dictionary<string, string> ApiVersionOverrides { get; private set; }

/// <summary>
/// Gets the default Azure subscription.
/// </summary>
public Subscription DefaultSubscription { get; private set; }
public virtual Subscription DefaultSubscription { get; private set; }

/// <summary>
/// Gets the Azure resource manager client options.
/// </summary>
internal AzureResourceManagerClientOptions ClientOptions { get; }
internal virtual AzureResourceManagerClientOptions ClientOptions { get; }

/// <summary>
/// Gets the Azure subscription operations.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<DefineConstants>$(DefineConstants);RESOURCES_RP</DefineConstants>
<TestHelperProjects>;</TestHelperProjects>
<UseNewMgmtFramework>true</UseNewMgmtFramework>
</PropertyGroup>
<PropertyGroup>
<NoWarn>SA1649;SA1633;SA1000;SA1028;SA1400;SA1508</NoWarn>
Expand All @@ -16,17 +17,15 @@
<ProjectReference Include="..\src\Azure.ResourceManager.Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="ScenarioTests\*.json">
<None Update="Unit\TestAssets\Identity\*.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestAssets\Identity\*.json">
<None Update="Unit\TestAssets\UserAssignedIdentity\*.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestAssets\UserAssignedIdentity\*.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="TestAssets\SystemAssignedIdentity\*.json">
<None Update="Unit\TestAssets\SystemAssignedIdentity\*.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading

0 comments on commit 9f52f9e

Please sign in to comment.