using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Extensions.Logging; using MarketAlly.AIPlugin.DevOps.Plugins; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Moq; namespace MarketAlly.AIPlugin.DevOps.Tests { [TestClass] public class DevOpsScanPluginTests { private DevOpsScanPlugin _plugin; private Mock> _mockLogger; private string _testDataPath; [TestInitialize] public void Setup() { _mockLogger = new Mock>(); _plugin = new DevOpsScanPlugin(_mockLogger.Object); // Find the project directory by traversing up from the assembly location var assemblyLocation = Path.GetDirectoryName(typeof(DevOpsScanPluginTests).Assembly.Location); var projectDir = assemblyLocation; while (projectDir != null && !Directory.Exists(Path.Combine(projectDir, "Tests", "TestData"))) { projectDir = Directory.GetParent(projectDir)?.FullName; } _testDataPath = Path.Combine(projectDir ?? assemblyLocation, "Tests", "TestData", "SamplePipelines"); } [TestMethod] public async Task ExecuteAsync_GitHubActions_ReturnsValidAnalysis() { // Arrange var githubFile = Path.Combine(_testDataPath, "github-actions-sample.yml"); var parameters = new Dictionary { ["pipelinePath"] = githubFile, ["pipelineType"] = "github", ["checkSecurity"] = true, ["optimizeBuild"] = true, ["checkBestPractices"] = true, ["generateRecommendations"] = true }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); Assert.IsNotNull(result.Data); var data = result.Data as dynamic; Assert.IsNotNull(data); Assert.AreEqual("DevOps pipeline scan completed", data.GetType().GetProperty("Message")?.GetValue(data)); } [TestMethod] public async Task ExecuteAsync_NonExistentFile_ReturnsError() { // Arrange var parameters = new Dictionary { ["pipelinePath"] = "/nonexistent/path/pipeline.yml" }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsFalse(result.Success); Assert.IsNotNull(result.Error); Assert.IsTrue(result.Error is FileNotFoundException); } [TestMethod] public async Task ExecuteAsync_SecurityCheck_DetectsIssues() { // Arrange var tempFile = Path.GetTempFileName(); var insecureContent = @" name: Insecure Pipeline on: [push] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@main - name: Deploy env: API_KEY: ghp_1234567890abcdefghijklmnopqrstuvwxyz12 run: echo 'Deploying' "; await File.WriteAllTextAsync(tempFile, insecureContent); var parameters = new Dictionary { ["pipelinePath"] = tempFile, ["pipelineType"] = "github", ["checkSecurity"] = true }; try { // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var data = result.Data as dynamic; Assert.IsNotNull(data); // Should detect security issues var securityIssues = data.GetType().GetProperty("SecurityIssues")?.GetValue(data) as System.Collections.IList; Assert.IsNotNull(securityIssues); Assert.IsTrue(securityIssues.Count > 0); } finally { if (File.Exists(tempFile)) File.Delete(tempFile); } } [TestMethod] public void SupportedParameters_ReturnsExpectedParameters() { // Act var parameters = _plugin.SupportedParameters; // Assert Assert.IsNotNull(parameters); Assert.IsTrue(parameters.ContainsKey("pipelinePath")); Assert.IsTrue(parameters.ContainsKey("pipelineType")); Assert.IsTrue(parameters.ContainsKey("checkSecurity")); Assert.IsTrue(parameters.ContainsKey("optimizeBuild")); Assert.IsTrue(parameters.ContainsKey("checkBestPractices")); Assert.IsTrue(parameters.ContainsKey("generateRecommendations")); } [TestMethod] public async Task ExecuteAsync_AzureDevOps_ParsesBasicStructure() { // Arrange var azureFile = Path.Combine(_testDataPath, "azure-pipelines-sample.yml"); var parameters = new Dictionary { ["pipelinePath"] = azureFile, ["pipelineType"] = "azure" }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var data = result.Data as dynamic; Assert.IsNotNull(data); Assert.AreEqual("azure", data.GetType().GetProperty("PipelineType")?.GetValue(data)); } [TestMethod] public async Task ExecuteAsync_GitLabCI_ParsesBasicStructure() { // Arrange var gitlabFile = Path.Combine(_testDataPath, "gitlab-ci-sample.yml"); var parameters = new Dictionary { ["pipelinePath"] = gitlabFile, ["pipelineType"] = "gitlab" }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var data = result.Data as dynamic; Assert.IsNotNull(data); Assert.AreEqual("gitlab", data.GetType().GetProperty("PipelineType")?.GetValue(data)); } [TestMethod] public async Task ExecuteAsync_AutoDetection_DetectsGitHubActions() { // Arrange var githubFile = Path.Combine(_testDataPath, "github-actions-sample.yml"); var parameters = new Dictionary { ["pipelinePath"] = githubFile, ["pipelineType"] = "auto" }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); // Note: Auto-detection would need the file to be in .github/workflows/ directory // This test validates the auto parameter is handled correctly } } }