common/ManagementTestShared/Redesign/ManagementRecordedTestBase.cs (192 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Azure.Core;
using Azure.Core.TestFramework;
using Azure.Core.TestFramework.Models;
using Azure.ResourceManager.Resources;
using Castle.DynamicProxy;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
#nullable disable
namespace Azure.ResourceManager.TestFramework
{
public abstract class ManagementRecordedTestBase<TEnvironment> : RecordedTestBase<TEnvironment>
where TEnvironment : TestEnvironment, new()
{
protected ResourceGroupCleanupPolicy ResourceGroupCleanupPolicy = new ResourceGroupCleanupPolicy();
protected ResourceGroupCleanupPolicy OneTimeResourceGroupCleanupPolicy = new ResourceGroupCleanupPolicy();
protected ManagementGroupCleanupPolicy ManagementGroupCleanupPolicy = new ManagementGroupCleanupPolicy();
protected ManagementGroupCleanupPolicy OneTimeManagementGroupCleanupPolicy = new ManagementGroupCleanupPolicy();
protected ResponseNullFilterPolicy NullFilterPolicy = new ResponseNullFilterPolicy();
protected ArmClient GlobalClient { get; private set; }
public TestEnvironment SessionEnvironment { get; private set; }
public TestRecording SessionRecording { get; private set; }
private ArmClient _cleanupClient;
private WaitUntil _waitForCleanup;
private ResourceType _resourceType;
protected string ApiVersion { get; }
protected ManagementRecordedTestBase(bool isAsync, RecordedTestMode? mode = default)
: base(isAsync, mode)
{
AdditionalInterceptors = new[] { new ManagementInterceptor(this) };
SessionEnvironment = new TEnvironment();
SessionEnvironment.Mode = Mode;
Initialize();
IgnoredQueryParameters.Add("api-version");
}
protected ManagementRecordedTestBase(bool isAsync, ResourceType resourceType, string apiVersion, RecordedTestMode? mode = default)
: this(isAsync, mode)
{
_resourceType = resourceType;
ApiVersion = apiVersion;
}
protected void SetTagResourceUsage(ArmClient client, bool? useTagResource)
{
var target = client.GetType().GetField("__target", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(client);
target.GetType().GetField("_canUseTagResource", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(target, useTagResource);
}
private void Initialize()
{
_waitForCleanup = Mode == RecordedTestMode.Live ? WaitUntil.Completed : WaitUntil.Started;
}
protected void IgnoreNetworkDependencyVersions()
{
// Ignore the api-version of Network operations
UriRegexSanitizers.Add(new UriRegexSanitizer(@"/providers\/Microsoft.Network\/(.*?)\?api-version=(?<group>[a-z0-9-]+)")
{
GroupForReplace = "group",
Value = "**"
});
}
protected void IgnoreAuthorizationDependencyVersions()
{
// Ignore the api-version of Authorization operations
UriRegexSanitizers.Add(new UriRegexSanitizer(@"/providers\/Microsoft.Authorization\/(.*?)\?api-version=(?<group>[a-z0-9-]+)")
{
GroupForReplace = "group",
Value = "**"
});
}
protected void IgnoreKeyVaultDependencyVersions()
{
// Ignore the api-version of KeyVault operations
UriRegexSanitizers.Add(new UriRegexSanitizer(@"/providers\/Microsoft.KeyVault\/(.*?)\?api-version=(?<group>[a-z0-9-]+)")
{
GroupForReplace = "group",
Value = "**"
});
}
protected void IgnoreManagedIdentityDependencyVersions()
{
// Ignore the api-version of ManagedIdentity operations
UriRegexSanitizers.Add(new UriRegexSanitizer(@"/providers\/Microsoft.ManagedIdentity\/(.*?)\?api-version=(?<group>[a-z0-9-]+)")
{
GroupForReplace = "group",
Value = "**"
});
}
private ArmClient GetCleanupClient()
{
if (Mode != RecordedTestMode.Playback)
{
return new ArmClient(
TestEnvironment.Credential,
TestEnvironment.SubscriptionId,
new ArmClientOptions() { Environment = GetArmEnvironment(TestEnvironment) });
}
return null;
}
protected TClient InstrumentClientExtension<TClient>(TClient client) => (TClient)InstrumentClient(typeof(TClient), client, new IInterceptor[] { new ManagementInterceptor(this) });
protected ArmClient GetArmClient(ArmClientOptions clientOptions = default, string subscriptionId = default, bool enableDeleteAfter = false)
{
var options = InstrumentClientOptions(clientOptions ?? new ArmClientOptions());
options.Environment = GetArmEnvironment(TestEnvironment);
options.AddPolicy(ResourceGroupCleanupPolicy, HttpPipelinePosition.PerCall);
options.AddPolicy(ManagementGroupCleanupPolicy, HttpPipelinePosition.PerCall);
options.AddPolicy(NullFilterPolicy, HttpPipelinePosition.PerRetry);
if (enableDeleteAfter)
{
AddDeleteAfterTagPolicy deleteAfterTagPolicy = new AddDeleteAfterTagPolicy(Recording.UtcNow);
options.AddPolicy(deleteAfterTagPolicy, HttpPipelinePosition.PerCall);
}
if (ApiVersion is not null)
options.SetApiVersion(_resourceType, ApiVersion);
return InstrumentClient(new ArmClient(
TestEnvironment.Credential,
subscriptionId ?? TestEnvironment.SubscriptionId,
options), new IInterceptor[] { new ManagementInterceptor(this) });
}
private ArmEnvironment GetArmEnvironment(TestEnvironment environment)
{
var endpoint = environment.ResourceManagerUrl;
if (string.IsNullOrEmpty(endpoint))
{
return ArmEnvironment.AzurePublicCloud;
}
var baseUri = new Uri(endpoint);
if (baseUri == ArmEnvironment.AzurePublicCloud.Endpoint)
return ArmEnvironment.AzurePublicCloud;
if (baseUri == ArmEnvironment.AzureChina.Endpoint)
return ArmEnvironment.AzureChina;
if (baseUri == ArmEnvironment.AzureGermany.Endpoint)
return ArmEnvironment.AzureGermany;
if (baseUri == ArmEnvironment.AzureGovernment.Endpoint)
return ArmEnvironment.AzureGovernment;
return new ArmEnvironment(new Uri(endpoint), environment.ServiceManagementUrl ?? $"{endpoint}/.default");
}
[SetUp]
protected void CreateCleanupClient()
{
_cleanupClient ??= GetCleanupClient();
}
[TearDown]
protected void CleanupResourceGroups()
{
if (Mode != RecordedTestMode.Playback)
{
Parallel.ForEach(ResourceGroupCleanupPolicy.ResourceGroupsCreated, resourceGroup =>
{
try
{
SubscriptionResource sub = _cleanupClient.GetSubscriptions().Exists(TestEnvironment.SubscriptionId)
? _cleanupClient.GetSubscriptions().Get(TestEnvironment.SubscriptionId)
: null;
sub?.GetResourceGroups().Get(resourceGroup).Value.Delete(_waitForCleanup);
}
catch (RequestFailedException e) when (e.Status == 404)
{
//we assume the test case cleaned it up if it no longer exists.
}
});
Parallel.ForEach(ManagementGroupCleanupPolicy.ManagementGroupsCreated, mgmtGroupId =>
{
try
{
_cleanupClient.GetManagementGroupResource(new ResourceIdentifier(mgmtGroupId)).Delete(_waitForCleanup);
}
catch (RequestFailedException e) when (e.Status == 404 || e.Status == 403)
{
//we assume the test case cleaned it up if it no longer exists.
}
});
}
}
private async Task StartSessionRecordingAsync()
{
// 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 = await CreateTestRecordingAsync(Mode, GetSessionFilePath());
SessionEnvironment.SetRecording(SessionRecording);
ValidateClientInstrumentation = SessionRecording.HasRequests;
}
protected async Task StopSessionRecordingAsync()
{
if (ValidateClientInstrumentation)
{
throw new InvalidOperationException("The test didn't instrument any clients but had recordings. Please call InstrumentClient for the client being recorded.");
}
if (SessionRecording != null)
{
await SessionRecording.DisposeAsync();
}
GlobalClient = null;
}
[OneTimeSetUp]
public async Task OneTimeSetUp()
{
if (!HasOneTimeSetup())
return;
await StartSessionRecordingAsync();
var options = InstrumentClientOptions(new ArmClientOptions(), SessionRecording);
options.AddPolicy(OneTimeResourceGroupCleanupPolicy, HttpPipelinePosition.PerCall);
options.AddPolicy(OneTimeManagementGroupCleanupPolicy, HttpPipelinePosition.PerCall);
options.Environment = GetArmEnvironment(SessionEnvironment);
GlobalClient = InstrumentClient(new ArmClient(
SessionEnvironment.Credential,
SessionEnvironment.SubscriptionId,
options), new IInterceptor[] { new ManagementInterceptor(this) });
}
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(OneTimeResourceGroupCleanupPolicy.ResourceGroupsCreated, resourceGroup =>
{
SubscriptionResource sub = _cleanupClient.GetSubscriptions().Exists(SessionEnvironment.SubscriptionId)
? _cleanupClient.GetSubscriptions().Get(SessionEnvironment.SubscriptionId)
: null;
sub?.GetResourceGroups().Get(resourceGroup).Value.Delete(_waitForCleanup);
});
Parallel.ForEach(OneTimeManagementGroupCleanupPolicy.ManagementGroupsCreated, mgmtGroupId =>
{
_cleanupClient.GetManagementGroupResource(new ResourceIdentifier(mgmtGroupId)).Delete(_waitForCleanup);
});
}
if (!(GlobalClient is null))
throw new InvalidOperationException("StopSessionRecording was never called please make sure you call that at the end of your OneTimeSetup");
}
}
}