Compare commits
3 Commits
435be882c9
...
b46c3c8ebe
| Author | SHA1 | Date | |
|---|---|---|---|
| b46c3c8ebe | |||
| 9f712a6b8c | |||
| 4449743105 |
@@ -27,6 +27,8 @@ namespace EnvelopeGenerator.API.Controllers;
|
||||
[Authorize]
|
||||
public class EnvelopeController : ControllerBase
|
||||
{
|
||||
public record DeleteEnvelopeRequest(string Reason);
|
||||
|
||||
private readonly ILogger<EnvelopeController> _logger;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
@@ -108,4 +110,17 @@ public class EnvelopeController : ControllerBase
|
||||
else
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthScheme.Sender)]
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> DeleteAsync([FromRoute] int id, [FromBody] DeleteEnvelopeRequest request)
|
||||
{
|
||||
var result = await _mediator.Send(new DeleteEnvelopeCommand
|
||||
{
|
||||
EnvelopeId = id,
|
||||
Reason = request?.Reason ?? string.Empty
|
||||
}.WithAuth(User.GetId()));
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ public class ExceptionHandlingMiddleware
|
||||
message = notFoundEx.Message;
|
||||
break;
|
||||
|
||||
case ForbiddenException forbiddenEx:
|
||||
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
message = forbiddenEx.Message;
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.LogError(exception, "Unhandled exception occurred.");
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using MediatR;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Notifications.EnvelopeDeleted;
|
||||
|
||||
public record EnvelopeDeletedNotification : INotification, ISendMailNotification
|
||||
{
|
||||
public required Envelope Envelope { get; init; }
|
||||
|
||||
public required EnvelopeReceiver EnvelopeReceiver { get; init; }
|
||||
|
||||
public required string Reason { get; init; }
|
||||
|
||||
public required string SenderReference { get; init; }
|
||||
|
||||
public EmailTemplateType TemplateType => EmailTemplateType.DocumentDeleted;
|
||||
|
||||
public string EmailAddress => EnvelopeReceiver.Receiver?.EmailAddress
|
||||
?? throw new InvalidOperationException("Receiver email is missing for EnvelopeDeletedNotification.");
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
||||
using EnvelopeGenerator.Application.Common.Configurations;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Common.Notifications.EnvelopeDeleted.Handlers;
|
||||
|
||||
public class SendDeletedMailHandler : SendMailHandler<EnvelopeDeletedNotification>
|
||||
{
|
||||
public SendDeletedMailHandler(
|
||||
IRepository<EmailTemplate> tempRepo,
|
||||
IRepository<EmailOut> emailOutRepo,
|
||||
IOptions<MailParams> mailParamsOptions,
|
||||
IOptions<DispatcherParams> dispatcherParamsOptions)
|
||||
: base(tempRepo, emailOutRepo, mailParamsOptions, dispatcherParamsOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ConfigureEmailOut(EnvelopeDeletedNotification notification, EmailOut emailOut)
|
||||
{
|
||||
emailOut.ReferenceString = notification.EmailAddress;
|
||||
emailOut.ReferenceId = notification.EnvelopeReceiver.ReceiverId;
|
||||
emailOut.WfId = (int)EnvelopeStatus.MessageDeletionSent;
|
||||
}
|
||||
|
||||
protected override Dictionary<string, string> CreatePlaceHolders(EnvelopeDeletedNotification notification)
|
||||
{
|
||||
var senderName = notification.Envelope.User?.Email;
|
||||
if (string.IsNullOrWhiteSpace(senderName))
|
||||
senderName = notification.SenderReference;
|
||||
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
["[NAME_RECEIVER]"] = notification.EnvelopeReceiver.Name ?? string.Empty,
|
||||
["[NAME_SENDER]"] = senderName ?? string.Empty,
|
||||
["[DOCUMENT_TITLE]"] = notification.Envelope.Title ?? string.Empty,
|
||||
["[REASON]"] = notification.Reason
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using MediatR;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
|
||||
public record DeleteEnvelopeResultDto(int EnvelopeId, EnvelopeStatus AppliedStatus, bool IsNoOp);
|
||||
|
||||
public record DeleteEnvelopeCommand : IRequest<DeleteEnvelopeResultDto>
|
||||
{
|
||||
[Required]
|
||||
public int EnvelopeId { get; init; }
|
||||
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public string Reason { get; init; } = string.Empty;
|
||||
|
||||
internal int UserId { get; private set; }
|
||||
|
||||
public DeleteEnvelopeCommand WithAuth(int userId)
|
||||
{
|
||||
UserId = userId;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using EnvelopeGenerator.Application.Common.Notifications.EnvelopeDeleted;
|
||||
using DigitalData.Core.Abstraction.Application.Repository;
|
||||
using DigitalData.Core.Exceptions;
|
||||
using EnvelopeGenerator.Application.Histories.Commands;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
|
||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
|
||||
public class DeleteEnvelopeCommandHandler : IRequestHandler<DeleteEnvelopeCommand, DeleteEnvelopeResultDto>
|
||||
{
|
||||
private readonly IRepository<Envelope> _envelopeRepository;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DeleteEnvelopeCommandHandler(
|
||||
IRepository<Envelope> envelopeRepository,
|
||||
IMediator mediator)
|
||||
{
|
||||
_envelopeRepository = envelopeRepository;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public async Task<DeleteEnvelopeResultDto> Handle(DeleteEnvelopeCommand request, CancellationToken cancel)
|
||||
{
|
||||
var reason = request.Reason?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
throw new BadRequestException("Please provide a reason.");
|
||||
|
||||
var envelope = await _envelopeRepository.Query
|
||||
.Include(e => e.User)
|
||||
.Include(e => e.EnvelopeReceivers)
|
||||
.ThenInclude(er => er.Receiver)
|
||||
.FirstOrDefaultAsync(e => e.Id == request.EnvelopeId, cancel)
|
||||
?? throw new NotFoundException($"Envelope {request.EnvelopeId} not found.");
|
||||
|
||||
if (envelope.UserId != request.UserId)
|
||||
throw new ForbiddenException();
|
||||
|
||||
if (envelope.Status is EnvelopeStatus.EnvelopeDeleted or EnvelopeStatus.EnvelopeWithdrawn)
|
||||
return new DeleteEnvelopeResultDto(envelope.Id, envelope.Status, IsNoOp: true);
|
||||
|
||||
var targetStatus = envelope.Status > EnvelopeStatus.EnvelopeSaved
|
||||
? EnvelopeStatus.EnvelopeWithdrawn
|
||||
: EnvelopeStatus.EnvelopeDeleted;
|
||||
|
||||
envelope.Status = targetStatus;
|
||||
envelope.Comment = reason;
|
||||
envelope.ChangedWhen = DateTime.Now;
|
||||
|
||||
await _envelopeRepository.UpdateAsync(envelope, e => e.Id == envelope.Id, cancel);
|
||||
|
||||
var userReference = envelope.User?.Email;
|
||||
if (string.IsNullOrWhiteSpace(userReference))
|
||||
throw new BadRequestException("Envelope sender email is missing.");
|
||||
|
||||
await _mediator.Send(new CreateHistoryCommand
|
||||
{
|
||||
EnvelopeId = envelope.Id,
|
||||
UserReference = userReference,
|
||||
Status = targetStatus,
|
||||
Comment = reason
|
||||
}, cancel);
|
||||
|
||||
foreach (var envelopeReceiver in envelope.EnvelopeReceivers ?? Enumerable.Empty<EnvelopeReceiver>())
|
||||
{
|
||||
if (envelopeReceiver.Receiver is null)
|
||||
continue;
|
||||
|
||||
await _mediator.Publish(new EnvelopeDeletedNotification
|
||||
{
|
||||
Envelope = envelope,
|
||||
EnvelopeReceiver = envelopeReceiver,
|
||||
Reason = reason,
|
||||
SenderReference = userReference
|
||||
}, cancel);
|
||||
}
|
||||
|
||||
return new DeleteEnvelopeResultDto(envelope.Id, targetStatus, IsNoOp: false);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace EnvelopeGenerator.Server.Client.Services;
|
||||
public class EnvelopeService(IHttpClientFactory clientFactory)
|
||||
{
|
||||
private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
|
||||
private sealed record DeleteEnvelopeRequest(string Reason);
|
||||
private sealed record ErrorResponse(string? Message);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches envelopes from the API with optional filters.
|
||||
@@ -61,4 +63,35 @@ public class EnvelopeService(IHttpClientFactory clientFactory)
|
||||
|
||||
return await response.Content.ReadFromJsonAsync<IEnumerable<EnvelopeDto>>(_jsonOptions, cancel);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int envelopeId, string reason, CancellationToken cancel = default)
|
||||
{
|
||||
var httpClient = clientFactory.CreateClient("EnvelopeGenerator.Server");
|
||||
using var request = new HttpRequestMessage(HttpMethod.Delete, $"/api/Envelope/{envelopeId}")
|
||||
{
|
||||
Content = JsonContent.Create(new DeleteEnvelopeRequest(reason), options: _jsonOptions)
|
||||
};
|
||||
|
||||
var response = await httpClient.SendAsync(request, cancel);
|
||||
if (response.IsSuccessStatusCode)
|
||||
return;
|
||||
|
||||
string? message = null;
|
||||
try
|
||||
{
|
||||
var error = await response.Content.ReadFromJsonAsync<ErrorResponse>(_jsonOptions, cancel);
|
||||
message = error?.Message;
|
||||
}
|
||||
catch
|
||||
{
|
||||
message = null;
|
||||
}
|
||||
|
||||
var statusCode = (int)response.StatusCode;
|
||||
var reasonPhrase = response.ReasonPhrase ?? "Unknown error";
|
||||
throw new HttpRequestException(
|
||||
$"Failed to delete envelope. Status: {statusCode} ({reasonPhrase}){(string.IsNullOrWhiteSpace(message) ? string.Empty : $" - {message}")}",
|
||||
null,
|
||||
response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,6 +521,54 @@
|
||||
</BodyContentTemplate>
|
||||
</DxPopup>
|
||||
|
||||
<DxPopup @bind-Visible="@_deletePopupVisible"
|
||||
HeaderText="Umschlag löschen"
|
||||
Width="560px"
|
||||
MaxWidth="95vw"
|
||||
ShowFooter="true"
|
||||
CloseOnOutsideClick="false"
|
||||
ShowCloseButton="true"
|
||||
CloseOnEscape="true"
|
||||
ShowHeader="true"
|
||||
CssClass="sender-delete-popup">
|
||||
<BodyContentTemplate>
|
||||
<div style="display:flex;flex-direction:column;gap:0.75rem;">
|
||||
<p style="margin:0;color:#374151;font-size:0.9rem;line-height:1.45;">
|
||||
Bitte geben Sie einen Grund an, warum der Umschlag gelöscht/zurückgezogen werden soll.
|
||||
</p>
|
||||
@if (_selectedEnvelope is not null)
|
||||
{
|
||||
<p style="margin:0;color:#6b7280;font-size:0.825rem;">
|
||||
Umschlag: <strong>#@_selectedEnvelope.Id - @_selectedEnvelope.Title</strong>
|
||||
</p>
|
||||
}
|
||||
<DxMemo @bind-Text="_deleteReason"
|
||||
NullText="Begründung eingeben..."
|
||||
Rows="5"
|
||||
MaxLength="2000"
|
||||
CssClass="w-100" />
|
||||
@if (!string.IsNullOrWhiteSpace(_deleteErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger" style="margin:0;padding:0.5rem 0.75rem;font-size:0.85rem;">
|
||||
@_deleteErrorMessage
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</BodyContentTemplate>
|
||||
<FooterContentTemplate>
|
||||
<div style="display:flex;justify-content:flex-end;gap:0.5rem;width:100%;">
|
||||
<DxButton Text="Abbrechen"
|
||||
RenderStyle="ButtonRenderStyle.Secondary"
|
||||
Click="CloseDeletePopup"
|
||||
Enabled="@(!_isDeletingEnvelope)" />
|
||||
<DxButton Text="Löschen"
|
||||
RenderStyle="ButtonRenderStyle.Danger"
|
||||
Click="ConfirmDeleteEnvelopeAsync"
|
||||
Enabled="@(!_isDeletingEnvelope)" />
|
||||
</div>
|
||||
</FooterContentTemplate>
|
||||
</DxPopup>
|
||||
|
||||
@code {
|
||||
private IEnumerable<EnvelopeDto>? _allEnvelopes;
|
||||
private IEnumerable<EnvelopeDto>? _activeEnvelopes;
|
||||
@@ -549,6 +597,11 @@
|
||||
private bool _exportSelectedRowsOnly = false;
|
||||
private bool _isExporting = false;
|
||||
|
||||
private bool _deletePopupVisible = false;
|
||||
private bool _isDeletingEnvelope = false;
|
||||
private string _deleteReason = string.Empty;
|
||||
private string? _deleteErrorMessage;
|
||||
|
||||
// Stable keys used to store/restore each grid's layout in UserPreferences.GridLayouts
|
||||
private const string GridKeyActive = "sender.active-envelopes";
|
||||
private const string GridKeyCompleted = "sender.completed-envelopes";
|
||||
@@ -681,7 +734,51 @@
|
||||
void DeleteEnvelope()
|
||||
{
|
||||
if (_selectedEnvelope == null) return;
|
||||
// TODO: Show delete confirmation dialog
|
||||
_deleteReason = string.Empty;
|
||||
_deleteErrorMessage = null;
|
||||
_deletePopupVisible = true;
|
||||
}
|
||||
|
||||
void CloseDeletePopup()
|
||||
{
|
||||
if (_isDeletingEnvelope)
|
||||
return;
|
||||
|
||||
_deletePopupVisible = false;
|
||||
_deleteErrorMessage = null;
|
||||
}
|
||||
|
||||
async Task ConfirmDeleteEnvelopeAsync()
|
||||
{
|
||||
if (_selectedEnvelope is null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_deleteReason))
|
||||
{
|
||||
_deleteErrorMessage = "Bitte geben Sie einen Löschgrund an.";
|
||||
return;
|
||||
}
|
||||
|
||||
_isDeletingEnvelope = true;
|
||||
_deleteErrorMessage = null;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
try
|
||||
{
|
||||
await EnvelopeService.DeleteAsync(_selectedEnvelope.Id, _deleteReason.Trim());
|
||||
_deletePopupVisible = false;
|
||||
_selectedEnvelope = null;
|
||||
await LoadEnvelopesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_deleteErrorMessage = ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDeletingEnvelope = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
async Task LogoutAsync()
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace EnvelopeGenerator.Server.Controllers;
|
||||
[Authorize]
|
||||
public class EnvelopeController : ControllerBase
|
||||
{
|
||||
public record DeleteEnvelopeRequest(string Reason);
|
||||
|
||||
private readonly ILogger<EnvelopeController> _logger;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
@@ -108,4 +110,17 @@ public class EnvelopeController : ControllerBase
|
||||
else
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthScheme.Sender)]
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> DeleteAsync([FromRoute] int id, [FromBody] DeleteEnvelopeRequest request)
|
||||
{
|
||||
var result = await _mediator.Send(new DeleteEnvelopeCommand
|
||||
{
|
||||
EnvelopeId = id,
|
||||
Reason = request?.Reason ?? string.Empty
|
||||
}.WithAuth(User.GetId()));
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ public class ExceptionHandlingMiddleware
|
||||
message = notFoundEx.Message;
|
||||
break;
|
||||
|
||||
case ForbiddenException forbiddenEx:
|
||||
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
message = forbiddenEx.Message;
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.LogError(exception, "Unhandled exception occurred.");
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
},
|
||||
"DbTriggerParams": {
|
||||
"Envelope": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
|
||||
"EnvelopeHistory": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
|
||||
"History": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
|
||||
"EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ],
|
||||
"EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ],
|
||||
"Receiver": [],
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
using DigitalData.Core.Exceptions;
|
||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
||||
using EnvelopeGenerator.Application.Envelopes.Commands;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using EnvelopeGenerator.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace EnvelopeGenerator.Tests.Application;
|
||||
|
||||
[TestFixture]
|
||||
public class DeleteEnvelopeCommandTests : TestBase
|
||||
{
|
||||
protected override void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteEnvelope_DraftEnvelope_ShouldSetDeleted_CreateHistory_AndQueueDeletionEmail()
|
||||
{
|
||||
CancellationToken cancel = default;
|
||||
var reason = "Invalid receiver data";
|
||||
|
||||
var envelope = this.CreateEnvelope(User.Id);
|
||||
envelope.Status = EnvelopeStatus.EnvelopeCreated;
|
||||
envelope = await GetRepository<Envelope>().CreateAsync(envelope, cancel);
|
||||
|
||||
var createReceiverCmd = this.CreateReceiverCommand();
|
||||
(var receiver, _) = await Mediator.Send(createReceiverCmd, cancel);
|
||||
|
||||
var envReceiver = this.CreateEnvelopeReceiver(envelope.Id, receiver.Id);
|
||||
await GetRepository<EnvelopeReceiver>().CreateAsync(envReceiver, cancel);
|
||||
|
||||
var result = await Mediator.Send(new DeleteEnvelopeCommand
|
||||
{
|
||||
EnvelopeId = envelope.Id,
|
||||
Reason = reason
|
||||
}.WithAuth(User.Id), cancel);
|
||||
|
||||
Assert.That(result.EnvelopeId, Is.EqualTo(envelope.Id));
|
||||
Assert.That(result.AppliedStatus, Is.EqualTo(EnvelopeStatus.EnvelopeDeleted));
|
||||
Assert.That(result.IsNoOp, Is.False);
|
||||
|
||||
var histories = await GetRepository<History>().Query
|
||||
.Where(h => h.EnvelopeId == envelope.Id && h.Status == EnvelopeStatus.EnvelopeDeleted)
|
||||
.ToListAsync(cancel);
|
||||
Assert.That(histories, Has.Count.EqualTo(1));
|
||||
Assert.That(histories[0].Comment, Is.EqualTo(reason));
|
||||
|
||||
var emails = await GetRepository<EmailOut>().Query
|
||||
.Where(e => e.WfId == (int)EnvelopeStatus.MessageDeletionSent && e.ReferenceId == receiver.Id)
|
||||
.ToListAsync(cancel);
|
||||
Assert.That(emails, Has.Count.EqualTo(1));
|
||||
Assert.That(emails[0].EmailAddress, Is.EqualTo(receiver.EmailAddress));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteEnvelope_SentEnvelope_ShouldSetWithdrawn()
|
||||
{
|
||||
CancellationToken cancel = default;
|
||||
|
||||
var envelope = this.CreateEnvelope(User.Id);
|
||||
envelope.Status = EnvelopeStatus.EnvelopeQueued;
|
||||
envelope = await GetRepository<Envelope>().CreateAsync(envelope, cancel);
|
||||
|
||||
var result = await Mediator.Send(new DeleteEnvelopeCommand
|
||||
{
|
||||
EnvelopeId = envelope.Id,
|
||||
Reason = "Sender requested cancellation"
|
||||
}.WithAuth(User.Id), cancel);
|
||||
|
||||
Assert.That(result.AppliedStatus, Is.EqualTo(EnvelopeStatus.EnvelopeWithdrawn));
|
||||
|
||||
var histories = await GetRepository<History>().Query
|
||||
.Where(h => h.EnvelopeId == envelope.Id && h.Status == EnvelopeStatus.EnvelopeWithdrawn)
|
||||
.ToListAsync(cancel);
|
||||
Assert.That(histories, Has.Count.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeleteEnvelope_MissingReason_ShouldThrowBadRequest()
|
||||
{
|
||||
Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
await Mediator.Send(new DeleteEnvelopeCommand
|
||||
{
|
||||
EnvelopeId = 123,
|
||||
Reason = " "
|
||||
}.WithAuth(User.Id)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DeleteEnvelope_WrongOwner_ShouldThrowForbidden()
|
||||
{
|
||||
CancellationToken cancel = default;
|
||||
|
||||
var envelope = this.CreateEnvelope(User.Id);
|
||||
envelope = await GetRepository<Envelope>().CreateAsync(envelope, cancel);
|
||||
|
||||
var otherUserCmd = this.CreateUserCommand();
|
||||
var otherUser = await GetRepository<DigitalData.UserManager.Domain.Entities.User>().CreateAsync(otherUserCmd, cancel);
|
||||
|
||||
Assert.ThrowsAsync<ForbiddenException>(async () =>
|
||||
await Mediator.Send(new DeleteEnvelopeCommand
|
||||
{
|
||||
EnvelopeId = envelope.Id,
|
||||
Reason = "Not allowed"
|
||||
}.WithAuth(otherUser.Id), cancel));
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class Fake
|
||||
|
||||
services.AddEnvelopeGeneratorInfrastructureServices(opt =>
|
||||
{
|
||||
opt.AddDbContext(dbCtxOpt => dbCtxOpt.UseInMemoryDatabase("EnvelopeGeneratorTestDb"));
|
||||
opt.AddDbContext(dbCtxOpt => dbCtxOpt.UseInMemoryDatabase($"EnvelopeGeneratorTestDb_{Guid.NewGuid():N}"));
|
||||
|
||||
opt.AddDbTriggerParams(context.Configuration);
|
||||
|
||||
@@ -308,4 +308,4 @@ public static class Extensions
|
||||
.Select(_ => fake.CreateUserCommand())
|
||||
.ToList();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ public abstract class TestBase : Faker
|
||||
|
||||
// Add seed email templates
|
||||
foreach (var temp in SeedEmailTemplates)
|
||||
{
|
||||
temp.LangCode ??= "de";
|
||||
await repo.CreateAsync(temp);
|
||||
}
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
@@ -140,4 +143,4 @@ public abstract class TestBase : Faker
|
||||
ChangedWhen = DateTime.Parse("2025-05-12 10:43:44.290")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user