Skip to content
This repository was archived by the owner on Dec 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Application/Ed-Fi-ODS-AdminApi.sln
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdFi.Ods.AdminApi.Common.Un
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdFi.Ods.AdminApi.V1", "EdFi.Ods.AdminApi.V1\EdFi.Ods.AdminApi.V1.csproj", "{760F71C6-9FD9-4F5B-AE77-34501EE76FBD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdFi.Ods.AdminApi.V1.DBTests", "EdFi.Ods.AdminApi.V1.DBTests\EdFi.Ods.AdminApi.V1.DBTests.csproj", "{4377A3CF-71EC-4097-AC8C-789ED63075C9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -82,6 +84,14 @@ Global
{760F71C6-9FD9-4F5B-AE77-34501EE76FBD}.Release|Any CPU.Build.0 = Release|Any CPU
{760F71C6-9FD9-4F5B-AE77-34501EE76FBD}.Release|x64.ActiveCfg = Release|Any CPU
{760F71C6-9FD9-4F5B-AE77-34501EE76FBD}.Release|x64.Build.0 = Release|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Debug|x64.ActiveCfg = Debug|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Debug|x64.Build.0 = Debug|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Release|Any CPU.Build.0 = Release|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Release|x64.ActiveCfg = Release|Any CPU
{4377A3CF-71EC-4097-AC8C-789ED63075C9}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -90,6 +100,7 @@ Global
{1D317139-CB19-4D7F-91AC-9A560189C55B} = {D8A26B59-6DAD-4046-9DDE-00D2CFDAE9B6}
{F62C9CF6-A632-4894-B61A-674198DAB86E} = {9A9D18B4-718D-4681-BAFE-A1C42E18A7CC}
{73259EC2-4AA0-40C2-9C60-8AB1BF369CF5} = {D8A26B59-6DAD-4046-9DDE-00D2CFDAE9B6}
{4377A3CF-71EC-4097-AC8C-789ED63075C9} = {D8A26B59-6DAD-4046-9DDE-00D2CFDAE9B6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {30CF2BE4-58CA-4598-9B59-D334FC971A0F}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ protected enum CheckpointPolicyOptions

private readonly Checkpoint _checkpoint = new()
{
TablesToIgnore = new[]
{
TablesToIgnore =
[
"__MigrationHistory", "DeployJournal", "AdminApiDeployJournal"
},
SchemasToExclude = Array.Empty<string>()
],
SchemasToExclude = []
};

protected virtual string ConnectionString => TestContext.Database.GetConnectionString();
Expand Down
12 changes: 6 additions & 6 deletions Application/EdFi.Ods.AdminApi.DBTests/SecurityDataTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protected override SqlServerSecurityContext CreateDbContext()
}
public virtual string AdminTestingConnectionString => Testing.AdminConnectionString;

public virtual SqlServerUsersContext AdminDbContext => new SqlServerUsersContext(Testing.GetDbContextOptions(AdminTestingConnectionString));
public virtual SqlServerUsersContext AdminDbContext => new(Testing.GetDbContextOptions(AdminTestingConnectionString));

// This bool controls whether or not to run SecurityContext initialization
// method. Setting this flag to true will cause seed data to be
Expand Down Expand Up @@ -158,8 +158,8 @@ void GetOrCreateResourceClaimAuthorizationMetadata(Action action,
{
Action = action,
AuthorizationStrategies = authorizationStrategy != null ?
new List<ResourceClaimActionAuthorizationStrategies> { new ResourceClaimActionAuthorizationStrategies
{ AuthorizationStrategy = authorizationStrategy} } : null,
[ new ResourceClaimActionAuthorizationStrategies
{ AuthorizationStrategy = authorizationStrategy} ] : null,
ResourceClaim = resourceClaim,
ValidationRuleSetName = null
});
Expand Down Expand Up @@ -299,7 +299,7 @@ protected IReadOnlyCollection<ResourceClaim> SetupResourceClaimsWithChildren(
};
})).ToList();

