Recordings for identity (#3156)

Adding test recordings for Identity
This commit is contained in:
Victor Vazquez 2021-12-14 13:42:08 -08:00 committed by GitHub
parent a2b2b74080
commit 7301f348b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 182 additions and 148 deletions

5
.vscode/cspell.json vendored
View File

@ -16,10 +16,7 @@
"json.hpp",
"*nlohmann-json*",
"eng/docs/api/assets/**/*",
"sdk/keyvault/azure-security-keyvault-keys/test/ut/recordings/*.json",
"sdk/keyvault/azure-security-keyvault-certificates/test/ut/recordings/*.json",
"sdk/keyvault/azure-security-keyvault-certificates/inc/azure/keyvault/certificates/certificate_client_models.hpp",
"sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_import_test_live.cpp"
"**/test/ut/recordings/*.json"
],
// * Unless configured otherwise, these words are not case sensitive
// * Alphabetize the list when making changes so the list is easier for future

View File

@ -73,8 +73,9 @@ jobs:
value: "https://non-real-account.vault.azure.net"
- name: AZURE_KEYVAULT_HSM_URL
value: "https://non-real-account.vault.azure.net"
# Tenant ID should use the uniqueID format for playback recordings
- name: AZURE_TENANT_ID
value: "non-real-tenant"
value: "33333333-3333-3333-3333-333333333333"
- name: AZURE_CLIENT_ID
value: "non-real-client"
- name: AZURE_CLIENT_SECRET

View File

