|
17 | 17 | using Bit.Test.Common.AutoFixture; |
18 | 18 | using Bit.Test.Common.AutoFixture.Attributes; |
19 | 19 | using Microsoft.AspNetCore.Mvc; |
| 20 | +using Microsoft.Extensions.Logging; |
20 | 21 | using NSubstitute; |
| 22 | +using NSubstitute.ExceptionExtensions; |
21 | 23 | using Xunit; |
22 | 24 | using ZiggyCreatures.Caching.Fusion; |
23 | 25 |
|
@@ -672,6 +674,76 @@ await Assert.ThrowsAsync<NotFoundException>(() => |
672 | 674 | sutProvider.Sut.RenewFileUploadUrlAsync(orgId, report.Id, "wrong-file-id")); |
673 | 675 | } |
674 | 676 |
|
| 677 | + [Theory, BitAutoData] |
| 678 | + public async Task RenewFileUploadUrlAsync_NullReportFileId_ThrowsBadRequestException( |
| 679 | + SutProvider<OrganizationReportsController> sutProvider, |
| 680 | + Guid orgId, |
| 681 | + Guid reportId) |
| 682 | + { |
| 683 | + // Act & Assert |
| 684 | + var exception = await Assert.ThrowsAsync<BadRequestException>(() => |
| 685 | + sutProvider.Sut.RenewFileUploadUrlAsync(orgId, reportId, null)); |
| 686 | + |
| 687 | + Assert.Equal("ReportFileId is required.", exception.Message); |
| 688 | + } |
| 689 | + |
| 690 | + [Theory, BitAutoData] |
| 691 | + public async Task RenewFileUploadUrlAsync_EmptyReportFileId_ThrowsBadRequestException( |
| 692 | + SutProvider<OrganizationReportsController> sutProvider, |
| 693 | + Guid orgId, |
| 694 | + Guid reportId) |
| 695 | + { |
| 696 | + // Act & Assert |
| 697 | + var exception = await Assert.ThrowsAsync<BadRequestException>(() => |
| 698 | + sutProvider.Sut.RenewFileUploadUrlAsync(orgId, reportId, string.Empty)); |
| 699 | + |
| 700 | + Assert.Equal("ReportFileId is required.", exception.Message); |
| 701 | + } |
| 702 | + |
| 703 | + [Theory, BitAutoData] |
| 704 | + public async Task DeleteOrganizationReportAsync_StorageFailure_StillCompletesWithoutThrowing( |
| 705 | + SutProvider<OrganizationReportsController> sutProvider, |
| 706 | + Guid orgId, |
| 707 | + OrganizationReport report) |
| 708 | + { |
| 709 | + // Arrange |
| 710 | + var reportFile = new ReportFile { Id = "file-id", FileName = "report.json", Size = 1024, Validated = true }; |
| 711 | + report.OrganizationId = orgId; |
| 712 | + report.SetReportFile(reportFile); |
| 713 | + |
| 714 | + SetupAuthorization(sutProvider, orgId); |
| 715 | + |
| 716 | + sutProvider.GetDependency<IOrganizationReportRepository>() |
| 717 | + .GetByIdAsync(report.Id) |
| 718 | + .Returns(report); |
| 719 | + |
| 720 | + sutProvider.GetDependency<IOrganizationReportStorageService>() |
| 721 | + .DeleteReportFilesAsync(report, "file-id") |
| 722 | + .ThrowsAsync(new Exception("Azure storage unavailable")); |
| 723 | + |
| 724 | + // Act — should not throw despite storage failure |
| 725 | + await sutProvider.Sut.DeleteOrganizationReportAsync(orgId, report.Id); |
| 726 | + |
| 727 | + // Assert — DB delete and cache invalidation still happened |
| 728 | + await sutProvider.GetDependency<IOrganizationReportRepository>() |
| 729 | + .Received(1) |
| 730 | + .DeleteAsync(report); |
| 731 | + |
| 732 | + await sutProvider.GetDependency<IFusionCache>() |
| 733 | + .Received(1) |
| 734 | + .RemoveByTagAsync( |
| 735 | + OrganizationReportCacheConstants.BuildCacheTagForOrganizationReports(orgId)); |
| 736 | + |
| 737 | + sutProvider.GetDependency<ILogger<OrganizationReportsController>>() |
| 738 | + .Received(1) |
| 739 | + .Log( |
| 740 | + LogLevel.Warning, |
| 741 | + Arg.Any<EventId>(), |
| 742 | + Arg.Any<object>(), |
| 743 | + Arg.Any<Exception>(), |
| 744 | + Arg.Any<Func<object, Exception?, string>>()); |
| 745 | + } |
| 746 | + |
675 | 747 | // UpdateOrganizationReportAsync - V1 (flag off) |
676 | 748 |
|
677 | 749 | [Theory, BitAutoData] |
|
0 commit comments