Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests #38

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/DependencyUpdated.Core/Config/AzureDevOpsConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace DependencyUpdated.Core.Config;

public class AzureDevOpsConfig : IValidatableObject
public record AzureDevOpsConfig : IValidatableObject
{
public string? Username { get; set; }

Expand Down
4 changes: 2 additions & 2 deletions src/DependencyUpdated.Core/Config/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace DependencyUpdated.Core.Config;

public sealed class Project : IValidatableObject
public sealed record Project : IValidatableObject
{
public ProjectType Type { get; set; }

Expand Down Expand Up @@ -55,7 +55,7 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex

if (Groups.Count == 0)
{
yield return new ValidationResult($"Missing ${nameof(Groups)}.");
yield return new ValidationResult($"Missing {nameof(Groups)}.");
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/DependencyUpdated.Repositories.AzureDevOps/AzureDevOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
using LibGit2Sharp.Handlers;
using Microsoft.Extensions.Options;
using Serilog;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

namespace DependencyUpdated.Repositories.AzureDevOps;

[ExcludeFromCodeCoverage]
internal sealed class AzureDevOps(TimeProvider timeProvider, IOptions<UpdaterConfig> config, ILogger logger)
: IRepositoryProvider
{
Expand Down
69 changes: 69 additions & 0 deletions tests/DependencyUpdated.Core.UnitTests/AzueDevOpsConfigTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using DependencyUpdated.Core.Config;
using DependencyUpdated.Core.Models.Enums;
using System.ComponentModel.DataAnnotations;

namespace DependencyUpdated.Core.UnitTests;

public class AzureDevOpsConfigTest : BaseConfigTest
{
protected override IEnumerable<Tuple<IValidatableObject, IEnumerable<ValidationResult>>> TestCases
{
get
{
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(CreateValidConfig(),
ArraySegment<ValidationResult>.Empty);
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidConfig() with { Username = string.Empty },
new[]
{
new ValidationResult(
$"{nameof(AzureDevOpsConfig.Username)} must be provided in {nameof(AzureDevOpsConfig)}")
});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidConfig() with { Email = string.Empty },
new[]
{
new ValidationResult(
$"{nameof(AzureDevOpsConfig.Email)} must be provided in {nameof(AzureDevOpsConfig)}")
});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidConfig() with { Organization = string.Empty },
new[]
{
new ValidationResult(
$"{nameof(AzureDevOpsConfig.Organization)} must be provided in {nameof(AzureDevOpsConfig)}")
});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidConfig() with { Project = string.Empty },
new[]
{
new ValidationResult(
$"{nameof(AzureDevOpsConfig.Project)} must be provided in {nameof(AzureDevOpsConfig)}")
});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidConfig() with { Repository = string.Empty },
new[]
{
new ValidationResult(
$"{nameof(AzureDevOpsConfig.Repository)} must be provided in {nameof(AzureDevOpsConfig)}")
});
}
}

private static AzureDevOpsConfig CreateValidConfig()
{
return new AzureDevOpsConfig()
{
Username = "User",
Email = "Email",
Project = "Project",
Organization = "Organization",
Repository = "Repository",
AutoComplete = true,
BranchName = "Branch",
PAT = "PAT",
TargetBranchName = "TargetBranch",
WorkItemId = 1500
};
}
}
26 changes: 26 additions & 0 deletions tests/DependencyUpdated.Core.UnitTests/BaseConfigTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using FluentAssertions;
using FluentAssertions.Execution;
using System.ComponentModel.DataAnnotations;
using Xunit;

namespace DependencyUpdated.Core.UnitTests;