@ -8,6 +8,7 @@
#include <gtest/gtest.h>
#include <azure/core/credentials/credentials.hpp>
#include <azure/core/credentials/token_credential_options.hpp>
#include <azure/core/internal/client_options.hpp>
#include <azure/core/internal/diagnostics/log.hpp>
@ -30,6 +31,19 @@ namespace Azure { namespace Core { namespace Test {
class TestBase : public ::testing::Test {
private:
void PrepareOptions(Azure::Core::_internal::ClientOptions& options)
{
// Set up client options depending on the test-mode
if (m_testContext.IsPlaybackMode())
{
options.Transport.Transport = m_interceptor->GetPlaybackTransport();
}
else if (!m_testContext.IsLiveMode())
{
options.PerRetryPolicies.push_back(m_interceptor->GetRecordPolicy());
}
}
// Call this method to update client options with the required configuration to
// support Record & Playback.
// If Playback or Record is not set, no changes will be done to the clientOptions or credential.
@ -84,6 +98,16 @@ namespace Azure { namespace Core { namespace Test {
return testName;
}
// Creates the sdk client for testing.
// The client will be set for record and playback before it is created.
Azure::Core::Credentials::TokenCredentialOptions GetTokenCredentialOptions()
{
// Run instrumentation before creating the client
Azure::Core::Credentials::TokenCredentialOptions options;
PrepareOptions(options);
return options;
}
// Creates the sdk client for testing.
// The client will be set for record and playback before it is created.
template <class T, class O>

View File

@ -13,6 +13,7 @@
#include <fstream>
#include <iostream>
#include <regex>
#include <stdexcept>
#include <string>
@ -62,7 +63,13 @@ Url InterceptorManager::RedactUrl(Url const& url)
auto host = url.GetHost();
auto hostWithNoAccount = std::find(host.begin(), host.end(), '.');
redactedUrl.SetHost("REDACTED" + std::string(hostWithNoAccount, host.end()));
redactedUrl.SetPath(url.GetPath());
// replace any uniqueID from the path for a hardcoded id
// For the regex, we should not assume anything about the version of UUID format being used. So,
// using the most general regex to get any uuid version.
redactedUrl.SetPath(std::regex_replace(
url.GetPath(),
std::regex("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"),
"33333333-3333-3333-3333-333333333333"));
// Query parameters
for (auto const& qp : url.GetQueryParameters())
{

View File

@ -39,8 +39,7 @@ stages:
# libraries to check for potential regressions everywhere.
CtestRegex: .*
# Storage tests have no recordings
# Identity live tests are pending to be recorded
CtestExcludeRegex: azure-storage|azure-identity-livetest
CtestExcludeRegex: azure-storage
LiveTestCtestRegex: '"azure-core.|json-test"'
LiveTestTimeoutInMinutes: 90 # default is 60 min. We need a little longer on worst case for Win+jsonTests
LineCoverageTarget: 93

View File

@ -90,7 +90,6 @@ if(BUILD_TESTING)
endif()
add_subdirectory(test/e2e)
add_subdirectory(test/live)
add_subdirectory(test/ut)
endif()

View File

@ -1,43 +0,0 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: MIT
cmake_minimum_required (VERSION 3.13)
project (azure-identity-livetest LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
include(GoogleTest)
add_executable (
azure-identity-livetest
azure_identity_live_test.cpp
client_secret_credential_test.cpp
environment_credential_test.cpp
getenv.hpp
)
if (MSVC)
# Disable warnings:
# - C26495: Variable
# - 'testing::internal::Mutex::critical_section_'
# - 'testing::internal::Mutex::critical_section_init_phase_'
# - 'testing::internal::Mutex::owner_thread_id_'
# - 'testing::internal::Mutex::type_'
# is uninitialized. Always initialize member variables (type.6).
# - C26812: The enum type
# - 'testing::internal::Mutex::StaticConstructorSelector'
# - 'testing::TestPartResult::Type'
# is unscoped. Prefer 'enum class' over 'enum' (Enum.3)
target_compile_options(azure-identity-livetest PUBLIC /wd6326 /wd26495 /wd26812)
endif()
target_link_libraries(azure-identity-livetest PRIVATE azure-identity gtest gmock)
# gtest_discover_tests will scan the test from azure-core-test and call add_test
# for each test to ctest. This enables `ctest -r` to run specific tests directly.
gtest_discover_tests(azure-identity-livetest
TEST_PREFIX azure-identity-livetest.
NO_PRETTY_TYPES
NO_PRETTY_VALUES
)

View File

@ -1,11 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
auto r = RUN_ALL_TESTS();
return r;
}

View File

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <azure/identity/client_secret_credential.hpp>
#include <gtest/gtest.h>
#include <chrono>
#include "getenv.hpp"
TEST(ClientSecretCredential, Basic)
{
Azure::Identity::ClientSecretCredential const credential(
GetEnv("AZURE_TENANT_ID"), GetEnv("AZURE_CLIENT_ID"), GetEnv("AZURE_CLIENT_SECRET"));
auto const token = credential.GetToken(
{{"https://vault.azure.net/.default"}}, Azure::Core::Context::ApplicationContext);
EXPECT_FALSE(token.Token.empty());
EXPECT_GE(token.ExpiresOn, std::chrono::system_clock::now());
}

View File

@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <azure/identity/environment_credential.hpp>
#include <gtest/gtest.h>
#include <chrono>
#include "getenv.hpp"
TEST(EnvironmentCredential, ClientSecret)
{
// See EnvironmentCredential's documentation - these below are the environment variables it reads
// from.
EXPECT_FALSE(GetEnv("AZURE_TENANT_ID").empty());
EXPECT_FALSE(GetEnv("AZURE_CLIENT_ID").empty());
EXPECT_FALSE(GetEnv("AZURE_CLIENT_SECRET").empty());
Azure::Identity::EnvironmentCredential const credential;
auto const token = credential.GetToken(
{{"https://vault.azure.net/.default"}}, Azure::Core::Context::ApplicationContext);
EXPECT_FALSE(token.Token.empty());
EXPECT_GE(token.ExpiresOn, std::chrono::system_clock::now());
}

View File

@ -1,19 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <cstdlib>
inline std::string GetEnv(std::string const& name)
{
#if defined(_MSC_VER)
#pragma warning(push)
// warning C4996: 'getenv': This function or variable may be unsafe. Consider using _dupenv_s
// instead.
#pragma warning(disable : 4996)
#endif
auto const result = std::getenv(name.c_str());
return result != nullptr ? result : "";
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
}

View File

@ -11,9 +11,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
include(GoogleTest)
# Export the test folder for recordings access.
add_compile_definitions(AZURE_TEST_RECORDING_DIR="${CMAKE_CURRENT_LIST_DIR}")
add_executable (
azure-identity-test
azure_identity_test.cpp
client_secret_credential_test.cpp
credential_test_helper.cpp
credential_test_helper.hpp
@ -21,7 +23,9 @@ add_executable (
macro_guard_test.cpp
managed_identity_credential_test.cpp
simplified_header_test.cpp
token_credential_impl_test.cpp)
token_credential_impl_test.cpp
token_credential_test.cpp
)
if (MSVC)
# Disable warnings:
@ -41,11 +45,11 @@ endif()
# Adding private headers from identity to the tests so we can test the private APIs with no relative paths include.
target_include_directories (azure-identity-test PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src>)
target_link_libraries(azure-identity-test PRIVATE azure-identity gtest gmock)
target_link_libraries(azure-identity-test PRIVATE azure-identity azure-core-test-fw gtest gtest_main gmock)
# gtest_discover_tests will scan the test from azure-identity-test and call add_test
# for each test to ctest. This enables `ctest -r` to run specific tests directly.
gtest_discover_tests(azure-identity-test
TEST_PREFIX azure-identity-unittest.
TEST_PREFIX azure-identity.
NO_PRETTY_TYPES
NO_PRETTY_VALUES)

View File

@ -1,11 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
auto r = RUN_ALL_TESTS();
return r;
}

View File

@ -0,0 +1,29 @@
{
"networkCallRecords": [
{
"Headers": {
"content-type": "application/x-www-form-urlencoded",
"user-agent": "azsdk-cpp-identity/1.2.0-beta.1 (Linux 5.4.0-1063-azure x86_64 #66~18.04.1-Ubuntu SMP Thu Oct 21 09:59:28 UTC 2021)",
"x-ms-client-request-id": "c187c183-9e54-4f1b-6aa0-93eec70c4975"
},
"Method": "POST",
"Response": {
"BODY": "{\"token_type\":\"Bearer\",\"expires_in\":86399,\"ext_expires_in\":86399,\"access_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCIsImtpZCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCJ9.eyJhdWQiOiJodHRwczovL3ZhdWx0LmF6dXJlLm5ldCIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0Ny8iLCJpYXQiOjE2Mzg5MDEzMTgsIm5iZiI6MTYzODkwMTMxOCwiZXhwIjoxNjM4OTg4MDE4LCJhaW8iOiJFMlpnWURnWTdkaHZuM2gwaTcxT2Q2cGRTWFVhQUE9PSIsImFwcGlkIjoiYzhhZDg5N2EtNmZjZC00NTQwLTk4NjYtODc1NDQ5M2NiNzhkIiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3LyIsIm9pZCI6IjBiZWY3ZjM2LWM5ZDItNGQyNC04MDY2LWM3ZTkxNWZlNTE0MyIsInJoIjoiMC5BUm9BdjRqNWN2R0dyMEdScXkxODBCSGJSM3FKcmNqTmIwQkZtR2FIVkVrOHQ0MGFBQUEuIiwic3ViIjoiMGJlZjdmMzYtYzlkMi00ZDI0LTgwNjYtYzdlOTE1ZmU1MTQzIiwidGlkIjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3IiwidXRpIjoicGxVN0hJRkRaVUNScnFpd3V5d3ZBQSIsInZlciI6IjEuMCJ9.oE4JLChCYYk2ACkD0LAzlEMZIGw2hNmS2mqxpEYNtgfF4kpwYNdw-JgruNXV4MZR7_nwtCsXzBt2F3Mz1VZ9LNk-9_2iWVKtGxkwW5bhzY8qckpT7odq5050adV1398ph_TNpW8Wao-YKNpIa6Nb5lRF2KxoPw1xRJ6vfhOkVXdVEeB5iK9l6cmZ9F-THMZsoz8v08QphI5Dehd-MNn1u4WQWseG508oCVej1u-jNHlkfe7FrXRNTGO2XKVJnMZy-M7fC1E55iFZLBr0eHbdF_2qQdDpTp40Se4ps9zg8CNbRmZyEl8N8cSsblkVm3i6DPQWk6GU6b_xtG3fBoizzA\"}",
"STATUS_CODE": "200",
"cache-control": "no-store, no-cache",
"content-length": "1315",
"content-type": "application/json; charset=utf-8",
"date": "Tue, 07 Dec 2021 18:26:58 GMT",
"expires": "-1",
"p3p": "CP=\"DSP CUR OTPi IND OTRi ONL FIN\"",
"pragma": "no-cache",
"set-cookie": "stsservicecookie=estsfd; path=/; secure; samesite=none; httponly",
"strict-transport-security": "max-age=31536000; includeSubDomains",
"x-content-type-options": "nosniff",
"x-ms-ests-server": "2.1.12249.17 - EUS ProdSlices",
"x-ms-request-id": "1c3b55a6-4381-4065-91ae-a8b0bb2c2f00"
},
"Url": "https://REDACTED.microsoftonline.com/33333333-3333-3333-3333-333333333333/oauth2/v2.0/token"
}
]
}

View File

@ -0,0 +1,29 @@
{
"networkCallRecords": [
{
"Headers": {
"content-type": "application/x-www-form-urlencoded",
"user-agent": "azsdk-cpp-identity/1.2.0-beta.1 (Linux 5.4.0-1063-azure x86_64 #66~18.04.1-Ubuntu SMP Thu Oct 21 09:59:28 UTC 2021)",
"x-ms-client-request-id": "249e2bf4-bc08-4dcf-477e-37ca159e120d"
},
"Method": "POST",
"Response": {
"BODY": "{\"token_type\":\"Bearer\",\"expires_in\":86399,\"ext_expires_in\":86399,\"access_token\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCIsImtpZCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCJ9.eyJhdWQiOiJodHRwczovL3ZhdWx0LmF6dXJlLm5ldCIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0Ny8iLCJpYXQiOjE2Mzg5MDEzMTgsIm5iZiI6MTYzODkwMTMxOCwiZXhwIjoxNjM4OTg4MDE4LCJhaW8iOiJFMlpnWUhBenpOc1NHcVR5aVZkejYvSVRFK1FhQUE9PSIsImFwcGlkIjoiYzhhZDg5N2EtNmZjZC00NTQwLTk4NjYtODc1NDQ5M2NiNzhkIiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3LyIsIm9pZCI6IjBiZWY3ZjM2LWM5ZDItNGQyNC04MDY2LWM3ZTkxNWZlNTE0MyIsInJoIjoiMC5BUm9BdjRqNWN2R0dyMEdScXkxODBCSGJSM3FKcmNqTmIwQkZtR2FIVkVrOHQ0MGFBQUEuIiwic3ViIjoiMGJlZjdmMzYtYzlkMi00ZDI0LTgwNjYtYzdlOTE1ZmU1MTQzIiwidGlkIjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3IiwidXRpIjoiQWFMd082Tm5UMFNRMktEQjE3VXZBQSIsInZlciI6IjEuMCJ9.MTe892F5GKhmLIT6bgsQupj8Yko0MoBYG4hgBRpsVYdS8AUV9kLBN8aZFhXhDlJZnb9a2LM067jf8MYD483YzYBJyqMYs2USsttdxBT7BOW0-lyvLcZXMEOX4YOTU9BUJ2J0gRuEsEOjXu-Q6ULuNpTkYCQQYzbJSsEkvdWAzl7MQcoJNJtmfmLqqnxU6z6Bw6dUq1-EKckL_e45nYEB2klsuY4tm0NsZM2wApECgv9dwldCdCvODBKNTnJJ5OGe9OxfH1oJA_TRDr2wk_CrTOyzH0ikKgAyrlTPKDk_SL7pjXhNKd61UIBP4vOzGA8YD_yorjrLVyag4s0OiS8JIg\"}",
"STATUS_CODE": "200",
"cache-control": "no-store, no-cache",
"content-length": "1315",
"content-type": "application/json; charset=utf-8",
"date": "Tue, 07 Dec 2021 18:26:58 GMT",
"expires": "-1",
"p3p": "CP=\"DSP CUR OTPi IND OTRi ONL FIN\"",
"pragma": "no-cache",
"set-cookie": "stsservicecookie=estsfd; path=/; secure; samesite=none; httponly",
"strict-transport-security": "max-age=31536000; includeSubDomains",
"x-content-type-options": "nosniff",
"x-ms-ests-server": "2.1.12249.17 - SCUS ProdSlices",
"x-ms-request-id": "3bf0a201-67a3-444f-90d8-a0c1d7b52f00"
},
"Url": "https://REDACTED.microsoftonline.com/33333333-3333-3333-3333-333333333333/oauth2/v2.0/token"
}
]
}

View File

@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <gtest/gtest.h>
#include <azure/core/test/test_base.hpp>
#include <azure/identity/client_secret_credential.hpp>
#include <azure/identity/environment_credential.hpp>
#include <chrono>
#include <thread>
namespace Azure { namespace Identity { namespace Test {
class TokenCredentialTest : public Azure::Core::Test::TestBase {
protected:
// Required to rename the test properly once the test is started.
// We can only know the test instance name until the test instance is run.
std::unique_ptr<Azure::Identity::ClientSecretCredential> GetClientSecretCredential(
std::string const& testName)
{
// set the interceptor for the current test
m_testContext.RenameTest(testName);
Azure::Core::Credentials::TokenCredentialOptions options = GetTokenCredentialOptions();
return std::make_unique<Azure::Identity::ClientSecretCredential>(
GetEnv("AZURE_TENANT_ID"),
GetEnv("AZURE_CLIENT_ID"),
GetEnv("AZURE_CLIENT_SECRET"),
options);
}
// Required to rename the test properly once the test is started.
// We can only know the test instance name until the test instance is run.
std::unique_ptr<Azure::Identity::EnvironmentCredential> GetEnvironmentCredential(
std::string const& testName)
{
// set the interceptor for the current test
m_testContext.RenameTest(testName);
Azure::Core::Credentials::TokenCredentialOptions options = GetTokenCredentialOptions();
return std::make_unique<Azure::Identity::EnvironmentCredential>(options);
}
// Runs before every test.
virtual void SetUp() override
{
Azure::Core::Test::TestBase::SetUpTestBase(AZURE_TEST_RECORDING_DIR);
}
};
}}} // namespace Azure::Identity::Test
using namespace Azure::Identity::Test;
using namespace Azure::Identity;
TEST_F(TokenCredentialTest, ClientSecret)
{
std::string const testName(GetTestName());
auto const clientSecretCredential = GetClientSecretCredential(testName);
auto const token = clientSecretCredential->GetToken(
{{"https://vault.azure.net/.default"}}, Azure::Core::Context::ApplicationContext);
EXPECT_FALSE(token.Token.empty());
EXPECT_GE(token.ExpiresOn, std::chrono::system_clock::now());
}
TEST_F(TokenCredentialTest, EnvironmentCredential)
{
std::string const testName(GetTestName());
auto const clientSecretCredential = GetEnvironmentCredential(testName);
auto const token = clientSecretCredential->GetToken(
{{"https://vault.azure.net/.default"}}, Azure::Core::Context::ApplicationContext);
EXPECT_FALSE(token.Token.empty());
EXPECT_GE(token.ExpiresOn, std::chrono::system_clock::now());
}

View File

@ -26,8 +26,8 @@ stages:
- template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml
parameters:
ServiceDirectory: identity
CtestRegex: azure-identity-unittest.
LiveTestCtestRegex: azure-identity-livetest.
CtestRegex: azure-identity.
LiveTestCtestRegex: azure-identity.
LineCoverageTarget: 99
BranchCoverageTarget: 62
Artifacts: