feat: initial commit
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.DataSources.Models;
|
||||
using Strata.Analytics.Biz.DataSources.Services;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.DataSchema.Client;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.DataSources
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class DataSourceCreateTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public async Task DataSource_CurrentUserAssignedAsEditor_GivenUserIsNotStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var dataSource = await dataSourceService.CreateAsync(new DataSourceSaveInfo() { Name = "DataSource1" }, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dataSource.CreatedByUserGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
dataSource.CurrentUserRole.Should().Be(DataSourceRole.Editor);
|
||||
dbContext.DataSourceRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().Role.Should().Be(DataSourceRole.Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DataSource_CurrentUserAssignedAsEditorButNoRoleAssignmentsCreated_GivenUserIsStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var dataSource = await dataSourceService.CreateAsync(new DataSourceSaveInfo() { Name = "DataSource1" }, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dataSource.CreatedByUserGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
dataSource.CurrentUserRole.Should().Be(DataSourceRole.Editor);
|
||||
dbContext.DataSourceRoleAssignments.Should().HaveCount(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.DataSources.Models;
|
||||
using Strata.Analytics.Biz.DataSources.Services;
|
||||
using Strata.Analytics.Models.Security;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.DataSchema.Client;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.DataSources
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class DataSourceGetCurrentUserRoleTest
|
||||
{
|
||||
[Test]
|
||||
public async Task DataSourceCurrentUserRole_IsNull_GivenNoRoleAssignments()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var role = await dataSourceService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.HasValue.Should().BeFalse();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task DataSourceCurrentUserRole_IsEditor_GivenUserIsStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var role = await dataSourceService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(DataSourceRole.Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DataSourceCurrentUserRole_UsesCurrentUser_GivenMultipleUsersAssignedToMultipleRoles()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var userRoleAssignemnt = new DataSourceRoleAssignment() { StrataId = 5, DataSourceId = 5, IdentityGuid = claimsPrincipal.GetUserGuid(), IdentityName = claimsPrincipal.GetUsername(), IdentityType = IdentityType.User, Role = DataSourceRole.Viewer };
|
||||
var differentUserRoleAssignemnt = new DataSourceRoleAssignment() { StrataId = 5, DataSourceId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Random User", IdentityType = IdentityType.User, Role = DataSourceRole.Editor };
|
||||
|
||||
dbContext.DataSourceRoleAssignments.Add(userRoleAssignemnt);
|
||||
dbContext.DataSourceRoleAssignments.Add(differentUserRoleAssignemnt);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// act
|
||||
var role = await dataSourceService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(userRoleAssignemnt.Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DataSourceCurrentUserRole_ReturnsMostPrivilege_GivenUserAssignedToMultipleRoles()
|
||||
{
|
||||
// arrange
|
||||
var userGroup = "DSS - Admin";
|
||||
var userGroupGuid = Guid.NewGuid();
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, false, false, new string[] { userGroup });
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var viewerRoleAssignment = new DataSourceRoleAssignment() { StrataId = 5, DataSourceId = 5, IdentityGuid = claimsPrincipal.GetUserGuid(), IdentityName = claimsPrincipal.GetUsername(), IdentityType = IdentityType.User, Role = DataSourceRole.Viewer };
|
||||
var editorRoleAssignment = new DataSourceRoleAssignment() { StrataId = 5, DataSourceId = 5, IdentityGuid = userGroupGuid, IdentityName = userGroup, IdentityType = IdentityType.UserGroup, Role = DataSourceRole.Editor };
|
||||
dbContext.DataSourceRoleAssignments.Add(viewerRoleAssignment);
|
||||
dbContext.DataSourceRoleAssignments.Add(editorRoleAssignment);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// act
|
||||
var role = await dataSourceService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(DataSourceRole.Editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.DataSources.Models;
|
||||
using Strata.Analytics.Biz.DataSources.Services;
|
||||
using Strata.Analytics.Models.Security;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.DataSchema.Client;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.DataSources
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class DataSourceRoleAssignmentTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public async Task DataSourceRoleAssignment_Created_GivenRoleAssignmentWithIdentityIsNotAlreadyInDatabase()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var batch = new Batch<DataSourceRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Updates = new List<DataSourceRoleAssignmentSaveInfo>()
|
||||
{
|
||||
new DataSourceRoleAssignmentSaveInfo() { IdentityGuid = Guid.NewGuid(), IdentityName="Admin", IdentityType=IdentityType.User, Role=DataSourceRole.Editor}
|
||||
}
|
||||
};
|
||||
|
||||
// act
|
||||
await dataSourceService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.DataSourceRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().DataSourceId.Should().Be(5);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(batch.Updates.First().IdentityGuid);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().IdentityName.Should().Be(batch.Updates.First().IdentityName);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().Role.Should().Be(batch.Updates.First().Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DataSourceRoleAssignment_Updated_GivenRoleAssignmentWithIdentityIsAlreadyInDatabase()
|
||||
{
|
||||
// arrange
|
||||
var strataId = 5;
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(strataId);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var existingRoleAssigment = new DataSourceRoleAssignment() { StrataId = strataId, DataSourceId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Admin", IdentityType = IdentityType.User, Role = DataSourceRole.Viewer };
|
||||
dbContext.DataSourceRoleAssignments.Add(existingRoleAssigment);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// change role to Editor
|
||||
var batch = new Batch<DataSourceRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Updates = new List<DataSourceRoleAssignmentSaveInfo>()
|
||||
{
|
||||
new DataSourceRoleAssignmentSaveInfo() { IdentityGuid = existingRoleAssigment.IdentityGuid, IdentityName="Admin", IdentityType=IdentityType.User, Role=DataSourceRole.Editor}
|
||||
}
|
||||
};
|
||||
|
||||
// act
|
||||
await dataSourceService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.DataSourceRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().DataSourceId.Should().Be(5);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(batch.Updates.First().IdentityGuid);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().IdentityName.Should().Be(batch.Updates.First().IdentityName);
|
||||
dbContext.DataSourceRoleAssignments.FirstOrDefault().Role.Should().Be(batch.Updates.First().Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DataSourceRoleAssignment_Deleted_GivenIdentityGuidIsPartOfBatchDeletes()
|
||||
{
|
||||
// arrange
|
||||
var strataId = 5;
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(strataId);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var dataSourceService = new DataSourceService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var existingRoleAssigment = new DataSourceRoleAssignment() { StrataId = strataId, DataSourceId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Admin", IdentityType = IdentityType.User, Role = DataSourceRole.Editor };
|
||||
dbContext.DataSourceRoleAssignments.Add(existingRoleAssigment);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
var batch = new Batch<DataSourceRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Deletes = new List<Guid>() { existingRoleAssigment.IdentityGuid }
|
||||
};
|
||||
|
||||
// act
|
||||
await dataSourceService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.DataSourceRoleAssignments.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class ExtractCreateTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public async Task Extract_CurrentUserAssignedAsEditor_GivenUserIsNotStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var extract = await extractService.CreateAsync(new ExtractCreateInfo()
|
||||
{
|
||||
Name = "Extract1",
|
||||
Content = "{\"dataSourceId\":1,\"rows\":[],\"columnGroups\":[{\"groupId\":\"ABCD1\",\"values\":[],\"dimensions\":[],\"sortOrder\":0}],\"filters\":[],\"queryConfig\":{\"rows\":[],\"values\":[],\"filterGroups\":[],\"withTotals\":false,\"rowLimit\":25}}"
|
||||
}, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
extract.CreatedByUserGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
extract.CurrentUserRole.Should().Be(ExtractRole.Editor);
|
||||
dbContext.ExtractRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().Role.Should().Be(ExtractRole.Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Extract_CurrentUserAssignedAsEditorButNoRoleAssignmentsCreated_GivenUserIsStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var extract = await extractService.CreateAsync(new ExtractCreateInfo()
|
||||
{
|
||||
Name = "Extract1",
|
||||
Content = "{\"dataSourceId\":1,\"rows\":[],\"columnGroups\":[{\"groupId\":\"ABCD1\",\"values\":[],\"dimensions\":[],\"sortOrder\":0}],\"filters\":[],\"queryConfig\":{\"rows\":[],\"values\":[],\"filterGroups\":[],\"withTotals\":false,\"rowLimit\":25}}"
|
||||
}, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
extract.CreatedByUserGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
extract.CurrentUserRole.Should().Be(ExtractRole.Editor);
|
||||
dbContext.ExtractRoleAssignments.Should().HaveCount(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.DbContexts;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class ExtractCrudTests
|
||||
{
|
||||
private IClaimsPrincipalAccessor _claimsPrincipalAccessor;
|
||||
private AnalyticsDbContext _dbContext;
|
||||
private IMapper _mapper;
|
||||
private IExtractService _extractService;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
_claimsPrincipalAccessor = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
_dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(_claimsPrincipalAccessor);
|
||||
_mapper = AutoMapperMock.GetAutoMapper();
|
||||
_extractService = new ExtractService(_dbContext, _claimsPrincipalAccessor, _mapper);
|
||||
_dbContext.Extracts.AddRange(ExtractTestData.Extracts());
|
||||
_dbContext.ExtractExportHistory.AddRange(ExtractTestData.ExtractExportHistories());
|
||||
_dbContext.SaveChanges();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task OneTimeTearDown()
|
||||
{
|
||||
await _dbContext.Database.EnsureDeletedAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanCreate()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
// arrange
|
||||
var saveInfo = new ExtractCreateInfo()
|
||||
{
|
||||
Name = "My First Unit Test Extract!",
|
||||
Description = "Be Happy For Me!!!",
|
||||
ExtractType = ExtractType.DataSource,
|
||||
Content = "{\"dataSourceId\":1,\"rows\":[],\"columnGroups\":[{\"groupId\":\"ABCD1\",\"values\":[],\"dimensions\":[],\"sortOrder\":0}],\"filters\":[],\"queryConfig\":{\"rows\":[],\"values\":[],\"filterGroups\":[],\"withTotals\":false,\"rowLimit\":25}}"
|
||||
};
|
||||
|
||||
// act
|
||||
var result = await _extractService.CreateAsync(saveInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.ExtractId, Is.GreaterThan(ExtractTestData.Extracts().Max(h => h.ExtractId)));
|
||||
Assert.That(result.ExtractType, Is.EqualTo(ExtractType.DataSource));
|
||||
Assert.That(result.Content, Is.EqualTo(saveInfo.Content));
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.IsNotNull(result.CreatedDate);
|
||||
Assert.IsNotNull(result.UpdatedDate);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task CanGetById()
|
||||
{
|
||||
// act
|
||||
var result = await _extractService.GetByIdAsync(1, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task CanUpdate()
|
||||
{
|
||||
// arrange
|
||||
var extractId = _dbContext.Extracts.First().ExtractId;
|
||||
|
||||
var extract = await _extractService.GetByIdAsync(extractId, CancellationToken.None);
|
||||
|
||||
var saveInfo = new ExtractSaveInfo();
|
||||
_mapper.Map(extract, saveInfo);
|
||||
|
||||
saveInfo.Name = "My First RENAMED Extract!";
|
||||
saveInfo.Description = "Fear Me!!!";
|
||||
saveInfo.RawSql = "My New and IMPROVED Query...";
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
// act
|
||||
var result = await _extractService.UpdateAsync(extract.ExtractId, saveInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.ExtractId, Is.EqualTo(extract.ExtractId));
|
||||
Assert.That(result.Name, Is.EqualTo(saveInfo.Name));
|
||||
Assert.That(result.Description, Is.EqualTo(saveInfo.Description));
|
||||
Assert.That(result.RawSql, Is.EqualTo(saveInfo.RawSql));
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(extract.CreatedByUserGuid));
|
||||
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.That(result.CreatedDate, Is.EqualTo(extract.CreatedDate));
|
||||
Assert.That(result.UpdatedDate, Is.EqualTo(DateTimeOffset.UtcNow).Within(500).Milliseconds); // this could fail in debug
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanDelete_LeaveOrphanHistory()
|
||||
{
|
||||
// arrange
|
||||
var extractId = _dbContext.Extracts.Last().ExtractId;
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
// act
|
||||
await _extractService.DeleteAsync(extractId, CancellationToken.None);
|
||||
|
||||
var result = await _extractService.GetByIdAsync(extractId, CancellationToken.None);
|
||||
var histories = new ExtractExportHistoryService(_dbContext, _claimsPrincipalAccessor, _mapper).GetByExtractIdAsync(extractId, CancellationToken.None);
|
||||
|
||||
|
||||
// assert
|
||||
Assert.IsNull(result);
|
||||
Assert.IsNotNull(histories);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Snowflake.Data.Client;
|
||||
using Strata.Analytics.Biz.DataQuery;
|
||||
using Strata.Analytics.Biz.Extracts;
|
||||
using Strata.Analytics.Biz.Extracts.Export;
|
||||
using Strata.Analytics.Biz.Extracts.Jobs;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Biz.Hangfire;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.DataSchema.Models.Query;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[TestFixture]
|
||||
internal class ExtractExportJobTest
|
||||
{
|
||||
private Mock<IS3BucketService> _s3BucketService;
|
||||
private Mock<IExtractService> _extractService;
|
||||
private Mock<IExtractExportHistoryService> _historyService;
|
||||
private Mock<IRecurringJobService> _recurringJobService;
|
||||
private Mock<IExtractScheduleService> _scheduleService;
|
||||
private Mock<ISftpService> _sftpService;
|
||||
private Mock<IDataQueryService> _dataQueryService;
|
||||
private Mock<ILogger<ExtractExportJob>> _logger;
|
||||
private IClaimsPrincipalAccessor _claimsPrincipalAccessor;
|
||||
private Mock<IOptions<ExtractConfiguration>> _config;
|
||||
|
||||
|
||||
[Test]
|
||||
public void ExecuteExport_3Files_NoOutbound()
|
||||
{
|
||||
const string SFTP_PATH = "SFTP://directory/folder/subpath";
|
||||
var exportJob = CreateExtractExportJob();
|
||||
var extract = GetRawSqlExtract();
|
||||
var exportRequest = GetExtractExportRequest(extract);
|
||||
var externalStageTokens = new ExternalStageUrlTokens
|
||||
{ BucketName = "Big Bucket", FolderPath = "s3://somewhere/out/there/" };
|
||||
var files = GetFiles(exportRequest.OutputFileName, externalStageTokens, 3);
|
||||
var outboundFiles = files.Select(x => new OutboundFileInfo() { Destination = SFTP_PATH, FileName = x.FilePath, FileSize = x.FileSize, RowCount = x.RowCount }).ToList();
|
||||
|
||||
_extractService.Setup(x => x.GetByIdForExecutionAsync(It.IsAny<int>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(extract));
|
||||
_dataQueryService.Setup(x => x.BuildSqlQuery(It.IsAny<QueryConfig>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(new SqlResponse { SqlQuery = extract.RawSql }));
|
||||
_dataQueryService.Setup(x => x.GetExternalStageUrlTokensAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(externalStageTokens));
|
||||
_dataQueryService.Setup(x => x.ExecuteSqlQueryAsync(It.IsAny<string>(), It.IsAny<IEnumerable<KeyValuePair<string, object>>>())).Returns(Task.FromResult(new List<dynamic>().AsEnumerable()));
|
||||
_s3BucketService
|
||||
.Setup(x => x.GetFiles(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(),
|
||||
It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(files));
|
||||
_sftpService.Setup(x => x.UploadFilesFromS3(It.IsAny<string>(), files, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(outboundFiles));
|
||||
_historyService
|
||||
.Setup(x => x.CreateAsync(It.IsAny<ExtractExportHistorySaveInfo>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(1));
|
||||
_historyService
|
||||
.Setup(x => x.UpdateAsync(It.IsAny<int>(), It.IsAny<ExtractExportHistorySaveInfo>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(new ExtractExportHistory
|
||||
{ ExtractId = extract.ExtractId, ExtractHistoryId = 1, ExportStartDate = DateTimeOffset.UtcNow, ExportEndDate = DateTimeOffset.UtcNow }));
|
||||
_historyService
|
||||
.Setup(x => x.HasRunningExtractByExtractId(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
//assert
|
||||
Assert.DoesNotThrowAsync(() => exportJob.ExecuteExport(It.IsAny<string>(),It.IsAny<Guid>(),It.IsAny<int>(), It.IsAny<Guid>(), exportRequest, new PerformContextMock().Object, It.IsAny<CancellationToken>()));
|
||||
_extractService.Verify(s => s.GetByIdForExecutionAsync(extract.ExtractId, It.IsAny<CancellationToken>()), Times.Once);
|
||||
_dataQueryService.Verify(s => s.GetExternalStageUrlTokensAsync(It.IsAny<CancellationToken>()), Times.Once);
|
||||
_dataQueryService.Verify(s => s.ExecuteSqlQueryAsync(It.Is<string>(query => query.Contains(extract.RawSql)), It.IsAny<IEnumerable<KeyValuePair<string, object>>>()), Times.Once);
|
||||
_s3BucketService.Verify(s => s.GetFiles(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(),
|
||||
It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_sftpService.Verify(
|
||||
s => s.UploadFilesFromS3(externalStageTokens.BucketName,
|
||||
files, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_historyService.Verify(
|
||||
s => s.HasRunningExtractByExtractId(It.IsAny<int>(), It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
_historyService.Verify(
|
||||
s => s.CreateAsync(
|
||||
It.Is<ExtractExportHistorySaveInfo>(h => HistorySaveInfosAreEquivalent(h,
|
||||
new ExtractExportHistorySaveInfo
|
||||
{
|
||||
ExtractId = extract.ExtractId,
|
||||
OutputFilename = exportRequest.OutputFileName,
|
||||
SqlExecuted = extract.RawSql,
|
||||
TotalFileSize = files.Sum(f => f.FileSize),
|
||||
TotalRowCount = files.Sum(f => f.RowCount),
|
||||
Destination = SFTP_PATH
|
||||
})),
|
||||
It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
}
|
||||
|
||||
private static bool HistorySaveInfosAreEquivalent(ExtractExportHistorySaveInfo actual, ExtractExportHistorySaveInfo expected)
|
||||
{
|
||||
Assert.That(actual.ExtractId, Is.EqualTo(expected.ExtractId));
|
||||
Assert.That(actual.OutputFilename, Is.EqualTo(expected.OutputFilename));
|
||||
Assert.That(actual.SqlExecuted.Contains(expected.SqlExecuted));
|
||||
Assert.That(actual.TotalFileSize, Is.EqualTo(expected.TotalFileSize));
|
||||
Assert.That(actual.TotalRowCount, Is.EqualTo(expected.TotalRowCount));
|
||||
Assert.That(actual.Destination, Is.EqualTo(expected.Destination));
|
||||
Assert.That(actual.ExportStartDate, Is.LessThan(actual.ExportEndDate));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExecuteExport_FileSizeException()
|
||||
{
|
||||
var exportJob = CreateExtractExportJob();
|
||||
var extract = GetRawSqlExtract();
|
||||
var exportRequest = GetExtractExportRequest(extract);
|
||||
var externalStageTokens = new ExternalStageUrlTokens
|
||||
{ BucketName = "Big Bucket", FolderPath = "s3://somewhere/out/there/" };
|
||||
var files = GetFiles(exportRequest.OutputFileName, externalStageTokens, 3);
|
||||
|
||||
_extractService.Setup(x => x.GetByIdForExecutionAsync(It.IsAny<int>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(extract));
|
||||
_dataQueryService.Setup(x => x.BuildSqlQuery(It.IsAny<QueryConfig>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(new SqlResponse { SqlQuery = extract.RawSql }));
|
||||
_dataQueryService.Setup(x => x.GetExternalStageUrlTokensAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(externalStageTokens));
|
||||
_dataQueryService.Setup(x => x.ExecuteSqlQueryAsync(It.IsAny<string>(), It.IsAny<IEnumerable<KeyValuePair<string, object>>>())).Throws(new SnowflakeDbException(string.Empty, 0, "Max file size exceeded for unload"));
|
||||
_s3BucketService
|
||||
.Setup(x => x.GetFiles(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(),
|
||||
It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(files));
|
||||
_sftpService.Setup(x => x.UploadFilesFromS3(It.IsAny<string>(), files, "test", It.IsAny<string>(), It.IsAny<CancellationToken>()));
|
||||
_historyService
|
||||
.Setup(x => x.CreateAsync(It.IsAny<ExtractExportHistorySaveInfo>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(1));
|
||||
_historyService
|
||||
.Setup(x => x.HasRunningExtractByExtractId(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
Assert.ThrowsAsync<SnowflakeDbException>(() => exportJob.ExecuteExport(It.IsAny<string>(),It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<Guid>(), exportRequest, new PerformContextMock().Object, It.IsAny<CancellationToken>()));
|
||||
|
||||
_extractService.Verify(s => s.GetByIdForExecutionAsync(extract.ExtractId, It.IsAny<CancellationToken>()), Times.Once);
|
||||
_dataQueryService.Verify(s => s.GetExternalStageUrlTokensAsync(It.IsAny<CancellationToken>()), Times.Once);
|
||||
_dataQueryService.Verify(s => s.ExecuteSqlQueryAsync(It.Is<string>(query => query.Contains(extract.RawSql)), It.IsAny<IEnumerable<KeyValuePair<string, object>>>()), Times.Once);
|
||||
_s3BucketService.Verify(s => s.GetFiles(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(),
|
||||
It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
_sftpService.Verify(
|
||||
s => s.UploadFilesFromS3(externalStageTokens.BucketName,
|
||||
files, "test", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
|
||||
_historyService.Verify(
|
||||
s => s.HasRunningExtractByExtractId(It.IsAny<int>(), It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
// should be created
|
||||
_historyService.Verify(
|
||||
s => s.CreateAsync(It.IsAny<ExtractExportHistorySaveInfo>(), It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
// should be updated with an error status
|
||||
_historyService.Verify(
|
||||
s => s.UpdateAsync(1, It.Is<ExtractExportHistorySaveInfo>(h => h.Status == ExtractExportStatus.Error), It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
}
|
||||
[Test]
|
||||
public void ExecuteExport_3Files_WithOutbound()
|
||||
{
|
||||
const string SFTP_PATH = "SFTP://directory/folder/subpath";
|
||||
|
||||
var exportJob = CreateExtractExportJob();
|
||||
var extract = GetRawSqlExtract("SELECT * FROM dbo.ALLDATAEVER");
|
||||
var exportRequest = GetExtractExportRequest(extract);
|
||||
var externalStageTokens = new ExternalStageUrlTokens
|
||||
{ BucketName = "Big Bucket", FolderPath = "s3://somewhere/out/there/" };
|
||||
var files = GetFiles(exportRequest.OutputFileName, externalStageTokens, 3);
|
||||
var outboundFiles = files.Select(x => new OutboundFileInfo() { Destination = SFTP_PATH, FileName = x.FilePath, FileSize = x.FileSize, RowCount = x.RowCount }).ToList();
|
||||
|
||||
_extractService.Setup(x => x.GetByIdForExecutionAsync(It.IsAny<int>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(extract));
|
||||
_dataQueryService.Setup(x => x.GetExternalStageUrlTokensAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(externalStageTokens));
|
||||
_dataQueryService.Setup(x => x.ExecuteSqlQueryAsync(It.IsAny<string>(), It.IsAny<IEnumerable<KeyValuePair<string, object>>>())).Returns(Task.FromResult(new List<dynamic>().AsEnumerable()));
|
||||
_s3BucketService
|
||||
.Setup(x => x.GetFiles(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(),
|
||||
It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(files));
|
||||
_sftpService.Setup(x => x.UploadFilesFromS3(It.IsAny<string>(), files, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(outboundFiles));
|
||||
_historyService
|
||||
.Setup(x => x.CreateAsync(It.IsAny<ExtractExportHistorySaveInfo>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(1));
|
||||
_historyService
|
||||
.Setup(x => x.UpdateAsync(It.IsAny<int>(), It.IsAny<ExtractExportHistorySaveInfo>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(new ExtractExportHistory
|
||||
{ ExtractId = extract.ExtractId, ExtractHistoryId = 1, ExportStartDate = DateTimeOffset.UtcNow, ExportEndDate = DateTimeOffset.UtcNow }));
|
||||
|
||||
//assert
|
||||
Assert.DoesNotThrowAsync(() => exportJob.ExecuteExport(It.IsAny<string>(),It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<Guid>(), exportRequest, new PerformContextMock().Object, It.IsAny<CancellationToken>()));
|
||||
_extractService.Verify(s => s.GetByIdForExecutionAsync(extract.ExtractId, It.IsAny<CancellationToken>()), Times.Once);
|
||||
_dataQueryService.Verify(s => s.GetExternalStageUrlTokensAsync(It.IsAny<CancellationToken>()), Times.Once);
|
||||
_dataQueryService.Verify(s => s.ExecuteSqlQueryAsync(It.Is<string>(query => query.Contains(extract.RawSql)), It.IsAny<IEnumerable<KeyValuePair<string, object>>>()), Times.Exactly(2));
|
||||
_s3BucketService.Verify(s => s.GetFiles(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>(),
|
||||
It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_sftpService.Verify(
|
||||
s => s.UploadFilesFromS3(externalStageTokens.BucketName,
|
||||
files, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_sftpService.Verify(s => s.UploadFilesFromMemory(It.IsAny<MemoryStream>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
_historyService.Verify(
|
||||
s => s.CreateAsync(
|
||||
It.Is<ExtractExportHistorySaveInfo>(h => HistorySaveInfosAreEquivalent(h,
|
||||
new ExtractExportHistorySaveInfo
|
||||
{
|
||||
ExtractId = extract.ExtractId,
|
||||
OutputFilename = exportRequest.OutputFileName,
|
||||
SqlExecuted = extract.RawSql,
|
||||
TotalFileSize = files.Sum(f => f.FileSize),
|
||||
TotalRowCount = files.Sum(f => f.RowCount),
|
||||
Destination = SFTP_PATH
|
||||
})),
|
||||
It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
}
|
||||
[Test]
|
||||
public async Task TestDataToString()
|
||||
{
|
||||
const string SFTP_PATH = "SFTP://directory/folder/subpath";
|
||||
|
||||
var exportJob = CreateExtractExportJob();
|
||||
var files = new List<OutboundFileInfo>
|
||||
{
|
||||
new()
|
||||
{
|
||||
FileName = "/123123131231/121212412/query_2023-04-07-18-56-36.csv_0_0_0.csv.gz",
|
||||
Destination = SFTP_PATH,
|
||||
FileSize = 100,
|
||||
RowCount = 200
|
||||
},
|
||||
new()
|
||||
{
|
||||
FileName = "/123123131231/121212412/query_2024-04-07-18-56-36.csv_0_0_0.csv.gz",
|
||||
Destination = SFTP_PATH,
|
||||
FileSize = 100,
|
||||
RowCount = 200
|
||||
}
|
||||
};
|
||||
var data = new Dictionary<string, List<string>>()
|
||||
{
|
||||
{ "Column 1", new List<string> {"value1","value2","value3"} },
|
||||
{ "Column 2", new List<string> {"value1","value3"} },
|
||||
{ "Column 3", new List<string> {"value1"} },
|
||||
};
|
||||
|
||||
var stringWriter = await exportJob.CollapseDataAndSendFileAsync(files, data, It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>());
|
||||
var stringToTest = stringWriter.ToString();
|
||||
var numberOfColumns = stringToTest.Count(x => x == '|');
|
||||
var numberOfTimesDataSeparated = stringToTest.Count(x => x == ',');
|
||||
var numberOfNewLines = Regex.Matches(stringToTest, "\r\n").Count;
|
||||
Assert.That(numberOfColumns, Is.EqualTo(24));
|
||||
Assert.That(numberOfTimesDataSeparated, Is.EqualTo(6));
|
||||
Assert.IsFalse(stringToTest.Contains("/123123131231/121212412/"));
|
||||
Assert.That(numberOfNewLines, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExecuteExport_ConcurrentExtractExportException()
|
||||
{
|
||||
var exportJob = CreateExtractExportJob();
|
||||
var extract = GetRawSqlExtract();
|
||||
var exportRequest = GetExtractExportRequest(extract);
|
||||
_historyService
|
||||
.Setup(x => x.HasRunningExtractByExtractId(It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
Assert.ThrowsAsync<ConcurrentExtractExportException>(() => exportJob.ExecuteExport(It.IsAny<string>(),It.IsAny<Guid>(), It.IsAny<int>(), It.IsAny<Guid>(), exportRequest, new PerformContextMock().Object, It.IsAny<CancellationToken>()));
|
||||
|
||||
_historyService.Verify(
|
||||
s => s.HasRunningExtractByExtractId(It.IsAny<int>(), It.IsAny<CancellationToken>()),
|
||||
Times.Once);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static Extract GetRawSqlExtract(string outboundFileSql = null)
|
||||
{
|
||||
var extract = new Extract()
|
||||
{
|
||||
ExtractId = 1001,
|
||||
Name = "Extraction Point",
|
||||
Description = "insert something here...",
|
||||
ExtractType = ExtractType.CustomSql,
|
||||
StrataId = 1,
|
||||
RawSql = "SELECT * FROM dbo.ALLDATAEVER",
|
||||
OutboundFileSql = outboundFileSql
|
||||
|
||||
};
|
||||
return extract;
|
||||
}
|
||||
|
||||
private static List<S3FileInfo> GetFiles(string fileName, ExternalStageUrlTokens externalStage, int fileNumber)
|
||||
{
|
||||
var files = new List<S3FileInfo>();
|
||||
for (var i = 0; i < fileNumber; i++)
|
||||
{
|
||||
files.Add(new S3FileInfo { FilePath = $"{externalStage.BucketName}/{externalStage.FolderPath}/{fileName}{i}.csv.gz", FileSize = 100, RowCount = 1000 });
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
private ExtractExportJob CreateExtractExportJob()
|
||||
{
|
||||
_s3BucketService = new Mock<IS3BucketService>();
|
||||
_extractService = new Mock<IExtractService>();
|
||||
_historyService = new Mock<IExtractExportHistoryService>();
|
||||
_recurringJobService = new Mock<IRecurringJobService>();
|
||||
_sftpService = new Mock<ISftpService>();
|
||||
_dataQueryService = new Mock<IDataQueryService>();
|
||||
_claimsPrincipalAccessor = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
_logger = new Mock<ILogger<ExtractExportJob>>();
|
||||
_scheduleService = new Mock<IExtractScheduleService>();
|
||||
_config = new Mock<IOptions<ExtractConfiguration>>();
|
||||
|
||||
|
||||
var config = new ExtractConfiguration{FileExtension = "txt", MaxFileSize = 1000, SnowflakeMaxFileSize = 1000, OutboundFileExtension = "csv", MaxRowsPerFile = 10};
|
||||
_config.Setup(c => c.Value).Returns(config);
|
||||
|
||||
return new ExtractExportJob(_s3BucketService.Object, _extractService.Object, _historyService.Object, _recurringJobService.Object, _sftpService.Object,
|
||||
_dataQueryService.Object, _claimsPrincipalAccessor, _logger.Object, _scheduleService.Object, _config.Object);
|
||||
}
|
||||
|
||||
private static ExtractExportRequest GetExtractExportRequest(Extract extract)
|
||||
{
|
||||
return new ExtractExportRequest
|
||||
{
|
||||
ExtractId = extract.ExtractId,
|
||||
IncludeOutboundFile = false,
|
||||
OutputFileName = $"{extract.Name}_extended"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Models.Security;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class ExtractGetCurrentUserRoleTest
|
||||
{
|
||||
[Test]
|
||||
public async Task ExtractCurrentUserRole_IsNull_GivenNoRoleAssignments()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var role = await extractService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.HasValue.Should().BeFalse();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task ExtractCurrentUserRole_IsEditor_GivenUserIsStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var role = await extractService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(ExtractRole.Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExtractCurrentUserRole_UsesCurrentUser_GivenMultipleUsersAssignedToMultipleRoles()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var userRoleAssignemnt = new ExtractRoleAssignment() { StrataId = 5, ExtractId = 5, IdentityGuid = claimsPrincipal.GetUserGuid(), IdentityName = claimsPrincipal.GetUsername(), IdentityType = IdentityType.User, Role = ExtractRole.Viewer };
|
||||
var differentUserRoleAssignemnt = new ExtractRoleAssignment() { StrataId = 5, ExtractId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Random User", IdentityType = IdentityType.User, Role = ExtractRole.Editor };
|
||||
|
||||
dbContext.ExtractRoleAssignments.Add(userRoleAssignemnt);
|
||||
dbContext.ExtractRoleAssignments.Add(differentUserRoleAssignemnt);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// act
|
||||
var role = await extractService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(userRoleAssignemnt.Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExtractCurrentUserRole_ReturnsMostPrivilege_GivenUserAssignedToMultipleRoles()
|
||||
{
|
||||
// arrange
|
||||
var userGroup = "DSS - Admin";
|
||||
var userGroupGuid = Guid.NewGuid();
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, false, false, new string[] { userGroup });
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var viewerRoleAssignment = new ExtractRoleAssignment() { StrataId = 5, ExtractId = 5, IdentityGuid = claimsPrincipal.GetUserGuid(), IdentityName = claimsPrincipal.GetUsername(), IdentityType = IdentityType.User, Role = ExtractRole.Viewer };
|
||||
var editorRoleAssignment = new ExtractRoleAssignment() { StrataId = 5, ExtractId = 5, IdentityGuid = userGroupGuid, IdentityName = userGroup, IdentityType = IdentityType.UserGroup, Role = ExtractRole.Editor };
|
||||
dbContext.ExtractRoleAssignments.Add(viewerRoleAssignment);
|
||||
dbContext.ExtractRoleAssignments.Add(editorRoleAssignment);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// act
|
||||
var role = await extractService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(ExtractRole.Editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.DbContexts;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class ExtractHistoryCrudTests
|
||||
{
|
||||
private IClaimsPrincipalAccessor _claimsPrincipalAccessor;
|
||||
private AnalyticsDbContext _dbContext;
|
||||
private IMapper _mapper;
|
||||
private ExtractExportHistoryService _historyService;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
_claimsPrincipalAccessor = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
_dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(_claimsPrincipalAccessor);
|
||||
_mapper = AutoMapperMock.GetAutoMapper();
|
||||
_historyService = new ExtractExportHistoryService(_dbContext, _claimsPrincipalAccessor, _mapper);
|
||||
_dbContext.Extracts.AddRange(ExtractTestData.Extracts());
|
||||
_dbContext.ExtractExportHistory.AddRange(ExtractTestData.ExtractExportHistories());
|
||||
_dbContext.SaveChanges();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task OneTimeTearDown()
|
||||
{
|
||||
await _dbContext.Database.EnsureDeletedAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanCreate()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
// arrange
|
||||
var saveInfo = new ExtractExportHistorySaveInfo()
|
||||
{
|
||||
ExtractId = 2,
|
||||
OutputFilename = "Newest Export File",
|
||||
ExportStartDate = now.AddMinutes(-10),
|
||||
ExportEndDate = now.AddMinutes(-1),
|
||||
SqlExecuted = "This is the Sql Executed",
|
||||
TotalFileSize = 1000,
|
||||
TotalRowCount = 50000
|
||||
};
|
||||
|
||||
// act
|
||||
var historyId = await _historyService.CreateAsync(saveInfo, CancellationToken.None);
|
||||
var result = await _historyService.GetByIdAsync(historyId, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.ExtractHistoryId, Is.GreaterThan(ExtractTestData.ExtractExportHistories().Max(h => h.ExtractHistoryId)));
|
||||
Assert.That(result.ExtractId, Is.EqualTo(saveInfo.ExtractId));
|
||||
Assert.That(result.OutputFilename, Is.EqualTo(saveInfo.OutputFilename));
|
||||
Assert.That(result.ExportStartDate, Is.EqualTo(saveInfo.ExportStartDate));
|
||||
Assert.That(result.ExportEndDate, Is.EqualTo(saveInfo.ExportEndDate));
|
||||
Assert.That(result.SqlExecuted, Is.EqualTo(saveInfo.SqlExecuted));
|
||||
Assert.That(result.TotalFileSize, Is.EqualTo(saveInfo.TotalFileSize));
|
||||
Assert.That(result.TotalRowCount, Is.EqualTo(saveInfo.TotalRowCount));
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.IsNotNull(result.CreatedDate);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task CanGetByExtractId()
|
||||
{
|
||||
// act
|
||||
var result = await _historyService.GetByExtractIdAsync(1, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.Count, Is.EqualTo(ExtractTestData.ExtractExportHistories().Count(h => h.ExtractId == 1)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Models.Security;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class ExtractRoleAssignmentTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public async Task ExtractRoleAssignment_Created_GivenRoleAssignmentWithIdentityIsNotAlreadyInDatabase()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var batch = new Batch<ExtractRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Updates = new List<ExtractRoleAssignmentSaveInfo>()
|
||||
{
|
||||
new ExtractRoleAssignmentSaveInfo() { IdentityGuid = Guid.NewGuid(), IdentityName="Admin", IdentityType=IdentityType.User, Role=ExtractRole.Editor}
|
||||
}
|
||||
};
|
||||
|
||||
// act
|
||||
await extractService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.ExtractRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().ExtractId.Should().Be(5);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(batch.Updates.First().IdentityGuid);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().IdentityName.Should().Be(batch.Updates.First().IdentityName);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().Role.Should().Be(batch.Updates.First().Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExtractRoleAssignment_Updated_GivenRoleAssignmentWithIdentityIsAlreadyInDatabase()
|
||||
{
|
||||
// arrange
|
||||
var strataId = 5;
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(strataId);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var existingRoleAssigment = new ExtractRoleAssignment() { StrataId = strataId, ExtractId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Admin", IdentityType = IdentityType.User, Role = ExtractRole.Viewer };
|
||||
dbContext.ExtractRoleAssignments.Add(existingRoleAssigment);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// change role to Editor
|
||||
var batch = new Batch<ExtractRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Updates = new List<ExtractRoleAssignmentSaveInfo>()
|
||||
{
|
||||
new ExtractRoleAssignmentSaveInfo() { IdentityGuid = existingRoleAssigment.IdentityGuid, IdentityName="Admin", IdentityType=IdentityType.User, Role=ExtractRole.Editor}
|
||||
}
|
||||
};
|
||||
|
||||
// act
|
||||
await extractService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.ExtractRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().ExtractId.Should().Be(5);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(batch.Updates.First().IdentityGuid);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().IdentityName.Should().Be(batch.Updates.First().IdentityName);
|
||||
dbContext.ExtractRoleAssignments.FirstOrDefault().Role.Should().Be(batch.Updates.First().Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExtractRoleAssignment_Deleted_GivenIdentityGuidIsPartOfBatchDeletes()
|
||||
{
|
||||
// arrange
|
||||
var strataId = 5;
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(strataId);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var extractService = new ExtractService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var existingRoleAssigment = new ExtractRoleAssignment() { StrataId = strataId, ExtractId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Admin", IdentityType = IdentityType.User, Role = ExtractRole.Editor };
|
||||
dbContext.ExtractRoleAssignments.Add(existingRoleAssigment);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
var batch = new Batch<ExtractRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Deletes = new List<Guid>() { existingRoleAssigment.IdentityGuid }
|
||||
};
|
||||
|
||||
// act
|
||||
await extractService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.ExtractRoleAssignments.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.DbContexts;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Biz.Hangfire;
|
||||
using Strata.Analytics.Biz.Utils;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.None)]
|
||||
public class ExtractScheduleTests
|
||||
{
|
||||
private IClaimsPrincipalAccessor _claimsPrincipalAccessor;
|
||||
private AnalyticsDbContext _dbContext;
|
||||
private IMapper _mapper;
|
||||
private IExtractScheduleService _scheduleService;
|
||||
private Mock<IRecurringJobService> _recurringJobService;
|
||||
private string _userTimeZone;
|
||||
private static readonly DateTime Now = DateTime.Now;
|
||||
private static readonly DateOnly NowDate = new(Now.Year, Now.Month, Now.Day);
|
||||
private static readonly TimeOnly NowTime = new(Now.Hour, Now.Minute);
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
_claimsPrincipalAccessor = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
_dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(_claimsPrincipalAccessor);
|
||||
_mapper = AutoMapperMock.GetAutoMapper();
|
||||
_recurringJobService = new Mock<IRecurringJobService>();
|
||||
_scheduleService = new ExtractScheduleService(_dbContext, _claimsPrincipalAccessor, _mapper, _recurringJobService.Object);
|
||||
_dbContext.Extracts.AddRange(ExtractTestData.Extracts());
|
||||
_dbContext.ExtractSchedule.AddRange(ExtractTestData.ExtractSchedules());
|
||||
_dbContext.SaveChanges();
|
||||
_userTimeZone = _claimsPrincipalAccessor.GetTimezone();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task OneTimeTearDown()
|
||||
{
|
||||
await _dbContext.Database.EnsureDeletedAsync();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task CanCreate_And_Add_RecurringJob()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
// arrange
|
||||
var saveInfo = new ExtractScheduleSaveInfo
|
||||
{
|
||||
ExtractId = 1,
|
||||
Enabled = true,
|
||||
Repeater = ScheduleRepeaterType.Daily,
|
||||
OnDays = "",
|
||||
StartDate = NowDate,
|
||||
StartTime = NowTime,
|
||||
MaxOccurrences = 12
|
||||
};
|
||||
|
||||
// act
|
||||
var result = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.ExtractScheduleId, Is.GreaterThan(ExtractTestData.ExtractSchedules().Max(h => h.ExtractScheduleId)));
|
||||
Assert.That(result.ExtractId, Is.EqualTo(saveInfo.ExtractId));
|
||||
Assert.That(result.Enabled, Is.EqualTo(saveInfo.Enabled));
|
||||
Assert.That(result.ToCronString(), Is.EqualTo($"{NowTime.Minute} {NowTime.Hour} * * *")); // utc to CST conversion
|
||||
Assert.That(result.StartDate, Is.EqualTo(saveInfo.StartDate));
|
||||
Assert.IsNull(result.EndDate);
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.IsNotNull(result.CreatedDate);
|
||||
Assert.IsNotNull(result.UpdatedDate);
|
||||
|
||||
_recurringJobService.Verify(
|
||||
s => s.AddOrUpdateExtractExportJobAsync(It.Is<ExtractExportRequest>(r =>
|
||||
r.ExtractScheduleId == result.ExtractScheduleId && r.ExtractId == result.ExtractId &&
|
||||
r.Cron == result.ToCronString()), It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task CanGetById()
|
||||
{
|
||||
// act
|
||||
var result = await _scheduleService.GetByIdAsync(1, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task CanUpdate()
|
||||
{
|
||||
// arrange
|
||||
var scheduleId = _dbContext.ExtractSchedule.First(s => s.MaxOccurrences.HasValue).ExtractId;
|
||||
|
||||
var schedule = await _scheduleService.GetByIdAsync(scheduleId, CancellationToken.None);
|
||||
|
||||
var saveInfo = new ExtractScheduleSaveInfo();
|
||||
_mapper.Map(schedule, saveInfo);
|
||||
|
||||
saveInfo.EndDate = NowDate.AddYears(1);
|
||||
saveInfo.MaxOccurrences = null;
|
||||
saveInfo.Repeater = ScheduleRepeaterType.Monthly;
|
||||
saveInfo.OnDays = "";
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
// act
|
||||
var result = await _scheduleService.UpdateAsync(schedule.ExtractId, saveInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.ExtractId, Is.EqualTo(schedule.ExtractId));
|
||||
Assert.That(result.EndDate, Is.EqualTo(saveInfo.EndDate));
|
||||
Assert.IsNull(result.MaxOccurrences);
|
||||
Assert.That(result.ToCronString(), Is.EqualTo($"{schedule.StartTime.Minute} {schedule.StartTime.Hour} {schedule.StartDate.Day} * *"));
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(schedule.CreatedByUserGuid));
|
||||
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.That(result.CreatedDate, Is.EqualTo(schedule.CreatedDate));
|
||||
Assert.That(result.UpdatedDate, Is.EqualTo(DateTimeOffset.UtcNow).Within(500).Milliseconds); // this could fail in debug
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanGetAll_FilterNotEnabled()
|
||||
{
|
||||
// arrange
|
||||
var now = DateTime.UtcNow;
|
||||
// arrange
|
||||
var saveInfo = new ExtractScheduleSaveInfo
|
||||
{
|
||||
ExtractId = 1,
|
||||
Enabled = false,
|
||||
Repeater = ScheduleRepeaterType.Daily,
|
||||
OnDays = "",
|
||||
StartDate = NowDate,
|
||||
StartTime = NowTime,
|
||||
MaxOccurrences = 12
|
||||
};
|
||||
|
||||
var disabledSchedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
||||
|
||||
// act
|
||||
var result = (await _scheduleService.GetAllAsync(CancellationToken.None)).ToList();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.Count, Is.GreaterThan(0));
|
||||
Assert.IsFalse(result.Select(s => s.ExtractScheduleId).Contains(disabledSchedule.ExtractScheduleId));
|
||||
Assert.IsTrue(result.TrueForAll(s => s.Enabled));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task Disabling_Removes_RecurringJob()
|
||||
{
|
||||
// arrange
|
||||
var scheduleId = _dbContext.ExtractSchedule.Last(s => s.Enabled).ExtractId;
|
||||
|
||||
var schedule = await _scheduleService.GetByIdAsync(scheduleId, CancellationToken.None);
|
||||
|
||||
var saveInfo = new ExtractScheduleSaveInfo();
|
||||
_mapper.Map(schedule, saveInfo);
|
||||
|
||||
saveInfo.Enabled = false;
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
// act
|
||||
await _scheduleService.UpdateAsync(schedule.ExtractId, saveInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
|
||||
_recurringJobService.Verify(
|
||||
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
||||
It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task HittingMaxOccurrenceLimit_Removes_RecurringJob()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
// arrange
|
||||
var saveInfo = new ExtractScheduleSaveInfo
|
||||
{
|
||||
ExtractId = 1,
|
||||
Enabled = true,
|
||||
Repeater = ScheduleRepeaterType.Daily,
|
||||
OnDays = "",
|
||||
StartDate = NowDate.AddDays(-7), // starting 7 days ago
|
||||
StartTime = NowTime,
|
||||
MaxOccurrences = 6 // run a total of 6 times
|
||||
};
|
||||
|
||||
var schedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
// act
|
||||
var result = await _scheduleService.UpdateLastOccurrence(schedule.ExtractScheduleId, DateTimeOffset.UtcNow,
|
||||
_userTimeZone, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.That(result.Enabled, Is.EqualTo(false));
|
||||
|
||||
_recurringJobService.Verify(
|
||||
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
||||
It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task HittingEndDateLimit_Removes_RecurringJob()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
// arrange
|
||||
var saveInfo = new ExtractScheduleSaveInfo
|
||||
{
|
||||
ExtractId = 1,
|
||||
Enabled = true,
|
||||
Repeater = ScheduleRepeaterType.Daily,
|
||||
OnDays = "",
|
||||
StartDate = NowDate.AddDays(-7), // starting 7 days ago
|
||||
StartTime = NowTime,
|
||||
EndDate = NowDate // run until today
|
||||
};
|
||||
|
||||
var schedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
// act
|
||||
var result = await _scheduleService.UpdateLastOccurrence(schedule.ExtractScheduleId, DateTimeOffset.UtcNow,
|
||||
_userTimeZone, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.That(result.Enabled, Is.EqualTo(false));
|
||||
|
||||
_recurringJobService.Verify(
|
||||
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
||||
It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanDelete_And_Remove_RecurringJob()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
// arrange
|
||||
var saveInfo = new ExtractScheduleSaveInfo
|
||||
{
|
||||
ExtractId = 1,
|
||||
Enabled = true,
|
||||
Repeater = ScheduleRepeaterType.Daily,
|
||||
OnDays = "",
|
||||
StartDate = NowDate.AddDays(-7), // starting 7 days ago
|
||||
StartTime = NowTime,
|
||||
};
|
||||
|
||||
var schedule = await _scheduleService.CreateAsync(saveInfo, CancellationToken.None);
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
// act
|
||||
await _scheduleService.DeleteAsync(schedule.ExtractScheduleId, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
var result = await _scheduleService.ContainsAsync(schedule.ExtractScheduleId, CancellationToken.None);
|
||||
|
||||
Assert.That(result, Is.EqualTo(false));
|
||||
|
||||
_recurringJobService.Verify(
|
||||
s => s.RemoveExtractExportJobAsync(It.IsAny<int>(), It.Is<int>(r => r == schedule.ExtractScheduleId),
|
||||
It.IsAny<CancellationToken>()), Times.Once);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanGetDailyCronFromSchedule()
|
||||
{
|
||||
// arrange
|
||||
var schedule = new ExtractSchedule
|
||||
{
|
||||
Repeater = ScheduleRepeaterType.Daily,
|
||||
OnDays = "",
|
||||
StartDate = new DateOnly(2023, 01, 01),
|
||||
StartTime = new TimeOnly(15, 30, 00)
|
||||
};
|
||||
|
||||
// assert
|
||||
|
||||
Assert.That(schedule.ToCronString(), Is.EqualTo("30 15 * * *"));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void CanGetWeekdayCronFromSchedule()
|
||||
{
|
||||
// arrange
|
||||
var schedule = new ExtractSchedule
|
||||
{
|
||||
Repeater = ScheduleRepeaterType.Weekdays,
|
||||
StartDate = new DateOnly(2023, 01, 01),
|
||||
StartTime = new TimeOnly(15, 30, 00)
|
||||
};
|
||||
|
||||
// assert
|
||||
|
||||
Assert.That(schedule.ToCronString(), Is.EqualTo("30 15 * * MON-FRI"));
|
||||
}
|
||||
|
||||
[TestCase("MON,WED,FRI")]
|
||||
[TestCase("SUN,SAT")]
|
||||
[TestCase("SUN,THU")]
|
||||
public void CanGetWeeklyCronFromSchedule(string onDays)
|
||||
{
|
||||
// arrange
|
||||
var schedule = new ExtractSchedule
|
||||
{
|
||||
Repeater = ScheduleRepeaterType.Weekly,
|
||||
OnDays = onDays,
|
||||
StartDate = new DateOnly(2023, 01, 01),
|
||||
StartTime = new TimeOnly(15, 30, 00)
|
||||
};
|
||||
|
||||
// assert
|
||||
|
||||
Assert.That(schedule.ToCronString(),
|
||||
onDays == string.Empty
|
||||
? Is.EqualTo("30 15 * * *")
|
||||
: Is.EqualTo($"30 15 * * {onDays}"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanGetMonthlyCronFromSchedule()
|
||||
{
|
||||
// arrange
|
||||
var schedule = new ExtractSchedule
|
||||
{
|
||||
Repeater = ScheduleRepeaterType.Monthly,
|
||||
OnDays = "",
|
||||
StartDate = new DateOnly(2023, 01, 01),
|
||||
StartTime = new TimeOnly(15, 30, 00)
|
||||
};
|
||||
|
||||
// assert
|
||||
|
||||
Assert.That(schedule.ToCronString(), Is.EqualTo("30 15 1 * *"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.Extracts.Export;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Extracts
|
||||
{
|
||||
[TestFixture]
|
||||
public class ExtractSqlHelperTest
|
||||
{
|
||||
[Test]
|
||||
public void GetFolderPathFromExternalStage_UrlStringOneLevel_GetFilePath()
|
||||
{
|
||||
//Arrange
|
||||
const string urlString = "s3://sdt-dev-tabularreporter/b8018250af34403ebdcc1770714042b2/";
|
||||
const string expectedString = "b8018250af34403ebdcc1770714042b2";
|
||||
|
||||
//Act
|
||||
var externalStageUrlTokens = ExtractSqlHelper.StripS3BucketFromUrl(urlString);
|
||||
|
||||
//Assert
|
||||
Assert.AreEqual(externalStageUrlTokens.FolderPath, expectedString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtractUrlValue_HasUrlProperty_GetUrlList()
|
||||
{
|
||||
|
||||
//Arrange
|
||||
var urlList = new List<string> { "someUrl" };
|
||||
var urlListString = JsonSerializer.Serialize(urlList);
|
||||
List<dynamic> dynamicList = new() { new Dictionary<string, object>() { { "property", "URL" }, { "property_value", urlListString } } };
|
||||
|
||||
//Act
|
||||
var urlValue = ExtractSqlHelper.ExtractUrlValue(dynamicList);
|
||||
|
||||
//Assert
|
||||
Assert.AreEqual(urlValue, urlList);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFolderPathFromExternalStage_UrlStringTwoLevels_GetFilePath()
|
||||
{
|
||||
//Arrange
|
||||
const string urlString = "s3://sdt-dev-tabularreporter/b8018250af34403ebdcc1770714042b2/b8018250af34403ebdcc1770714042b2/";
|
||||
const string expectedString = "b8018250af34403ebdcc1770714042b2/b8018250af34403ebdcc1770714042b2";
|
||||
|
||||
//Act
|
||||
var externalStageUrlTokens = ExtractSqlHelper.StripS3BucketFromUrl(urlString);
|
||||
|
||||
//Assert
|
||||
Assert.AreEqual(externalStageUrlTokens.FolderPath, expectedString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFolderPathFromExternalStage_UrlStringNoLevels_GetFilePath()
|
||||
{
|
||||
//Arrange
|
||||
const string urlString = "s3://sdt-dev-tabularreporter/";
|
||||
const string expectedString = "";
|
||||
|
||||
//Act
|
||||
var externalStageUrlTokens = ExtractSqlHelper.StripS3BucketFromUrl(urlString);
|
||||
|
||||
//Assert
|
||||
Assert.AreEqual(externalStageUrlTokens.FolderPath, expectedString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBucketNameFromExternalStage_UrlStringNoLevels_GetBucketName()
|
||||
{
|
||||
//Arrange
|
||||
const string urlString = "s3://sdt-dev-tabularreporter/";
|
||||
const string expectedString = "sdt-dev-tabularreporter";
|
||||
|
||||
//Act
|
||||
var externalStageUrlTokens = ExtractSqlHelper.StripS3BucketFromUrl(urlString);
|
||||
|
||||
//Assert
|
||||
Assert.AreEqual(externalStageUrlTokens.BucketName, expectedString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBucketNameFromExternalStage_UrlStringOneLevel_GetBucketName()
|
||||
{
|
||||
//Arrange
|
||||
const string urlString = "s3://sdt-dev-tabularreporter/b8018250af34403ebdcc1770714042b2/";
|
||||
const string expectedString = "sdt-dev-tabularreporter";
|
||||
|
||||
//Act
|
||||
var externalStageUrlTokens = ExtractSqlHelper.StripS3BucketFromUrl(urlString);
|
||||
|
||||
//Assert
|
||||
Assert.AreEqual(externalStageUrlTokens.BucketName, expectedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Strata.Analytics.Biz.DbContexts;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
using Strata.CoreLib.Claims;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
internal class AnalyticsDbContextMock
|
||||
{
|
||||
public static AnalyticsDbContext GetAnalyticsDbContext(IClaimsPrincipalAccessor claimsPrincipalAccessor)
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<AnalyticsDbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).EnableSensitiveDataLogging().Options;
|
||||
return new AnalyticsDbContextJsonSafe(options, claimsPrincipalAccessor);
|
||||
}
|
||||
}
|
||||
|
||||
internal class AnalyticsDbContextJsonSafe : AnalyticsDbContext
|
||||
{
|
||||
public AnalyticsDbContextJsonSafe(DbContextOptions<AnalyticsDbContext> options, IClaimsPrincipalAccessor claimsPrincipalAccessor) : base(options, claimsPrincipalAccessor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Worksheet>().Property(p => p.Content)
|
||||
.HasConversion(
|
||||
v => JsonDocumentToString(v),
|
||||
v => JsonDocument.Parse(v, new JsonDocumentOptions()));
|
||||
|
||||
modelBuilder.Entity<SavedFilter>().Property(p => p.FiltersJson)
|
||||
.HasConversion(
|
||||
v => JsonDocumentToString(v),
|
||||
v => JsonDocument.Parse(v, new JsonDocumentOptions()));
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
|
||||
internal static string JsonDocumentToString(JsonDocument document)
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
|
||||
document.WriteTo(writer);
|
||||
writer.Flush();
|
||||
return Encoding.UTF8.GetString(stream.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using AutoMapper;
|
||||
using Strata.Analytics.Biz.ContentMigration;
|
||||
using Strata.Analytics.Biz.DataSources;
|
||||
using Strata.Analytics.Biz.Extracts;
|
||||
using Strata.Analytics.Biz.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
internal static class AutoMapperMock
|
||||
{
|
||||
public static IMapper GetAutoMapper()
|
||||
{
|
||||
var mapperConfig = new MapperConfiguration(cfg =>
|
||||
{
|
||||
cfg.AddProfile(new DataSourceAutoMapperProfile());
|
||||
cfg.AddProfile(new WorkbookAutoMapperProfile());
|
||||
cfg.AddProfile(new WorkbookSaveInfoAutoMapperProfile());
|
||||
cfg.AddProfile(new ExtractAutoMapperProfile());
|
||||
cfg.AddProfile(new ExtractSaveInfoAutoMapperProfile());
|
||||
cfg.AddProfile(new ContentMigrationAutoMapperProfile());
|
||||
});
|
||||
return mapperConfig.CreateMapper();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using AutoMapper;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
public class ExtractSaveInfoAutoMapperProfile : Profile
|
||||
{
|
||||
public ExtractSaveInfoAutoMapperProfile()
|
||||
{
|
||||
CreateMap<Extract, ExtractSaveInfo > ();
|
||||
CreateMap<ExtractExportHistory, ExtractExportHistorySaveInfo>();
|
||||
CreateMap<ExtractSchedule, ExtractScheduleSaveInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
internal static class ExtractTestData
|
||||
{
|
||||
private static readonly Guid UserGuid1 = Guid.NewGuid();
|
||||
private static readonly Guid UserGuid2 = Guid.NewGuid();
|
||||
private static readonly DateTimeOffset Now = DateTimeOffset.UtcNow;
|
||||
private static readonly DateOnly NowDate = DateOnly.Parse(DateTime.Today.ToShortDateString());
|
||||
private static readonly TimeOnly NowTime = TimeOnly.Parse(DateTime.Today.ToShortTimeString());
|
||||
|
||||
public static IQueryable<Extract> Extracts()
|
||||
{
|
||||
var extracts = new List<Extract>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ExtractId = 1,
|
||||
Name = "Extract 1",
|
||||
Description = "This is Extract 1",
|
||||
ExtractType = ExtractType.CustomSql,
|
||||
RawSql = "SELECT * FROM ALLTABLES",
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid1,
|
||||
CreatedDate = Now.AddDays(-1),
|
||||
UpdatedDate = Now.AddHours(-1)
|
||||
},
|
||||
new()
|
||||
{
|
||||
ExtractId = 2,
|
||||
Name = "Extract 2",
|
||||
Description = "This is Extract 2",
|
||||
ExtractType = ExtractType.CustomSql,
|
||||
RawSql = "SELECT * FROM JustTheOneTable",
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid1,
|
||||
CreatedDate = Now.AddDays(-1),
|
||||
UpdatedDate = Now.AddHours(-1)
|
||||
}
|
||||
};
|
||||
return extracts.AsQueryable();
|
||||
}
|
||||
|
||||
public static IQueryable<ExtractExportHistory> ExtractExportHistories()
|
||||
{
|
||||
var extractExportHistories = new List<ExtractExportHistory>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ExtractHistoryId = 101,
|
||||
ExtractId = 1,
|
||||
OutputFilename = "ExtractExportHistory 101",
|
||||
ExportStartDate = Now.AddDays(-3).AddMinutes(-50),
|
||||
ExportEndDate = Now.AddDays(-3).AddMinutes(-5),
|
||||
SqlExecuted = "This is the Sql Executed",
|
||||
TotalFileSize = 1024,
|
||||
TotalRowCount = 8675309,
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
CreatedDate = Now.AddDays(-3),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ExtractHistoryId = 102,
|
||||
ExtractId = 1,
|
||||
OutputFilename = "ExtractExportHistory 102",
|
||||
ExportStartDate = Now.AddDays(-2).AddMinutes(-50),
|
||||
ExportEndDate = Now.AddDays(-2).AddMinutes(-4),
|
||||
SqlExecuted = "This is the Sql Executed",
|
||||
TotalFileSize = 1025,
|
||||
TotalRowCount = 8675310,
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
CreatedDate = Now.AddDays(-2),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ExtractHistoryId = 103,
|
||||
ExtractId = 1,
|
||||
OutputFilename = "ExtractExportHistory 103",
|
||||
ExportStartDate = Now.AddDays(-1).AddMinutes(-50),
|
||||
ExportEndDate = Now.AddDays(-1).AddMinutes(-1),
|
||||
SqlExecuted = "This is the Sql Executed",
|
||||
TotalFileSize = 1500,
|
||||
TotalRowCount = 9000001,
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
CreatedDate = Now.AddDays(-1),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ExtractHistoryId = 201,
|
||||
ExtractId = 2,
|
||||
OutputFilename = "ExtractExportHistory 201",
|
||||
ExportStartDate = Now.AddDays(-3).AddMinutes(-3),
|
||||
ExportEndDate = Now.AddDays(-3).AddMinutes(-1),
|
||||
SqlExecuted = "This is the Sql Executed",
|
||||
TotalFileSize = 9890498,
|
||||
TotalRowCount = 106840864650680,
|
||||
CreatedByUserGuid = UserGuid2,
|
||||
CreatedDate = Now.AddDays(-3),
|
||||
},
|
||||
new()
|
||||
{
|
||||
ExtractHistoryId = 202,
|
||||
ExtractId = 1,
|
||||
OutputFilename = "ExtractExportHistory 102",
|
||||
ExportStartDate = Now.AddMinutes(-1),
|
||||
ExportEndDate = Now.AddSeconds(-1),
|
||||
SqlExecuted = "This is the Sql Executed",
|
||||
TotalFileSize = 1025,
|
||||
TotalRowCount = 8675310,
|
||||
CreatedByUserGuid = UserGuid2,
|
||||
CreatedDate = Now.AddDays(-2),
|
||||
}
|
||||
};
|
||||
return extractExportHistories.AsQueryable();
|
||||
}
|
||||
|
||||
public static IQueryable<ExtractSchedule> ExtractSchedules()
|
||||
{
|
||||
var schedules = new List<ExtractSchedule>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ExtractId = 1,
|
||||
ExtractScheduleId = 1,
|
||||
Enabled = true,
|
||||
Repeater = ScheduleRepeaterType.Daily,
|
||||
OnDays = "",
|
||||
StartDate = NowDate,
|
||||
StartTime = NowTime,
|
||||
EndDate = NowDate.AddYears(1),
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid1,
|
||||
CreatedDate = Now.AddDays(-1),
|
||||
UpdatedDate = Now.AddDays(-1),
|
||||
LastRunDate = Now
|
||||
},
|
||||
new()
|
||||
{
|
||||
ExtractId = 2,
|
||||
ExtractScheduleId = 2,
|
||||
Enabled = true,
|
||||
Repeater = ScheduleRepeaterType.Weekly,
|
||||
OnDays = "MON,WED,FRI",
|
||||
StartDate = NowDate,
|
||||
StartTime = NowTime,
|
||||
MaxOccurrences = 12,
|
||||
CreatedByUserGuid = UserGuid2,
|
||||
UpdatedByUserGuid = UserGuid2,
|
||||
CreatedDate = Now.AddDays(-1),
|
||||
UpdatedDate = Now.AddDays(-1),
|
||||
LastRunDate = Now
|
||||
},
|
||||
new()
|
||||
{
|
||||
ExtractId = 3,
|
||||
ExtractScheduleId = 3,
|
||||
Enabled = true,
|
||||
Repeater = ScheduleRepeaterType.Monthly,
|
||||
OnDays = "",
|
||||
StartDate = NowDate,
|
||||
StartTime = NowTime,
|
||||
MaxOccurrences = 12,
|
||||
CreatedByUserGuid = UserGuid2,
|
||||
UpdatedByUserGuid = UserGuid2,
|
||||
CreatedDate = Now.AddDays(-1),
|
||||
UpdatedDate = Now.AddDays(-1),
|
||||
LastRunDate = Now
|
||||
}
|
||||
};
|
||||
|
||||
return schedules.AsQueryable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using IdentityModel;
|
||||
using Moq;
|
||||
using Strata.CoreLib.Claims;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
internal class IClaimsPrincipalAccessorMock
|
||||
{
|
||||
public static IClaimsPrincipalAccessor GetClaimsPrincipalAccessor(int strataId = 5, bool isStrataUser = false, bool isAdmin = true, IEnumerable<string> additionalUserGroups = null)
|
||||
{
|
||||
var userGroups = new List<string> { "Everyone" };
|
||||
|
||||
if (additionalUserGroups != null)
|
||||
{
|
||||
userGroups.AddRange(additionalUserGroups);
|
||||
}
|
||||
|
||||
var claims = new List<Claim> {
|
||||
new Claim(StrataClaims.StrataId, strataId.ToString()),
|
||||
new Claim(StrataClaims.IsSdtEmployee, isStrataUser.ToString()),
|
||||
new Claim(StrataClaims.UserGuid, "9d1bf676-b8cc-4403-a5fc-686f33538d3b"),
|
||||
new Claim(StrataClaims.FirstName, "Test"),
|
||||
new Claim(StrataClaims.LastName, "User"),
|
||||
new Claim(StrataClaims.Username, "testuser"),
|
||||
new Claim(StrataClaims.IsAdmin, isAdmin.ToString()),
|
||||
new Claim(StrataClaims.DatabaseGuid, "3ABAEFE1-A5D3-4F6E-80DD-8967B255206A"),
|
||||
new Claim(StrataClaims.Timezone, "Central Standard Time")
|
||||
};
|
||||
claims.AddRange(userGroups.Select(userGroup => new Claim(JwtClaimTypes.Role, userGroup)));
|
||||
|
||||
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));
|
||||
|
||||
return Mock.Of<IClaimsPrincipalAccessor>(a => a.GetCurrentClaimsPrincipal() == principal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using Hangfire;
|
||||
using Hangfire.Common;
|
||||
using Hangfire.Server;
|
||||
using Hangfire.Storage;
|
||||
using Moq;
|
||||
|
||||
// https://github.com/HangfireIO/Hangfire/blob/master/tests/Hangfire.Core.Tests/Mocks/PerformContextMock.cs
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
public class PerformContextMock
|
||||
{
|
||||
private readonly Lazy<PerformContext> _context;
|
||||
|
||||
public PerformContextMock()
|
||||
{
|
||||
Storage = new Mock<JobStorage>();
|
||||
Connection = new Mock<IStorageConnection>();
|
||||
BackgroundJob = new BackgroundJobMock();
|
||||
CancellationToken = new Mock<IJobCancellationToken>();
|
||||
|
||||
_context = new Lazy<PerformContext>(
|
||||
() => new PerformContext(Storage.Object, Connection.Object, BackgroundJob.Object, CancellationToken.Object));
|
||||
}
|
||||
|
||||
public Mock<JobStorage> Storage { get; set; }
|
||||
public Mock<IStorageConnection> Connection { get; set; }
|
||||
public BackgroundJobMock BackgroundJob { get; set; }
|
||||
public Mock<IJobCancellationToken> CancellationToken { get; set; }
|
||||
|
||||
public PerformContext Object => _context.Value;
|
||||
|
||||
public PerformingContext GetPerformingContext()
|
||||
{
|
||||
return new PerformingContext(Object);
|
||||
}
|
||||
|
||||
public PerformedContext GetPerformedContext(object result = null, bool canceled = false, Exception exception = null)
|
||||
{
|
||||
return new PerformedContext(Object, result, canceled, exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class BackgroundJobMock
|
||||
{
|
||||
private readonly Lazy<BackgroundJob> _object;
|
||||
|
||||
public BackgroundJobMock()
|
||||
{
|
||||
Id = "JobId";
|
||||
Job = Job.FromExpression(() => SomeMethod());
|
||||
CreatedAt = DateTime.UtcNow;
|
||||
|
||||
_object = new Lazy<BackgroundJob>(
|
||||
() => new BackgroundJob(Id, Job, CreatedAt));
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
public Job Job { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
public BackgroundJob Object => _object.Value;
|
||||
|
||||
public static void SomeMethod() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using AutoMapper;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
public class WorkbookSaveInfoAutoMapperProfile : Profile
|
||||
{
|
||||
public WorkbookSaveInfoAutoMapperProfile()
|
||||
{
|
||||
CreateMap<Workbook, WorkbookSaveInfo>();
|
||||
CreateMap<Workbook, WorkbookCreateInfo>();
|
||||
CreateMap<Worksheet, WorksheetSaveInfo>();
|
||||
CreateMap<WorkbookRoleAssignment, WorkbookRoleAssignmentSaveInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.TestData
|
||||
{
|
||||
internal static class WorkbookTestData
|
||||
{
|
||||
private static readonly Guid UserGuid1 = Guid.NewGuid();
|
||||
private static readonly Guid UserGuid2 = Guid.NewGuid();
|
||||
|
||||
public static IQueryable<Workbook> Workbooks()
|
||||
{
|
||||
var workbooks = new List<Workbook>
|
||||
{
|
||||
new()
|
||||
{
|
||||
WorkbookId = 1,
|
||||
Name = "Workbook 1",
|
||||
Description = "This is Workbook 1",
|
||||
DefaultDataSourceId = 1001,
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid1,
|
||||
CreatedDate = DateTime.UtcNow.AddDays(-1),
|
||||
UpdatedDate = DateTime.UtcNow.AddHours(-1)
|
||||
},
|
||||
new()
|
||||
{
|
||||
WorkbookId = 2,
|
||||
Name = "Workbook 2",
|
||||
Description = "This is Workbook 2",
|
||||
DefaultDataSourceId = 2001,
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid1,
|
||||
CreatedDate = DateTime.UtcNow.AddDays(-1),
|
||||
UpdatedDate = DateTime.UtcNow.AddHours(-1)
|
||||
}
|
||||
};
|
||||
return workbooks.AsQueryable();
|
||||
}
|
||||
|
||||
public static IQueryable<Worksheet> Worksheets()
|
||||
{
|
||||
var worksheets = new List<Worksheet>
|
||||
{
|
||||
new()
|
||||
{
|
||||
WorksheetId = 101,
|
||||
WorkbookId = 1,
|
||||
Name = "Worksheet 101",
|
||||
WorksheetType = WorksheetType.Adhoc,
|
||||
Content = JsonDocument.Parse("{\"MyContent\": \"WS 101\"}"),
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid2,
|
||||
CreatedDate = DateTime.UtcNow.AddDays(-1),
|
||||
UpdatedDate = DateTime.UtcNow.AddHours(-1)
|
||||
},
|
||||
new()
|
||||
{
|
||||
WorksheetId = 102,
|
||||
WorkbookId = 1,
|
||||
Name = "Worksheet 102",
|
||||
WorksheetType = WorksheetType.Blank,
|
||||
Content = JsonDocument.Parse("{\"MyContent\": \"WS 102\"}"),
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid1,
|
||||
CreatedDate = DateTime.UtcNow.AddDays(-1),
|
||||
UpdatedDate = DateTime.UtcNow.AddHours(-1)
|
||||
},
|
||||
new()
|
||||
{
|
||||
WorksheetId = 103,
|
||||
WorkbookId = 1,
|
||||
Name = "Worksheet 103",
|
||||
WorksheetType = WorksheetType.Dashboard,
|
||||
Content = JsonDocument.Parse("{\"MyContent\": \"WS 103\"}"),
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid1,
|
||||
CreatedDate = DateTime.UtcNow.AddDays(-1),
|
||||
UpdatedDate = DateTime.UtcNow.AddHours(-1)
|
||||
},
|
||||
new()
|
||||
{
|
||||
WorksheetId = 201,
|
||||
WorkbookId = 2,
|
||||
Name = "Worksheet 201",
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid2,
|
||||
CreatedDate = DateTime.UtcNow.AddDays(-1),
|
||||
UpdatedDate = DateTime.UtcNow.AddHours(-1)
|
||||
},
|
||||
new()
|
||||
{
|
||||
WorksheetId = 202,
|
||||
WorkbookId = 2,
|
||||
Name = "Worksheet 202",
|
||||
CreatedByUserGuid = UserGuid1,
|
||||
UpdatedByUserGuid = UserGuid2,
|
||||
CreatedDate = DateTime.UtcNow.AddDays(-1),
|
||||
UpdatedDate = DateTime.UtcNow.AddHours(-1)
|
||||
}
|
||||
};
|
||||
return worksheets.AsQueryable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Biz.DbContexts;
|
||||
using Strata.Analytics.Biz.Workbooks.Services;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Workbooks
|
||||
{
|
||||
[Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class WorkbookCrudTests
|
||||
{
|
||||
private IClaimsPrincipalAccessor _claimsPrincipalAccessor;
|
||||
private AnalyticsDbContext _dbContext;
|
||||
private IMapper _mapper;
|
||||
private WorkbookService _workbookService;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
_claimsPrincipalAccessor = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
_dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(_claimsPrincipalAccessor);
|
||||
_mapper = AutoMapperMock.GetAutoMapper();
|
||||
_workbookService = new WorkbookService(_dbContext, _claimsPrincipalAccessor, _mapper);
|
||||
_dbContext.Workbooks.AddRange(WorkbookTestData.Workbooks());
|
||||
_dbContext.Worksheets.AddRange(WorkbookTestData.Worksheets());
|
||||
_dbContext.SaveChanges();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task OneTimeTearDown()
|
||||
{
|
||||
await _dbContext.Database.EnsureDeletedAsync();
|
||||
}
|
||||
|
||||
[Test, Order(1)]
|
||||
public async Task CanCreateWorkbook()
|
||||
{
|
||||
// arrange
|
||||
var workbookCreateInfo = new WorkbookCreateInfo
|
||||
{
|
||||
Name = "New",
|
||||
Description = "This is a New Workbook",
|
||||
DefaultDataSourceId = 1001
|
||||
};
|
||||
|
||||
// act
|
||||
var result = await _workbookService.CreateAsync(workbookCreateInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.WorkbookId, Is.GreaterThan(WorkbookTestData.Workbooks().Max(wb => wb.WorkbookId)));
|
||||
Assert.That(result.Name, Is.EqualTo(workbookCreateInfo.Name));
|
||||
Assert.That(result.Description, Is.EqualTo(workbookCreateInfo.Description));
|
||||
Assert.That(result.DefaultDataSourceId, Is.EqualTo(workbookCreateInfo.DefaultDataSourceId));
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
|
||||
Assert.That(result.Worksheets.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test, Order(1)]
|
||||
public async Task CanCreateWorkbookWithSheets()
|
||||
{
|
||||
var newWorksheets = new List<WorksheetSaveInfo>();
|
||||
var worksheet1 = new WorksheetSaveInfo
|
||||
{
|
||||
Name = "New Worksheet",
|
||||
SortOrder = 3,
|
||||
WorksheetType = WorksheetType.Adhoc,
|
||||
Content = JsonDocument.Parse("{\"MyContent\": \"stuff goes here\"}")
|
||||
};
|
||||
var worksheet2 = new WorksheetSaveInfo
|
||||
{
|
||||
Name = "New Worksheet2",
|
||||
SortOrder = 4,
|
||||
WorksheetType = WorksheetType.Dashboard,
|
||||
Content = JsonDocument.Parse("{\"MyContent\": \"stuff2 goes here\"}")
|
||||
};
|
||||
newWorksheets.Add(worksheet1);
|
||||
newWorksheets.Add(worksheet2);
|
||||
// arrange
|
||||
var workbookCreateInfo = new WorkbookCreateInfo
|
||||
{
|
||||
Name = "New",
|
||||
Description = "This is a New Workbook",
|
||||
DefaultDataSourceId = 1001,
|
||||
Worksheets = newWorksheets
|
||||
};
|
||||
|
||||
// act
|
||||
var result = await _workbookService.CreateAsync(workbookCreateInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.WorkbookId, Is.GreaterThan(WorkbookTestData.Workbooks().Max(wb => wb.WorkbookId)));
|
||||
Assert.That(result.Name, Is.EqualTo(workbookCreateInfo.Name));
|
||||
Assert.That(result.Description, Is.EqualTo(workbookCreateInfo.Description));
|
||||
Assert.That(result.DefaultDataSourceId, Is.EqualTo(workbookCreateInfo.DefaultDataSourceId));
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
|
||||
Assert.That(result.Worksheets.Count(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
|
||||
[Test, Order(1)]
|
||||
public async Task CanUpdateWorkbook()
|
||||
{
|
||||
// arrange
|
||||
var workbookId = _dbContext.Workbooks.First().WorkbookId;
|
||||
await _workbookService.SaveRoleAssignmentsAsync(workbookId,
|
||||
new Batch<WorkbookRoleAssignmentSaveInfo, Guid>
|
||||
{
|
||||
Updates = new WorkbookRoleAssignmentSaveInfo[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
IdentityGuid = _claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid(),
|
||||
Role = WorkbookRole.Editor
|
||||
}
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
|
||||
var workbook = await _workbookService.GetByIdAsync(workbookId, CancellationToken.None);
|
||||
|
||||
var workbookSaveInfo = new WorkbookSaveInfo();
|
||||
_mapper.Map(workbook, workbookSaveInfo);
|
||||
|
||||
var updatedWorksheet = workbook.Worksheets[1];
|
||||
updatedWorksheet.Name = "Updated WS Name";
|
||||
updatedWorksheet.Content = JsonDocument.Parse("{\"MyContent\": \"updated content here\"}");
|
||||
|
||||
var newWorksheet = new WorksheetSaveInfo
|
||||
{
|
||||
Name = "New Worksheet",
|
||||
SortOrder = 3,
|
||||
WorksheetType = WorksheetType.Adhoc,
|
||||
Content = JsonDocument.Parse("{\"MyContent\": \"stuff goes here\"}")
|
||||
};
|
||||
|
||||
var batch = new Batch<WorksheetSaveInfo, int>
|
||||
{
|
||||
Updates = new List<WorksheetSaveInfo> { _mapper.Map<WorksheetSaveInfo>(updatedWorksheet), newWorksheet },
|
||||
Deletes = new List<int> { workbook.Worksheets[0].WorksheetId }
|
||||
};
|
||||
|
||||
|
||||
workbookSaveInfo.WorksheetBatch = batch;
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
|
||||
// act
|
||||
var result = await _workbookService.UpdateAsync(workbook.WorkbookId, workbookSaveInfo, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.That(result.WorkbookId, Is.EqualTo(workbook.WorkbookId));
|
||||
Assert.That(result.Name, Is.EqualTo(workbookSaveInfo.Name));
|
||||
Assert.That(result.Description, Is.EqualTo(workbookSaveInfo.Description));
|
||||
Assert.That(result.DefaultDataSourceId, Is.EqualTo(workbookSaveInfo.DefaultDataSourceId));
|
||||
Assert.That(result.CreatedByUserGuid, Is.EqualTo(workbook.CreatedByUserGuid));
|
||||
Assert.That(result.UpdatedByUserGuid, Is.EqualTo(_claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid()));
|
||||
Assert.That(result.CreatedDate, Is.EqualTo(workbook.CreatedDate));
|
||||
Assert.That(result.UpdatedDate, Is.EqualTo(DateTimeOffset.UtcNow).Within(500).Milliseconds); // this could fail in debug
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
foreach (var worksheetId in workbookSaveInfo.WorksheetBatch.Deletes)
|
||||
{
|
||||
var resultWorksheet = result.Worksheets.FirstOrDefault(ws => ws.WorksheetId == worksheetId);
|
||||
Assert.That(resultWorksheet, Is.Null);
|
||||
}
|
||||
|
||||
foreach (var worksheetSaveInfo in workbookSaveInfo.WorksheetBatch.Updates)
|
||||
{
|
||||
// get the worksheet - if it was added, grab the last
|
||||
var resultWorksheet = worksheetSaveInfo.WorksheetId.HasValue ?
|
||||
result.Worksheets.FirstOrDefault(ws => ws.WorksheetId == worksheetSaveInfo.WorksheetId) : result.Worksheets.LastOrDefault();
|
||||
|
||||
var expectedCreatedBy = worksheetSaveInfo.WorksheetId.HasValue
|
||||
? workbook.Worksheets.First(ws => ws.WorksheetId == worksheetSaveInfo.WorksheetId)
|
||||
.CreatedByUserGuid
|
||||
: _claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid();
|
||||
|
||||
var expectedUpdateBy = !worksheetSaveInfo.WorksheetId.HasValue || worksheetSaveInfo.WorksheetId == updatedWorksheet.WorksheetId
|
||||
? _claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid() : workbook.Worksheets
|
||||
.First(ws => ws.WorksheetId == worksheetSaveInfo.WorksheetId)
|
||||
.UpdatedByUserGuid;
|
||||
|
||||
Assert.That(resultWorksheet.WorkbookId, Is.EqualTo(workbook.WorkbookId));
|
||||
Assert.That(resultWorksheet.Name, Is.EqualTo(worksheetSaveInfo.Name));
|
||||
Assert.That(AnalyticsDbContextJsonSafe.JsonDocumentToString(resultWorksheet.Content), Is.EqualTo(AnalyticsDbContextJsonSafe.JsonDocumentToString(worksheetSaveInfo.Content)));
|
||||
Assert.That(resultWorksheet.SortOrder, Is.EqualTo(worksheetSaveInfo.SortOrder));
|
||||
Assert.That(resultWorksheet.CreatedByUserGuid, Is.EqualTo(expectedCreatedBy));
|
||||
Assert.That(resultWorksheet.UpdatedByUserGuid, Is.EqualTo(expectedUpdateBy));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test, Order(int.MaxValue)] // always run last
|
||||
public async Task CanDeleteWorkbookAndCascadeToChildren()
|
||||
{
|
||||
// arrange
|
||||
var workbookId = _dbContext.Workbooks.First().WorkbookId;
|
||||
await _workbookService.SaveRoleAssignmentsAsync(workbookId,
|
||||
new Batch<WorkbookRoleAssignmentSaveInfo, Guid>
|
||||
{
|
||||
Updates = new WorkbookRoleAssignmentSaveInfo[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
IdentityGuid = _claimsPrincipalAccessor.GetCurrentClaimsPrincipal().GetUserGuid(),
|
||||
Role = WorkbookRole.Editor
|
||||
}
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
|
||||
var workbook = await _workbookService.GetByIdAsync(workbookId, CancellationToken.None);
|
||||
|
||||
Assert.That(workbook.Worksheets.Count, Is.GreaterThan(0));
|
||||
await _dbContext.SaveChangesAsync();
|
||||
_dbContext.ChangeTracker.Clear();
|
||||
|
||||
|
||||
// act
|
||||
await _workbookService.DeleteAsync(workbookId, CancellationToken.None);
|
||||
var deletedWorkbook = _dbContext.Workbooks.Where(wb => wb.WorkbookId == workbook.WorkbookId);
|
||||
var deletedWorksheets = _dbContext.Worksheets.Where(ws => ws.WorkbookId == workbook.WorkbookId);
|
||||
var deletedRoleAssignments = _dbContext.WorkbookRoleAssignments.Where(ra => ra.WorkbookId == workbook.WorkbookId);
|
||||
|
||||
// assert
|
||||
Assert.That(deletedWorkbook, Is.Empty);
|
||||
Assert.That(deletedWorksheets, Is.Empty);
|
||||
Assert.That(deletedRoleAssignments, Is.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Models.Security;
|
||||
using Strata.Analytics.Biz.Workbooks.Services;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Workbooks
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class WorkbookGetCurrentUserRoleTest
|
||||
{
|
||||
[Test]
|
||||
public async Task WorkbookCurrentUserRole_IsNull_GivenNoRoleAssignments()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var workbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var role = await workbookService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.HasValue.Should().BeFalse();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task WorkbookCurrentUserRole_IsEditor_GivenUserIsStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var workbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var role = await workbookService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(WorkbookRole.Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WorkbookCurrentUserRole_UsesCurrentUser_GivenMultipleUsersAssignedToMultipleRoles()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var workbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var userRoleAssignemnt = new WorkbookRoleAssignment() { StrataId = 5, WorkbookId = 5, IdentityGuid = claimsPrincipal.GetUserGuid(), IdentityName = claimsPrincipal.GetUsername(), IdentityType = IdentityType.User, Role = WorkbookRole.Viewer };
|
||||
var differentUserRoleAssignemnt = new WorkbookRoleAssignment() { StrataId = 5, WorkbookId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Random User", IdentityType = IdentityType.User, Role = WorkbookRole.Editor };
|
||||
|
||||
dbContext.WorkbookRoleAssignments.Add(userRoleAssignemnt);
|
||||
dbContext.WorkbookRoleAssignments.Add(differentUserRoleAssignemnt);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// act
|
||||
var role = await workbookService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(userRoleAssignemnt.Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WorkbookCurrentUserRole_ReturnsMostPrivilege_GivenUserAssignedToMultipleRoles()
|
||||
{
|
||||
// arrange
|
||||
var userGroup = "DSS - Admin";
|
||||
var userGroupGuid = Guid.NewGuid();
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, false, false, new string[] { userGroup });
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var workbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var viewerRoleAssignment = new WorkbookRoleAssignment() { StrataId = 5, WorkbookId = 5, IdentityGuid = claimsPrincipal.GetUserGuid(), IdentityName = claimsPrincipal.GetUsername(), IdentityType = IdentityType.User, Role = WorkbookRole.Viewer };
|
||||
var editorRoleAssignment = new WorkbookRoleAssignment() { StrataId = 5, WorkbookId = 5, IdentityGuid = userGroupGuid, IdentityName = userGroup, IdentityType = IdentityType.UserGroup, Role = WorkbookRole.Editor };
|
||||
dbContext.WorkbookRoleAssignments.Add(viewerRoleAssignment);
|
||||
dbContext.WorkbookRoleAssignments.Add(editorRoleAssignment);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// act
|
||||
var role = await workbookService.GetCurrentUserRoleAsync(5, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
role.Should().Be(WorkbookRole.Editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Models.Security;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using Strata.Analytics.Biz.Workbooks.Services;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.Testing.Categories;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Workbooks
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class WorkbookRoleAssignmentTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public async Task WorkbookRoleAssignment_Created_GivenRoleAssignmentWithIdentityIsNotAlreadyInDatabase()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var WorkbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var batch = new Batch<WorkbookRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Updates = new List<WorkbookRoleAssignmentSaveInfo>()
|
||||
{
|
||||
new WorkbookRoleAssignmentSaveInfo() { IdentityGuid = Guid.NewGuid(), IdentityName="Admin", IdentityType=IdentityType.User, Role=WorkbookRole.Editor}
|
||||
}
|
||||
};
|
||||
|
||||
// act
|
||||
await WorkbookService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.WorkbookRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().WorkbookId.Should().Be(5);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(batch.Updates.First().IdentityGuid);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().IdentityName.Should().Be(batch.Updates.First().IdentityName);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().Role.Should().Be(batch.Updates.First().Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WorkbookRoleAssignment_Updated_GivenRoleAssignmentWithIdentityIsAlreadyInDatabase()
|
||||
{
|
||||
// arrange
|
||||
var strataId = 5;
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(strataId);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var WorkbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var existingRoleAssigment = new WorkbookRoleAssignment() { StrataId = strataId, WorkbookId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Admin", IdentityType = IdentityType.User, Role = WorkbookRole.Viewer };
|
||||
dbContext.WorkbookRoleAssignments.Add(existingRoleAssigment);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// change role to Editor
|
||||
var batch = new Batch<WorkbookRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Updates = new List<WorkbookRoleAssignmentSaveInfo>()
|
||||
{
|
||||
new WorkbookRoleAssignmentSaveInfo() { IdentityGuid = existingRoleAssigment.IdentityGuid, IdentityName="Admin", IdentityType=IdentityType.User, Role=WorkbookRole.Editor}
|
||||
}
|
||||
};
|
||||
|
||||
// act
|
||||
await WorkbookService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.WorkbookRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().WorkbookId.Should().Be(5);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(batch.Updates.First().IdentityGuid);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().IdentityName.Should().Be(batch.Updates.First().IdentityName);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().Role.Should().Be(batch.Updates.First().Role);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WorkbookRoleAssignment_Deleted_GivenIdentityGuidIsPartOfBatchDeletes()
|
||||
{
|
||||
// arrange
|
||||
var strataId = 5;
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(strataId);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var WorkbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
var existingRoleAssigment = new WorkbookRoleAssignment() { StrataId = strataId, WorkbookId = 5, IdentityGuid = Guid.NewGuid(), IdentityName = "Admin", IdentityType = IdentityType.User, Role = WorkbookRole.Editor };
|
||||
dbContext.WorkbookRoleAssignments.Add(existingRoleAssigment);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
var batch = new Batch<WorkbookRoleAssignmentSaveInfo, Guid>()
|
||||
{
|
||||
Deletes = new List<Guid>() { existingRoleAssigment.IdentityGuid }
|
||||
};
|
||||
|
||||
// act
|
||||
await WorkbookService.SaveRoleAssignmentsAsync(5, batch, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
dbContext.WorkbookRoleAssignments.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
using Strata.Analytics.Biz.Workbooks.Services;
|
||||
using Strata.Analytics.Test.Unit.Biz.TestData;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Testing.Categories;
|
||||
|
||||
namespace Strata.Analytics.Test.Unit.Biz.Workbooks
|
||||
{
|
||||
[NUnit.Framework.Category(TestCategory.Unit)]
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
public class WorkbookSourceCreateTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public async Task Workbook_CurrentUserAssignedAsEditor_GivenUserIsNotStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor();
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var workbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var workbook = await workbookService.CreateAsync(new WorkbookCreateInfo() { Name = "Workbook1" }, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
workbook.CreatedByUserGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
workbook.CurrentUserRole.Should().Be(WorkbookRole.Editor);
|
||||
dbContext.WorkbookRoleAssignments.Should().HaveCount(1);
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().IdentityGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
dbContext.WorkbookRoleAssignments.FirstOrDefault().Role.Should().Be(WorkbookRole.Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Workbook_CurrentUserAssignedAsEditorButNoRoleAssignmentsCreated_GivenUserIsStrata()
|
||||
{
|
||||
// arrange
|
||||
var claimsPrincipal = IClaimsPrincipalAccessorMock.GetClaimsPrincipalAccessor(5, true);
|
||||
var dbContext = AnalyticsDbContextMock.GetAnalyticsDbContext(claimsPrincipal);
|
||||
var workbookService = new WorkbookService(dbContext, claimsPrincipal, AutoMapperMock.GetAutoMapper());
|
||||
|
||||
// act
|
||||
var workbook = await workbookService.CreateAsync(new WorkbookCreateInfo() { Name = "Workbook1" }, CancellationToken.None);
|
||||
|
||||
// assert
|
||||
workbook.CreatedByUserGuid.Should().Be(claimsPrincipal.GetUserGuid());
|
||||
workbook.CurrentUserRole.Should().Be(WorkbookRole.Editor);
|
||||
dbContext.WorkbookRoleAssignments.Should().HaveCount(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user