diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs index 0d6d99c..fbb53b2 100644 --- a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs @@ -13,6 +13,7 @@ namespace ADotNet.Clients.Builders /// /// Builder for creating a GitHub pipeline. /// + [Obsolete("No longer in use. Please migrate to GitHubPipelineBuilderV2.")] public class GitHubPipelineBuilder { private readonly GithubPipeline githubPipeline; diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilderV2.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilderV2.cs new file mode 100644 index 0000000..5e8feb4 --- /dev/null +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilderV2.cs @@ -0,0 +1,108 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; + +namespace ADotNet.Clients.Builders +{ + /// + /// Builder for creating a GitHub pipeline. + /// + public class GitHubPipelineBuilderV2 + { + private readonly GithubPipelineV2 githubPipelineV2; + private readonly IADotNetClient aDotNetClient; + + internal GitHubPipelineBuilderV2(IADotNetClient aDotNetClient) + { + this.githubPipelineV2 = new GithubPipelineV2 + { + OnEvents = new Events(), + Jobs = new Dictionary() + }; + + this.aDotNetClient = aDotNetClient; + } + + /// + /// Creates a new instance of the class + /// with a default . + /// + /// A new instance of . + public static GitHubPipelineBuilderV2 CreateNewPipeline() + { + var aDotNetClient = new ADotNetClient(); + + return new GitHubPipelineBuilderV2(aDotNetClient); + } + + /// + /// Sets the name of the GitHub pipeline. + /// + /// The name of the pipeline. + /// The current instance of . + public GitHubPipelineBuilderV2 SetName(string name) + { + this.githubPipelineV2.Name = name; + + return this; + } + + /// + /// Configures the pipeline to trigger on push events for specified branches. + /// + /// The branches to trigger on push events. + /// The current instance of . + public GitHubPipelineBuilderV2 OnPush(params string[] branches) + { + this.githubPipelineV2.OnEvents.Push = new PushEvent + { + Branches = branches + }; + + return this; + } + + /// + /// Configures the pipeline to trigger on pull request events for specified branches. + /// + /// The branches to trigger on pull request events. + /// The current instance of . + public GitHubPipelineBuilderV2 OnPullRequest(params string[] branches) + { + this.githubPipelineV2.OnEvents.PullRequest = new PullRequestEvent + { + Branches = branches + }; + + return this; + } + + /// + /// Adds a job to the GitHub pipeline. + /// + /// The unique identifier for the job. + /// The action to configure the job. + /// The current instance of . + public GitHubPipelineBuilderV2 AddJob(string jobIdentifier, Action configureJob) + { + var jobBuilder = new JobBuilderV2(); + configureJob(jobBuilder); + this.githubPipelineV2.Jobs[jobIdentifier] = jobBuilder.Build(); + + return this; + } + + /// + /// Saves the configured pipeline (yml) to the specified file path. + /// + /// The file path where the pipeline will be saved. + public void SaveToFile(string path) => + this.aDotNetClient.SerializeAndWriteToFile(this.githubPipelineV2, path); + } +} diff --git a/ADotNet/Clients/Builders/JobBuilder.cs b/ADotNet/Clients/Builders/JobBuilder.cs index 883f1f8..ad1bb02 100644 --- a/ADotNet/Clients/Builders/JobBuilder.cs +++ b/ADotNet/Clients/Builders/JobBuilder.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System; using System.Collections.Generic; using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; @@ -14,6 +15,7 @@ namespace ADotNet.Clients.Builders /// /// A builder to create a job for a GitHub Actions workflow. /// + [Obsolete("No longer in use. Please migrate to JobBuilderV2.")] public class JobBuilder { private readonly Job job; diff --git a/ADotNet/Clients/Builders/JobBuilderV2.cs b/ADotNet/Clients/Builders/JobBuilderV2.cs new file mode 100644 index 0000000..9007caa --- /dev/null +++ b/ADotNet/Clients/Builders/JobBuilderV2.cs @@ -0,0 +1,195 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV5s; + +namespace ADotNet.Clients.Builders +{ + /// + /// A builder to create a job for a GitHub Actions workflow. + /// + public class JobBuilderV2 + { + private readonly JobV2 job; + + internal JobBuilderV2() + { + this.job = new JobV2 + { + Steps = new List(), + EnvironmentVariables = null + }; + } + + /// + /// Sets the name of the job. + /// + /// The name of the job. + /// The current instance of . + public JobBuilderV2 WithName(string name) + { + this.job.Name = name; + + return this; + } + + /// + /// Specifies the machine on which the job will run. + /// + /// The machine or environment to run the job on. + /// The current instance of . + public JobBuilderV2 RunsOn(string machine) + { + this.job.RunsOn = machine; + + return this; + } + + /// + /// Adds an environment variable to the job. + /// + /// The key of the environment variable. + /// The value of the environment variable. + /// The current instance of . + public JobBuilderV2 AddEnvironmentVariable(string key, string value) + { + this.job.EnvironmentVariables ??= new Dictionary(); + + this.job.EnvironmentVariables[key] = value; + + return this; + } + + /// + /// Adds multiple environment variables to the job. + /// + /// A dictionary of environment variables to add. + /// The current instance of . + public JobBuilderV2 AddEnvironmentVariables(Dictionary variables) + { + this.job.EnvironmentVariables ??= new Dictionary(); + + foreach (var variable in variables) + { + this.job.EnvironmentVariables[variable.Key] = variable.Value; + } + + return this; + } + + /// + /// Adds a checkout step to the job. + /// + /// The name of the checkout step (default: "Check out"). + /// The current instance of . + public JobBuilderV2 AddCheckoutStep(string name = "Check out") + { + this.job.Steps.Add(new CheckoutTaskV5 { Name = name }); + + return this; + } + + /// + /// Adds a setup step for a specific .NET version to the job. + /// + /// The version of .NET to set up. + /// The name of the setup step (default: "Setup Dot Net Version"). + /// Specifies whether to include prerelease versions. + /// The current instance of . + public JobBuilderV2 AddSetupDotNetStep( + string version, + string stepName = "Setup Dot Net Version", + bool includePrerelease = false) + { + this.job.Steps.Add(new SetupDotNetTaskV5 + { + Name = stepName, + With = new TargetDotNetVersionV5 + { + DotNetVersion = version, + } + }); + + return this; + } + + /// + /// Adds a restore step to the job. + /// + /// The name of the restore step (default: "Restore"). + /// The current instance of . + public JobBuilderV2 AddRestoreStep(string name = "Restore") + { + this.job.Steps.Add(new RestoreTask { Name = name }); + + return this; + } + + /// + /// Adds a build step to the job. + /// + /// The name of the build step (default: "Build"). + /// The current instance of . + public JobBuilderV2 AddBuildStep(string name = "Build") + { + this.job.Steps.Add(new DotNetBuildTask { Name = name }); + + return this; + } + + /// + /// Adds a test step to the job. + /// + /// The name of the test step (default: "Test"). + /// The command to execute the test + /// (default: "dotnet test --no-build --verbosity normal"). + /// The current instance of . + public JobBuilderV2 AddTestStep(string name = "Test", string command = null) + { + this.job.Steps.Add(new TestTask + { + Name = name, + Run = command ?? "dotnet test --no-build --verbosity normal" + }); + + return this; + } + + /// + /// Adds a generic run-based step, optionally with an id so later steps can reference its outputs. + /// + /// The name of the step. + /// The command to execute for this step. + /// The id of the step. + /// The shell to use for the step. + /// The current instance of . + public JobBuilderV2 AddGenericStep( + string name, + string runCommand, + string id = null, + string shell = null) + { + this.job.Steps.Add(new GithubTask + { + Id = id, + Name = name, + Run = runCommand, + Shell = shell + }); + + return this; + } + + /// + /// Builds and returns the configured job. + /// + /// The configured instance. + public JobV2 Build() => this.job; + } +} \ No newline at end of file diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs new file mode 100644 index 0000000..515dde2 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs @@ -0,0 +1,19 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class Credentials + { + [YamlMember(Order = 0, Alias = "username", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Username { get; set; } + + [YamlMember(Order = 1, Alias = "password", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Password { get; set; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/GithubPipelineV2.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/GithubPipelineV2.cs new file mode 100644 index 0000000..3ed6cea --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/GithubPipelineV2.cs @@ -0,0 +1,25 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class GithubPipelineV2 + { + public string Name { get; set; } + + [YamlMember(Alias = "on")] + public Events OnEvents { get; set; } + + [YamlMember(Alias = "env", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary EnvironmentVariables { get; set; } + + [YamlMember(Alias = "jobs")] + public Dictionary Jobs { get; set; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs index 1d47976..3e3bae5 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs @@ -1,9 +1,10 @@ -// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System; using System.Collections.Generic; using System.ComponentModel; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; @@ -11,6 +12,7 @@ namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets { + [Obsolete("No longer in use. Please migrate to JobV2.")] public class Job { [YamlMember(Order = 0, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/JobV2.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/JobV2.cs new file mode 100644 index 0000000..8a840cd --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/JobV2.cs @@ -0,0 +1,60 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.ComponentModel; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class JobV2 + { + [YamlMember(Order = 0, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string Name { get; set; } + + [YamlMember(Order = 1, Alias = "runs-on")] + public virtual string RunsOn { get; set; } + + [YamlMember(Order = 2, Alias = "needs", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string[] Needs { get; set; } + + [YamlMember(Order = 3, Alias = "if", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string If { get; set; } + + [YamlMember(Order = 4, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string Environment { get; set; } + + [YamlMember(Order = 5, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual DefaultValues Defaults { get; set; } + + [YamlMember(Order = 6)] + public virtual List Steps { get; set; } + + [DefaultValue(0)] + [YamlMember(Order = 7, Alias = "timeout-minutes", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual int TimeoutInMinutes { get; set; } + + [YamlMember(Order = 8, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual StrategyV2 Strategy { get; set; } + + [YamlMember(Order = 9, Alias = "env", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary EnvironmentVariables { get; set; } + + [YamlMember(Order = 10, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary Outputs { get; set; } + + [DefaultValue(false)] + [YamlMember(Order = 11, Alias = "continue-on-error", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual bool ContinueOnError { get; set; } + + [YamlMember(Order = 12, Alias = "permissions", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary Permissions { get; set; } + + [YamlMember(Order = 13, Alias = "services", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary Services { get; set; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs new file mode 100644 index 0000000..03397f5 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs @@ -0,0 +1,32 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class Service + { + [YamlMember(Order = 0, Alias = "image", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Image { get; set; } + + [YamlMember(Order = 1, Alias = "credentials", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Credentials Credentials { get; set; } + + [YamlMember(Order = 2, Alias = "env", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary Environment { get; set; } + + [YamlMember(Order = 3, Alias = "ports", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List Ports { get; set; } + + [YamlMember(Order = 4, Alias = "volumes", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List Volumes { get; set; } + + [YamlMember(Order = 5, Alias = "options", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Options { get; set; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs index e5bc155..e4cb020 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs @@ -4,14 +4,17 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System; using System.Collections.Generic; using YamlDotNet.Serialization; namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets { + [Obsolete("No longer in use. Please migrate to StrategyV2.")] public class Strategy { - [YamlMember(Alias = "matrix")] - public Dictionary> Matrix { get; set; } + [Obsolete("This property is now obsolete. Please migrate to MatrixV2.")] + [YamlMember(Order = 0, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary Matrix { get; set; } } } diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/StrategyV2.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/StrategyV2.cs new file mode 100644 index 0000000..1aeec8e --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/StrategyV2.cs @@ -0,0 +1,30 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class StrategyV2 + { + [YamlMember(Order = 0, Alias = "fail-fast", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public bool? FailFast { get; set; } + + [YamlMember(Order = 1, Alias = "max-parallel", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public int? MaxParallel { get; set; } + + [YamlMember(Order = 3, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary Matrix { get; set; } + + [YamlMember(Order = 4, Alias = "include", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List> Include { get; set; } + + [YamlMember(Order = 5, Alias = "exclude", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List> Exclude { get; set; } + } +} diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index dd8dbe5..36ffa96 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -219,6 +219,157 @@ static void Main(string[] args) "dotnet run --project .\\{projectName}\\{projectName}.csproj")) .SaveToFile("github-pipelines-fluent.yaml"); + + + var githubPipelineV2 = new GithubPipelineV2 + { + Name = "test-strategy-services-surface", + + OnEvents = new Events + { + Push = new PushEvent + { + Branches = new[] { "main" } + }, + + PullRequest = new PullRequestEvent + { + Branches = new[] { "main" } + } + }, + + Jobs = new Dictionary + { + ["full_surface"] = new JobV2 + { + Name = "Full Strategy + Services Surface", + RunsOn = BuildMachines.UbuntuLatest, + + Strategy = new StrategyV2 + { + FailFast = false, + MaxParallel = 2, + + Matrix = new Dictionary + { + ["provider"] = new List + { + "sqlserver", + "postgres" + }, + + ["dotnet-version"] = new List + { + "8.0.x", + "10.0.100" + } + }, + + Include = new List> + { + new() + { + ["provider"] = "sqlserver", + ["connection_string"] = + "Server=localhost;Database=TestDb;User Id=sa;" + + "Password=Your_password123!;TrustServerCertificate=True;" + + "MultipleActiveResultSets=true;Pooling=false" + }, + + new() + { + ["provider"] = "postgres", + ["connection_string"] = + "Host=localhost;Database=TestDb;" + + "Username=postgres;Password=postgres;Pooling=false" + }, + + new() + { + ["provider"] = "postgres", + ["dotnet-version"] = "9.0.x", + ["connection_string"] = + "Host=localhost;Database=TestDb;" + + "Username=postgres;Password=postgres;Pooling=false" + } + }, + + Exclude = new List> + { + new() + { + ["provider"] = "sqlserver", + ["dotnet-version"] = "8.0.x" + } + } + }, + + Services = new Dictionary + { + ["postgres"] = new Service + { + Image = "postgres:17", + + Environment = new Dictionary + { + ["POSTGRES_DB"] = "TestDb", + ["POSTGRES_USER"] = "postgres", + ["POSTGRES_PASSWORD"] = "postgres" + }, + + Ports = new List + { + "5432:5432" + }, + + Options = + "--health-cmd pg_isready " + + "--health-interval 10s " + + "--health-timeout 5s " + + "--health-retries 5" + } + }, + + Steps = new List + { + new CheckoutTaskV5 + { + Name = "Check out" + }, + + new SetupDotNetTaskV5 + { + Name = "Setup .NET", + + With = new TargetDotNetVersionV5 + { + DotNetVersion = "${{ matrix.dotnet-version }}" + } + }, + + new RestoreTask + { + Name = "Restore" + }, + + new DotNetBuildTask + { + Name = "Build" + }, + + new GithubTask + { + Name = "Test", + Run = + "dotnet test Test.Core.Tests.Acceptance " + + "--no-build --verbosity normal" + } + } + } + } + }; + + adoClient.SerializeAndWriteToFile(githubPipeline, "github-pipelines-strategy-services.yaml"); } } } diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs index f362b5a..ea1bf98 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System.Collections.Generic; using System.IO; using ADotNet.Clients; using ADotNet.Clients.Builders; @@ -52,7 +53,20 @@ private static GithubPipeline CreateRandomGithubPipeline(string name) => private static GithubPipeline CreateRandomGithubPipeline() => CreateGithubPipelineFiller(name: GetRandomString()).Create(); - private static Filler CreateGithubPipelineFiller(string name) => - new Filler(); + private static Filler CreateGithubPipelineFiller(string name) + { + var filler = new Filler(); + + filler.Setup() + .OnProperty(p => p.EnvironmentVariables) + .Use(() => new Dictionary + { + { GetRandomString(), GetRandomString() }, + { GetRandomString(), GetRandomString() } + }) + .OnType().Use(() => GetRandomString()); + + return filler; + } } } diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs new file mode 100644 index 0000000..2429ba1 --- /dev/null +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs @@ -0,0 +1,144 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using ADotNet.Clients.Builders; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; +using FluentAssertions; +using Moq; +using Xunit; + +namespace ADotNet.Tests.Unit.Clients.Builders +{ + public partial class GitHubPipelineBuilderV2Tests + { + [Fact] + public void ShouldCreateNewPipeline() + { + // given..when + var builder = GitHubPipelineBuilderV2.CreateNewPipeline(); + + // then + builder.Should().NotBeNull(); + } + + [Fact] + public void ShouldSetPipelineName() + { + // given + string inputName = "My GitHub Pipeline"; + string expectedName = inputName; + + // when + var pipelineBuilder = GitHubPipelineBuilderV2.CreateNewPipeline() + .SetName(inputName); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + actualPipeline.Should().NotBeNull(); + actualPipeline.Name.Should().BeEquivalentTo(expectedName); + } + + [Fact] + public void ShouldAddPushTrigger() + { + // given + string[] inputBranches = { "main", "dev" }; + + // when + var pipelineBuilder = GitHubPipelineBuilderV2.CreateNewPipeline() + .OnPush(inputBranches); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + actualPipeline.OnEvents.Push.Should().NotBeNull(); + actualPipeline.OnEvents.Push.Branches.Should().BeEquivalentTo(inputBranches); + } + + [Fact] + public void ShouldAddPullRequestTrigger() + { + // given + string[] inputBranches = { "main", "feature/*" }; + + // when + var pipelineBuilder = GitHubPipelineBuilderV2.CreateNewPipeline() + .OnPullRequest(inputBranches); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + actualPipeline.OnEvents.PullRequest.Should().NotBeNull(); + actualPipeline.OnEvents.PullRequest.Branches.Should().BeEquivalentTo(inputBranches); + } + + [Fact] + public void ShouldAddJobToPipeline() + { + // given + string inputJobName = "build"; + string inputRunsOn = BuildMachines.WindowsLatest; + string inputTaskName = "Restore"; + + string expectedRunsOn = inputRunsOn; + string expectedTaskName = inputTaskName; + + // when + var pipelineBuilder = GitHubPipelineBuilderV2.CreateNewPipeline() + .AddJob(inputJobName, job => + job.RunsOn(inputRunsOn) + .AddRestoreStep(inputTaskName)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Should().NotBeNull(); + actualJob.RunsOn.Should().Be(expectedRunsOn); + actualJob.Steps.Should().HaveCount(1); + + actualJob.Steps[0].Should().BeOfType() + .Which.Name.Should().Be(expectedTaskName); + } + + [Fact] + public void ShouldSavePipelineToFile() + { + // given + string randomFileName = GetRandomFileName(); + string randomPipelineName = GetRandomString(); + + GithubPipelineV2 randomPipeline = + CreateRandomGithubPipelineV2(randomPipelineName); + + GithubPipelineV2 inputPipeline = randomPipeline; + string inputPath = randomFileName; + string inputPipelineName = randomPipelineName; + + this.aDotNetClientMock.Setup(client => + client.SerializeAndWriteToFile( + inputPipeline, + inputPath)) + .Verifiable(); + + this.gitHubPipelineBuilderV2.SetName(inputPipelineName); + + // when + this.gitHubPipelineBuilderV2.SaveToFile(inputPath); + + // then + this.aDotNetClientMock.Verify(client => + client.SerializeAndWriteToFile( + It.IsAny(), + It.IsAny()), + Times.Once); + + this.aDotNetClientMock.VerifyNoOtherCalls(); + } + } +} diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.cs new file mode 100644 index 0000000..fed3fb6 --- /dev/null +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.cs @@ -0,0 +1,72 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.IO; +using ADotNet.Clients; +using ADotNet.Clients.Builders; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; +using Moq; +using Tynamix.ObjectFiller; + +namespace ADotNet.Tests.Unit.Clients.Builders +{ + public partial class GitHubPipelineBuilderV2Tests + { + private readonly Mock aDotNetClientMock; + private readonly GitHubPipelineBuilderV2 gitHubPipelineBuilderV2; + + public GitHubPipelineBuilderV2Tests() + { + this.aDotNetClientMock = new Mock(); + + this.gitHubPipelineBuilderV2 = new GitHubPipelineBuilderV2( + aDotNetClient: aDotNetClientMock.Object); + } + + private static GithubPipelineV2 GetPipeline(GitHubPipelineBuilderV2 builder) + { + var privateField = typeof(GitHubPipelineBuilderV2) + .GetField( + name: "githubPipelineV2", + bindingAttr: System.Reflection.BindingFlags.NonPublic + | System.Reflection.BindingFlags.Instance); + + return (GithubPipelineV2)privateField.GetValue(builder); + } + + private static string GetRandomString() => + new MnemonicString(wordCount: GetRandomNumber()).GetValue(); + + private static int GetRandomNumber() => + new IntRange(min: 2, max: 10).GetValue(); + + private static string GetRandomFileName() => + Path.GetRandomFileName(); + + private static GithubPipelineV2 CreateRandomGithubPipelineV2(string name) => + CreateGithubPipelineV2Filler(name).Create(); + + private static GithubPipelineV2 CreateRandomGithubPipelineV2() => + CreateGithubPipelineV2Filler(name: GetRandomString()).Create(); + + private static Filler CreateGithubPipelineV2Filler(string name) + { + var filler = new Filler(); + + filler.Setup() + .OnProperty(p => p.EnvironmentVariables) + .Use(() => new Dictionary + { + { GetRandomString(), GetRandomString() }, + { GetRandomString(), GetRandomString() } + }) + .OnType().Use(() => GetRandomString()); + + return filler; + } + } +}