save progress

This commit is contained in:
master
2026-01-09 18:27:36 +02:00
parent e608752924
commit a21d3dbc1f
361 changed files with 63068 additions and 1192 deletions

View File

@@ -0,0 +1,155 @@
// <copyright file="AzureDevOpsGeneratorTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using FluentAssertions;
using Xunit;
namespace StellaOps.Tools.WorkflowGenerator.Tests;
/// <summary>
/// Tests for <see cref="AzureDevOpsGenerator"/>.
/// </summary>
[Trait("Category", "Unit")]
public class AzureDevOpsGeneratorTests
{
private readonly AzureDevOpsGenerator _generator = new();
[Fact]
public void Platform_ReturnsAzureDevOps()
{
_generator.Platform.Should().Be(CiPlatform.AzureDevOps);
}
[Fact]
public void PlatformName_ReturnsAzureDevOps()
{
_generator.PlatformName.Should().Be("Azure DevOps");
}
[Fact]
public void DefaultFileName_ReturnsAzurePipelinesYml()
{
_generator.DefaultFileName.Should().Be("azure-pipelines.yml");
}
[Fact]
public void Generate_DefaultOptions_ContainsPipelineName()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("name: StellaOps Security Scan");
}
[Fact]
public void Generate_DefaultOptions_ContainsTrigger()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("trigger:");
yaml.Should().Contain("branches:");
yaml.Should().Contain("include:");
}
[Fact]
public void Generate_DefaultOptions_ContainsPr()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("pr:");
}
[Fact]
public void Generate_DefaultOptions_ContainsPool()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("pool:");
yaml.Should().Contain("vmImage: 'ubuntu-latest'");
}
[Fact]
public void Generate_DefaultOptions_ContainsStages()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("stages:");
yaml.Should().Contain("- stage: Security");
}
[Fact]
public void Generate_DefaultOptions_ContainsBashTask()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("task: Bash@3");
}
[Fact]
public void Generate_DefaultOptions_ContainsPublishArtifact()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("PublishBuildArtifacts@1");
yaml.Should().Contain("artifactName: 'CodeAnalysisLogs'");
}
[Fact]
public void Generate_DefaultOptions_ContainsCheckout()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("- checkout: self");
yaml.Should().Contain("fetchDepth: 0");
}
[Fact]
public void Generate_WithSchedule_ContainsCron()
{
var options = WorkflowOptions.AzureDevOpsDefault with
{
Triggers = TriggerConfig.WeeklySchedule
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("schedules:");
yaml.Should().Contain("cron:");
}
[Fact]
public void Validate_CorrectPlatform_ReturnsValid()
{
var options = WorkflowOptions.AzureDevOpsDefault;
var result = _generator.Validate(options);
result.IsValid.Should().BeTrue();
}
[Fact]
public void Validate_WrongPlatform_ReturnsInvalid()
{
var options = WorkflowOptions.GitHubActionsDefault;
var result = _generator.Validate(options);
result.IsValid.Should().BeFalse();
}
}

View File

