1
0
mirror of https://github.com/bitwarden/server synced 2026-01-04 01:23:25 +00:00

Organization report tables, repos, services, and endpoints (#6158)

* PM-23754 initial commit

* pm-23754 fixing controller tests

* pm-23754 adding commands and queries

* pm-23754 adding endpoints, command/queries, repositories, and sql migrations

* pm-23754 add new sql scripts

* PM-23754 adding sql scripts

* pm-23754

* PM-23754 fixing migration script

* PM-23754 fixing migration script again

* PM-23754 fixing migration script validation

* PM-23754 fixing db validation script issue

* PM-23754 fixing endpoint and db validation

* PM-23754 fixing unit tests

* PM-23754 fixing implementation based on comments and tests

* PM-23754 updating logging statements

* PM-23754 making changes based on PR comments.

* updating migration scripts

* removing old migration files

* update code based testing for whole data object for OrganizationReport and add a stored procedure.

* updating services, unit tests, repository tests

* fixing unit tests

* fixing migration script

* fixing migration script again

* fixing migration script

* another fix

* fixing sql file, updating controller to account for different orgIds in the url and body.

* updating error message in controllers without a body

* making a change to the command

* Refactor ReportsController by removing organization reports

The IDropOrganizationReportCommand is no longer needed

* will code based on PR comments.

* fixing unit test

* fixing migration script based on last changes.

* adding another check in endpoint and adding unit tests

* fixing route parameter.

* PM-23754 updating data fields to return just the column

* PM-23754 fixing repository method signatures

* PM-23754 making change to orgId parameter through out code to align with api naming

---------

Co-authored-by: Tom <144813356+ttalty@users.noreply.github.com>
This commit is contained in:
Graham Walker
2025-09-08 15:06:13 -05:00
committed by GitHub
parent cb0d5a5ba6
commit 226f274a72
79 changed files with 24744 additions and 1047 deletions

View File

@@ -1,194 +0,0 @@
using AutoFixture;
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class DeleteOrganizationReportCommandTests
{
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withValidRequest_Success(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var OrganizationReports = fixture.CreateMany<OrganizationReport>(2).ToList();
// only take one id from the list - we only want to drop one record
var request = fixture.Build<DropOrganizationReportRequest>()
.With(x => x.OrganizationReportIds,
OrganizationReports.Select(x => x.Id).Take(1).ToList())
.Create();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(OrganizationReports);
// Act
await sutProvider.Sut.DropOrganizationReportAsync(request);
// Assert
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1)
.GetByOrganizationIdAsync(request.OrganizationId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1)
.DeleteAsync(Arg.Is<OrganizationReport>(_ =>
request.OrganizationReportIds.Contains(_.Id)));
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withValidRequest_nothingToDrop(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var OrganizationReports = fixture.CreateMany<OrganizationReport>(2).ToList();
// we are passing invalid data
var request = fixture.Build<DropOrganizationReportRequest>()
.With(x => x.OrganizationReportIds, new List<Guid> { Guid.NewGuid() })
.Create();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(OrganizationReports);
// Act
await sutProvider.Sut.DropOrganizationReportAsync(request);
// Assert
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1)
.GetByOrganizationIdAsync(request.OrganizationId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(0)
.DeleteAsync(Arg.Any<OrganizationReport>());
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withNodata_fails(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
// we are passing invalid data
var request = fixture.Build<DropOrganizationReportRequest>()
.Create();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(null as List<OrganizationReport>);
// Act
await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.DropOrganizationReportAsync(request));
// Assert
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1)
.GetByOrganizationIdAsync(request.OrganizationId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(0)
.DeleteAsync(Arg.Any<OrganizationReport>());
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withInvalidOrganizationId_ShouldThrowError(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<DropOrganizationReportRequest>();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(null as List<OrganizationReport>);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
Assert.Equal("No data found.", exception.Message);
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withInvalidOrganizationReportId_ShouldThrowError(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<DropOrganizationReportRequest>();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(new List<OrganizationReport>());
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
Assert.Equal("No data found.", exception.Message);
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withNullOrganizationId_ShouldThrowError(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<DropOrganizationReportRequest>()
.With(x => x.OrganizationId, default(Guid))
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
Assert.Equal("No data found.", exception.Message);
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withNullOrganizationReportIds_ShouldThrowError(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<DropOrganizationReportRequest>()
.With(x => x.OrganizationReportIds, default(List<Guid>))
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
Assert.Equal("No data found.", exception.Message);
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withEmptyOrganizationReportIds_ShouldThrowError(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<DropOrganizationReportRequest>()
.With(x => x.OrganizationReportIds, new List<Guid>())
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
Assert.Equal("No data found.", exception.Message);
}
[Theory, BitAutoData]
public async Task DropOrganizationReportAsync_withEmptyRequest_ShouldThrowError(
SutProvider<DropOrganizationReportCommand> sutProvider)
{
// Arrange
var request = new DropOrganizationReportRequest();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.DropOrganizationReportAsync(request));
Assert.Equal("No data found.", exception.Message);
}
}

View File

@@ -0,0 +1,116 @@
using AutoFixture;
using Bit.Core.Dirt.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class GetOrganizationReportApplicationDataQueryTests
{
[Theory]
[BitAutoData]
public async Task GetOrganizationReportApplicationDataAsync_WithValidParams_ShouldReturnApplicationData(
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
var reportId = fixture.Create<Guid>();
var applicationDataResponse = fixture.Build<OrganizationReportApplicationDataResponse>()
.Create();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetApplicationDataAsync(reportId)
.Returns(applicationDataResponse);
// Act
var result = await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, reportId);
// Assert
Assert.NotNull(result);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).GetApplicationDataAsync(reportId);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportApplicationDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
{
// Arrange
var reportId = Guid.NewGuid();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(Guid.Empty, reportId));
Assert.Equal("OrganizationId is required.", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().GetApplicationDataAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportApplicationDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, Guid.Empty));
Assert.Equal("ReportId is required.", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().GetApplicationDataAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportApplicationDataAsync_WhenDataNotFound_ShouldThrowNotFoundException(
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetApplicationDataAsync(reportId)
.Returns((OrganizationReportApplicationDataResponse)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, reportId));
Assert.Equal("Organization report application data not found.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportApplicationDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
SutProvider<GetOrganizationReportApplicationDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
var expectedMessage = "Database connection failed";
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetApplicationDataAsync(reportId)
.Throws(new InvalidOperationException(expectedMessage));
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await sutProvider.Sut.GetOrganizationReportApplicationDataAsync(organizationId, reportId));
Assert.Equal(expectedMessage, exception.Message);
}
}

View File

@@ -0,0 +1,116 @@
using AutoFixture;
using Bit.Core.Dirt.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class GetOrganizationReportDataQueryTests
{
[Theory]
[BitAutoData]
public async Task GetOrganizationReportDataAsync_WithValidParams_ShouldReturnReportData(
SutProvider<GetOrganizationReportDataQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
var reportId = fixture.Create<Guid>();
var reportDataResponse = fixture.Build<OrganizationReportDataResponse>()
.Create();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetReportDataAsync(reportId)
.Returns(reportDataResponse);
// Act
var result = await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, reportId);
// Assert
Assert.NotNull(result);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).GetReportDataAsync(reportId);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportDataQuery> sutProvider)
{
// Arrange
var reportId = Guid.NewGuid();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportDataAsync(Guid.Empty, reportId));
Assert.Equal("OrganizationId is required.", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().GetReportDataAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, Guid.Empty));
Assert.Equal("ReportId is required.", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().GetReportDataAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportDataAsync_WhenDataNotFound_ShouldThrowNotFoundException(
SutProvider<GetOrganizationReportDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetReportDataAsync(reportId)
.Returns((OrganizationReportDataResponse)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, reportId));
Assert.Equal("Organization report data not found.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
SutProvider<GetOrganizationReportDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
var expectedMessage = "Database connection failed";
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetReportDataAsync(reportId)
.Throws(new InvalidOperationException(expectedMessage));
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await sutProvider.Sut.GetOrganizationReportDataAsync(organizationId, reportId));
Assert.Equal(expectedMessage, exception.Message);
}
}

