Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using NUnit.Framework;
using Platform.Prerequisites.Domain;
using Platform.Prerequisites.Infrastructure;

namespace Platform.Prerequisites.Tests;

[TestFixture]
public class InMemoryPrerequisiteRepositoryTests
{
private readonly Guid _labA = Guid.NewGuid();
private readonly Guid _labB = Guid.NewGuid();

[Test]
public async Task AddAsync_NewPrerequisite_IsSaved()
{
var repository = new InMemoryPrerequisiteRepository();

AddPrerequisiteResult result =
await repository.AddAsync(new LabPrerequisite(_labA, _labB));
var saved = await repository.GetForLaboratoryAsync(_labA);

Assert.Multiple(() =>
{
Assert.That(result, Is.EqualTo(AddPrerequisiteResult.Added));
Assert.That(saved, Has.Count.EqualTo(1));
});
}

[Test]
public async Task AddAsync_Duplicate_IsRejected()
{
var repository = new InMemoryPrerequisiteRepository();
await repository.AddAsync(new LabPrerequisite(_labA, _labB));

AddPrerequisiteResult result =
await repository.AddAsync(new LabPrerequisite(_labA, _labB));
var saved = await repository.GetForLaboratoryAsync(_labA);

Assert.Multiple(() =>
{
Assert.That(result, Is.EqualTo(AddPrerequisiteResult.Duplicate));
Assert.That(saved, Has.Count.EqualTo(1));
});
}

[Test]
public async Task AddAsync_Cycle_IsRejected()
{
var repository = new InMemoryPrerequisiteRepository();
await repository.AddAsync(new LabPrerequisite(_labA, _labB));

AddPrerequisiteResult result =
await repository.AddAsync(new LabPrerequisite(_labB, _labA));
var saved = await repository.GetForLaboratoryAsync(_labB);

Assert.Multiple(() =>
{
Assert.That(result, Is.EqualTo(AddPrerequisiteResult.WouldCreateCycle));
Assert.That(saved, Is.Empty);
});
}

[Test]
public async Task RemoveAsync_ExistingPrerequisite_ReturnsTrue()
{
var repository = new InMemoryPrerequisiteRepository();
await repository.AddAsync(new LabPrerequisite(_labA, _labB));

bool removed = await repository.RemoveAsync(new LabPrerequisite(_labA, _labB));
var saved = await repository.GetForLaboratoryAsync(_labA);

Assert.Multiple(() =>
{
Assert.That(removed, Is.True);
Assert.That(saved, Is.Empty);
});
}

[Test]
public async Task RemoveAsync_MissingPrerequisite_ReturnsFalse()
{
var repository = new InMemoryPrerequisiteRepository();

bool removed = await repository.RemoveAsync(new LabPrerequisite(_labA, _labB));

Assert.That(removed, Is.False);
}
}
39 changes: 39 additions & 0 deletions backend/Platform.Prerequisites.Tests/LabPrerequisiteTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using NUnit.Framework;
using Platform.Prerequisites.Domain;

namespace Platform.Prerequisites.Tests;