@@ -0,0 +1,246 @@
// <copyright file="GitHubActionsGeneratorTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using FluentAssertions;
using Xunit;
namespace StellaOps.Tools.WorkflowGenerator.Tests;
/// <summary>
/// Tests for <see cref="GitHubActionsGenerator"/>.
/// </summary>
[Trait("Category", "Unit")]
public class GitHubActionsGeneratorTests
{
private readonly GitHubActionsGenerator _generator = new();
[Fact]
public void Platform_ReturnsGitHubActions()
{
_generator.Platform.Should().Be(CiPlatform.GitHubActions);
}
[Fact]
public void PlatformName_ReturnsGitHubActions()
{
_generator.PlatformName.Should().Be("GitHub Actions");
}
[Fact]
public void DefaultFileName_ReturnsYml()
{
_generator.DefaultFileName.Should().Be("stellaops-scan.yml");
}
[Fact]
public void Generate_DefaultOptions_ContainsWorkflowName()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("name: StellaOps Security Scan");
}
[Fact]
public void Generate_DefaultOptions_ContainsPushTrigger()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("push:");
yaml.Should().Contain("- main");
}
[Fact]
public void Generate_DefaultOptions_ContainsPullRequestTrigger()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("pull_request:");
}
[Fact]
public void Generate_DefaultOptions_ContainsWorkflowDispatch()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("workflow_dispatch:");
}
[Fact]
public void Generate_DefaultOptions_ContainsSecurityEventsPermission()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("security-events: write");
}
[Fact]
public void Generate_DefaultOptions_ContainsCheckoutStep()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("uses: actions/checkout@v4");
}
[Fact]
public void Generate_DefaultOptions_ContainsCliInstallation()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("curl -fsSL https://get.stellaops.io | sh");
}
[Fact]
public void Generate_DefaultOptions_ContainsSarifUpload()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("github/codeql-action/upload-sarif@v3");
}
[Fact]
public void Generate_WithCustomName_UsesCustomName()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Name = "Custom Security Scan"
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("name: Custom Security Scan");
}
[Fact]
public void Generate_WithSchedule_ContainsCron()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Triggers = TriggerConfig.WeeklySchedule
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("schedule:");
yaml.Should().Contain("cron: '0 0 * * 0'");
}
[Fact]
public void Generate_WithNoComments_OmitsComments()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
IncludeComments = false
};
var yaml = _generator.Generate(options);
yaml.Should().NotContain("# StellaOps Security Scan Workflow");
}
[Fact]
public void Generate_WithImageScan_UsesImageSyntax()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Scan = ScanConfig.ContainerImage("myregistry.io/myimage:latest")
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("image:myregistry.io/myimage:latest");
}
[Fact]
public void Generate_WithReachability_IncludesReachabilityFlag()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Scan = ScanConfig.DefaultRepository with { IncludeReachability = true }
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("--reachability");
}
[Fact]
public void Generate_WithFailOnSeverity_IncludesExitCode()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Scan = ScanConfig.DefaultRepository with { FailOnSeverity = "high" }
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("--exit-code-on high");
}
[Fact]
public void Generate_WithEnvironmentVariables_IncludesEnvBlock()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
EnvironmentVariables = new Dictionary<string, string>
{
["STELLAOPS_API_KEY"] = "${{ secrets.STELLAOPS_API_KEY }}"
}
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("env:");
yaml.Should().Contain("STELLAOPS_API_KEY:");
}
[Fact]
public void Generate_WithCategory_IncludesCategory()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Upload = UploadConfig.Default with { Category = "stellaops-scan" }
};
var yaml = _generator.Generate(options);
yaml.Should().Contain("category: stellaops-scan");
}
[Fact]
public void Validate_CorrectPlatform_ReturnsValid()
{
var options = WorkflowOptions.GitHubActionsDefault;
var result = _generator.Validate(options);
result.IsValid.Should().BeTrue();
}
[Fact]
public void Validate_WrongPlatform_ReturnsInvalid()
{
var options = WorkflowOptions.GitLabCiDefault;
var result = _generator.Validate(options);
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.Contains("GitLabCi"));
}
}

View File

@@ -0,0 +1,155 @@
// <copyright file="GitLabCiGeneratorTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using FluentAssertions;
using Xunit;
namespace StellaOps.Tools.WorkflowGenerator.Tests;
/// <summary>
/// Tests for <see cref="GitLabCiGenerator"/>.
/// </summary>
[Trait("Category", "Unit")]
public class GitLabCiGeneratorTests
{
private readonly GitLabCiGenerator _generator = new();
[Fact]
public void Platform_ReturnsGitLabCi()
{
_generator.Platform.Should().Be(CiPlatform.GitLabCi);
}
[Fact]
public void PlatformName_ReturnsGitLabCiCd()
{
_generator.PlatformName.Should().Be("GitLab CI/CD");
}
[Fact]
public void DefaultFileName_ReturnsGitLabCiYml()
{
_generator.DefaultFileName.Should().Be(".gitlab-ci.yml");
}
[Fact]
public void Generate_DefaultOptions_ContainsStages()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("stages:");
yaml.Should().Contain("- security");
}
[Fact]
public void Generate_DefaultOptions_ContainsVariables()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("variables:");
yaml.Should().Contain("STELLAOPS_VERSION:");
}
[Fact]
public void Generate_DefaultOptions_ContainsStellaopsJob()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("stellaops-scan:");
yaml.Should().Contain("stage: security");
}
[Fact]
public void Generate_DefaultOptions_ContainsRules()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("rules:");
yaml.Should().Contain("$CI_COMMIT_BRANCH");
}
[Fact]
public void Generate_DefaultOptions_ContainsArtifacts()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("artifacts:");
yaml.Should().Contain("reports:");
yaml.Should().Contain("sast:");
}
[Fact]
public void Generate_DefaultOptions_ContainsCliInstallation()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("before_script:");
yaml.Should().Contain("curl -fsSL https://get.stellaops.io | sh");
}
[Fact]
public void Generate_DefaultOptions_AllowsFailure()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("allow_failure: true");
}
[Fact]
public void Generate_WithFailOnSeverity_DoesNotAllowFailure()
{
var options = WorkflowOptions.GitLabCiDefault with
{
Scan = ScanConfig.DefaultRepository with { FailOnSeverity = "critical" }
};
var yaml = _generator.Generate(options);
yaml.Should().NotContain("allow_failure: true");
}
[Fact]
public void Generate_WithMergeRequest_IncludesMrRule()
{
var options = WorkflowOptions.GitLabCiDefault;
var yaml = _generator.Generate(options);
yaml.Should().Contain("merge_request_event");
}
[Fact]
public void Validate_CorrectPlatform_ReturnsValid()
{
var options = WorkflowOptions.GitLabCiDefault;
var result = _generator.Validate(options);
result.IsValid.Should().BeTrue();
}
[Fact]
public void Validate_WrongPlatform_ReturnsInvalid()
{
var options = WorkflowOptions.GitHubActionsDefault;
var result = _generator.Validate(options);
result.IsValid.Should().BeFalse();
}
}