View File

@@ -1,188 +0,0 @@
using AutoFixture;
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class GetOrganizationReportQueryTests
{
[Theory]
[BitAutoData]
public async Task GetOrganizationReportAsync_WithValidOrganizationId_ShouldReturnOrganizationReport(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(fixture.CreateMany<OrganizationReport>(2).ToList());
// Act
var result = await sutProvider.Sut.GetOrganizationReportAsync(organizationId);
// Assert
Assert.NotNull(result);
Assert.True(result.Count() == 2);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportAsync_WithInvalidOrganizationId_ShouldFail(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Is<Guid>(x => x == Guid.Empty))
.Returns(new List<OrganizationReport>());
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(Guid.Empty));
// Assert
Assert.Equal("OrganizationId is required.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetLatestOrganizationReportAsync_WithValidOrganizationId_ShouldReturnOrganizationReport(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetLatestByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(fixture.Create<OrganizationReport>());
// Act
var result = await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId);
// Assert
Assert.NotNull(result);
}
[Theory]
[BitAutoData]
public async Task GetLatestOrganizationReportAsync_WithInvalidOrganizationId_ShouldFail(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetLatestByOrganizationIdAsync(Arg.Is<Guid>(x => x == Guid.Empty))
.Returns(default(OrganizationReport));
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(Guid.Empty));
// Assert
Assert.Equal("OrganizationId is required.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportAsync_WithNoReports_ShouldReturnEmptyList(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(new List<OrganizationReport>());
// Act
var result = await sutProvider.Sut.GetOrganizationReportAsync(organizationId);
// Assert
Assert.NotNull(result);
Assert.Empty(result);
}
[Theory]
[BitAutoData]
public async Task GetLatestOrganizationReportAsync_WithNoReports_ShouldReturnNull(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetLatestByOrganizationIdAsync(Arg.Any<Guid>())
.Returns(default(OrganizationReport));
// Act
var result = await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId);
// Assert
Assert.Null(result);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportAsync_WithNullOrganizationId_ShouldThrowException(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = default(Guid);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(organizationId));
// Assert
Assert.Equal("OrganizationId is required.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetLatestOrganizationReportAsync_WithNullOrganizationId_ShouldThrowException(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = default(Guid);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId));
// Assert
Assert.Equal("OrganizationId is required.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportAsync_WithInvalidOrganizationId_ShouldThrowException(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = Guid.Empty;
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetOrganizationReportAsync(organizationId));
// Assert
Assert.Equal("OrganizationId is required.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetLatestOrganizationReportAsync_WithInvalidOrganizationId_ShouldThrowException(
SutProvider<GetOrganizationReportQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = Guid.Empty;
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.GetLatestOrganizationReportAsync(organizationId));
// Assert
Assert.Equal("OrganizationId is required.", exception.Message);
}
}

