diff --git a/backend/Platform.LabMetadata.Tests/LaboratoryTests.cs b/backend/Platform.LabMetadata.Tests/LaboratoryTests.cs new file mode 100644 index 0000000..b51bf44 --- /dev/null +++ b/backend/Platform.LabMetadata.Tests/LaboratoryTests.cs @@ -0,0 +1,118 @@ + +using System; +using NUnit.Framework; +using Platform.LabMetadata; + +namespace Platform.LabMetadata.Tests +{ + [TestFixture] + public class LaboratoryMetadataTests + { + [Test] + public void Constructor_ShouldPreserveLaboratoryId() + { + var id = Guid.NewGuid(); + + var metadata = new LaboratoryMetadata( + id, + "Learn access control", + LaboratoryDifficulty.Easy, + LaboratoryType.AccessControl, + new[] { "security" } + ); + + Assert.That(metadata.LaboratoryId, Is.EqualTo(id)); + } + + [Test] + public void Constructor_ShouldRejectInvalidDifficulty() + { + var invalidDifficulty = (LaboratoryDifficulty)999; + + Assert.Throws(() => + new LaboratoryMetadata( + Guid.NewGuid(), + "Test objective", + invalidDifficulty, + LaboratoryType.AccessControl, + new[] { "security" } + ) + ); + } + + [Test] + public void Constructor_ShouldRejectInvalidType() + { + var invalidType = (LaboratoryType)999; + + Assert.Throws(() => + new LaboratoryMetadata( + Guid.NewGuid(), + "Test objective", + LaboratoryDifficulty.Easy, + invalidType, + new[] { "security" } + ) + ); + } + + [Test] + public void Constructor_ShouldNormalizeTags() + { + var metadata = new LaboratoryMetadata( + Guid.NewGuid(), + "Test objective", + LaboratoryDifficulty.Easy, + LaboratoryType.AccessControl, + new[] { " Security ", "MFA", "security", " " } + ); + + Assert.That( + metadata.Tags, + Is.EqualTo(new[] { "security", "mfa" }) + ); + } + + [Test] + public void Constructor_ShouldRejectEmptyId() + { + Assert.Throws(() => + new LaboratoryMetadata( + Guid.Empty, + "Test objective", + LaboratoryDifficulty.Easy, + LaboratoryType.AccessControl, + new[] { "security" } + ) + ); + } + + [Test] + public void Constructor_ShouldRejectEmptyObjective() + { + Assert.Throws(() => + new LaboratoryMetadata( + Guid.NewGuid(), + " ", + LaboratoryDifficulty.Easy, + LaboratoryType.AccessControl, + new[] { "security" } + ) + ); + } + + [Test] + public void Constructor_ShouldRejectNullTags() + { + Assert.Throws(() => + new LaboratoryMetadata( + Guid.NewGuid(), + "Test objective", + LaboratoryDifficulty.Easy, + LaboratoryType.AccessControl, + null! + ) + ); + } + } +} diff --git a/backend/Platform.LabMetadata.Tests/Platform.LabMetadata.Tests.csproj b/backend/Platform.LabMetadata.Tests/Platform.LabMetadata.Tests.csproj new file mode 100644 index 0000000..1d565f9 --- /dev/null +++ b/backend/Platform.LabMetadata.Tests/Platform.LabMetadata.Tests.csproj @@ -0,0 +1,27 @@ + + + + net10.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + diff --git a/backend/Platform.LabMetadata/LaboratoryDifficulty.cs b/backend/Platform.LabMetadata/LaboratoryDifficulty.cs new file mode 100644 index 0000000..6f41dbc --- /dev/null +++ b/backend/Platform.LabMetadata/LaboratoryDifficulty.cs @@ -0,0 +1,10 @@ + +namespace Platform.LabMetadata +{ + public enum LaboratoryDifficulty + { + Easy, + Medium, + Hard + } +} diff --git a/backend/Platform.LabMetadata/LaboratoryMetadata.cs b/backend/Platform.LabMetadata/LaboratoryMetadata.cs new file mode 100644 index 0000000..25f6894 --- /dev/null +++ b/backend/Platform.LabMetadata/LaboratoryMetadata.cs @@ -0,0 +1,59 @@ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Platform.LabMetadata +{ + public class LaboratoryMetadata + { + // Referenca na postojecu laboratoriju iz LAB-01. + // LAB-02 ne generise novi identitet. + public Guid LaboratoryId { get; } + + public string Objective { get; private set; } + public LaboratoryDifficulty Difficulty { get; private set; } + public LaboratoryType Type { get; private set; } + public IReadOnlyList Tags { get; private set; } + + public LaboratoryMetadata( + Guid laboratoryId, + string objective, + LaboratoryDifficulty difficulty, + LaboratoryType type, + IEnumerable tags) + { + if (laboratoryId == Guid.Empty) + throw new ArgumentException( + "Laboratory ID cannot be empty.", + nameof(laboratoryId)); + + if (string.IsNullOrWhiteSpace(objective)) + throw new ArgumentException( + "Laboratory objective is required.", + nameof(objective)); + + if (!Enum.IsDefined(difficulty)) + throw new ArgumentOutOfRangeException(nameof(difficulty)); + + if (!Enum.IsDefined(type)) + throw new ArgumentOutOfRangeException(nameof(type)); + + if (tags is null) + throw new ArgumentNullException(nameof(tags)); + + LaboratoryId = laboratoryId; + Objective = objective.Trim(); + Difficulty = difficulty; + Type = type; + + Tags = Array.AsReadOnly( + tags + .Where(tag => !string.IsNullOrWhiteSpace(tag)) + .Select(tag => tag.Trim().ToLowerInvariant()) + .Distinct() + .ToArray() + ); + } + } +} diff --git a/backend/Platform.LabMetadata/LaboratoryType.cs b/backend/Platform.LabMetadata/LaboratoryType.cs new file mode 100644 index 0000000..01d31c0 --- /dev/null +++ b/backend/Platform.LabMetadata/LaboratoryType.cs @@ -0,0 +1,15 @@ + +namespace Platform.LabMetadata +{ + public enum LaboratoryType + { + AccessControl, + LogAnalysis, + SecurityConfiguration, + ThreatModeling, + MfaAndSessions, + ContextualAuthorization, + IncidentResponse, + IsolatedService + } +} diff --git a/backend/Platform.LabMetadata/Platform.LabMetadata.csproj b/backend/Platform.LabMetadata/Platform.LabMetadata.csproj new file mode 100644 index 0000000..b760144 --- /dev/null +++ b/backend/Platform.LabMetadata/Platform.LabMetadata.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/backend/SecurityLab.slnx b/backend/SecurityLab.slnx index 2f30a35..1302954 100644 --- a/backend/SecurityLab.slnx +++ b/backend/SecurityLab.slnx @@ -4,6 +4,8 @@ + +