From 49012ec26cb2046a27cb6dd08e89ad9ab4b3e020 Mon Sep 17 00:00:00 2001 From: Are Almaas Date: Wed, 25 Sep 2024 15:04:09 +0200 Subject: [PATCH 1/4] ci: upgrade az cli version (#1195) ## Description New version of Bicep dropped, upgrading to latest azure cli to include it. ## Related Issue(s) - #{issue number} ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] Documentation is updated (either in `docs`-directory, Altinnpedia or a separate linked PR in [altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if applicable) ## Summary by CodeRabbit - **New Features** - Updated Azure CLI version to 2.64.0 for improved deployment capabilities. - **Bug Fixes** - Enhanced performance and stability with the latest Azure CLI updates. --- .github/workflows/action-deploy-apps.yml | 2 +- .github/workflows/action-deploy-infra.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/action-deploy-apps.yml b/.github/workflows/action-deploy-apps.yml index f7572d878..35325f8d5 100644 --- a/.github/workflows/action-deploy-apps.yml +++ b/.github/workflows/action-deploy-apps.yml @@ -1,6 +1,6 @@ name: Deploy apps env: - AZ_CLI_VERSION: 2.61.0 + AZ_CLI_VERSION: 2.64.0 on: workflow_call: secrets: diff --git a/.github/workflows/action-deploy-infra.yml b/.github/workflows/action-deploy-infra.yml index 9bd67f867..e554d07ce 100644 --- a/.github/workflows/action-deploy-infra.yml +++ b/.github/workflows/action-deploy-infra.yml @@ -1,7 +1,7 @@ name: Deploy infrastructure env: - AZ_CLI_VERSION: 2.61.0 + AZ_CLI_VERSION: 2.64.0 on: workflow_call: From 29f0496bf91cb34f0a756640f666f8c0492c38bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:10:24 +0200 Subject: [PATCH 2/4] chore(deps): update mcr.microsoft.com/dotnet/sdk docker tag to v8.0.402 (#1188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [mcr.microsoft.com/dotnet/sdk](https://redirect.github.com/dotnet/sdk) | final | patch | `8.0.401` -> `8.0.402` | --- ### Release Notes
dotnet/sdk (mcr.microsoft.com/dotnet/sdk) ### [`v8.0.402`](https://redirect.github.com/dotnet/sdk/compare/v8.0.401...v8.0.402) [Compare Source](https://redirect.github.com/dotnet/sdk/compare/v8.0.401...v8.0.402)
--- ### Configuration 📅 **Schedule**: Branch creation - "before 7am on Sunday,before 7am on Wednesday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/digdir/dialogporten). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ole Jørgen Skogstad --- global.json | 2 +- src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile | 2 +- src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile | 2 +- .../MigrationBundle.dockerfile | 2 +- src/Digdir.Domain.Dialogporten.Janitor/Dockerfile | 2 +- src/Digdir.Domain.Dialogporten.Service/Dockerfile | 2 +- src/Digdir.Domain.Dialogporten.WebApi/Dockerfile | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/global.json b/global.json index c55fa7dfa..ecdcdb9ba 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.401", + "version": "8.0.402", "rollForward": "disable" } } diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile index 54332c3eb..29c6c23bb 100644 --- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile +++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile @@ -1,7 +1,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build WORKDIR /src COPY [".editorconfig", "."] diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile b/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile index bbd00ef38..c744a48cb 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile +++ b/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile @@ -2,7 +2,7 @@ WORKDIR /app EXPOSE 8080 -FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build WORKDIR /src COPY [".editorconfig", "."] diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile b/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile index e077b3a71..ca1227908 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile @@ -1,7 +1,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build WORKDIR /src COPY [".editorconfig", "."] diff --git a/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile b/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile index 51148343e..b46233015 100644 --- a/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile +++ b/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile @@ -1,7 +1,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build WORKDIR /src COPY [".editorconfig", "."] diff --git a/src/Digdir.Domain.Dialogporten.Service/Dockerfile b/src/Digdir.Domain.Dialogporten.Service/Dockerfile index 472d0ee26..a38fb9247 100644 --- a/src/Digdir.Domain.Dialogporten.Service/Dockerfile +++ b/src/Digdir.Domain.Dialogporten.Service/Dockerfile @@ -1,7 +1,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0.8@sha256:e935e3424c1043dad7f904240d767c76e0c32a3c9a8bf661c32eac6f29f5ea73 AS base WORKDIR /app -FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build WORKDIR /src COPY [".editorconfig", "."] diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile b/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile index 822ed00cf..04d9c5547 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile +++ b/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile @@ -2,7 +2,7 @@ WORKDIR /app EXPOSE 8080 -FROM mcr.microsoft.com/dotnet/sdk:8.0.401@sha256:c9966505b18c198b7b5000b07ec3c7102bc257de580be270ce39ec278d549ef8 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0.402@sha256:b6333c015432da2dd392b8a865bba4e76e9d39d105d65e72b485cf92bcf71316 AS build WORKDIR /src COPY [".editorconfig", "."] From 8e570b4737ea7c7ba5aeacfc10f393ed52ccea2e Mon Sep 17 00:00:00 2001 From: Knut Haug <154342485+knuhau@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:53:42 +0200 Subject: [PATCH 3/4] chore: Fix redundant call to resource registry API (#1181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This is due to the application caching two separate dictionaries of info from the resource registry, grouped by different keys. ![CleanShot 2024-09-25 at 15 30 41@2x](https://github.com/user-attachments/assets/004eb728-3ed7-46d9-8b22-76f7dd7e6451) Added caching of the raw response from the resource registry and removed the dependent caches to lower complexity. ## Related Issue(s) - #1136 ## Verification - [x] **Your** code builds clean without any errors or warnings - [x] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] Documentation is updated (either in `docs`-directory, Altinnpedia or a separate linked PR in [altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if applicable) ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Improved caching strategy for service resource information retrieval, enhancing performance and efficiency. - Centralized data fetching logic for service resources, allowing for more streamlined and efficient access. - **Bug Fixes** - Streamlined data fetching logic to minimize unnecessary calls to the endpoint by utilizing the updated caching mechanism. --------- Co-authored-by: Ole Jørgen Skogstad --- .../ResourceRegistryClient.cs | 82 +++++++------------ 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs index 9bbf49c17..300dccf69 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/ResourceRegistry/ResourceRegistryClient.cs @@ -7,11 +7,11 @@ namespace Digdir.Domain.Dialogporten.Infrastructure.Altinn.ResourceRegistry; internal sealed class ResourceRegistryClient : IResourceRegistry { - private const string ServiceResourceInformationByOrgCacheKey = "ServiceResourceInformationByOrgCacheKey"; - private const string ServiceResourceInformationByResourceIdCacheKey = "ServiceResourceInformationByResourceIdCacheKey"; + private const string ServiceResourceInformationCacheKey = "ServiceResourceInformationCacheKey"; private const string ResourceTypeGenericAccess = "GenericAccessResource"; private const string ResourceTypeAltinnApp = "AltinnApp"; private const string ResourceTypeCorrespondence = "CorrespondenceService"; + private const string ResourceRegistryResourceEndpoint = "resourceregistry/api/v1/resource/"; private readonly IFusionCache _cache; private readonly HttpClient _client; @@ -26,27 +26,24 @@ public async Task> GetResourceIn string orgNumber, CancellationToken cancellationToken) { - var dic = await GetOrSetResourceInformationByOrg(cancellationToken); - if (!dic.TryGetValue(orgNumber, out var resources)) - { - resources = []; - } - - return resources.AsReadOnly(); + var resources = await FetchServiceResourceInformation(cancellationToken); + return resources + .Where(x => x.OwnerOrgNumber == orgNumber) + .ToList(); } public async Task GetResourceInformation( string serviceResourceId, CancellationToken cancellationToken) { - var dic = await GetOrSetResourceInformationByResourceId(cancellationToken); - dic.TryGetValue(serviceResourceId, out var resource); - return resource; + var resources = await FetchServiceResourceInformation(cancellationToken); + return resources + .FirstOrDefault(x => x.ResourceId == serviceResourceId); } public async IAsyncEnumerable> GetUpdatedSubjectResources(DateTimeOffset since, int batchSize, [EnumeratorCancellation] CancellationToken cancellationToken) { - const string searchEndpoint = "resourceregistry/api/v1/resource/updated"; + const string searchEndpoint = $"{ResourceRegistryResourceEndpoint}updated"; var nextUrl = searchEndpoint + $"?since={Uri.EscapeDataString(since.ToString("O"))}&limit={batchSize}"; do @@ -66,56 +63,33 @@ public async IAsyncEnumerable> GetUpdatedSubjectRes } while (nextUrl is not null); } - - private async Task> GetOrSetResourceInformationByOrg( - CancellationToken cancellationToken) + private async Task FetchServiceResourceInformation(CancellationToken cancellationToken) { - return await _cache.GetOrSetAsync( - ServiceResourceInformationByOrgCacheKey, - async cToken => - { - var resources = await FetchServiceResourceInformation(cToken); - return resources - .GroupBy(x => x.OwnerOrgNumber) - .ToDictionary(x => x.Key, x => x.ToArray()); - }, - token: cancellationToken); - } + const string searchEndpoint = $"{ResourceRegistryResourceEndpoint}resourcelist"; - private async Task> GetOrSetResourceInformationByResourceId( - CancellationToken cancellationToken) - { return await _cache.GetOrSetAsync( - ServiceResourceInformationByResourceIdCacheKey, + ServiceResourceInformationCacheKey, async cToken => { - var resources = await FetchServiceResourceInformation(cToken); - return resources.ToDictionary(x => x.ResourceId); + var response = await _client + .GetFromJsonEnsuredAsync>(searchEndpoint, + cancellationToken: cToken); + + return response + .Where(x => !string.IsNullOrWhiteSpace(x.HasCompetentAuthority.Organization)) + .Where(x => x.ResourceType is + ResourceTypeGenericAccess or + ResourceTypeAltinnApp or + ResourceTypeCorrespondence) + .Select(x => new ServiceResourceInformation( + $"{Constants.ServiceResourcePrefix}{x.Identifier}", + x.ResourceType, + x.HasCompetentAuthority.Organization!)) + .ToArray(); }, token: cancellationToken); } - private async Task FetchServiceResourceInformation(CancellationToken cancellationToken) - { - const string searchEndpoint = "resourceregistry/api/v1/resource/resourcelist"; - - var response = await _client - .GetFromJsonEnsuredAsync>(searchEndpoint, - cancellationToken: cancellationToken); - - return response - .Where(x => !string.IsNullOrWhiteSpace(x.HasCompetentAuthority.Organization)) - .Where(x => x.ResourceType is - ResourceTypeGenericAccess or - ResourceTypeAltinnApp or - ResourceTypeCorrespondence) - .Select(x => new ServiceResourceInformation( - $"{Constants.ServiceResourcePrefix}{x.Identifier}", - x.ResourceType, - x.HasCompetentAuthority.Organization!)) - .ToArray(); - } - private sealed class ResourceListResponse { public required string Identifier { get; init; } From d5729de122036a8623233d4946c666895c9e3f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Thu, 26 Sep 2024 18:09:23 +0200 Subject: [PATCH 4/4] chore: Add transmission content values test (#1200) ## Description Adding test for required content on transmissions ## Related Issue(s) - #1197 ## Verification - [x] **Your** code builds clean without any errors or warnings - [x] Manual testing done (required) - [x] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] Documentation is updated (either in `docs`-directory, Altinnpedia or a separate linked PR in [altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if applicable) ## Summary by CodeRabbit - **New Features** - Added support for generating fake dialog transmissions with new parameters in dialog generation methods. - **Bug Fixes** - Introduced validation tests to ensure that dialog transmissions cannot be created without required content values. - **Tests** - Added new test cases to validate the creation of dialog transmissions under specific conditions. --- .../DialogGenerator.cs | 16 +++-- .../Program.cs | 8 +-- .../Dialogs/Commands/CreateDialogTests.cs | 71 ++++++++++++++++++- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs index 62f226ee0..a9bb58561 100644 --- a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs +++ b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs @@ -36,7 +36,8 @@ public static CreateDialogCommand GenerateFakeDialog( List? attachments = null, List? guiActions = null, List? apiActions = null, - List? activities = null) + List? activities = null, + List? transmissions = null) { return GenerateFakeDialogs( seed, @@ -60,12 +61,12 @@ public static CreateDialogCommand GenerateFakeDialog( attachments, guiActions, apiActions, - activities + activities, + transmissions )[0]; } - public static List GenerateFakeDialogs( - int? seed = null, + public static List GenerateFakeDialogs(int? seed = null, int count = 1, Guid? id = null, string? serviceResource = null, @@ -86,7 +87,8 @@ public static List GenerateFakeDialogs( List? attachments = null, List? guiActions = null, List? apiActions = null, - List? activities = null) + List? activities = null, + List? transmissions = null) { Randomizer.Seed = seed.HasValue ? new Random(seed.Value) : new Random(); return new Faker() @@ -108,6 +110,7 @@ public static List GenerateFakeDialogs( .RuleFor(o => o.ApiActions, _ => apiActions ?? GenerateFakeDialogApiActions()) .RuleFor(o => o.Activities, _ => activities ?? GenerateFakeDialogActivities()) .RuleFor(o => o.Process, f => process ?? GenerateFakeProcessUri()) + .RuleFor(o => o.Transmissions, f => transmissions ?? GenerateFakeDialogTransmissions()) .Generate(count); } @@ -121,7 +124,8 @@ public static CreateDialogCommand GenerateSimpleFakeDialog(Guid? id = null) attachments: [], guiActions: [], apiActions: [], - searchTags: []); + searchTags: [], + transmissions: []); } public static string GenerateFakeResource(Func? generator = null) diff --git a/src/Digdir.Tool.Dialogporten.GenerateFakeData/Program.cs b/src/Digdir.Tool.Dialogporten.GenerateFakeData/Program.cs index 93896625a..70fc67ee4 100644 --- a/src/Digdir.Tool.Dialogporten.GenerateFakeData/Program.cs +++ b/src/Digdir.Tool.Dialogporten.GenerateFakeData/Program.cs @@ -35,9 +35,7 @@ private static async Task RunAsync(Options options) Randomizer.Seed = new Random(options.Seed); var dialogs = DialogGenerator.GenerateFakeDialogs( seed: new Randomizer().Number(int.MaxValue), - serviceResourceGenerator: () => MaybeGetRandomResource(options), - partyGenerator: () => MaybeGetRandomParty(options), - count: options.Count); + count: options.Count, serviceResourceGenerator: () => MaybeGetRandomResource(options), partyGenerator: () => MaybeGetRandomParty(options)); var serialized = JsonSerializer.Serialize(dialogs, JsonSerializerOptions); Console.WriteLine(serialized); return; @@ -118,9 +116,7 @@ private static void ProduceDialogs(Options options, BlockingCollection<(int, Cre var dialogsToGenerate = Math.Min(DialogsPerBatch, totalDialogs - dialogCounter); var dialogs = DialogGenerator.GenerateFakeDialogs( seed: new Randomizer().Number(int.MaxValue), - serviceResourceGenerator: () => MaybeGetRandomResource(options), - partyGenerator: () => MaybeGetRandomParty(options), - count: dialogsToGenerate).Take(dialogsToGenerate); + count: dialogsToGenerate, serviceResourceGenerator: () => MaybeGetRandomResource(options), partyGenerator: () => MaybeGetRandomParty(options)).Take(dialogsToGenerate); foreach (var dialog in dialogs) { dialogQueue.Add((dialogCounter + 1, dialog), cancellationToken); diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs index db17226ed..58accba55 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs @@ -1,5 +1,8 @@ -using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; +using Castle.Core.Logging; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; using Digdir.Tool.Dialogporten.GenerateFakeData; using FluentAssertions; using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils; @@ -203,4 +206,70 @@ public async Task Can_Create_Dialog_With_UpdatedAt_And_CreatedAt_Being_Equal() response.TryPickT0(out var success, out _).Should().BeTrue(); success.Should().NotBeNull(); } + + [Fact] + public async Task Cannot_Create_Transmission_Without_Content() + { + // Arrange + var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0]; + transmission.Content = null!; + + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + createDialogCommand.Transmissions = [transmission]; + + // Act + var response = await Application.Send(createDialogCommand); + + // Assert + response.TryPickT2(out var validationError, out _).Should().BeTrue(); + validationError.Should().NotBeNull(); + validationError.Errors.Should().HaveCount(1); + validationError.Errors.First().ErrorMessage.Should().Contain("'Content' must not be empty"); + } + + [Fact] + public async Task Cannot_Create_Transmission_Without_Content_Value() + { + // Arrange + var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0]; + transmission.Content.Summary.Value = []; + transmission.Content.Title.Value = []; + + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + createDialogCommand.Transmissions = [transmission]; + + // Act + var response = await Application.Send(createDialogCommand); + + // Assert + response.TryPickT2(out var validationError, out _).Should().BeTrue(); + validationError.Should().NotBeNull(); + validationError.Errors + .Count(e => e.PropertyName.Contains(nameof(createDialogCommand.Content))) + .Should() + .Be(2); + } + + [Fact] + public async Task Cannot_Create_Transmission_With_Empty_Content_Localization_Values() + { + // Arrange + var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0]; + transmission.Content.Summary.Value = [new LocalizationDto { LanguageCode = "nb", Value = "" }]; + transmission.Content.Title.Value = [new LocalizationDto { LanguageCode = "nb", Value = "" }]; + + var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(); + createDialogCommand.Transmissions = [transmission]; + + // Act + var response = await Application.Send(createDialogCommand); + + // Assert + response.TryPickT2(out var validationError, out _).Should().BeTrue(); + validationError.Should().NotBeNull(); + validationError.Errors + .Count(e => e.PropertyName.Contains(nameof(createDialogCommand.Content))) + .Should() + .Be(2); + } }