View File

@@ -0,0 +1,133 @@
using AutoFixture;
using Bit.Core.Dirt.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class GetOrganizationReportSummaryDataByDateRangeQueryTests
{
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithValidParams_ShouldReturnSummaryData(
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
var reportId = fixture.Create<Guid>();
var startDate = DateTime.UtcNow.AddDays(-30);
var endDate = DateTime.UtcNow;
var summaryDataList = fixture.Build<OrganizationReportSummaryDataResponse>()
.CreateMany(3);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate)
.Returns(summaryDataList);
// Act
var result = await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
// Assert
Assert.NotNull(result);
Assert.Equal(3, result.Count());
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
{
// Arrange
var reportId = Guid.NewGuid();
var startDate = DateTime.UtcNow.AddDays(-30);
var endDate = DateTime.UtcNow;
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(Guid.Empty, startDate, endDate));
Assert.Equal("OrganizationId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive()
.GetSummaryDataByDateRangeAsync(
Arg.Any<Guid>(),
Arg.Any<DateTime>(),
Arg.Any<DateTime>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithStartDateAfterEndDate_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
var startDate = DateTime.UtcNow;
var endDate = DateTime.UtcNow.AddDays(-30);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate));
Assert.Equal("StartDate must be earlier than or equal to EndDate", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().GetSummaryDataByDateRangeAsync(Arg.Any<Guid>(), Arg.Any<DateTime>(), Arg.Any<DateTime>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WithEmptyResult_ShouldReturnEmptyList(
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
var startDate = DateTime.UtcNow.AddDays(-30);
var endDate = DateTime.UtcNow;
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate)
.Returns(new List<OrganizationReportSummaryDataResponse>());
// Act
var result = await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate);
// Assert
Assert.NotNull(result);
Assert.Empty(result);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataByDateRangeAsync_WhenRepositoryThrowsException_ShouldPropagateException(
SutProvider<GetOrganizationReportSummaryDataByDateRangeQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
var startDate = DateTime.UtcNow.AddDays(-30);
var endDate = DateTime.UtcNow;
var expectedMessage = "Database connection failed";
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetSummaryDataByDateRangeAsync(organizationId, startDate, endDate)
.Throws(new InvalidOperationException(expectedMessage));
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await sutProvider.Sut.GetOrganizationReportSummaryDataByDateRangeAsync(organizationId, startDate, endDate));
Assert.Equal(expectedMessage, exception.Message);
}
}