View File

@@ -0,0 +1,214 @@
// <copyright file="GoldenFixtureTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using FluentAssertions;
using Xunit;
namespace StellaOps.Tools.WorkflowGenerator.Tests;
/// <summary>
/// Golden fixture tests for workflow generation.
/// These tests ensure generated workflows match expected output.
/// </summary>
[Trait("Category", "Unit")]
public class GoldenFixtureTests
{
[Fact]
public void GitHubActions_Minimal_MatchesExpected()
{
var generator = new GitHubActionsGenerator();
var options = new WorkflowOptions
{
Platform = CiPlatform.GitHubActions,
Name = "StellaOps Scan",
Triggers = new TriggerConfig { PushBranches = ["main"] },
Scan = new ScanConfig { ScanPath = "." }
};
var result = generator.Generate(options);
// Should contain workflow name
result.Should().Contain("name: StellaOps Scan");
// Should contain push trigger
result.Should().Contain("push:");
// Should have jobs section
result.Should().Contain("jobs:");
}
[Fact]
public void GitHubActions_WithSchedule_IncludesCron()
{
var generator = new GitHubActionsGenerator();
var options = new WorkflowOptions
{
Platform = CiPlatform.GitHubActions,
Name = "Scheduled Scan",
Triggers = new TriggerConfig
{
Schedule = "0 0 * * *"
},
Scan = new ScanConfig { ScanPath = "." }
};
var result = generator.Generate(options);
result.Should().Contain("schedule:");
result.Should().Contain("- cron: '0 0 * * *'");
}
[Fact]
public void GitHubActions_WithSarifUpload_IncludesCodeScanningAction()
{
var generator = new GitHubActionsGenerator();
var options = new WorkflowOptions
{
Platform = CiPlatform.GitHubActions,
Name = "Scan with SARIF",
Triggers = new TriggerConfig { PushBranches = ["main"] },
Scan = new ScanConfig { ScanPath = "." },
Upload = new UploadConfig
{
UploadSarif = true,
SarifFileName = "results.sarif"
}
};
var result = generator.Generate(options);
result.Should().Contain("github/codeql-action/upload-sarif");
result.Should().Contain("sarif_file: results.sarif");
}
[Fact]
public void GitLabCi_Minimal_MatchesExpected()
{
var generator = new GitLabCiGenerator();
var options = new WorkflowOptions
{
Platform = CiPlatform.GitLabCi,
Name = "stellaops-scan",
Scan = new ScanConfig { ScanPath = "." }
};
var result = generator.Generate(options);
// Should contain stages
result.Should().Contain("stages:");
// Should have scan job
result.Should().Contain("stellaops-scan:");
}
[Fact]
public void GitLabCi_WithRules_IncludesBranchFilter()
{
var generator = new GitLabCiGenerator();
var options = new WorkflowOptions
{
Platform = CiPlatform.GitLabCi,
Name = "stellaops-scan",
Triggers = new TriggerConfig { PushBranches = ["main", "develop"] },
Scan = new ScanConfig { ScanPath = "." }
};
var result = generator.Generate(options);
result.Should().Contain("rules:");
result.Should().Contain("$CI_COMMIT_BRANCH == \"main\"");
result.Should().Contain("$CI_COMMIT_BRANCH == \"develop\"");
}
[Fact]
public void AzureDevOps_Minimal_MatchesExpected()
{
var generator = new AzureDevOpsGenerator();
var options = new WorkflowOptions
{
Platform = CiPlatform.AzureDevOps,
Name = "StellaOps Scan",
Triggers = new TriggerConfig { PushBranches = ["main"] },
Scan = new ScanConfig { ScanPath = "." }
};
var result = generator.Generate(options);
// Should contain trigger configuration
result.Should().Contain("trigger:");
// Should have stages section
result.Should().Contain("stages:");
}
[Fact]
public void AzureDevOps_WithPr_IncludesPrTrigger()
{
var generator = new AzureDevOpsGenerator();
var options = new WorkflowOptions
{
Platform = CiPlatform.AzureDevOps,
Name = "PR Scan",
Triggers = new TriggerConfig { PullRequestBranches = ["main"] },
Scan = new ScanConfig { ScanPath = "." }
};
var result = generator.Generate(options);
result.Should().Contain("pr:");
result.Should().Contain("branches:");
result.Should().Contain("include:");
result.Should().Contain("- main");
}
[Fact]
public void AllPlatforms_WithAllTriggers_GenerateValidYaml()
{
var options = new WorkflowOptions
{
Platform = CiPlatform.GitHubActions,
Name = "Full Test",
Triggers = new TriggerConfig
{
PushBranches = ["main", "release/*"],
PullRequestBranches = ["main"],
Schedule = "0 0 * * 0",
ManualTrigger = true
},
Scan = new ScanConfig
{
ScanPath = "./src"
},
Upload = new UploadConfig
{
UploadSarif = true
}
};
var githubGenerator = new GitHubActionsGenerator();
var gitlabGenerator = new GitLabCiGenerator();
var azureGenerator = new AzureDevOpsGenerator();
var githubResult = githubGenerator.Generate(options);
var gitlabResult = gitlabGenerator.Generate(options with { Platform = CiPlatform.GitLabCi });
var azureResult = azureGenerator.Generate(options with { Platform = CiPlatform.AzureDevOps });
// All should generate non-empty YAML
githubResult.Should().NotBeNullOrEmpty();
gitlabResult.Should().NotBeNullOrEmpty();
azureResult.Should().NotBeNullOrEmpty();
// All should have scan path
githubResult.Should().Contain("./src");
gitlabResult.Should().Contain("./src");
azureResult.Should().Contain("./src");
}
[Fact]
public void Factory_CreatesCorrectGeneratorForPlatform()
{
var factory = new WorkflowGeneratorFactory();
factory.GetGenerator(CiPlatform.GitHubActions).Should().BeOfType<GitHubActionsGenerator>();
factory.GetGenerator(CiPlatform.GitLabCi).Should().BeOfType<GitLabCiGenerator>();
factory.GetGenerator(CiPlatform.AzureDevOps).Should().BeOfType<AzureDevOpsGenerator>();
factory.GetGenerator(CiPlatform.GiteaActions).Should().BeOfType<GitHubActionsGenerator>(); // Uses GH syntax
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\StellaOps.Tools.WorkflowGenerator\StellaOps.Tools.WorkflowGenerator.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.TestKit\StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,98 @@
// <copyright file="WorkflowGeneratorFactoryTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using FluentAssertions;
using Xunit;
namespace StellaOps.Tools.WorkflowGenerator.Tests;
/// <summary>
/// Tests for <see cref="WorkflowGeneratorFactory"/>.
/// </summary>
[Trait("Category", "Unit")]
public class WorkflowGeneratorFactoryTests
{
private readonly WorkflowGeneratorFactory _factory = new();
[Theory]
[InlineData(CiPlatform.GitHubActions)]
[InlineData(CiPlatform.GitLabCi)]
[InlineData(CiPlatform.AzureDevOps)]
[InlineData(CiPlatform.GiteaActions)]
public void GetGenerator_SupportedPlatform_ReturnsGenerator(CiPlatform platform)
{
var generator = _factory.GetGenerator(platform);
generator.Should().NotBeNull();
}
[Fact]
public void GetGenerator_GitHubActions_ReturnsGitHubActionsGenerator()
{
var generator = _factory.GetGenerator(CiPlatform.GitHubActions);
generator.Should().BeOfType<GitHubActionsGenerator>();
}
[Fact]
public void GetGenerator_GitLabCi_ReturnsGitLabCiGenerator()
{
var generator = _factory.GetGenerator(CiPlatform.GitLabCi);
generator.Should().BeOfType<GitLabCiGenerator>();
}
[Fact]
public void GetGenerator_AzureDevOps_ReturnsAzureDevOpsGenerator()
{
var generator = _factory.GetGenerator(CiPlatform.AzureDevOps);
generator.Should().BeOfType<AzureDevOpsGenerator>();
}
[Fact]
public void GetGenerator_GiteaActions_ReturnsGitHubActionsCompatibleGenerator()
{
// Gitea Actions is compatible with GitHub Actions syntax
var generator = _factory.GetGenerator(CiPlatform.GiteaActions);
generator.Should().BeOfType<GitHubActionsGenerator>();
}
[Fact]
public void Generate_ValidOptions_ReturnsYaml()
{
var options = WorkflowOptions.GitHubActionsDefault;
var yaml = _factory.Generate(options);
yaml.Should().NotBeNullOrEmpty();
yaml.Should().Contain("name:");
}
[Fact]
public void Generate_InvalidOptions_ThrowsException()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Scan = new ScanConfig { ImageRef = null, ScanPath = null }
};
var act = () => _factory.Generate(options);
act.Should().Throw<InvalidOperationException>()
.WithMessage("*Invalid options*");
}
[Fact]
public void SupportedPlatforms_ContainsAllPlatforms()
{
var supported = _factory.SupportedPlatforms.ToList();
supported.Should().Contain(CiPlatform.GitHubActions);
supported.Should().Contain(CiPlatform.GitLabCi);
supported.Should().Contain(CiPlatform.AzureDevOps);
supported.Should().Contain(CiPlatform.GiteaActions);
}
}