var grandChildResourceClaims = grandChildRcNames == null || !grandChildRcNames.Any() ? new List<ResourceClaim>() : childResourceClaims.SelectMany(child => grandChildRcNames.Select(grandChildName =>
var grandChildResourceClaims = grandChildRcNames == null || !grandChildRcNames.Any() ? [] : childResourceClaims.SelectMany(child => grandChildRcNames.Select(grandChildName =>
{
var fullName = $"{grandChildName}-{child.ClaimName}";
return new ResourceClaim
Expand Down Expand Up @@ -350,7 +350,7 @@ protected IReadOnlyCollection<ResourceClaimAction> SetupResourcesWithDefaultAuth

var rcActionAuthorizationStrategies = testAuthorizationStrategy != null ?
new List<ResourceClaimActionAuthorizationStrategies> {
new ResourceClaimActionAuthorizationStrategies { AuthorizationStrategy = testAuthorizationStrategy } } : null;
new() { AuthorizationStrategy = testAuthorizationStrategy } } : null;

var resourceClaimWithDefaultAuthStrategy = new ResourceClaimAction
{
Expand All @@ -374,7 +374,7 @@ protected IReadOnlyCollection<ResourceClaimAction> SetupResourcesWithDefaultAuth
using (var securityContext = CreateDbContext())
{
var getResourcesByClaimSetIdQuery = new ClaimSetEditorTypes.GetResourcesByClaimSetIdQuery(securityContext, Mapper());
list = getResourcesByClaimSetIdQuery.AllResources(securityContextClaimSetId).ToList();
list = [.. getResourcesByClaimSetIdQuery.AllResources(securityContextClaimSetId)];
}
return list;
}
Expand Down
69 changes: 69 additions & 0 deletions Application/EdFi.Ods.AdminApi.V1.DBTests/AssertionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Results;
using Shouldly;
using static System.Environment;

namespace EdFi.Ods.AdminApi.V1.DBTests;

public static class AssertionExtensions
{
public static void ShouldValidate<TModel>(this AbstractValidator<TModel> validator, TModel model)
=> validator.Validate(model).ShouldBeSuccessful();

public static void ShouldNotValidate<TModel>(this AbstractValidator<TModel> validator, TModel model, params string[] expectedErrors)
=> validator.Validate(model).ShouldBeFailure(expectedErrors);

private static void ShouldBeSuccessful(this ValidationResult result)
{
var indentedErrorMessages = result
.Errors
.OrderBy(x => x.ErrorMessage)
.Select(x => " " + x.ErrorMessage)
.ToArray();

var actual = string.Join(NewLine, indentedErrorMessages);

result.IsValid.ShouldBeTrue($"Expected no validation errors, but found {result.Errors.Count}:{NewLine}{actual}");
}

private static void ShouldBeFailure(this ValidationResult result, params string[] expectedErrors)
{
result.IsValid.ShouldBeFalse("Expected validation errors, but the message passed validation.");

result.Errors
.OrderBy(x => x.ErrorMessage)
.Select(x => x.ErrorMessage)
.ToArray()
.ShouldBe([.. expectedErrors.OrderBy(x => x)]);
}

public static void ShouldSatisfy<T>(this IEnumerable<T> actual, params Action<T>[] itemExpectations)
{
var actualItems = actual.ToArray();

if (actualItems.Length != itemExpectations.Length)
throw new Exception(
$"Expected the collection to have {itemExpectations.Length} " +
$"items, but there were {actualItems.Length} items.");

for (var i = 0; i < actualItems.Length; i++)
{
try
{
itemExpectations[i](actualItems[i]);
}
catch (Exception failure)
{
throw new Exception($"Assertion failed for item at position [{i}].", failure);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using System;
using System.Linq;
using EdFi.Ods.AdminApi.V1.Infrastructure.Services.ClaimSetEditor;
using NUnit.Framework;
using Shouldly;
using Application = EdFi.Ods.AdminApi.V1.Security.DataAccess.Models.Application;
using ClaimSet = EdFi.Ods.AdminApi.V1.Security.DataAccess.Models.ClaimSet;

namespace EdFi.Ods.AdminApi.V1.DBTests.ClaimSetEditorTests;

[TestFixture]
public class AddClaimSetCommandServiceTests : SecurityDataTestBase
{
[Test]
public void ShouldAddClaimSet()
{
var testApplication = new Application
{
ApplicationName = $"Test Application {DateTime.Now:O}"
};
Save(testApplication);

var newClaimSet = new AddClaimSetModel { ClaimSetName = "TestClaimSet" };

var addedClaimSetId = 0;
ClaimSet addedClaimSet = null;
using (var securityContext = TestContext)
{
var command = new AddClaimSetCommandService(securityContext);
addedClaimSetId = command.Execute(newClaimSet);
addedClaimSet = securityContext.ClaimSets.Single(x => x.ClaimSetId == addedClaimSetId);
}
addedClaimSet.ClaimSetName.ShouldBe(newClaimSet.ClaimSetName);
addedClaimSet.ForApplicationUseOnly.ShouldBe(false);
addedClaimSet.IsEdfiPreset.ShouldBe(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// SPDX-License-Identifier: Apache-2.0
// Licensed to the Ed-Fi Alliance under one or more agreements.
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
// See the LICENSE and NOTICES files in the project root for more information.

using System;
using System.Linq;
using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling;
using EdFi.Ods.AdminApi.V1.Infrastructure.Services.ClaimSetEditor;
using Moq;
using NUnit.Framework;
using Shouldly;
using Application = EdFi.Ods.AdminApi.V1.Security.DataAccess.Models.Application;
using ClaimSet = EdFi.Ods.AdminApi.V1.Security.DataAccess.Models.ClaimSet;

namespace EdFi.Ods.AdminApi.V1.DBTests.ClaimSetEditorTests;

[TestFixture]
public class DeleteClaimSetCommandServiceTests : SecurityDataTestBase
{

[Test]
public void ShouldDeleteClaimSet()
{
var testApplication = new Application
{
ApplicationName = $"Test Application {DateTime.Now:O}"
};
Save(testApplication);

var testClaimSetToDelete = new ClaimSet
{ ClaimSetName = "TestClaimSet_Delete", Application = testApplication };
Save(testClaimSetToDelete);
SetupParentResourceClaimsWithChildren(testClaimSetToDelete, testApplication, UniqueNameList("ParentRc", 3), UniqueNameList("ChildRc", 1));

var testClaimSetToPreserve = new ClaimSet
{ ClaimSetName = "TestClaimSet_Preserve", Application = testApplication };
Save(testClaimSetToPreserve);
var resourceClaimsForPreservedClaimSet = SetupParentResourceClaimsWithChildren(testClaimSetToPreserve, testApplication, UniqueNameList("ParentRc", 3),
UniqueNameList("ChildRc", 1));

var deleteModel = new Mock<IDeleteClaimSetModel>();
deleteModel.Setup(x => x.Name).Returns(testClaimSetToDelete.ClaimSetName);
deleteModel.Setup(x => x.Id).Returns(testClaimSetToDelete.ClaimSetId);

using var securityContext = TestContext;
var command = new DeleteClaimSetCommandService(securityContext);
command.Execute(deleteModel.Object);
var deletedClaimSet = securityContext.ClaimSets.SingleOrDefault(x => x.ClaimSetId == testClaimSetToDelete.ClaimSetId);
deletedClaimSet.ShouldBeNull();
var deletedClaimSetResourceActions = securityContext.ClaimSetResourceClaimActions.Count(x => x.ClaimSet.ClaimSetId == testClaimSetToDelete.ClaimSetId);
deletedClaimSetResourceActions.ShouldBe(0);

var preservedClaimSet = securityContext.ClaimSets.Single(x => x.ClaimSetId == testClaimSetToPreserve.ClaimSetId);
preservedClaimSet.ClaimSetName.ShouldBe(testClaimSetToPreserve.ClaimSetName);

var results = ResourceClaimsForClaimSet(testClaimSetToPreserve.ClaimSetId);

var testParentResourceClaimsForId =
resourceClaimsForPreservedClaimSet.Where(x => x.ClaimSet.ClaimSetId == testClaimSetToPreserve.ClaimSetId && x.ResourceClaim.ParentResourceClaim == null).Select(x => x.ResourceClaim).ToArray();

results.Count.ShouldBe(testParentResourceClaimsForId.Length);
results.Select(x => x.Name).ShouldBe(testParentResourceClaimsForId.Select(x => x.ResourceName), true);
results.Select(x => x.Id).ShouldBe(testParentResourceClaimsForId.Select(x => x.ResourceClaimId), true);
results.All(x => x.Create).ShouldBe(true);

foreach (var testParentResourceClaim in testParentResourceClaimsForId)
{
var testChildren = securityContext.ResourceClaims.Where(x =>
x.ParentResourceClaimId == testParentResourceClaim.ResourceClaimId).ToList();
var parentResult = results.First(x => x.Id == testParentResourceClaim.ResourceClaimId);
parentResult.Children.Select(x => x.Name).ShouldBe(testChildren.Select(x => x.ResourceName), true);
parentResult.Children.Select(x => x.Id).ShouldBe(testChildren.Select(x => x.ResourceClaimId), true);
parentResult.Children.All(x => x.Create).ShouldBe(true);
}
}

[Test]
public void ShouldThrowExceptionOnEditSystemReservedClaimSet()
{
var testApplication = new Application
{
ApplicationName = $"Test Application {DateTime.Now:O}"
};
Save(testApplication);

var systemReservedClaimSet = new ClaimSet { ClaimSetName = "SIS Vendor", Application = testApplication, IsEdfiPreset = true };
Save(systemReservedClaimSet);

var deleteModel = new Mock<IDeleteClaimSetModel>();
deleteModel.Setup(x => x.Name).Returns(systemReservedClaimSet.ClaimSetName);
deleteModel.Setup(x => x.Id).Returns(systemReservedClaimSet.ClaimSetId);
using var securityContext = TestContext;
var exception = Assert.Throws<AdminApiException>(() =>
{
var command = new DeleteClaimSetCommandService(securityContext);
command.Execute(deleteModel.Object);
});
exception.ShouldNotBeNull();
exception.Message.ShouldBe($"Claim set({systemReservedClaimSet.ClaimSetName}) is system reserved.Can not be deleted.");
}

// TODO: move these to UnitTests, using appropriate validator from the API project
//[Test]
//public void ShouldNotDeleteClaimSetIfNotEditable()
//{
// var testApplication = new Application
// {
// ApplicationName = "TestApplication1"
// };
// Save(testApplication);

// var testClaimSet = new ClaimSet {ClaimSetName = $"TestClaimSet{DateTime.Now:O}", Application = testApplication};
// Save(testClaimSet);

// var claimSetToDelete = new DeleteClaimSetModel()
// {
// Name = testClaimSet.ClaimSetName,
// Id = testClaimSet.ClaimSetId,
// IsEditable = false
// };

// using var securityContext = TestContext;
// var getClaimSetByIdQuery = ClaimSetByIdQuery(securityContext);
// var validator = new DeleteClaimSetModelValidator(getClaimSetByIdQuery);
// var validationResults = validator.Validate(claimSetToDelete);
// validationResults.IsValid.ShouldBe(false);
// validationResults.Errors.Single().ErrorMessage.ShouldBe("Only user created claim sets can be deleted");
//}

//[Test]
//public void ShouldNotDeleteClaimSetIfNotAnExistingId()
//{
// var testApplication = new Application
// {
// ApplicationName = "TestApplication2"
// };
// Save(testApplication);

// var testClaimSet = new ClaimSet { ClaimSetName = $"TestClaimSet{DateTime.Now:O}", Application = testApplication };
// Save(testClaimSet);

// var claimSetToDelete = new DeleteClaimSetModel()
// {
// Name = testClaimSet.ClaimSetName,
// Id = 99,
// IsEditable = true
// };
// using var securityContext = TestContext;
// var getClaimSetByIdQuery = ClaimSetByIdQuery(securityContext);
// var validator = new DeleteClaimSetModelValidator(getClaimSetByIdQuery);
// var validationResults = validator.Validate(claimSetToDelete);
// validationResults.IsValid.ShouldBe(false);
// validationResults.Errors.Single().ErrorMessage.ShouldBe("No such claim set exists in the database");
//}

//[Test]
//public void ShouldNotDeleteClaimSetHasAnAssociatedApplication()
//{
// var testApplication = new Application
// {
// ApplicationName = "TestApplication3"
// };
// Save(testApplication);

// var testClaimSet = new ClaimSet { ClaimSetName = $"TestClaimSet{DateTime.Now:O}", Application = testApplication };
// Save(testClaimSet);

// var claimSetToDelete = new DeleteClaimSetModel()
// {
// Name = testClaimSet.ClaimSetName,
// Id = testClaimSet.ClaimSetId,
// IsEditable = true,
// VendorApplicationCount = 1
// };

// using var securityContext = TestContext;
// var getClaimSetByIdQuery = ClaimSetByIdQuery(securityContext);
// var validator = new DeleteClaimSetModelValidator(getClaimSetByIdQuery);
// var validationResults = validator.Validate(claimSetToDelete);
// validationResults.IsValid.ShouldBe(false);
// validationResults.Errors.Single().ErrorMessage.ShouldBe($"Cannot delete this claim set. This claim set has {claimSetToDelete.VendorApplicationCount} associated application(s).");
//}

//private GetClaimSetByIdQuery ClaimSetByIdQuery(ISecurityContext securityContext) => new GetClaimSetByIdQuery(new StubOdsSecurityModelVersionResolver.V6(),
// null, new GetClaimSetByIdQueryV6Service(securityContext));
}
Loading
Loading