View File

@@ -0,0 +1,116 @@
using AutoFixture;
using Bit.Core.Dirt.Models.Data;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class GetOrganizationReportSummaryDataQueryTests
{
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataAsync_WithValidParams_ShouldReturnSummaryData(
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
{
// Arrange
var fixture = new Fixture();
var organizationId = fixture.Create<Guid>();
var reportId = fixture.Create<Guid>();
var summaryDataResponse = fixture.Build<OrganizationReportSummaryDataResponse>()
.Create();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetSummaryDataAsync(reportId)
.Returns(summaryDataResponse);
// Act
var result = await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, reportId);
// Assert
Assert.NotNull(result);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).GetSummaryDataAsync(reportId);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
{
// Arrange
var reportId = Guid.NewGuid();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(Guid.Empty, reportId));
Assert.Equal("OrganizationId is required.", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().GetSummaryDataAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, Guid.Empty));
Assert.Equal("ReportId is required.", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().GetSummaryDataAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataAsync_WhenDataNotFound_ShouldThrowNotFoundException(
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetSummaryDataAsync(reportId)
.Returns((OrganizationReportSummaryDataResponse)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, reportId));
Assert.Equal("Organization report summary data not found.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationReportSummaryDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
SutProvider<GetOrganizationReportSummaryDataQuery> sutProvider)
{
// Arrange
var organizationId = Guid.NewGuid();
var reportId = Guid.NewGuid();
var expectedMessage = "Database connection failed";
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetSummaryDataAsync(reportId)
.Throws(new InvalidOperationException(expectedMessage));
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await sutProvider.Sut.GetOrganizationReportSummaryDataAsync(organizationId, reportId));
Assert.Equal(expectedMessage, exception.Message);
}
}

View File

