Add unittests
This commit is contained in:
@@ -11,7 +11,8 @@
|
|||||||
"Bash(npm create:*)",
|
"Bash(npm create:*)",
|
||||||
"Bash(npm install:*)",
|
"Bash(npm install:*)",
|
||||||
"Bash(npm run:*)",
|
"Bash(npm run:*)",
|
||||||
"Bash(npx playwright:*)"
|
"Bash(npx playwright:*)",
|
||||||
|
"Bash(dotnet test:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@@ -7,10 +7,28 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# ── Unit Tests ─────────────────────────────────────────────────────────────
|
||||||
|
unit-tests:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up .NET
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: '10.0.x'
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: dotnet test src/backend/tests/unit/Randall.Domain.UnitTests.csproj --logger "console;verbosity=normal"
|
||||||
|
|
||||||
# ── E2E Tests ──────────────────────────────────────────────────────────────
|
# ── E2E Tests ──────────────────────────────────────────────────────────────
|
||||||
e2e:
|
e2e:
|
||||||
name: E2E Tests
|
name: E2E Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: unit-tests
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
@@ -5,4 +5,8 @@
|
|||||||
<Project Path="src/Randall.Domain/Randall.Domain.csproj" />
|
<Project Path="src/Randall.Domain/Randall.Domain.csproj" />
|
||||||
<Project Path="src/Randall.Infrastructure/Randall.Infrastructure.csproj" />
|
<Project Path="src/Randall.Infrastructure/Randall.Infrastructure.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
|
<Folder Name="/tests/" />
|
||||||
|
<Folder Name="/tests/unit/">
|
||||||
|
<Project Path="tests/unit/Randall.Domain.UnitTests.csproj" />
|
||||||
|
</Folder>
|
||||||
</Solution>
|
</Solution>
|
||||||
|
|||||||
53
src/backend/tests/unit/Common/ResultTests.cs
Normal file
53
src/backend/tests/unit/Common/ResultTests.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using Randall.Domain.Common;
|
||||||
|
|
||||||
|
namespace Randall.Domain.UnitTests.Common;
|
||||||
|
|
||||||
|
public class ResultTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Success_IsSuccessIsTrue()
|
||||||
|
{
|
||||||
|
var result = Result.Success();
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
Assert.Null(result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Failure_IsSuccessIsFalse()
|
||||||
|
{
|
||||||
|
var result = Result.Failure("Something went wrong");
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Equal("Something went wrong", result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SuccessOfT_ContainsValue()
|
||||||
|
{
|
||||||
|
var result = Result.Success(42);
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
Assert.Equal(42, result.Value);
|
||||||
|
Assert.Null(result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FailureOfT_ValueIsDefault()
|
||||||
|
{
|
||||||
|
var result = Result.Failure<int>("error");
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Equal(default, result.Value);
|
||||||
|
Assert.Equal("error", result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FailureOfT_ReferenceType_ValueIsNull()
|
||||||
|
{
|
||||||
|
var result = Result.Failure<string>("error");
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Null(result.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/backend/tests/unit/Randall.Domain.UnitTests.csproj
Normal file
25
src/backend/tests/unit/Randall.Domain.UnitTests.csproj
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Randall.Domain\Randall.Domain.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using Randall.Domain.Reservations;
|
||||||
|
|
||||||
|
namespace Randall.Domain.UnitTests.Reservations;
|
||||||
|
|
||||||
|
public class ReservationCancelTests
|
||||||
|
{
|
||||||
|
private static Reservation CreateActiveReservation()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
return Reservation.Create(Guid.NewGuid(), "jane@company.com", "Jane", today, today).Value!;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Cancel_ActiveReservation_Succeeds()
|
||||||
|
{
|
||||||
|
var reservation = CreateActiveReservation();
|
||||||
|
|
||||||
|
var result = reservation.Cancel();
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
Assert.Equal(ReservationStatus.Cancelled, reservation.Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Cancel_AlreadyCancelledReservation_Fails()
|
||||||
|
{
|
||||||
|
var reservation = CreateActiveReservation();
|
||||||
|
reservation.Cancel();
|
||||||
|
|
||||||
|
var result = reservation.Cancel();
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Contains("already cancelled", result.Error, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Cancel_DoesNotChangeOtherFields()
|
||||||
|
{
|
||||||
|
var reservation = CreateActiveReservation();
|
||||||
|
var originalDate = reservation.Date;
|
||||||
|
var originalEmail = reservation.EmployeeEmail;
|
||||||
|
|
||||||
|
reservation.Cancel();
|
||||||
|
|
||||||
|
Assert.Equal(originalDate, reservation.Date);
|
||||||
|
Assert.Equal(originalEmail, reservation.EmployeeEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
using Randall.Domain.Reservations;
|
||||||
|
|
||||||
|
namespace Randall.Domain.UnitTests.Reservations;
|
||||||
|
|
||||||
|
public class ReservationCreateTests
|
||||||
|
{
|
||||||
|
private static readonly Guid WorkplaceId = Guid.NewGuid();
|
||||||
|
private const string Email = "jane@company.com";
|
||||||
|
private const string Name = "Jane Smith";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithValidFutureDate_Succeeds()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, today, today);
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
Assert.NotNull(result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithTodayAsDate_Succeeds()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, today, today);
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithMaxAdvanceDate_Succeeds()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var maxDate = today.AddDays(Reservation.MaxAdvanceDays);
|
||||||
|
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, maxDate, today);
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithPastDate_Fails()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var yesterday = today.AddDays(-1);
|
||||||
|
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, yesterday, today);
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Contains("past", result.Error, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_BeyondMaxAdvanceDays_Fails()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var tooFar = today.AddDays(Reservation.MaxAdvanceDays + 1);
|
||||||
|
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, tooFar, today);
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Contains(Reservation.MaxAdvanceDays.ToString(), result.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_SetsStatusToActive()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, today, today);
|
||||||
|
|
||||||
|
Assert.Equal(ReservationStatus.Active, result.Value!.Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_PopulatesAllFields()
|
||||||
|
{
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, today, today);
|
||||||
|
|
||||||
|
var reservation = result.Value!;
|
||||||
|
Assert.Equal(WorkplaceId, reservation.WorkplaceId);
|
||||||
|
Assert.Equal(Email, reservation.EmployeeEmail);
|
||||||
|
Assert.Equal(Name, reservation.EmployeeName);
|
||||||
|
Assert.Equal(today, reservation.Date);
|
||||||
|
Assert.NotEqual(Guid.Empty, reservation.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_SetsCreatedAtToApproximatelyNow()
|
||||||
|
{
|
||||||
|
var before = DateTime.UtcNow;
|
||||||
|
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||||
|
var result = Reservation.Create(WorkplaceId, Email, Name, today, today);
|
||||||
|
var after = DateTime.UtcNow;
|
||||||
|
|
||||||
|
Assert.InRange(result.Value!.CreatedAt, before, after);
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/backend/tests/unit/Users/UserCreateTests.cs
Normal file
84
src/backend/tests/unit/Users/UserCreateTests.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using Randall.Domain.Users;
|
||||||
|
|
||||||
|
namespace Randall.Domain.UnitTests.Users;
|
||||||
|
|
||||||
|
public class UserCreateTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithValidData_Succeeds()
|
||||||
|
{
|
||||||
|
var result = User.Create("jane@company.com", "Jane Smith", "hash");
|
||||||
|
|
||||||
|
Assert.True(result.IsSuccess);
|
||||||
|
Assert.NotNull(result.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_NormalisesEmailToLowercase()
|
||||||
|
{
|
||||||
|
var result = User.Create("JANE@COMPANY.COM", "Jane Smith", "hash");
|
||||||
|
|
||||||
|
Assert.Equal("jane@company.com", result.Value!.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_TrimsNameWhitespace()
|
||||||
|
{
|
||||||
|
var result = User.Create("jane@company.com", " Jane Smith ", "hash");
|
||||||
|
|
||||||
|
Assert.Equal("Jane Smith", result.Value!.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithEmptyEmail_Fails()
|
||||||
|
{
|
||||||
|
var result = User.Create("", "Jane Smith", "hash");
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Contains("email", result.Error, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithWhitespaceEmail_Fails()
|
||||||
|
{
|
||||||
|
var result = User.Create(" ", "Jane Smith", "hash");
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_WithEmptyName_Fails()
|
||||||
|
{
|
||||||
|
var result = User.Create("jane@company.com", "", "hash");
|
||||||
|
|
||||||
|
Assert.False(result.IsSuccess);
|
||||||
|
Assert.Contains("name", result.Error, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_RegularUser_IsNotApprovedByDefault()
|
||||||
|
{
|
||||||
|
var result = User.Create("jane@company.com", "Jane Smith", "hash");
|
||||||
|
|
||||||
|
Assert.False(result.Value!.IsApproved);
|
||||||
|
Assert.False(result.Value!.IsAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_AdminUser_IsAutoApproved()
|
||||||
|
{
|
||||||
|
var result = User.Create("admin@company.com", "Admin", "hash", isAdmin: true);
|
||||||
|
|
||||||
|
Assert.True(result.Value!.IsAdmin);
|
||||||
|
Assert.True(result.Value!.IsApproved);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_AssignsUniqueId()
|
||||||
|
{
|
||||||
|
var a = User.Create("a@company.com", "A", "hash").Value!;
|
||||||
|
var b = User.Create("b@company.com", "B", "hash").Value!;
|
||||||
|
|
||||||
|
Assert.NotEqual(a.Id, b.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/backend/tests/unit/Users/UserStateTests.cs
Normal file
42
src/backend/tests/unit/Users/UserStateTests.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using Randall.Domain.Users;
|
||||||
|
|
||||||
|
namespace Randall.Domain.UnitTests.Users;
|
||||||
|
|
||||||
|
public class UserStateTests
|
||||||
|
{
|
||||||
|
private static User CreateRegularUser() =>
|
||||||
|
User.Create("jane@company.com", "Jane Smith", "hash").Value!;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Approve_SetsIsApprovedToTrue()
|
||||||
|
{
|
||||||
|
var user = CreateRegularUser();
|
||||||
|
|
||||||
|
user.Approve();
|
||||||
|
|
||||||
|
Assert.True(user.IsApproved);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeAdmin_SetsIsAdminAndIsApproved()
|
||||||
|
{
|
||||||
|
var user = CreateRegularUser();
|
||||||
|
|
||||||
|
user.MakeAdmin();
|
||||||
|
|
||||||
|
Assert.True(user.IsAdmin);
|
||||||
|
Assert.True(user.IsApproved);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MakeAdmin_OnAlreadyApprovedUser_RemainsApproved()
|
||||||
|
{
|
||||||
|
var user = CreateRegularUser();
|
||||||
|
user.Approve();
|
||||||
|
|
||||||
|
user.MakeAdmin();
|
||||||
|
|
||||||
|
Assert.True(user.IsApproved);
|
||||||
|
Assert.True(user.IsAdmin);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/backend/tests/unit/Workplaces/WorkplaceTests.cs
Normal file
61
src/backend/tests/unit/Workplaces/WorkplaceTests.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using Randall.Domain.Workplaces;
|
||||||
|
|
||||||
|
namespace Randall.Domain.UnitTests.Workplaces;
|
||||||
|
|
||||||
|
public class WorkplaceTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Create_IsActiveByDefault()
|
||||||
|
{
|
||||||
|
var workplace = new Workplace("A1", "Pod A");
|
||||||
|
|
||||||
|
Assert.True(workplace.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_SetsNameAndLocation()
|
||||||
|
{
|
||||||
|
var workplace = new Workplace("A1", "Pod A");
|
||||||
|
|
||||||
|
Assert.Equal("A1", workplace.Name);
|
||||||
|
Assert.Equal("Pod A", workplace.Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_AssignsNonEmptyId()
|
||||||
|
{
|
||||||
|
var workplace = new Workplace("A1", "Pod A");
|
||||||
|
|
||||||
|
Assert.NotEqual(Guid.Empty, workplace.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Deactivate_SetsIsActiveToFalse()
|
||||||
|
{
|
||||||
|
var workplace = new Workplace("A1", "Pod A");
|
||||||
|
|
||||||
|
workplace.Deactivate();
|
||||||
|
|
||||||
|
Assert.False(workplace.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Activate_AfterDeactivate_SetsIsActiveToTrue()
|
||||||
|
{
|
||||||
|
var workplace = new Workplace("A1", "Pod A");
|
||||||
|
workplace.Deactivate();
|
||||||
|
|
||||||
|
workplace.Activate();
|
||||||
|
|
||||||
|
Assert.True(workplace.IsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_TwoWorkplaces_HaveDifferentIds()
|
||||||
|
{
|
||||||
|
var a = new Workplace("A1", "Pod A");
|
||||||
|
var b = new Workplace("A2", "Pod A");
|
||||||
|
|
||||||
|
Assert.NotEqual(a.Id, b.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user