public abstract class BaseConfigTest
{
protected abstract IEnumerable<Tuple<IValidatableObject, IEnumerable<ValidationResult>>> TestCases { get; }

[Fact]
public void Validate()
{
var data = TestCases.ToList();
using (new AssertionScope())
{
foreach (var test in data)
{
var context = new ValidationContext(test.Item1);
var result = test.Item1.Validate(context);
result.Should().BeEquivalentTo(test.Item2);
}
}
}
}
56 changes: 56 additions & 0 deletions tests/DependencyUpdated.Core.UnitTests/ProjectConfigTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using DependencyUpdated.Core.Config;
using DependencyUpdated.Core.Models.Enums;
using System.ComponentModel.DataAnnotations;

namespace DependencyUpdated.Core.UnitTests;

public class ProjectConfigTest : BaseConfigTest
{
protected override IEnumerable<Tuple<IValidatableObject, IEnumerable<ValidationResult>>> TestCases
{
get
{
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(CreateValidProject(),
ArraySegment<ValidationResult>.Empty);
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidProject() with { Directories = ArraySegment<string>.Empty },
new[]{ new ValidationResult($"{nameof(Project.Directories)} cannot be empty")});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidProject() with { Directories = ["TestPath"] },
new[]{ new ValidationResult("Path TestPath not found")});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidProject() with { EachDirectoryAsSeparate = false, Name = string.Empty },
new[]
{
new ValidationResult(
$"{nameof(Project.Name)} must be provided when {nameof(Project.EachDirectoryAsSeparate)} is not set")
});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidProject() with { EachDirectoryAsSeparate = true, Name = "TestName" },
new[]
{
new ValidationResult(
$"{nameof(Project.Name)} must not be provided when {nameof(Project.EachDirectoryAsSeparate)} is set")
});
yield return Tuple.Create<IValidatableObject, IEnumerable<ValidationResult>>(
CreateValidProject() with { Groups = ArraySegment<string>.Empty },
new[]{ new ValidationResult($"Missing {nameof(Project.Groups)}.")});
}
}