[TestFixture]
public class LabPrerequisiteTests
{
[Test]
public void Constructor_ValidLaboratories_SavesBothIds()
{
Guid laboratoryId = Guid.NewGuid();
Guid requiredId = Guid.NewGuid();

var prerequisite = new LabPrerequisite(laboratoryId, requiredId);

Assert.Multiple(() =>
{
Assert.That(prerequisite.LaboratoryId, Is.EqualTo(laboratoryId));
Assert.That(prerequisite.RequiredLaboratoryId, Is.EqualTo(requiredId));
});
}

[Test]
public void Constructor_SameLaboratory_IsRejected()
{
Guid laboratoryId = Guid.NewGuid();

Assert.Throws<ArgumentException>(
() => new LabPrerequisite(laboratoryId, laboratoryId));
}

[Test]
public void Constructor_EmptyId_IsRejected()
{
Assert.Throws<ArgumentException>(
() => new LabPrerequisite(Guid.Empty, Guid.NewGuid()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>latest</LangVersion>
<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.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit.Analyzers" Version="4.7.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Platform.Prerequisites\Platform.Prerequisites.csproj" />
</ItemGroup>

</Project>
106 changes: 106 additions & 0 deletions backend/Platform.Prerequisites.Tests/PrerequisiteRulesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using NUnit.Framework;
using Platform.Prerequisites.Domain;

namespace Platform.Prerequisites.Tests;

[TestFixture]
public class PrerequisiteRulesTests
{
[Test]
public void IsDuplicate_SamePairExists_ReturnsTrue()
{
Guid labA = Guid.NewGuid();
Guid labB = Guid.NewGuid();
var existing = new[] { new LabPrerequisite(labA, labB) };

bool result = PrerequisiteRules.IsDuplicate(
existing,
new LabPrerequisite(labA, labB));

Assert.That(result, Is.True);
}

[Test]
public void IsDuplicate_DifferentPair_ReturnsFalse()
{
Guid labA = Guid.NewGuid();
Guid labB = Guid.NewGuid();
Guid labC = Guid.NewGuid();
var existing = new[] { new LabPrerequisite(labA, labB) };

bool result = PrerequisiteRules.IsDuplicate(
existing,
new LabPrerequisite(labA, labC));

Assert.That(result, Is.False);
}

[Test]
public void WouldCreateCycle_TwoLaboratories_ReturnsTrue()
{
Guid labA = Guid.NewGuid();
Guid labB = Guid.NewGuid();
var existing = new[] { new LabPrerequisite(labA, labB) };

bool result = PrerequisiteRules.WouldCreateCycle(
existing,
new LabPrerequisite(labB, labA));

Assert.That(result, Is.True);
}

[Test]
public void WouldCreateCycle_ThreeLaboratories_ReturnsTrue()
{
Guid labA = Guid.NewGuid();
Guid labB = Guid.NewGuid();
Guid labC = Guid.NewGuid();
var existing = new[]
{
new LabPrerequisite(labA, labB),
new LabPrerequisite(labB, labC)
};

bool result = PrerequisiteRules.WouldCreateCycle(
existing,
new LabPrerequisite(labC, labA));

Assert.That(result, Is.True);
}

[Test]
public void WouldCreateCycle_NormalChain_ReturnsFalse()
{
Guid labA = Guid.NewGuid();
Guid labB = Guid.NewGuid();
Guid labC = Guid.NewGuid();
var existing = new[] { new LabPrerequisite(labA, labB) };

bool result = PrerequisiteRules.WouldCreateCycle(
existing,
new LabPrerequisite(labB, labC));

Assert.That(result, Is.False);
}

[Test]
public void WouldCreateCycle_SharedPrerequisite_ReturnsFalse()
{
Guid labA = Guid.NewGuid();
Guid labB = Guid.NewGuid();
Guid labC = Guid.NewGuid();
Guid labD = Guid.NewGuid();
var existing = new[]
{
new LabPrerequisite(labA, labB),
new LabPrerequisite(labA, labC),
new LabPrerequisite(labB, labD)
};

bool result = PrerequisiteRules.WouldCreateCycle(
existing,
new LabPrerequisite(labC, labD));

Assert.That(result, Is.False);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using NUnit.Framework;
using Platform.Prerequisites.Infrastructure;

namespace Platform.Prerequisites.Tests;

[TestFixture]
public class StubCompletedLaboratoriesTests
{
[Test]
public async Task GetCompletedAsync_ReturnsMarkedLaboratory()
{
var stub = new StubCompletedLaboratories();
Guid studentId = Guid.NewGuid();
Guid laboratoryId = Guid.NewGuid();

stub.MarkCompleted(studentId, laboratoryId);
IReadOnlySet<Guid> completed = await stub.GetCompletedAsync(studentId);

Assert.That(completed, Does.Contain(laboratoryId));
}

[Test]
public async Task GetCompletedAsync_OtherStudent_ReturnsEmpty()
{
var stub = new StubCompletedLaboratories();
stub.MarkCompleted(Guid.NewGuid(), Guid.NewGuid());

IReadOnlySet<Guid> completed = await stub.GetCompletedAsync(Guid.NewGuid());

Assert.That(completed, Is.Empty);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Platform.Prerequisites.Contracts;

public interface ICompletedLaboratories
{
Task<IReadOnlySet<Guid>> GetCompletedAsync(
Guid studentId,
CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Platform.Prerequisites.Domain;

namespace Platform.Prerequisites.Contracts;

public interface IPrerequisiteRepository
{
Task<AddPrerequisiteResult> AddAsync(
LabPrerequisite prerequisite,
CancellationToken cancellationToken = default);

Task<bool> RemoveAsync(
LabPrerequisite prerequisite,
CancellationToken cancellationToken = default);

Task<IReadOnlyList<LabPrerequisite>> GetForLaboratoryAsync(
Guid laboratoryId,
CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Platform.Prerequisites.Domain;

public enum AddPrerequisiteResult
{
Added,
Duplicate,
WouldCreateCycle
}
23 changes: 23 additions & 0 deletions backend/Platform.Prerequisites/Domain/LabPrerequisite.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Platform.Prerequisites.Domain;

public sealed class LabPrerequisite
{
public Guid LaboratoryId { get; }
public Guid RequiredLaboratoryId { get; }

public LabPrerequisite(Guid laboratoryId, Guid requiredLaboratoryId)
{
if (laboratoryId == Guid.Empty || requiredLaboratoryId == Guid.Empty)
{
throw new ArgumentException("ID laboratorije ne sme biti prazan.");
}

if (laboratoryId == requiredLaboratoryId)
{
throw new ArgumentException("Laboratorija ne može biti sopstveni preduslov.");
}

LaboratoryId = laboratoryId;
RequiredLaboratoryId = requiredLaboratoryId;
}
}
Loading
Loading