diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/GitStoreTests.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/GitStoreTests.cs index 0ba9fdfbb24..295834eecc4 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/GitStoreTests.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/GitStoreTests.cs @@ -1,4 +1,5 @@ using Azure.Sdk.Tools.TestProxy.Common.Exceptions; +using Azure.Sdk.Tools.TestProxy.Common; using Azure.Sdk.Tools.TestProxy.Store; using System; using System.IO; @@ -36,7 +37,7 @@ public FakeCommandResultsHandler() { } public class GitStoretests { #region variable defs - private static string AssetsJson = "assets.json"; + public static string AssetsJson = "assets.json"; private GitStore _defaultStore = new GitStore(); private string[] basicFolderStructure = new string[] { @@ -67,11 +68,18 @@ public void TestEvaluateDirectoryGitRootExistsWithNoAssets() }; var testFolder = TestHelpers.DescribeTestFolder(String.Empty, folderStructure); - var evaluation = _defaultStore.EvaluateDirectory(testFolder); + try + { + var evaluation = _defaultStore.EvaluateDirectory(testFolder); - Assert.True(evaluation.IsGitRoot); - Assert.False(evaluation.AssetsJsonPresent); - Assert.False(evaluation.IsRoot); + Assert.True(evaluation.IsGitRoot); + Assert.False(evaluation.AssetsJsonPresent); + Assert.False(evaluation.IsRoot); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] @@ -86,10 +94,17 @@ public void TestEvaluateDirectoryFindsGitAssetsAlongsideGitRoot() var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, folderStructure); - var evaluation = _defaultStore.EvaluateDirectory(testFolder); - Assert.True(evaluation.IsGitRoot); - Assert.True(evaluation.AssetsJsonPresent); - Assert.False(evaluation.IsRoot); + try + { + var evaluation = _defaultStore.EvaluateDirectory(testFolder); + Assert.True(evaluation.IsGitRoot); + Assert.True(evaluation.AssetsJsonPresent); + Assert.False(evaluation.IsRoot); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] @@ -103,21 +118,34 @@ public void TestEvaluateDirectoryIdentifiesIntermediateDirectory() }; var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, folderStructure); - var evaluationDirectory = Path.Join(testFolder, "folder1"); + try + { + var evaluationDirectory = Path.Join(testFolder, "folder1"); - var evaluation = _defaultStore.EvaluateDirectory(evaluationDirectory); - Assert.False(evaluation.IsGitRoot); - Assert.False(evaluation.AssetsJsonPresent); - Assert.False(evaluation.IsRoot); + var evaluation = _defaultStore.EvaluateDirectory(evaluationDirectory); + Assert.False(evaluation.IsGitRoot); + Assert.False(evaluation.AssetsJsonPresent); + Assert.False(evaluation.IsRoot); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] public void ResolveAssetsJsonFindsAssetsInTargetFolder() { var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); - var path = _defaultStore.ResolveAssetsJson(testFolder); - - Assert.Equal(Path.Join(testFolder, AssetsJson), path); + try + { + var path = _defaultStore.ResolveAssetsJson(testFolder); + Assert.Equal(Path.Join(testFolder, AssetsJson), path); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] @@ -129,11 +157,18 @@ public void ResolveAssetsJsonFindsAssetsInTargetFolderBelowRoot() }; var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, folderStructure); - var evaluationDirectory = Path.Join(testFolder, "folder1"); + try + { + var evaluationDirectory = Path.Join(testFolder, "folder1"); - var path = _defaultStore.ResolveAssetsJson(evaluationDirectory); + var path = _defaultStore.ResolveAssetsJson(evaluationDirectory); - Assert.Equal(Path.Join(testFolder, "folder1", "assets.json"), path); + Assert.Equal(Path.Join(testFolder, "folder1", "assets.json"), path); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } @@ -147,23 +182,38 @@ public void ResolveAssetsJsonFindsAssetsAboveTargetFolder() }; var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, folderStructure); - var evaluationDirectory = Path.Join(testFolder, "folder1"); + try + { + var evaluationDirectory = Path.Join(testFolder, "folder1"); - var path = _defaultStore.ResolveAssetsJson(evaluationDirectory); + var path = _defaultStore.ResolveAssetsJson(evaluationDirectory); + + Assert.Equal(Path.Join(testFolder, "assets.json"), path); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } - Assert.Equal(Path.Join(testFolder, "assets.json"), path); } [Fact] public void ResolveAssetsJsonThrowsOnUnableToLocate() { var testFolder = TestHelpers.DescribeTestFolder(String.Empty, new string[] { }); - - var assertion = Assert.Throws(() => + try + { + var assertion = Assert.Throws(() => + { + _defaultStore.ResolveAssetsJson(testFolder); + }); + Assert.StartsWith("Unable to locate an assets.json at", assertion.Message); + } + finally { - _defaultStore.ResolveAssetsJson(testFolder); - }); - Assert.StartsWith("Unable to locate an assets.json at", assertion.Message); + DirectoryHelper.DeleteGitDirectory(testFolder); + } + } [Fact] @@ -176,13 +226,20 @@ public void ResolveAssetsJsonThrowsOnUnableToLocateAfterTraversal() }; var testFolder = TestHelpers.DescribeTestFolder(String.Empty, folderStructure); - var evaluationDirectory = Path.Join(testFolder, "folder1"); + try + { + var evaluationDirectory = Path.Join(testFolder, "folder1"); - var assertion = Assert.Throws(() => + var assertion = Assert.Throws(() => + { + _defaultStore.ResolveAssetsJson(evaluationDirectory); + }); + Assert.StartsWith("Unable to locate an assets.json at", assertion.Message); + } + finally { - _defaultStore.ResolveAssetsJson(evaluationDirectory); - }); - Assert.StartsWith("Unable to locate an assets.json at", assertion.Message); + DirectoryHelper.DeleteGitDirectory(testFolder); + } } @@ -208,9 +265,16 @@ public async Task ParseConfigurationEvaluatesValidConfigs(string inputJson) }; var testFolder = TestHelpers.DescribeTestFolder(inputJson, folderStructure); - var jsonFileLocation = Path.Join(testFolder, AssetsJson); + try + { + var jsonFileLocation = Path.Join(testFolder, AssetsJson); - var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); + var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Theory] @@ -236,13 +300,20 @@ public async Task ParseConfigurationThrowsOnMissingRequiredProperty(string input }; var testFolder = TestHelpers.DescribeTestFolder(inputJson, folderStructure); - var jsonFileLocation = Path.Join(testFolder, AssetsJson); + try + { + var jsonFileLocation = Path.Join(testFolder, AssetsJson); - var assertion = await Assert.ThrowsAsync(async () => + var assertion = await Assert.ThrowsAsync(async () => + { + await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); + }); + Assert.Contains("must contain value for the key \"AssetsRepo\"", assertion.Message); + } + finally { - await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); - }); - Assert.Contains("must contain value for the key \"AssetsRepo\"", assertion.Message); + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] @@ -256,10 +327,17 @@ public async Task ParseConfigurationEvaluatesTargetFolder() }; var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, folderStructure); - var jsonFileLocation = Path.Join(testFolder, folderPath); + try + { + var jsonFileLocation = Path.Join(testFolder, folderPath); - var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); - Assert.NotNull(parsedConfiguration); + var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); + Assert.NotNull(parsedConfiguration); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Theory] @@ -274,11 +352,19 @@ public async Task ParseConfigurationEvaluatesRelativePathCorrectly(params string }; var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, folderStructure); - var jsonFileLocation = Path.Join(testFolder, targetRelPath, AssetsJson); + try + { + var jsonFileLocation = Path.Join(testFolder, targetRelPath, AssetsJson); + + var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); + Assert.Equal(Path.Join(targetRelPath, AssetsJson), parsedConfiguration.AssetsJsonRelativeLocation); + Assert.Equal(jsonFileLocation, parsedConfiguration.AssetsJsonLocation); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } - var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); - Assert.Equal(Path.Join(targetRelPath, AssetsJson), parsedConfiguration.AssetsJsonRelativeLocation); - Assert.Equal(jsonFileLocation, parsedConfiguration.AssetsJsonLocation); } [Theory] @@ -287,26 +373,38 @@ public async Task ParseConfigurationEvaluatesRelativePathCorrectly(params string public async Task ParseConfigurationThrowsOnEmptyJson(string errorJson) { var testFolder = TestHelpers.DescribeTestFolder(errorJson, basicFolderStructure, ignoreEmptyAssetsJson: true); - - var assertion = await Assert.ThrowsAsync(async () => + try + { + var assertion = await Assert.ThrowsAsync(async () => + { + await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); + }); + Assert.StartsWith("The provided assets.json at ", assertion.Message); + Assert.EndsWith("did not have valid json present.", assertion.Message); + } + finally { - await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); - }); - Assert.StartsWith("The provided assets.json at ", assertion.Message); - Assert.EndsWith("did not have valid json present.", assertion.Message); + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] public async Task ParseConfigurationThrowsOnNonExistentJson() { var testFolder = TestHelpers.DescribeTestFolder(string.Empty, basicFolderStructure); - - var assertion = await Assert.ThrowsAsync(async () => + try { - await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); - }); - Assert.StartsWith("The provided assets.json path of ", assertion.Message); - Assert.EndsWith(" does not exist.", assertion.Message); + var assertion = await Assert.ThrowsAsync(async () => + { + await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); + }); + Assert.StartsWith("The provided assets.json path of ", assertion.Message); + Assert.EndsWith(" does not exist.", assertion.Message); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] @@ -314,13 +412,20 @@ public async Task GetDefaultBranchFailsWithInvalidRepo() { var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); - // we are resetting the default branch so we will see if fallback logic kicks in - _defaultStore.DefaultBranch = "not-main"; - var assetsConfiguration = await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); - assetsConfiguration.AssetsRepo = "Azure/an-invalid-repo"; + try + { + // we are resetting the default branch so we will see if fallback logic kicks in + _defaultStore.DefaultBranch = "not-main"; + var assetsConfiguration = await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); + assetsConfiguration.AssetsRepo = "Azure/an-invalid-repo"; - var result = await _defaultStore.GetDefaultBranch(assetsConfiguration); - Assert.Equal("not-main", result); + var result = await _defaultStore.GetDefaultBranch(assetsConfiguration); + Assert.Equal("not-main", result); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] @@ -328,13 +433,19 @@ public async Task UpdateRecordingJsonUpdatesProperly() { var fakeSha = "FakeReplacementSha"; var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); + try + { + var configuration = await _defaultStore.ParseConfigurationFile(testFolder); + await _defaultStore.UpdateAssetsJson(fakeSha, configuration); - var configuration = await _defaultStore.ParseConfigurationFile(testFolder); - await _defaultStore.UpdateAssetsJson(fakeSha, configuration); - - Assert.Equal(fakeSha, configuration.SHA); - var newConfiguration = await _defaultStore.ParseConfigurationFile(testFolder); - Assert.Equal(fakeSha, newConfiguration.SHA); + Assert.Equal(fakeSha, configuration.SHA); + var newConfiguration = await _defaultStore.ParseConfigurationFile(testFolder); + Assert.Equal(fakeSha, newConfiguration.SHA); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Theory] @@ -350,72 +461,99 @@ public async Task ResolveCheckPathsResolvesProperly(string assetsJsonPath, bool }; var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, expectedPaths); - - string configLocation; - - if(assetsJsonPath == "assets.json") - { - configLocation = testFolder; - } - else + try { - configLocation = Path.Join(testFolder, assetsJsonPath); + string configLocation; + + if (assetsJsonPath == "assets.json") + { + configLocation = testFolder; + } + else + { + configLocation = Path.Join(testFolder, assetsJsonPath); + } + + var configuration = await _defaultStore.ParseConfigurationFile(configLocation); + + if (!includePrefix) + { + configuration.AssetsRepoPrefixPath = null; + } + + var result = _defaultStore.ResolveCheckoutPaths(configuration); + Assert.Equal(expectedResult, result); } - - var configuration = await _defaultStore.ParseConfigurationFile(configLocation); - - if (!includePrefix) + finally { - configuration.AssetsRepoPrefixPath = null; + DirectoryHelper.DeleteGitDirectory(testFolder); } - - var result = _defaultStore.ResolveCheckoutPaths(configuration); - Assert.Equal(expectedResult, result); } [Fact] public async Task UpdateRecordingJsonNoOpsProperly() { var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); - var pathToAssets = Path.Combine(testFolder, "assets.json"); - var creationTime = File.GetLastWriteTime(pathToAssets); + try + { + var pathToAssets = Path.Combine(testFolder, "assets.json"); + var creationTime = File.GetLastWriteTime(pathToAssets); - var configuration = await _defaultStore.ParseConfigurationFile(testFolder); - await _defaultStore.UpdateAssetsJson(configuration.SHA, configuration); - var postUpdateLastWrite = File.GetLastWriteTime(pathToAssets); + var configuration = await _defaultStore.ParseConfigurationFile(testFolder); + await _defaultStore.UpdateAssetsJson(configuration.SHA, configuration); + var postUpdateLastWrite = File.GetLastWriteTime(pathToAssets); - Assert.Equal(creationTime, postUpdateLastWrite); - var newConfiguration = await _defaultStore.ParseConfigurationFile(testFolder); - Assert.Equal(configuration.SHA, newConfiguration.SHA); + Assert.Equal(creationTime, postUpdateLastWrite); + var newConfiguration = await _defaultStore.ParseConfigurationFile(testFolder); + Assert.Equal(configuration.SHA, newConfiguration.SHA); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact] public async Task UpdateRecordingJsonOnlyUpdatesTargetSHA() { var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); - var fakeSha = "FakeReplacementSha"; - var pathToAssets = Path.Combine(testFolder, "assets.json"); - var contentBeforeUpdate = File.ReadAllText(pathToAssets); - var configuration = await _defaultStore.ParseConfigurationFile(pathToAssets); - var originalSHA = configuration.SHA; + try + { + var fakeSha = "FakeReplacementSha"; + var pathToAssets = Path.Combine(testFolder, "assets.json"); + var contentBeforeUpdate = File.ReadAllText(pathToAssets); + var configuration = await _defaultStore.ParseConfigurationFile(pathToAssets); + var originalSHA = configuration.SHA; - await _defaultStore.UpdateAssetsJson(fakeSha, configuration); + await _defaultStore.UpdateAssetsJson(fakeSha, configuration); - var newConfiguration = await _defaultStore.ParseConfigurationFile(pathToAssets); - Assert.NotEqual(originalSHA, newConfiguration.SHA); - var contentAfterUpdate = File.ReadAllText(pathToAssets); + var newConfiguration = await _defaultStore.ParseConfigurationFile(pathToAssets); + Assert.NotEqual(originalSHA, newConfiguration.SHA); + var contentAfterUpdate = File.ReadAllText(pathToAssets); - Assert.NotEqual(contentBeforeUpdate, contentAfterUpdate); - Assert.Equal(contentBeforeUpdate.Replace(originalSHA, fakeSha), contentAfterUpdate); + Assert.NotEqual(contentBeforeUpdate, contentAfterUpdate); + Assert.Equal(contentBeforeUpdate.Replace(originalSHA, fakeSha), contentAfterUpdate); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact(Skip ="Skipping because we don't have an integration test suite working yet.")] public async Task GitCallHonorsLocalCredential() { var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); - var config = await _defaultStore.ParseConfigurationFile(testFolder); + try + { + var config = await _defaultStore.ParseConfigurationFile(testFolder); - var workDone = _defaultStore.InitializeAssetsRepo(config); + var workDone = _defaultStore.InitializeAssetsRepo(config); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Theory(Skip = "Skipping because we don't have an integration test suite working yet.")] @@ -424,25 +562,37 @@ public async Task GitCallHonorsLocalCredential() public async Task ResolveTargetBranchIntegration(string targetBranch, string result) { var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); - var config = await _defaultStore.ParseConfigurationFile(testFolder); - config.AssetsRepoBranch = targetBranch; + try + { + var config = await _defaultStore.ParseConfigurationFile(testFolder); + config.AssetsRepoBranch = targetBranch; - var defaultBranch = _defaultStore.ResolveCheckoutBranch(config); + var defaultBranch = _defaultStore.ResolveCheckoutBranch(config); - Assert.Equal(result, targetBranch); + Assert.Equal(result, targetBranch); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } [Fact(Skip = "Skipping due to integration tests not figured out yet.")] public async Task GetDefaultBranchWorksWithValidRepo() { var testFolder = TestHelpers.DescribeTestFolder(DefaultAssetsJson, basicFolderStructure); + try + { + _defaultStore.DefaultBranch = "not-main"; + var assetsConfiguration = await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); + var result = await _defaultStore.GetDefaultBranch(assetsConfiguration); - _defaultStore.DefaultBranch = "not-main"; - var assetsConfiguration = await _defaultStore.ParseConfigurationFile(Path.Join(testFolder, AssetsJson)); - var result = await _defaultStore.GetDefaultBranch(assetsConfiguration); - - Assert.Equal("main", result); + Assert.Equal("main", result); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } } - } } diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/IntegrationTests/GitStoreIntegrationRestoreTests.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/IntegrationTests/GitStoreIntegrationRestoreTests.cs new file mode 100644 index 00000000000..b4abe7caecd --- /dev/null +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/IntegrationTests/GitStoreIntegrationRestoreTests.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Azure.Sdk.Tools.TestProxy.Common; +using Azure.Sdk.Tools.TestProxy.Store; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Azure.Sdk.Tools.TestProxy.Tests.IntegrationTests +{ + // Pull Test Scenarios involving https://github.com/Azure/azure-sdk-assets-integration + + // Setup: + // The files live under https://github.com/Azure/azure-sdk-assets-integration/tree/main/pull/scenarios. + // Each file contains nothing but a single version digit, which is used for verification purposes. + // There are 3 pull test scenarios and each uses a different SHA. The scenarios are detailed down + // below with their test functions. + public class GitStoreIntegrationRestoreTests + { + // Right now, this is necessary for testing purposes but the real server won't have + // this issue. + public GitStoreIntegrationRestoreTests() + { + var loggerFactory = new LoggerFactory(); + DebugLogger.ConfigureLogger(loggerFactory); + } + + private GitStore _defaultStore = new GitStore(); + + // Scenario1 + // SHA fc54d000d0427c4a68bc8962d40f957f59e14577 + // This was the initial push of the test files: + // Added file1.txt + // Added file2.txt + // Added file3.txt + // Expect: each file should be version 1 + [Theory(Skip = "Skipping because we the integration branch permissions set for the test suite to run.")] + [InlineData( + @"{ + ""AssetsRepo"": ""Azure/azure-sdk-assets-integration"", + ""AssetsRepoPrefixPath"": ""pull/scenarios"", + ""AssetsRepoId"": """", + ""AssetsRepoBranch"": ""main"", + ""SHA"": ""fc54d000d0427c4a68bc8962d40f957f59e14577"" + }")] + public async Task Scenario1(string inputJson) + { + var folderStructure = new string[] + { + GitStoretests.AssetsJson + }; + + var testFolder = TestHelpers.DescribeTestFolder(inputJson, folderStructure); + try + { + var jsonFileLocation = Path.Join(testFolder, GitStoretests.AssetsJson); + + var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); + await _defaultStore.Restore(jsonFileLocation); + + // Calling Path.GetFullPath of the Path.Combine will ensure any directory separators are normalized for + // the OS the test is running on. The reason being is that AssetsRepoPrefixPath, if there's a separator, + // will be a forward one as expected by git but on Windows this won't result in a usable path. + string localFilePath = Path.GetFullPath(Path.Combine(parsedConfiguration.AssetsRepoLocation, parsedConfiguration.AssetsRepoPrefixPath)); + + Assert.Equal(3, System.IO.Directory.EnumerateFiles(localFilePath).Count()); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file1.txt", 1)); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file2.txt", 1)); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file3.txt", 1)); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } + } + + + // Scenario2 + // SHA 9e81fbb7d08c2df4cbdbfaffe79cde5d72f560d1 + // This was the second push of the test files. + // Unchanged file1.txt + // Updated file2.txt + // Updated file3.txt + // Added file4.txt + // Expect: file1 version 1 + // file2 version 2 + // file3 version 2 + // file4 version 1 + [Theory(Skip = "Skipping because we the integration branch permissions set for the test suite to run.")] + [InlineData( + @"{ + ""AssetsRepo"": ""Azure/azure-sdk-assets-integration"", + ""AssetsRepoPrefixPath"": ""pull/scenarios"", + ""AssetsRepoId"": """", + ""AssetsRepoBranch"": ""main"", + ""SHA"": ""9e81fbb7d08c2df4cbdbfaffe79cde5d72f560d1"" + }")] + public async Task Scenario2(string inputJson) + { + var folderStructure = new string[] + { + GitStoretests.AssetsJson + }; + + var testFolder = TestHelpers.DescribeTestFolder(inputJson, folderStructure); + try + { + var jsonFileLocation = Path.Join(testFolder, GitStoretests.AssetsJson); + + var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); + await _defaultStore.Restore(jsonFileLocation); + + // Calling Path.GetFullPath of the Path.Combine will ensure any directory separators are normalized for + // the OS the test is running on. The reason being is that AssetsRepoPrefixPath, if there's a separator, + // will be a forward one as expected by git but on Windows this won't result in a usable path. + string localFilePath = Path.GetFullPath(Path.Combine(parsedConfiguration.AssetsRepoLocation, parsedConfiguration.AssetsRepoPrefixPath)); + + Assert.Equal(4, System.IO.Directory.EnumerateFiles(localFilePath).Count()); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file1.txt", 1)); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file2.txt", 2)); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file3.txt", 2)); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file4.txt", 1)); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } + } + + // Scenario3 + // SHA bb2223a3aa0472ff481f8e1850e7647dc39fbfdd + // This was the third push of the test files. + // Deleted file1.txt + // Unchanged file2.txt + // Deleted file3.txt + // Unchanged file4.txt + // Added file5.txt + // Expect: file1 deleted + // file2 version 2 + // file3 deleted + // file4 version 1 + // file5 version 1 + [Theory(Skip = "Skipping because we the integration branch permissions set for the test suite to run.")] + [InlineData( + @"{ + ""AssetsRepo"": ""Azure/azure-sdk-assets-integration"", + ""AssetsRepoPrefixPath"": ""pull/scenarios"", + ""AssetsRepoId"": """", + ""AssetsRepoBranch"": ""main"", + ""SHA"": ""bb2223a3aa0472ff481f8e1850e7647dc39fbfdd"" + }")] + public async Task Scenario3(string inputJson) + { + var folderStructure = new string[] + { + GitStoretests.AssetsJson + }; + + var testFolder = TestHelpers.DescribeTestFolder(inputJson, folderStructure); + + try + { + var jsonFileLocation = Path.Join(testFolder, GitStoretests.AssetsJson); + + var parsedConfiguration = await _defaultStore.ParseConfigurationFile(jsonFileLocation); + await _defaultStore.Restore(jsonFileLocation); + + // Calling Path.GetFullPath of the Path.Combine will ensure any directory separators are normalized for + // the OS the test is running on. The reason being is that AssetsRepoPrefixPath, if there's a separator, + // will be a forward one as expected by git but on Windows this won't result in a usable path. + string localFilePath = Path.GetFullPath(Path.Combine(parsedConfiguration.AssetsRepoLocation, parsedConfiguration.AssetsRepoPrefixPath)); + + Assert.Equal(3, System.IO.Directory.EnumerateFiles(localFilePath).Count()); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file2.txt", 2)); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file4.txt", 1)); + Assert.True(TestHelpers.VerifyFileVersion(localFilePath, "file5.txt", 1)); + } + finally + { + DirectoryHelper.DeleteGitDirectory(testFolder); + } + } + } +} diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/TestHelpers.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/TestHelpers.cs index 77e88675616..d91b12ea2b5 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/TestHelpers.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/TestHelpers.cs @@ -163,5 +163,99 @@ public static string DescribeTestFolder(string assetsJsonContent, string[] sampl return testFolder.ToString(); } + + + /// + /// Remove the test folder created in the call to DescribeTestFolder + /// + /// The temporary test folder created by TestHelpers.DescribeTestFolder + public static void RemoveTestFolder(string testFolder) + { + // We can't Directory.Delete(path, true) to recursiverly delete the directory + // because the git files under .git\objects\pack have attributes on them that + // cause an UnauthorizedAccessException when trying to delete them. Fortunately, + // setting the attributes to normal allows them to be deleted. + File.SetAttributes(testFolder, FileAttributes.Normal); + + string[] files = Directory.GetFiles(testFolder); + string[] dirs = Directory.GetDirectories(testFolder); + + foreach (string file in files) + { + File.SetAttributes(file, FileAttributes.Normal); + File.Delete(file); + } + + foreach (string dir in dirs) + { + RemoveTestFolder(dir); + } + + Directory.Delete(testFolder, false); + } + + /// + /// Verify the version, inside the file, for a given file inside of a test folder. + /// + /// The temporary test folder created by TestHelpers.DescribeTestFolder + /// The fileName whose version needs verification + /// The expected version in the file + public static bool VerifyFileVersion(string testFolder, string fileName, int expectedVersion) + { + string fullFileName = Path.Combine(testFolder, fileName); + string stringVersion = ""; + int intVersion = -1; + + if (!File.Exists(fullFileName)) + { string errorString = String.Format("FileName {0} does not exist", fullFileName); + throw new ArgumentException(errorString); + } + + using (StreamReader reader = new StreamReader(fullFileName)) + { + stringVersion = reader.ReadLine() ?? ""; + } + + if (Int32.TryParse(stringVersion, out intVersion)) + { + if (expectedVersion == intVersion) + { + return true; + } + } + + return false; + } + + /// + /// Verify the version, inside the file, for a given file inside of a test folder. + /// + /// The temporary test folder created by TestHelpers.DescribeTestFolder + /// The fileName whose version needs verification + /// The expected version in the file + public static bool IncrementFileVersion(string testFolder, string fileName) + { + string fullFileName = Path.Combine(testFolder, fileName); + string stringVersion = ""; + int intVersion = -1; + + if (!File.Exists(fullFileName)) + { + string errorString = String.Format("FileName {0} does not exist", fullFileName); + throw new ArgumentException(errorString); + } + + using (StreamReader reader = new StreamReader(fullFileName)) + { + stringVersion = reader.ReadLine() ?? ""; + } + + if (Int32.TryParse(stringVersion, out intVersion)) + { + File.WriteAllText(fullFileName, (++intVersion).ToString()); + } + + return false; + } } } diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Common/DirectoryHelper.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Common/DirectoryHelper.cs new file mode 100644 index 00000000000..9b49674f46e --- /dev/null +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Common/DirectoryHelper.cs @@ -0,0 +1,35 @@ +using System.IO; + +namespace Azure.Sdk.Tools.TestProxy.Common +{ + public static class DirectoryHelper + { + /// + /// Recursively delete a git directory. Calling Directory.Delete(path, true), to recursiverly delete a directory + /// that was populated from sparse-checkout, will fail. This is because the git files under .git\objects\pack + /// have file attributes on them that will cause an UnauthorizedAccessException when trying to delete them. In order + /// to delete it, the file attributes need to be set to Normal. + /// + /// The git directory to delete + public static void DeleteGitDirectory(string directory) + { + File.SetAttributes(directory, FileAttributes.Normal); + + string[] files = Directory.GetFiles(directory); + string[] dirs = Directory.GetDirectories(directory); + + foreach (string file in files) + { + File.SetAttributes(file, FileAttributes.Normal); + File.Delete(file); + } + + foreach (string dir in dirs) + { + DeleteGitDirectory(dir); + } + + Directory.Delete(directory, false); + } + } +} diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitProcessHandler.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitProcessHandler.cs index ab679c90563..9ca8fafbe96 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitProcessHandler.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitProcessHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using Azure.Sdk.Tools.TestProxy.Common; using Azure.Sdk.Tools.TestProxy.Common.Exceptions; @@ -34,6 +35,21 @@ public virtual ProcessStartInfo CreateGitProcessInfo(GitAssetsConfiguration conf startInfo.EnvironmentVariables["PATH"] = Environment.GetEnvironmentVariable("PATH"); + // TODO + // 1. Check the git version >= 2.27.0 https://github.com/Azure/azure-sdk-tools/issues/3955 + // 2. If on Windows, check whether or not longpath is set + // The windows compat pack contains the registry classes for .net core + // Note: This may be done in a different way, the links below are for a potential solution + // that has not been fully investigated yet. + // https://docs.microsoft.com/en-us/dotnet/core/porting/windows-compat-pack + // How to add them is here + // https://www.nuget.org/packages/Microsoft.Windows.Compatibility#versions-body-tab + /* + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // TODO + } + */ return startInfo; } diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs index 37e97c71893..cd8784a6f9a 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Store/GitStore.cs @@ -9,6 +9,7 @@ using System.Text; using System.Linq; using Azure.Sdk.Tools.TestProxy.Common.Exceptions; +using Azure.Sdk.Tools.TestProxy.Common; namespace Azure.Sdk.Tools.TestProxy.Store { @@ -122,6 +123,7 @@ public async Task Restore(string pathToAssetsJson) { /// /// /// + // This should only ever be called by the user? public async Task Reset(string pathToAssetsJson) { var config = await ParseConfigurationFile(pathToAssetsJson); var initialized = config.IsAssetsRepoInitialized(); @@ -201,8 +203,12 @@ public void CheckoutRepoAtConfig(GitAssetsConfiguration config) try { - GitHandler.Run($"sparse-checkout set {checkoutPaths}", config); - GitHandler.Run($"checkout {config.SHA}", config); + // Set non-cone mode otherwise path filters will not work in git >= 2.37.0 + // See https://github.blog/2022-06-27-highlights-from-git-2-37/#tidbits + GitHandler.Run($"sparse-checkout set --no-cone {checkoutPaths}", config); + // The -c advice.detachedHead=false removes the verbose detatched head state + // warning that happens when syncing sparse-checkout to a particular SHA + GitHandler.Run($"-c advice.detachedHead=false checkout {config.SHA}", config); } catch(GitProcessException e) { @@ -224,7 +230,7 @@ public bool InitializeAssetsRepo(GitAssetsConfiguration config, bool forceInit = if (forceInit) { - Directory.Delete(assetRepo, true); + DirectoryHelper.DeleteGitDirectory(assetRepo); Directory.CreateDirectory(assetRepo); initialized = false; } @@ -233,7 +239,8 @@ public bool InitializeAssetsRepo(GitAssetsConfiguration config, bool forceInit = { try { - GitHandler.Run($"clone --no-checkout --filter=tree:0 https://github.com/{config.AssetsRepo} .", config); + // The -c core.longpaths=true is basically for Windows and is a noop for other platforms + GitHandler.Run($"clone -c core.longpaths=true --no-checkout --filter=tree:0 https://github.com/{config.AssetsRepo} .", config); GitHandler.Run($"sparse-checkout init", config); } catch(GitProcessException e)