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 DockerfileAnalyzerPluginTests { private DockerfileAnalyzerPlugin _plugin; private Mock> _mockLogger; private string _testDataPath; [TestInitialize] public void Setup() { _mockLogger = new Mock>(); _plugin = new DockerfileAnalyzerPlugin(_mockLogger.Object); // Find the project directory by traversing up from the assembly location var assemblyLocation = Path.GetDirectoryName(typeof(DockerfileAnalyzerPluginTests).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", "SampleDockerfiles"); } [TestMethod] public async Task ExecuteAsync_ProblematicDockerfile_DetectsIssues() { // Arrange var dockerfilePath = Path.Combine(_testDataPath, "Dockerfile.issues"); var parameters = new Dictionary { ["dockerfilePath"] = dockerfilePath, ["checkSecurity"] = true, ["optimizeSize"] = true, ["checkBestPractices"] = true }; // 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); // Should detect size optimization opportunities var sizeOptimizations = data.GetType().GetProperty("SizeOptimizations")?.GetValue(data) as System.Collections.IList; Assert.IsNotNull(sizeOptimizations); Assert.IsTrue(sizeOptimizations.Count > 0); // Should detect best practice violations var bestPracticeViolations = data.GetType().GetProperty("BestPracticeViolations")?.GetValue(data) as System.Collections.IList; Assert.IsNotNull(bestPracticeViolations); Assert.IsTrue(bestPracticeViolations.Count > 0); } [TestMethod] public async Task ExecuteAsync_NonExistentDockerfile_ReturnsError() { // Arrange var parameters = new Dictionary { ["dockerfilePath"] = "/nonexistent/Dockerfile" }; // 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_DetectsHardcodedSecrets() { // Arrange var tempFile = Path.GetTempFileName(); var insecureDockerfile = @" FROM ubuntu:latest ENV API_KEY=supersecretkey123 ENV DATABASE_PASSWORD=mypassword456 RUN echo $API_KEY > /tmp/key "; await File.WriteAllTextAsync(tempFile, insecureDockerfile); var parameters = new Dictionary { ["dockerfilePath"] = tempFile, ["checkSecurity"] = true }; try { // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var data = result.Data as dynamic; Assert.IsNotNull(data); 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 async Task ExecuteAsync_MultiStageAnalysis_DetectsMultiStage() { // Arrange var dockerfilePath = Path.Combine(_testDataPath, "Dockerfile.good"); var parameters = new Dictionary { ["dockerfilePath"] = dockerfilePath, ["checkMultiStage"] = true }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var data = result.Data as dynamic; Assert.IsNotNull(data); var multiStageAnalysis = data.GetType().GetProperty("MultiStageAnalysis")?.GetValue(data); Assert.IsNotNull(multiStageAnalysis); var isMultiStage = (bool)multiStageAnalysis.GetType().GetProperty("IsMultiStage")?.GetValue(multiStageAnalysis); Assert.IsTrue(isMultiStage); // Good dockerfile uses multi-stage build } [TestMethod] public void SupportedParameters_ReturnsExpectedParameters() { // Act var parameters = _plugin.SupportedParameters; // Assert Assert.IsNotNull(parameters); Assert.IsTrue(parameters.ContainsKey("dockerfilePath")); Assert.IsTrue(parameters.ContainsKey("checkSecurity")); Assert.IsTrue(parameters.ContainsKey("optimizeSize")); Assert.IsTrue(parameters.ContainsKey("checkBestPractices")); Assert.IsTrue(parameters.ContainsKey("checkMultiStage")); Assert.IsTrue(parameters.ContainsKey("generateOptimized")); } [TestMethod] public async Task ExecuteAsync_SizeOptimization_DetectsConsecutiveRuns() { // Arrange var tempFile = Path.GetTempFileName(); var unoptimizedDockerfile = @" FROM ubuntu:latest RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y wget RUN apt-get install -y git "; await File.WriteAllTextAsync(tempFile, unoptimizedDockerfile); var parameters = new Dictionary { ["dockerfilePath"] = tempFile, ["optimizeSize"] = true }; try { // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var data = result.Data as dynamic; Assert.IsNotNull(data); var sizeOptimizations = data.GetType().GetProperty("SizeOptimizations")?.GetValue(data) as System.Collections.IList; Assert.IsNotNull(sizeOptimizations); Assert.IsTrue(sizeOptimizations.Count > 0); } finally { if (File.Exists(tempFile)) File.Delete(tempFile); } } } }