diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..f83c930 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owners for all files +* @eventhub/maintainers diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ffaffde --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..603dccd --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Summary +- [ ] Describe the change in 1-2 bullet points +- [ ] Mention any related issues or links + +## Testing +- [ ] Added or updated tests +- [ ] `dotnet test` (with coverage) +- [ ] Other: ______ + +## Additional checks +- [ ] Documentation updated (if needed) +- [ ] Breaking changes noted and communicated +- [ ] Security/privacy impact considered diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c653894..131f601 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,8 +31,14 @@ jobs: - name: Restore dependencies run: dotnet restore + - name: Check code format + run: dotnet format --verify-no-changes + - name: Build solution run: dotnet build --configuration Release --no-restore - - name: Skip tests (for now) - run: echo "No test project yet — skipping tests" + - name: Run tests with coverage + run: dotnet test --configuration Release --no-build --collect "XPlat Code Coverage" + + - name: Scan for vulnerable packages + run: dotnet list package --vulnerable diff --git a/EventHub.sln b/EventHub.sln index 3c42a07..91aab19 100644 --- a/EventHub.sln +++ b/EventHub.sln @@ -1,87 +1,103 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Domain", "src\EventHub.Domain\EventHub.Domain.csproj", "{1CDAA880-1542-4644-9DF3-78BCADCE543E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{848702C5-0F6F-443E-9835-9B6AF62862CC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Application", "src\EventHub.Application\EventHub.Application.csproj", "{8D99A724-5894-4C57-A4A8-A59460512D15}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Domain", "src\\EventHub.Domain\\EventHub.Domain.csproj", "{1CDAA880-1542-4644-9DF3-78BCADCE543E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Infrastructure", "src\EventHub.Infrastructure\EventHub.Infrastructure.csproj", "{B97C9E27-790A-489E-B0EA-9F8B4700ECF9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Application", "src\\EventHub.Application\\EventHub.Application.csproj", "{8D99A724-5894-4C57-A4A8-A59460512D15}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.API", "src\EventHub.API\EventHub.API.csproj", "{B514BAC6-A727-4A6F-AB52-D89F40FCB561}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Infrastructure", "src\\EventHub.Infrastructure\\EventHub.Infrastructure.csproj", "{B97C9E27-790A-489E-B0EA-9F8B4700ECF9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.API", "src\\EventHub.API\\EventHub.API.csproj", "{B514BAC6-A727-4A6F-AB52-D89F40FCB561}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Domain.Tests", "tests\\EventHub.Domain.Tests\\EventHub.Domain.Tests.csproj", "{482DFB1D-EC75-44AA-953A-3557DA2B91F0}" EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x64.ActiveCfg = Debug|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x64.Build.0 = Debug|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x86.ActiveCfg = Debug|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x86.Build.0 = Debug|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|Any CPU.Build.0 = Release|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x64.ActiveCfg = Release|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x64.Build.0 = Release|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x86.ActiveCfg = Release|Any CPU - {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x86.Build.0 = Release|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x64.ActiveCfg = Debug|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x64.Build.0 = Debug|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x86.ActiveCfg = Debug|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x86.Build.0 = Debug|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|Any CPU.Build.0 = Release|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x64.ActiveCfg = Release|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x64.Build.0 = Release|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x86.ActiveCfg = Release|Any CPU - {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x86.Build.0 = Release|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x64.ActiveCfg = Debug|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x64.Build.0 = Debug|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x86.ActiveCfg = Debug|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x86.Build.0 = Debug|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|Any CPU.Build.0 = Release|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x64.ActiveCfg = Release|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x64.Build.0 = Release|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x86.ActiveCfg = Release|Any CPU - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x86.Build.0 = Release|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x64.ActiveCfg = Debug|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x64.Build.0 = Debug|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x86.ActiveCfg = Debug|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x86.Build.0 = Debug|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|Any CPU.Build.0 = Release|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x64.ActiveCfg = Release|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x64.Build.0 = Release|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x86.ActiveCfg = Release|Any CPU - {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {1CDAA880-1542-4644-9DF3-78BCADCE543E} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {8D99A724-5894-4C57-A4A8-A59460512D15} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {B97C9E27-790A-489E-B0EA-9F8B4700ECF9} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {B514BAC6-A727-4A6F-AB52-D89F40FCB561} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D8BB9D14-EA84-4F5B-A466-7721011ED58D} - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x64.ActiveCfg = Debug|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x64.Build.0 = Debug|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x86.ActiveCfg = Debug|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Debug|x86.Build.0 = Debug|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|Any CPU.Build.0 = Release|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x64.ActiveCfg = Release|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x64.Build.0 = Release|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x86.ActiveCfg = Release|Any CPU + {1CDAA880-1542-4644-9DF3-78BCADCE543E}.Release|x86.Build.0 = Release|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x64.ActiveCfg = Debug|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x64.Build.0 = Debug|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x86.ActiveCfg = Debug|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Debug|x86.Build.0 = Debug|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|Any CPU.Build.0 = Release|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x64.ActiveCfg = Release|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x64.Build.0 = Release|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x86.ActiveCfg = Release|Any CPU + {8D99A724-5894-4C57-A4A8-A59460512D15}.Release|x86.Build.0 = Release|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x64.ActiveCfg = Debug|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x64.Build.0 = Debug|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x86.ActiveCfg = Debug|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Debug|x86.Build.0 = Debug|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|Any CPU.Build.0 = Release|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x64.ActiveCfg = Release|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x64.Build.0 = Release|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x86.ActiveCfg = Release|Any CPU + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9}.Release|x86.Build.0 = Release|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x64.ActiveCfg = Debug|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x64.Build.0 = Debug|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x86.ActiveCfg = Debug|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Debug|x86.Build.0 = Debug|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|Any CPU.Build.0 = Release|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x64.ActiveCfg = Release|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x64.Build.0 = Release|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x86.ActiveCfg = Release|Any CPU + {B514BAC6-A727-4A6F-AB52-D89F40FCB561}.Release|x86.Build.0 = Release|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Debug|x64.ActiveCfg = Debug|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Debug|x64.Build.0 = Debug|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Debug|x86.ActiveCfg = Debug|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Debug|x86.Build.0 = Debug|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Release|Any CPU.Build.0 = Release|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Release|x64.ActiveCfg = Release|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Release|x64.Build.0 = Release|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Release|x86.ActiveCfg = Release|Any CPU + {482DFB1D-EC75-44AA-953A-3557DA2B91F0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1CDAA880-1542-4644-9DF3-78BCADCE543E} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {8D99A724-5894-4C57-A4A8-A59460512D15} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {B97C9E27-790A-489E-B0EA-9F8B4700ECF9} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {B514BAC6-A727-4A6F-AB52-D89F40FCB561} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {482DFB1D-EC75-44AA-953A-3557DA2B91F0} = {848702C5-0F6F-443E-9835-9B6AF62862CC} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D8BB9D14-EA84-4F5B-A466-7721011ED58D} + EndGlobalSection EndGlobal diff --git a/README.md b/README.md index ba93269..7de1d7c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,29 @@ # EventHub A clean-architecture, cloud-ready event scheduling and registration API built with .NET 8, Postgres, Docker, and full CI/CD pipelines. + +## Local setup +Ensure your environment matches CI so pull requests succeed: + +1. Install the .NET 8 SDK (the repo pins 8.0.100 via `global.json`). +2. Restore packages: + ```bash + dotnet restore + ``` +3. Verify formatting (CI runs this step): + ```bash + dotnet format --verify-no-changes + ``` +4. Build in Release mode: + ```bash + dotnet build --configuration Release --no-restore + ``` +5. Run tests with coverage (mirrors CI): + ```bash + dotnet test --configuration Release --no-build --collect "XPlat Code Coverage" + ``` +6. Check vulnerable dependencies: + ```bash + dotnet list package --vulnerable + ``` + +Running the above commands locally before opening a PR will align with the CI workflow and reduce failures. diff --git a/global.json b/global.json new file mode 100644 index 0000000..391ba3c --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.100", + "rollForward": "latestFeature" + } +} diff --git a/src/EventHub.API/EventHub.API.csproj b/src/EventHub.API/EventHub.API.csproj index cde9405..1c4296f 100644 --- a/src/EventHub.API/EventHub.API.csproj +++ b/src/EventHub.API/EventHub.API.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/EventHub.Application/DTOs/EventDto.cs b/src/EventHub.Application/DTOs/EventDto.cs index 4c995ab..f2229c0 100644 --- a/src/EventHub.Application/DTOs/EventDto.cs +++ b/src/EventHub.Application/DTOs/EventDto.cs @@ -1,21 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace EventHub.Application.DTOs; -namespace EventHub.Application.DTOs +public class EventDto { - - public class EventDto - { - public Guid Id { get; set; } - public string Title { get; set; } = string.Empty; - public string Description { get; set; } = string.Empty; - public DateTime StartTimeUtc { get; set; } - public DateTime EndTimeUtc { get; set; } - public string Location { get; set; } = string.Empty; - public DateTime CreatedAtUtc { get; set; } - } - + public Guid Id { get; set; } + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public DateTime StartTimeUtc { get; set; } + public DateTime EndTimeUtc { get; set; } + public string Location { get; set; } = string.Empty; + public DateTime CreatedAtUtc { get; set; } } diff --git a/src/EventHub.Domain/Exceptions/InvalidEmailException.cs b/src/EventHub.Domain/Exceptions/InvalidEmailException.cs index ba88799..d519a0f 100644 --- a/src/EventHub.Domain/Exceptions/InvalidEmailException.cs +++ b/src/EventHub.Domain/Exceptions/InvalidEmailException.cs @@ -1,6 +1,8 @@ namespace EventHub.Domain.Exceptions; -public class InvalidEmailException :DomainException +public class InvalidEmailException : DomainException { - public InvalidEmailException(string message) : base(message) { } + public InvalidEmailException(string message) : base(message) + { + } } \ No newline at end of file diff --git a/src/EventHub.Domain/ValueObjects/Email.cs b/src/EventHub.Domain/ValueObjects/Email.cs index c9e4fad..884251d 100644 --- a/src/EventHub.Domain/ValueObjects/Email.cs +++ b/src/EventHub.Domain/ValueObjects/Email.cs @@ -1,6 +1,5 @@ -using EventHub.Domain.Exceptions; using System.Text.RegularExpressions; -using EventHub.Domain.ValueObjects; +using EventHub.Domain.Exceptions; namespace EventHub.Domain.ValueObjects; @@ -8,12 +7,13 @@ public record Email { public string Value { get; } private static readonly Regex EmailRegex = new(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.Compiled); - + public Email(string value) { if (string.IsNullOrWhiteSpace(value) || !EmailRegex.IsMatch(value)) throw new InvalidEmailException("Invalid email format."); Value = value; } + public override string ToString() => Value; } \ No newline at end of file diff --git a/src/EventHub.Infrastructure/EventHub.Infrastructure.csproj b/src/EventHub.Infrastructure/EventHub.Infrastructure.csproj index bbd9c33..ba10ca6 100644 --- a/src/EventHub.Infrastructure/EventHub.Infrastructure.csproj +++ b/src/EventHub.Infrastructure/EventHub.Infrastructure.csproj @@ -1,13 +1,13 @@  - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/EventHub.Infrastructure/Repositories/RepositoryBase.cs b/src/EventHub.Infrastructure/Repositories/RepositoryBase.cs index a020f63..6ece9c9 100644 --- a/src/EventHub.Infrastructure/Repositories/RepositoryBase.cs +++ b/src/EventHub.Infrastructure/Repositories/RepositoryBase.cs @@ -1,6 +1,5 @@ -using EventHub.Application.Interfaces; +using EventHub.Application.Interfaces; using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; namespace EventHub.Infrastructure.Repositories; @@ -24,7 +23,7 @@ public async Task> GetAllAsync() => public async Task AddAsync(T entity) { await _set.AddAsync(entity); - await _context.SaveChangesAsync(); + await _context.SaveChangesAsync(); } public async Task RemoveAsync(T entity) diff --git a/tests/EventHub.Domain.Tests/EmailTests.cs b/tests/EventHub.Domain.Tests/EmailTests.cs new file mode 100644 index 0000000..ae4ceef --- /dev/null +++ b/tests/EventHub.Domain.Tests/EmailTests.cs @@ -0,0 +1,27 @@ +using EventHub.Domain.Exceptions; +using EventHub.Domain.ValueObjects; +using Xunit; + +namespace EventHub.Domain.Tests; + +public class EmailTests +{ + [Fact] + public void Constructor_WithValidEmail_SetsValue() + { + var email = new Email("user@example.com"); + + Assert.Equal("user@example.com", email.Value); + Assert.Equal("user@example.com", email.ToString()); + } + + [Theory] + [InlineData("")] + [InlineData("invalid")] + [InlineData("user@domain")] + [InlineData("@example.com")] + public void Constructor_WithInvalidEmail_Throws(string input) + { + Assert.Throws(() => new Email(input)); + } +} diff --git a/tests/EventHub.Domain.Tests/EventHub.Domain.Tests.csproj b/tests/EventHub.Domain.Tests/EventHub.Domain.Tests.csproj new file mode 100644 index 0000000..c712735 --- /dev/null +++ b/tests/EventHub.Domain.Tests/EventHub.Domain.Tests.csproj @@ -0,0 +1,22 @@ + + + net8.0 + enable + enable + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/tests/EventHub.Domain.Tests/EventTests.cs b/tests/EventHub.Domain.Tests/EventTests.cs new file mode 100644 index 0000000..bf9571b --- /dev/null +++ b/tests/EventHub.Domain.Tests/EventTests.cs @@ -0,0 +1,52 @@ +using EventHub.Domain.Entities; +using EventHub.Domain.Exceptions; +using Xunit; + +namespace EventHub.Domain.Tests; + +public class EventTests +{ + [Fact] + public void Constructor_WithValidData_SetsProperties() + { + var start = DateTime.UtcNow.AddHours(1); + var end = start.AddHours(2); + + var @event = new Event("Launch", "Product launch", start, end, "Online"); + + Assert.Equal("Launch", @event.Title); + Assert.Equal("Product launch", @event.Description); + Assert.Equal(start, @event.StartTimeUtc); + Assert.Equal(end, @event.EndTimeUtc); + Assert.Equal("Online", @event.Location); + Assert.True((DateTime.UtcNow - @event.CreatedAtUtc).TotalSeconds < 5); + } + + [Fact] + public void Constructor_WithEndBeforeStart_Throws() + { + var start = DateTime.UtcNow.AddHours(2); + var end = start.AddHours(-1); + + Assert.Throws(() => new Event("Title", "Description", start, end, "Room 1")); + } + + [Fact] + public void Update_WithValidData_ChangesProperties() + { + var start = DateTime.UtcNow.AddHours(1); + var end = start.AddHours(2); + var @event = new Event("Original", "Description", start, end, "Room 1"); + + var newStart = start.AddDays(1); + var newEnd = newStart.AddHours(3); + + @event.Update("Updated", "New description", newStart, newEnd, "Room 42"); + + Assert.Equal("Updated", @event.Title); + Assert.Equal("New description", @event.Description); + Assert.Equal(newStart, @event.StartTimeUtc); + Assert.Equal(newEnd, @event.EndTimeUtc); + Assert.Equal("Room 42", @event.Location); + } +} diff --git a/tests/EventHub.Domain.Tests/ParticipantTests.cs b/tests/EventHub.Domain.Tests/ParticipantTests.cs new file mode 100644 index 0000000..95b7641 --- /dev/null +++ b/tests/EventHub.Domain.Tests/ParticipantTests.cs @@ -0,0 +1,28 @@ +using EventHub.Domain.Entities; +using EventHub.Domain.ValueObjects; +using Xunit; + +namespace EventHub.Domain.Tests; + +public class ParticipantTests +{ + [Fact] + public void Constructor_WithValidData_SetsProperties() + { + var email = new Email("jane.doe@example.com"); + var participant = new Participant("Jane Doe", email); + + Assert.Equal("Jane Doe", participant.Name); + Assert.Equal(email, participant.Email); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + public void Constructor_WithEmptyName_Throws(string name) + { + var email = new Email("john@example.com"); + + Assert.Throws(() => new Participant(name, email)); + } +} diff --git a/tests/EventHub.Domain.Tests/RegistrationTests.cs b/tests/EventHub.Domain.Tests/RegistrationTests.cs new file mode 100644 index 0000000..fed8cc8 --- /dev/null +++ b/tests/EventHub.Domain.Tests/RegistrationTests.cs @@ -0,0 +1,20 @@ +using EventHub.Domain.Entities; +using Xunit; + +namespace EventHub.Domain.Tests; + +public class RegistrationTests +{ + [Fact] + public void Constructor_SetsPropertiesAndTimestamp() + { + var eventId = Guid.NewGuid(); + var participantId = Guid.NewGuid(); + + var registration = new Registration(eventId, participantId); + + Assert.Equal(eventId, registration.EventId); + Assert.Equal(participantId, registration.ParticipantId); + Assert.True((DateTime.UtcNow - registration.RegisteredAtUtc).TotalSeconds < 5); + } +}