private static Project CreateValidProject()
{
return new Project()
{
DependencyConfigurations = new[] { "Test" },
Directories = new[] { Environment.CurrentDirectory },
Exclude = new[] { "Exclude" },
Include = new[] { "Include" },
Groups = new[] { "Groups" },
Name = "Name",
Type = ProjectType.DotNet,
Version = VersionUpdateType.Major,
EachDirectoryAsSeparate = false
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@
<ProjectReference Include="..\..\src\DependencyUpdated.Projects.DotNet\DependencyUpdated.Projects.DotNet.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="Projects\Nuget.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using DependencyUpdated.Core.Models.Enums;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.Extensions.Caching.Memory;
using NSubstitute;
using Serilog;
using Xunit;
Expand All @@ -14,13 +13,13 @@ public class DotNetUpdaterTests
{
private readonly DotNetUpdater _target;
private readonly ILogger _logger;
private readonly IMemoryCache _memoryCahce;
private readonly MockMemoryCache _memoryCahce;
private readonly string _searchPath;

public DotNetUpdaterTests()
{
_logger = Substitute.For<ILogger>();
_memoryCahce = Substitute.For<IMemoryCache>();
_memoryCahce = new MockMemoryCache();
_target = new DotNetUpdater(_logger, _memoryCahce);
_searchPath = "Projects";
}
Expand Down Expand Up @@ -63,4 +62,81 @@ public async Task ExtractAllPackages_Should_ReturnPackagesFromCsProjFile()
packages.Should().BeEquivalentTo(expectedResult);
}
}

[Fact]
public async Task GetVersions_Should_ReturnVersionFromCache()
{
// Arrange
var packages = new DependencyDetails("TestName", new Version(1, 0, 0));
var projectConfiguration = new Project();
var cacheData = new List<DependencyDetails> { new("TestName", new Version(1, 2, 3)) };
_memoryCahce.AddEntry(packages.Name, cacheData);

// Act
var result = await _target.GetVersions(packages, projectConfiguration);

// Assert
using (new AssertionScope())
{
result.Should().BeEquivalentTo(cacheData);
}
}

[Fact]
public async Task GetVersions_Should_ThrowForMissingDependencyConfiguration()
{
// Arrange
var packages = new DependencyDetails("TestName", new Version(1, 0, 0));
var projectConfiguration = new Project();

// Act, Assert
await _target.Awaiting(x => x.GetVersions(packages, projectConfiguration)).Should()
.ThrowExactlyAsync<InvalidOperationException>();
}

[Fact]
public async Task GetVersions_Should_GetPackagesFromSource()
{
// Arrange
var packages = new DependencyDetails("Serilog", new Version(1, 0, 0));
var projectConfiguration = new Project() { Type = ProjectType.DotNet };
projectConfiguration.ApplyDefaultValue();
projectConfiguration.DependencyConfigurations = projectConfiguration.DependencyConfigurations
.Concat(new[] { "./Projects/Nuget.config" }).ToArray();

// Act
var result = await _target.GetVersions(packages, projectConfiguration);

// Assert
using (new AssertionScope())
{
result.Should().NotBeNullOrEmpty();
result.Should().ContainEquivalentOf(new DependencyDetails("Serilog", new Version(4, 0, 1, 0)));
}
}

[Fact]
public async Task UpdateCsProj_Should_UpdateVersion()
{
// Arrange
var projectToUpdate = "testProj.csproj";
if (File.Exists(projectToUpdate))
{
File.Delete(projectToUpdate);
}
File.Copy("./Projects/SampleProject.csproj", projectToUpdate);

// Act
var result = _target.HandleProjectUpdate([projectToUpdate],
new List<DependencyDetails>() { new("Serilog", new Version(4, 0, 0)) });

// Assert
using (new AssertionScope())
{
result.Should().ContainEquivalentOf(new UpdateResult("Serilog", "3.0.0", "4.0.0"));
var packagesFromfile = await _target.ExtractAllPackages([projectToUpdate]);
packagesFromfile.Should().ContainEquivalentOf(new DependencyDetails("Serilog", new Version(4, 0, 0, 0)));
File.Delete(projectToUpdate);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;

namespace DependencyUpdated.Projects.DotNet.UnitTests;

internal sealed class MockMemoryCache : IMemoryCache
{
private Dictionary<object, MockEntry> Cache { get; } = new();

public void Dispose()
{
}

public bool TryGetValue(object key, out object? value)
{
var exists = Cache.TryGetValue(key, out var dictValue);
value = dictValue?.Value;
return exists;
}

public ICacheEntry CreateEntry(object key)
{
return new MockEntry() { Key = key };
}

public void AddEntry(object key, object value)
{
Cache.Add(key, new MockEntry() { Key = key, Value = value });
}

public void Remove(object key)
{
Cache.Remove(key);
}
}

internal sealed class MockEntry : ICacheEntry
{
public void Dispose()
{
}

public object Key { get; init; }

Check warning on line 43 in tests/DependencyUpdated.Projects.DotNet.UnitTests/MockMemoryCache.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Key' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 43 in tests/DependencyUpdated.Projects.DotNet.UnitTests/MockMemoryCache.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Key' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public object? Value { get; set; }

public DateTimeOffset? AbsoluteExpiration { get; set; }

public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }

public TimeSpan? SlidingExpiration { get; set; }

public IList<IChangeToken> ExpirationTokens { get; }

Check warning on line 53 in tests/DependencyUpdated.Projects.DotNet.UnitTests/MockMemoryCache.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'ExpirationTokens' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 53 in tests/DependencyUpdated.Projects.DotNet.UnitTests/MockMemoryCache.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'ExpirationTokens' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; }

Check warning on line 55 in tests/DependencyUpdated.Projects.DotNet.UnitTests/MockMemoryCache.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'PostEvictionCallbacks' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 55 in tests/DependencyUpdated.Projects.DotNet.UnitTests/MockMemoryCache.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'PostEvictionCallbacks' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

public CacheItemPriority Priority { get; set; }

public long? Size { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
Loading