View File

@@ -0,0 +1,102 @@
// <copyright file="WorkflowOptionsTests.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
// </copyright>
using FluentAssertions;
using Xunit;
namespace StellaOps.Tools.WorkflowGenerator.Tests;
/// <summary>
/// Tests for <see cref="WorkflowOptions"/>.
/// </summary>
[Trait("Category", "Unit")]
public class WorkflowOptionsTests
{
[Fact]
public void Validate_ValidOptions_ReturnsValid()
{
var options = WorkflowOptions.GitHubActionsDefault;
var result = options.Validate();
result.IsValid.Should().BeTrue();
result.Errors.Should().BeEmpty();
}
[Fact]
public void Validate_EmptyName_ReturnsInvalid()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Name = ""
};
var result = options.Validate();
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.Contains("name"));
}
[Fact]
public void Validate_BothImageAndPath_ReturnsInvalid()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Scan = new ScanConfig
{
ImageRef = "myimage:latest",
ScanPath = "."
}
};
var result = options.Validate();
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.Contains("Only one"));
}
[Fact]
public void Validate_NeitherImageNorPath_ReturnsInvalid()
{
var options = WorkflowOptions.GitHubActionsDefault with
{
Scan = new ScanConfig
{
ImageRef = null,
ScanPath = null
}
};
var result = options.Validate();
result.IsValid.Should().BeFalse();
result.Errors.Should().Contain(e => e.Contains("Either"));
}
[Fact]
public void GitHubActionsDefault_HasCorrectPlatform()
{
var options = WorkflowOptions.GitHubActionsDefault;
options.Platform.Should().Be(CiPlatform.GitHubActions);
options.Runner.Should().Be("ubuntu-latest");
}
[Fact]
public void GitLabCiDefault_HasCorrectPlatform()
{
var options = WorkflowOptions.GitLabCiDefault;
options.Platform.Should().Be(CiPlatform.GitLabCi);
}
[Fact]
public void AzureDevOpsDefault_HasCorrectPlatform()
{
var options = WorkflowOptions.AzureDevOpsDefault;
options.Platform.Should().Be(CiPlatform.AzureDevOps);
options.Runner.Should().Be("ubuntu-latest");
}
}