@@ -0,0 +1,252 @@
using AutoFixture;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class UpdateOrganizationReportApplicationDataCommandTests
{
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithValidRequest_ShouldReturnUpdatedReport(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
.With(x => x.Id, Guid.NewGuid())
.With(x => x.OrganizationId, Guid.NewGuid())
.With(x => x.ApplicationData, "updated application data")
.Create();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.Id)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
var updatedReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.Id)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.Id)
.Returns(existingReport);
sutProvider.GetDependency<IOrganizationReportRepository>()
.UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData)
.Returns(updatedReport);
// Act
var result = await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(updatedReport.Id, result.Id);
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
.With(x => x.OrganizationId, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("OrganizationId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpdateApplicationDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithEmptyId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
.With(x => x.Id, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("Id is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpdateApplicationDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns((Organization)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("Invalid Organization", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithEmptyApplicationData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
.With(x => x.ApplicationData, string.Empty)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("Application Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithNullApplicationData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportApplicationDataRequest>()
.With(x => x.ApplicationData, (string)null)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("Application Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithNonExistentReport_ShouldThrowNotFoundException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.Id)
.Returns((OrganizationReport)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("Organization report not found", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.Id)
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.Id)
.Returns(existingReport);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportApplicationDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
SutProvider<UpdateOrganizationReportApplicationDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportApplicationDataRequest>();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.Id)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.Id)
.Returns(existingReport);
sutProvider.GetDependency<IOrganizationReportRepository>()
.UpdateApplicationDataAsync(request.OrganizationId, request.Id, request.ApplicationData)
.Throws(new InvalidOperationException("Database connection failed"));
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportApplicationDataAsync(request));
Assert.Equal("Database connection failed", exception.Message);
}
}

View File

@@ -0,0 +1,230 @@
using AutoFixture;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class UpdateOrganizationReportCommandTests
{
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithValidRequest_ShouldReturnUpdatedReport(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportRequest>()
.With(x => x.ReportId, Guid.NewGuid())
.With(x => x.OrganizationId, Guid.NewGuid())
.With(x => x.ReportData, "updated report data")
.Create();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
var updatedReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.With(x => x.ReportData, request.ReportData)
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
sutProvider.GetDependency<IOrganizationReportRepository>()
.UpsertAsync(Arg.Any<OrganizationReport>())
.Returns(Task.CompletedTask);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport, updatedReport);
// Act
var result = await sutProvider.Sut.UpdateOrganizationReportAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(updatedReport.Id, result.Id);
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
Assert.Equal(updatedReport.ReportData, result.ReportData);
await sutProvider.GetDependency<IOrganizationRepository>()
.Received(1).GetByIdAsync(request.OrganizationId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(2).GetByIdAsync(request.ReportId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).UpsertAsync(Arg.Any<OrganizationReport>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportRequest>()
.With(x => x.OrganizationId, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
Assert.Equal("OrganizationId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpsertAsync(Arg.Any<OrganizationReport>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithEmptyReportId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportRequest>()
.With(x => x.ReportId, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
Assert.Equal("ReportId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpsertAsync(Arg.Any<OrganizationReport>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportRequest>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns((Organization)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
Assert.Equal("Invalid Organization", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithEmptyReportData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportRequest>()
.With(x => x.ReportData, string.Empty)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
Assert.Equal("Report Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithNullReportData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportRequest>()
.With(x => x.ReportData, (string)null)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
Assert.Equal("Report Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithNonExistentReport_ShouldThrowNotFoundException(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportRequest>();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns((OrganizationReport)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
Assert.Equal("Organization report not found", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportRequest>();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportAsync(request));
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
}
}

View File

@@ -0,0 +1,252 @@
using AutoFixture;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class UpdateOrganizationReportDataCommandTests
{
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithValidRequest_ShouldReturnUpdatedReport(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
.With(x => x.ReportId, Guid.NewGuid())
.With(x => x.OrganizationId, Guid.NewGuid())
.With(x => x.ReportData, "updated report data")
.Create();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
var updatedReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
sutProvider.GetDependency<IOrganizationReportRepository>()
.UpdateReportDataAsync(request.OrganizationId, request.ReportId, request.ReportData)
.Returns(updatedReport);
// Act
var result = await sutProvider.Sut.UpdateOrganizationReportDataAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(updatedReport.Id, result.Id);
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).UpdateReportDataAsync(request.OrganizationId, request.ReportId, request.ReportData);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
.With(x => x.OrganizationId, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("OrganizationId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpdateReportDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithEmptyReportId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
.With(x => x.ReportId, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("ReportId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpdateReportDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns((Organization)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("Invalid Organization", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithEmptyReportData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
.With(x => x.ReportData, string.Empty)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("Report Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithNullReportData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportDataRequest>()
.With(x => x.ReportData, (string)null)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("Report Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithNonExistentReport_ShouldThrowNotFoundException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns((OrganizationReport)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("Organization report not found", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportDataAsync_WhenRepositoryThrowsException_ShouldPropagateException(
SutProvider<UpdateOrganizationReportDataCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportDataRequest>();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
sutProvider.GetDependency<IOrganizationReportRepository>()
.UpdateReportDataAsync(request.OrganizationId, request.ReportId, request.ReportData)
.Throws(new InvalidOperationException("Database connection failed"));
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportDataAsync(request));
Assert.Equal("Database connection failed", exception.Message);
}
}

View File

@@ -0,0 +1,252 @@
using AutoFixture;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Dirt.Entities;
using Bit.Core.Dirt.Reports.ReportFeatures;
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
using Bit.Core.Dirt.Repositories;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Bit.Core.Test.Dirt.ReportFeatures;
[SutProviderCustomize]
public class UpdateOrganizationReportSummaryCommandTests
{
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithValidRequest_ShouldReturnUpdatedReport(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
.With(x => x.ReportId, Guid.NewGuid())
.With(x => x.OrganizationId, Guid.NewGuid())
.With(x => x.SummaryData, "updated summary data")
.Create();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
var updatedReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
sutProvider.GetDependency<IOrganizationReportRepository>()
.UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData)
.Returns(updatedReport);
// Act
var result = await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(updatedReport.Id, result.Id);
Assert.Equal(updatedReport.OrganizationId, result.OrganizationId);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.Received(1).UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithEmptyOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
.With(x => x.OrganizationId, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("OrganizationId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpdateSummaryDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithEmptyReportId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
.With(x => x.ReportId, Guid.Empty)
.Create();
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("ReportId is required", exception.Message);
await sutProvider.GetDependency<IOrganizationReportRepository>()
.DidNotReceive().UpdateSummaryDataAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithInvalidOrganization_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns((Organization)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("Invalid Organization", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithEmptySummaryData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
.With(x => x.SummaryData, string.Empty)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("Summary Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithNullSummaryData_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Build<UpdateOrganizationReportSummaryRequest>()
.With(x => x.SummaryData, (string)null)
.Create();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("Summary Data is required", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithNonExistentReport_ShouldThrowNotFoundException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
var organization = fixture.Create<Organization>();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns((OrganizationReport)null);
// Act & Assert
var exception = await Assert.ThrowsAsync<NotFoundException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("Organization report not found", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WithMismatchedOrganizationId_ShouldThrowBadRequestException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, Guid.NewGuid()) // Different org ID
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
// Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("Organization report does not belong to the specified organization", exception.Message);
}
[Theory]
[BitAutoData]
public async Task UpdateOrganizationReportSummaryAsync_WhenRepositoryThrowsException_ShouldPropagateException(
SutProvider<UpdateOrganizationReportSummaryCommand> sutProvider)
{
// Arrange
var fixture = new Fixture();
var request = fixture.Create<UpdateOrganizationReportSummaryRequest>();
var organization = fixture.Create<Organization>();
var existingReport = fixture.Build<OrganizationReport>()
.With(x => x.Id, request.ReportId)
.With(x => x.OrganizationId, request.OrganizationId)
.Create();
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(request.OrganizationId)
.Returns(organization);
sutProvider.GetDependency<IOrganizationReportRepository>()
.GetByIdAsync(request.ReportId)
.Returns(existingReport);
sutProvider.GetDependency<IOrganizationReportRepository>()
.UpdateSummaryDataAsync(request.OrganizationId, request.ReportId, request.SummaryData)
.Throws(new InvalidOperationException("Database connection failed"));
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await sutProvider.Sut.UpdateOrganizationReportSummaryAsync(request));
Assert.Equal("Database connection failed", exception.Message);
}
}