From 74b5e3ab8a8c0a127af0ec8fc64dc5f1308c8289 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:10:17 +0000 Subject: [PATCH 1/7] Initial plan From 3186aea1f81d3991159a5c88b38214ab0ba5cb74 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:21:15 +0000 Subject: [PATCH 2/7] Add comprehensive unit tests for OdsInstanceContext management classes Co-authored-by: stephenfuqua <9324390+stephenfuqua@users.noreply.github.com> --- .../AddOdsInstanceContextTests.cs | 334 +++++++++++++++ .../DeleteOdsInstanceContextTests.cs | 78 ++++ .../EditOdsInstanceContextTests.cs | 387 ++++++++++++++++++ .../ReadOdsInstanceContextTests.cs | 236 +++++++++++ .../AddOdsInstanceContextCommandTests.cs | 184 +++++++++ .../DeleteOdsInstanceContextCommandTests.cs | 176 ++++++++ .../EditOdsInstanceContextCommandTests.cs | 248 +++++++++++ 7 files changed, 1643 insertions(+) create mode 100644 Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs create mode 100644 Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/DeleteOdsInstanceContextTests.cs create mode 100644 Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs create mode 100644 Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs create mode 100644 Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs create mode 100644 Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs create mode 100644 Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs new file mode 100644 index 000000000..0c013174e --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs @@ -0,0 +1,334 @@ +// 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.Threading.Tasks; +using AutoMapper; +using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Features.OdsInstanceContext; +using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; +using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; +using FakeItEasy; +using FluentValidation; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; + +[TestFixture] +public class AddOdsInstanceContextTests +{ + private AddOdsInstanceContext.Validator _validator; + private IGetOdsInstanceQuery _getOdsInstanceQuery; + private IGetOdsInstanceContextsQuery _getOdsInstanceContextsQuery; + + [SetUp] + public void SetUp() + { + _getOdsInstanceQuery = A.Fake(); + _getOdsInstanceContextsQuery = A.Fake(); + _validator = new AddOdsInstanceContext.Validator(_getOdsInstanceQuery, _getOdsInstanceContextsQuery); + } + + [Test] + public async Task Handle_ExecutesCommandAndReturnsCreated() + { + // Arrange + var validator = A.Fake(); + var command = A.Fake(); + var mapper = A.Fake(); + var request = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + OdsInstanceId = 1, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + var addedContext = new OdsInstanceContext { OdsInstanceContextId = 123 }; + + A.CallTo(() => command.Execute(request)).Returns(addedContext); + + // Act + var result = await AddOdsInstanceContext.Handle(validator, command, mapper, request); + + // Assert + A.CallTo(() => validator.GuardAsync(request)).MustHaveHappenedOnceExactly(); + A.CallTo(() => command.Execute(request)).MustHaveHappenedOnceExactly(); + result.ShouldNotBeNull(); + result.ShouldBeOfType>(); + } + + [Test] + public void Handle_WhenValidationFails_ThrowsValidationException() + { + // Arrange + var validator = A.Fake(); + var command = A.Fake(); + var mapper = A.Fake(); + var request = new AddOdsInstanceContext.AddOdsInstanceContextRequest(); + + A.CallTo(() => validator.GuardAsync(request)).Throws(new ValidationException("Validation failed")); + + // Act & Assert + Should.Throw(async () => await AddOdsInstanceContext.Handle(validator, command, mapper, request)); + } + + [Test] + public void Handle_WhenCommandThrows_ExceptionIsPropagated() + { + // Arrange + var validator = A.Fake(); + var command = A.Fake(); + var mapper = A.Fake(); + var request = new AddOdsInstanceContext.AddOdsInstanceContextRequest(); + + A.CallTo(() => command.Execute(request)).Throws(new System.Exception("Command failed")); + + // Act & Assert + Should.Throw(async () => await AddOdsInstanceContext.Handle(validator, command, mapper, request)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextKey_Is_Empty() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextKey)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextKey_Is_Null() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = null, + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextKey)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextValue_Is_Empty() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "TestKey", + ContextValue = "", + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextValue)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextValue_Is_Null() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "TestKey", + ContextValue = null, + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextValue)); + } + + [Test] + public void Validator_Should_Have_Error_When_OdsInstanceId_Is_Zero() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = 0 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.OdsInstanceId)); + } + + [Test] + public void Validator_Should_Have_Error_When_OdsInstance_Does_Not_Exist() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = 999 + }; + + A.CallTo(() => _getOdsInstanceQuery.Execute(999)).Throws(new System.Exception("OdsInstance not found")); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.OdsInstanceId)); + } + + [Test] + public void Validator_Should_Have_Error_When_Combined_Key_Is_Not_Unique() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "ExistingKey", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + var existingContexts = new List + { + new OdsInstanceContext + { + ContextKey = "ExistingKey", + OdsInstance = new OdsInstance { OdsInstanceId = 1 } + } + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(existingContexts); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == ""); + } + + [Test] + public void Validator_Should_Pass_When_Combined_Key_Is_Unique() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "UniqueKey", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + var existingContexts = new List + { + new OdsInstanceContext + { + ContextKey = "DifferentKey", + OdsInstance = new OdsInstance { OdsInstanceId = 1 } + } + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(existingContexts); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeTrue(); + } + + [Test] + public void Validator_Should_Pass_With_Valid_Model() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "ValidKey", + ContextValue = "ValidValue", + OdsInstanceId = 1 + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(new List()); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeTrue(); + } + + [Test] + public void BeAnExistingOdsInstance_Should_Return_True_When_OdsInstance_Exists() + { + // Arrange + var odsInstanceId = 1; + A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).Returns(new OdsInstance()); + + // Act + var result = _validator.Validate(new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = odsInstanceId + }); + + // Assert - The validation should pass (assuming other fields are valid) + A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).MustHaveHappenedOnceExactly(); + } + + [Test] + public void BeUniqueCombinedKey_Should_Return_True_When_Key_Is_Case_Insensitive_Unique() + { + // Arrange + var model = new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "TESTKEY", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + var existingContexts = new List + { + new OdsInstanceContext + { + ContextKey = "testkey", // different case + OdsInstance = new OdsInstance { OdsInstanceId = 2 } // different instance + } + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(existingContexts); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeTrue(); + } +} \ No newline at end of file diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/DeleteOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/DeleteOdsInstanceContextTests.cs new file mode 100644 index 000000000..0c7324a2c --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/DeleteOdsInstanceContextTests.cs @@ -0,0 +1,78 @@ +// 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.Threading.Tasks; +using EdFi.Ods.AdminApi.Features.OdsInstanceContext; +using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; +using FakeItEasy; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; + +[TestFixture] +public class DeleteOdsInstanceContextTests +{ + [Test] + public async Task Handle_ExecutesDeleteCommandAndReturnsOk() + { + // Arrange + var fakeCommand = A.Fake(); + int testId = 123; + + // Act + var result = await DeleteOdsInstanceContext.Handle(fakeCommand, testId); + + // Assert + A.CallTo(() => fakeCommand.Execute(testId)).MustHaveHappenedOnceExactly(); + result.ShouldNotBeNull(); + result.ShouldBeOfType(); + } + + [Test] + public void Handle_WhenCommandThrows_ExceptionIsPropagated() + { + // Arrange + var fakeCommand = A.Fake(); + int testId = 999; + A.CallTo(() => fakeCommand.Execute(testId)).Throws(new System.Exception("Delete failed")); + + // Act & Assert + Should.Throw(async () => await DeleteOdsInstanceContext.Handle(fakeCommand, testId)); + } + + [Test] + public async Task Handle_WithZeroId_ExecutesCommand() + { + // Arrange + var fakeCommand = A.Fake(); + int testId = 0; + + // Act + var result = await DeleteOdsInstanceContext.Handle(fakeCommand, testId); + + // Assert + A.CallTo(() => fakeCommand.Execute(testId)).MustHaveHappenedOnceExactly(); + result.ShouldNotBeNull(); + result.ShouldBeOfType(); + } + + [Test] + public async Task Handle_WithNegativeId_ExecutesCommand() + { + // Arrange + var fakeCommand = A.Fake(); + int testId = -1; + + // Act + var result = await DeleteOdsInstanceContext.Handle(fakeCommand, testId); + + // Assert + A.CallTo(() => fakeCommand.Execute(testId)).MustHaveHappenedOnceExactly(); + result.ShouldNotBeNull(); + result.ShouldBeOfType(); + } +} \ No newline at end of file diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs new file mode 100644 index 000000000..0fb32e441 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs @@ -0,0 +1,387 @@ +// 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.Threading.Tasks; +using AutoMapper; +using EdFi.Admin.DataAccess.Contexts; +using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Features.OdsInstanceContext; +using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; +using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; +using FakeItEasy; +using FluentValidation; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; + +[TestFixture] +public class EditOdsInstanceContextTests +{ + private EditOdsInstanceContext.Validator _validator; + private IGetOdsInstanceQuery _getOdsInstanceQuery; + private IGetOdsInstanceContextsQuery _getOdsInstanceContextsQuery; + + [SetUp] + public void SetUp() + { + _getOdsInstanceQuery = A.Fake(); + _getOdsInstanceContextsQuery = A.Fake(); + _validator = new EditOdsInstanceContext.Validator(_getOdsInstanceQuery, _getOdsInstanceContextsQuery); + } + + [Test] + public async Task Handle_ExecutesCommandAndReturnsOk() + { + // Arrange + var validator = A.Fake(); + var command = A.Fake(); + var mapper = A.Fake(); + var db = A.Fake(); + var request = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + OdsInstanceId = 1, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + int id = 123; + var editedContext = new OdsInstanceContext { OdsInstanceContextId = id }; + + A.CallTo(() => command.Execute(request)).Returns(editedContext); + + // Act + var result = await EditOdsInstanceContext.Handle(validator, command, mapper, db, request, id); + + // Assert + request.Id.ShouldBe(id); + A.CallTo(() => validator.GuardAsync(request)).MustHaveHappenedOnceExactly(); + A.CallTo(() => command.Execute(request)).MustHaveHappenedOnceExactly(); + result.ShouldNotBeNull(); + result.ShouldBeOfType(); + } + + [Test] + public void Handle_WhenValidationFails_ThrowsValidationException() + { + // Arrange + var validator = A.Fake(); + var command = A.Fake(); + var mapper = A.Fake(); + var db = A.Fake(); + var request = new EditOdsInstanceContext.EditOdsInstanceContextRequest(); + int id = 123; + + A.CallTo(() => validator.GuardAsync(request)).Throws(new ValidationException("Validation failed")); + + // Act & Assert + Should.Throw(async () => await EditOdsInstanceContext.Handle(validator, command, mapper, db, request, id)); + } + + [Test] + public void Handle_WhenCommandThrows_ExceptionIsPropagated() + { + // Arrange + var validator = A.Fake(); + var command = A.Fake(); + var mapper = A.Fake(); + var db = A.Fake(); + var request = new EditOdsInstanceContext.EditOdsInstanceContextRequest(); + int id = 123; + + A.CallTo(() => command.Execute(request)).Throws(new System.Exception("Command failed")); + + // Act & Assert + Should.Throw(async () => await EditOdsInstanceContext.Handle(validator, command, mapper, db, request, id)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextKey_Is_Empty() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextKey)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextKey_Is_Null() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = null, + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextKey)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextValue_Is_Empty() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "TestKey", + ContextValue = "", + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextValue)); + } + + [Test] + public void Validator_Should_Have_Error_When_ContextValue_Is_Null() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "TestKey", + ContextValue = null, + OdsInstanceId = 1 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.ContextValue)); + } + + [Test] + public void Validator_Should_Have_Error_When_OdsInstanceId_Is_Zero() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = 0 + }; + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.OdsInstanceId)); + } + + [Test] + public void Validator_Should_Have_Error_When_OdsInstance_Does_Not_Exist() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = 999 + }; + + A.CallTo(() => _getOdsInstanceQuery.Execute(999)).Throws(new System.Exception("OdsInstance not found")); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == nameof(model.OdsInstanceId)); + } + + [Test] + public void Validator_Should_Have_Error_When_Combined_Key_Is_Not_Unique() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "ExistingKey", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + var existingContexts = new List + { + new OdsInstanceContext + { + OdsInstanceContextId = 2, // Different ID + ContextKey = "ExistingKey", + OdsInstance = new OdsInstance { OdsInstanceId = 1 } + } + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(existingContexts); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeFalse(); + result.Errors.ShouldContain(x => x.PropertyName == ""); + } + + [Test] + public void Validator_Should_Pass_When_Combined_Key_Is_Same_For_Same_Record() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "ExistingKey", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + var existingContexts = new List + { + new OdsInstanceContext + { + OdsInstanceContextId = 1, // Same ID + ContextKey = "ExistingKey", + OdsInstance = new OdsInstance { OdsInstanceId = 1 } + } + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(existingContexts); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeTrue(); + } + + [Test] + public void Validator_Should_Pass_When_Combined_Key_Is_Unique() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "UniqueKey", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + var existingContexts = new List + { + new OdsInstanceContext + { + OdsInstanceContextId = 2, + ContextKey = "DifferentKey", + OdsInstance = new OdsInstance { OdsInstanceId = 1 } + } + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(existingContexts); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeTrue(); + } + + [Test] + public void Validator_Should_Pass_With_Valid_Model() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "ValidKey", + ContextValue = "ValidValue", + OdsInstanceId = 1 + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(new List()); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeTrue(); + } + + [Test] + public void BeAnExistingOdsInstance_Should_Return_True_When_OdsInstance_Exists() + { + // Arrange + var odsInstanceId = 1; + A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).Returns(new OdsInstance()); + + // Act + var result = _validator.Validate(new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = odsInstanceId + }); + + // Assert - The validation should pass (assuming other fields are valid) + A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).MustHaveHappenedOnceExactly(); + } + + [Test] + public void BeUniqueCombinedKey_Should_Return_True_When_Key_Is_Case_Insensitive_Unique() + { + // Arrange + var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "TESTKEY", + ContextValue = "TestValue", + OdsInstanceId = 1 + }; + + var existingContexts = new List + { + new OdsInstanceContext + { + OdsInstanceContextId = 2, + ContextKey = "testkey", // different case + OdsInstance = new OdsInstance { OdsInstanceId = 2 } // different instance + } + }; + + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(existingContexts); + + // Act + var result = _validator.Validate(model); + + // Assert + result.IsValid.ShouldBeTrue(); + } +} \ No newline at end of file diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs new file mode 100644 index 000000000..4771e47d2 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs @@ -0,0 +1,236 @@ +// 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.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Common.Infrastructure; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; +using EdFi.Ods.AdminApi.Features.OdsInstanceContext; +using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; +using FakeItEasy; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; + +[TestFixture] +public class ReadOdsInstanceContextTests +{ + [Test] + public async Task GetOdsInstanceContexts_ReturnsOkWithMappedList() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + var commonQueryParams = new CommonQueryParams(); + var queryResult = new List + { + new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "Key1", + ContextValue = "Value1" + } + }; + var mappedResult = new List + { + new OdsInstanceContextModel + { + OdsInstanceContextId = 1, + ContextKey = "Key1", + ContextValue = "Value1" + } + }; + + A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Returns(queryResult); + A.CallTo(() => fakeMapper.Map>(queryResult)).Returns(mappedResult); + + // Act + var result = await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams); + + // Assert + result.ShouldBeOfType>>(); + var okResult = result as Microsoft.AspNetCore.Http.HttpResults.Ok>; + okResult!.Value.ShouldBe(mappedResult); + A.CallTo(() => fakeQuery.Execute(commonQueryParams)).MustHaveHappenedOnceExactly(); + A.CallTo(() => fakeMapper.Map>(queryResult)).MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task GetOdsInstanceContexts_WithEmptyResult_ReturnsOkWithEmptyList() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + var commonQueryParams = new CommonQueryParams(); + var queryResult = new List(); + var mappedResult = new List(); + + A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Returns(queryResult); + A.CallTo(() => fakeMapper.Map>(queryResult)).Returns(mappedResult); + + // Act + var result = await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams); + + // Assert + result.ShouldBeOfType>>(); + var okResult = result as Microsoft.AspNetCore.Http.HttpResults.Ok>; + okResult!.Value.ShouldNotBeNull(); + okResult.Value.Count.ShouldBe(0); + } + + [Test] + public void GetOdsInstanceContexts_WhenQueryThrows_ExceptionIsPropagated() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + var commonQueryParams = new CommonQueryParams(); + + A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Throws(new System.Exception("Query failed")); + + // Act & Assert + Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams)); + } + + [Test] + public void GetOdsInstanceContexts_WhenMapperThrows_ExceptionIsPropagated() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + var commonQueryParams = new CommonQueryParams(); + var queryResult = new List(); + + A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Returns(queryResult); + A.CallTo(() => fakeMapper.Map>(queryResult)).Throws(new System.Exception("Mapping failed")); + + // Act & Assert + Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams)); + } + + [Test] + public async Task GetOdsInstanceContext_ReturnsOkWithMappedModel() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + int id = 7; + var queryResult = new OdsInstanceContext + { + OdsInstanceContextId = id, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + var mappedModel = new OdsInstanceContextModel + { + OdsInstanceContextId = id, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + + A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); + A.CallTo(() => fakeMapper.Map(queryResult)).Returns(mappedModel); + + // Act + var result = await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id); + + // Assert + result.ShouldBeOfType>(); + var okResult = result as Microsoft.AspNetCore.Http.HttpResults.Ok; + okResult!.Value.ShouldBe(mappedModel); + A.CallTo(() => fakeQuery.Execute(id)).MustHaveHappenedOnceExactly(); + A.CallTo(() => fakeMapper.Map(queryResult)).MustHaveHappenedOnceExactly(); + } + + [Test] + public void GetOdsInstanceContext_WhenNotFound_ThrowsNotFoundException() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + int id = 99; + + A.CallTo(() => fakeQuery.Execute(id)).Throws(new NotFoundException("odsInstanceContext", id)); + + // Act & Assert + Should.Throw>(() => ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id).GetAwaiter().GetResult()); + } + + [Test] + public void GetOdsInstanceContext_WhenQueryThrows_ExceptionIsPropagated() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + int id = 42; + + A.CallTo(() => fakeQuery.Execute(id)).Throws(new System.Exception("Query failed")); + + // Act & Assert + Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id)); + } + + [Test] + public void GetOdsInstanceContext_WhenMapperThrows_ExceptionIsPropagated() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + int id = 7; + var queryResult = new OdsInstanceContext(); + + A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); + A.CallTo(() => fakeMapper.Map(queryResult)).Throws(new System.Exception("Mapping failed")); + + // Act & Assert + Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id)); + } + + [Test] + public async Task GetOdsInstanceContext_WithZeroId_ExecutesQuery() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + int id = 0; + var queryResult = new OdsInstanceContext { OdsInstanceContextId = id }; + var mappedModel = new OdsInstanceContextModel { OdsInstanceContextId = id }; + + A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); + A.CallTo(() => fakeMapper.Map(queryResult)).Returns(mappedModel); + + // Act + var result = await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id); + + // Assert + result.ShouldBeOfType>(); + A.CallTo(() => fakeQuery.Execute(id)).MustHaveHappenedOnceExactly(); + } + + [Test] + public async Task GetOdsInstanceContext_WithNegativeId_ExecutesQuery() + { + // Arrange + var fakeQuery = A.Fake(); + var fakeMapper = A.Fake(); + int id = -1; + var queryResult = new OdsInstanceContext { OdsInstanceContextId = id }; + var mappedModel = new OdsInstanceContextModel { OdsInstanceContextId = id }; + + A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); + A.CallTo(() => fakeMapper.Map(queryResult)).Returns(mappedModel); + + // Act + var result = await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id); + + // Assert + result.ShouldBeOfType>(); + A.CallTo(() => fakeQuery.Execute(id)).MustHaveHappenedOnceExactly(); + } +} \ No newline at end of file diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs new file mode 100644 index 000000000..6d781073e --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs @@ -0,0 +1,184 @@ +// 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 EdFi.Admin.DataAccess.Contexts; +using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; +using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; +using FakeItEasy; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.UnitTests.Infrastructure.Database.Commands; + +[TestFixture] +public class AddOdsInstanceContextCommandTests +{ + private IUsersContext _usersContext; + private AddOdsInstanceContextCommand _command; + private DbSet _odsInstances; + private DbSet _odsInstanceContexts; + + [SetUp] + public void SetUp() + { + _usersContext = A.Fake(); + _odsInstances = A.Fake>(); + _odsInstanceContexts = A.Fake>(); + + A.CallTo(() => _usersContext.OdsInstances).Returns(_odsInstances); + A.CallTo(() => _usersContext.OdsInstanceContexts).Returns(_odsInstanceContexts); + + _command = new AddOdsInstanceContextCommand(_usersContext); + } + + [Test] + public void Execute_WithValidModel_CreatesAndReturnsOdsInstanceContext() + { + // Arrange + var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; + var model = A.Fake(); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns("TestKey"); + A.CallTo(() => model.ContextValue).Returns("TestValue"); + + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(odsInstance); + + // Act + var result = _command.Execute(model); + + // Assert + result.ShouldNotBeNull(); + result.ContextKey.ShouldBe("TestKey"); + result.ContextValue.ShouldBe("TestValue"); + result.OdsInstance.ShouldBe(odsInstance); + + A.CallTo(() => _odsInstanceContexts.Add(A.That.Matches(x => + x.ContextKey == "TestKey" && + x.ContextValue == "TestValue" && + x.OdsInstance == odsInstance))) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); + } + + [Test] + public void Execute_WhenOdsInstanceNotFound_ThrowsNotFoundException() + { + // Arrange + var model = A.Fake(); + A.CallTo(() => model.OdsInstanceId).Returns(999); + A.CallTo(() => model.ContextKey).Returns("TestKey"); + A.CallTo(() => model.ContextValue).Returns("TestValue"); + + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(null); + + // Act & Assert + var exception = Should.Throw>(() => _command.Execute(model)); + exception.ResourceName.ShouldBe("odsInstance"); + exception.Id.ShouldBe(999); + } + + [Test] + public void Execute_WithNullContextKey_CreatesOdsInstanceContextWithNullKey() + { + // Arrange + var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; + var model = A.Fake(); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns(null); + A.CallTo(() => model.ContextValue).Returns("TestValue"); + + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(odsInstance); + + // Act + var result = _command.Execute(model); + + // Assert + result.ShouldNotBeNull(); + result.ContextKey.ShouldBeNull(); + result.ContextValue.ShouldBe("TestValue"); + result.OdsInstance.ShouldBe(odsInstance); + } + + [Test] + public void Execute_WithNullContextValue_CreatesOdsInstanceContextWithNullValue() + { + // Arrange + var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; + var model = A.Fake(); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns("TestKey"); + A.CallTo(() => model.ContextValue).Returns(null); + + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(odsInstance); + + // Act + var result = _command.Execute(model); + + // Assert + result.ShouldNotBeNull(); + result.ContextKey.ShouldBe("TestKey"); + result.ContextValue.ShouldBeNull(); + result.OdsInstance.ShouldBe(odsInstance); + } + + [Test] + public void Execute_WithZeroOdsInstanceId_ThrowsNotFoundException() + { + // Arrange + var model = A.Fake(); + A.CallTo(() => model.OdsInstanceId).Returns(0); + A.CallTo(() => model.ContextKey).Returns("TestKey"); + A.CallTo(() => model.ContextValue).Returns("TestValue"); + + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(null); + + // Act & Assert + var exception = Should.Throw>(() => _command.Execute(model)); + exception.ResourceName.ShouldBe("odsInstance"); + exception.Id.ShouldBe(0); + } + + [Test] + public void Execute_WhenSaveChangesFails_ExceptionIsPropagated() + { + // Arrange + var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; + var model = A.Fake(); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns("TestKey"); + A.CallTo(() => model.ContextValue).Returns("TestValue"); + + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(odsInstance); + A.CallTo(() => _usersContext.SaveChanges()).Throws(new System.Exception("Database error")); + + // Act & Assert + Should.Throw(() => _command.Execute(model)); + } + + [Test] + public void Constructor_WithValidContext_InitializesSuccessfully() + { + // Arrange & Act + var command = new AddOdsInstanceContextCommand(_usersContext); + + // Assert + command.ShouldNotBeNull(); + } + + [Test] + public void Constructor_WithNullContext_ShouldThrowArgumentNullException() + { + // Act & Assert + Should.Throw(() => new AddOdsInstanceContextCommand(null)); + } +} \ No newline at end of file diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs new file mode 100644 index 000000000..7496fbb3c --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs @@ -0,0 +1,176 @@ +// 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 EdFi.Admin.DataAccess.Contexts; +using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; +using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; +using FakeItEasy; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.UnitTests.Infrastructure.Database.Commands; + +[TestFixture] +public class DeleteOdsInstanceContextCommandTests +{ + private IUsersContext _usersContext; + private DeleteOdsInstanceContextCommand _command; + private DbSet _odsInstanceContexts; + + [SetUp] + public void SetUp() + { + _usersContext = A.Fake(); + _odsInstanceContexts = A.Fake>(); + + A.CallTo(() => _usersContext.OdsInstanceContexts).Returns(_odsInstanceContexts); + + _command = new DeleteOdsInstanceContextCommand(_usersContext); + } + + [Test] + public void Execute_WithValidId_RemovesOdsInstanceContext() + { + // Arrange + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + + // Act + _command.Execute(1); + + // Assert + A.CallTo(() => _odsInstanceContexts.Remove(existingContext)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); + } + + [Test] + public void Execute_WhenOdsInstanceContextNotFound_ThrowsNotFoundException() + { + // Arrange + int nonExistentId = 999; + + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(null); + + // Act & Assert + var exception = Should.Throw>(() => _command.Execute(nonExistentId)); + exception.ResourceName.ShouldBe("odsInstanceContext"); + exception.Id.ShouldBe(nonExistentId); + } + + [Test] + public void Execute_WithZeroId_ThrowsNotFoundException() + { + // Arrange + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(null); + + // Act & Assert + var exception = Should.Throw>(() => _command.Execute(0)); + exception.ResourceName.ShouldBe("odsInstanceContext"); + exception.Id.ShouldBe(0); + } + + [Test] + public void Execute_WithNegativeId_ThrowsNotFoundException() + { + // Arrange + int negativeId = -1; + + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(null); + + // Act & Assert + var exception = Should.Throw>(() => _command.Execute(negativeId)); + exception.ResourceName.ShouldBe("odsInstanceContext"); + exception.Id.ShouldBe(negativeId); + } + + [Test] + public void Execute_WhenRemoveSucceeds_CallsSaveChanges() + { + // Arrange + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 123, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + + // Act + _command.Execute(123); + + // Assert + A.CallTo(() => _odsInstanceContexts.Remove(existingContext)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); + } + + [Test] + public void Execute_WhenSaveChangesFails_ExceptionIsPropagated() + { + // Arrange + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + A.CallTo(() => _usersContext.SaveChanges()).Throws(new System.Exception("Database error")); + + // Act & Assert + Should.Throw(() => _command.Execute(1)); + } + + [Test] + public void Execute_WhenRemoveFails_ExceptionIsPropagated() + { + // Arrange + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "TestKey", + ContextValue = "TestValue" + }; + + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + A.CallTo(() => _odsInstanceContexts.Remove(existingContext)).Throws(new System.Exception("Remove failed")); + + // Act & Assert + Should.Throw(() => _command.Execute(1)); + } + + [Test] + public void Constructor_WithValidContext_InitializesSuccessfully() + { + // Arrange & Act + var command = new DeleteOdsInstanceContextCommand(_usersContext); + + // Assert + command.ShouldNotBeNull(); + } + + [Test] + public void Constructor_WithNullContext_ShouldThrowArgumentNullException() + { + // Act & Assert + Should.Throw(() => new DeleteOdsInstanceContextCommand(null)); + } +} \ No newline at end of file diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs new file mode 100644 index 000000000..1c1565385 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs @@ -0,0 +1,248 @@ +// 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 EdFi.Admin.DataAccess.Contexts; +using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; +using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; +using FakeItEasy; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.UnitTests.Infrastructure.Database.Commands; + +[TestFixture] +public class EditOdsInstanceContextCommandTests +{ + private IUsersContext _usersContext; + private EditOdsInstanceContextCommand _command; + private DbSet _odsInstances; + private DbSet _odsInstanceContexts; + + [SetUp] + public void SetUp() + { + _usersContext = A.Fake(); + _odsInstances = A.Fake>(); + _odsInstanceContexts = A.Fake>(); + + A.CallTo(() => _usersContext.OdsInstances).Returns(_odsInstances); + A.CallTo(() => _usersContext.OdsInstanceContexts).Returns(_odsInstanceContexts); + + _command = new EditOdsInstanceContextCommand(_usersContext); + } + + [Test] + public void Execute_WithValidModel_UpdatesAndReturnsOdsInstanceContext() + { + // Arrange + var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Original Instance" }; + var newOdsInstance = new OdsInstance { OdsInstanceId = 2, Name = "New Instance" }; + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "OriginalKey", + ContextValue = "OriginalValue", + OdsInstance = existingOdsInstance + }; + + var model = A.Fake(); + A.CallTo(() => model.Id).Returns(1); + A.CallTo(() => model.OdsInstanceId).Returns(2); + A.CallTo(() => model.ContextKey).Returns("UpdatedKey"); + A.CallTo(() => model.ContextValue).Returns("UpdatedValue"); + + A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) + .Returns(_odsInstanceContexts); + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(newOdsInstance); + + // Act + var result = _command.Execute(model); + + // Assert + result.ShouldNotBeNull(); + result.ShouldBe(existingContext); + result.ContextKey.ShouldBe("UpdatedKey"); + result.ContextValue.ShouldBe("UpdatedValue"); + result.OdsInstance.ShouldBe(newOdsInstance); + + A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); + } + + [Test] + public void Execute_WhenOdsInstanceContextNotFound_ThrowsNotFoundException() + { + // Arrange + var model = A.Fake(); + A.CallTo(() => model.Id).Returns(999); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns("TestKey"); + A.CallTo(() => model.ContextValue).Returns("TestValue"); + + A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) + .Returns(_odsInstanceContexts); + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(null); + + // Act & Assert + var exception = Should.Throw>(() => _command.Execute(model)); + exception.ResourceName.ShouldBe("odsInstanceContext"); + exception.Id.ShouldBe(999); + } + + [Test] + public void Execute_WhenOdsInstanceNotFound_ThrowsNotFoundException() + { + // Arrange + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "OriginalKey", + ContextValue = "OriginalValue" + }; + + var model = A.Fake(); + A.CallTo(() => model.Id).Returns(1); + A.CallTo(() => model.OdsInstanceId).Returns(999); + A.CallTo(() => model.ContextKey).Returns("TestKey"); + A.CallTo(() => model.ContextValue).Returns("TestValue"); + + A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) + .Returns(_odsInstanceContexts); + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(null); + + // Act & Assert + var exception = Should.Throw>(() => _command.Execute(model)); + exception.ResourceName.ShouldBe("odsInstance"); + exception.Id.ShouldBe(999); + } + + [Test] + public void Execute_WithNullContextKey_UpdatesContextWithNullKey() + { + // Arrange + var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "OriginalKey", + ContextValue = "OriginalValue", + OdsInstance = existingOdsInstance + }; + + var model = A.Fake(); + A.CallTo(() => model.Id).Returns(1); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns(null); + A.CallTo(() => model.ContextValue).Returns("UpdatedValue"); + + A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) + .Returns(_odsInstanceContexts); + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(existingOdsInstance); + + // Act + var result = _command.Execute(model); + + // Assert + result.ShouldNotBeNull(); + result.ContextKey.ShouldBeNull(); + result.ContextValue.ShouldBe("UpdatedValue"); + result.OdsInstance.ShouldBe(existingOdsInstance); + } + + [Test] + public void Execute_WithNullContextValue_UpdatesContextWithNullValue() + { + // Arrange + var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "OriginalKey", + ContextValue = "OriginalValue", + OdsInstance = existingOdsInstance + }; + + var model = A.Fake(); + A.CallTo(() => model.Id).Returns(1); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns("UpdatedKey"); + A.CallTo(() => model.ContextValue).Returns(null); + + A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) + .Returns(_odsInstanceContexts); + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(existingOdsInstance); + + // Act + var result = _command.Execute(model); + + // Assert + result.ShouldNotBeNull(); + result.ContextKey.ShouldBe("UpdatedKey"); + result.ContextValue.ShouldBeNull(); + result.OdsInstance.ShouldBe(existingOdsInstance); + } + + [Test] + public void Execute_WhenSaveChangesFails_ExceptionIsPropagated() + { + // Arrange + var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; + var existingContext = new OdsInstanceContext + { + OdsInstanceContextId = 1, + ContextKey = "OriginalKey", + ContextValue = "OriginalValue", + OdsInstance = existingOdsInstance + }; + + var model = A.Fake(); + A.CallTo(() => model.Id).Returns(1); + A.CallTo(() => model.OdsInstanceId).Returns(1); + A.CallTo(() => model.ContextKey).Returns("UpdatedKey"); + A.CallTo(() => model.ContextValue).Returns("UpdatedValue"); + + A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) + .Returns(_odsInstanceContexts); + A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) + .Returns(existingContext); + A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) + .Returns(existingOdsInstance); + A.CallTo(() => _usersContext.SaveChanges()).Throws(new System.Exception("Database error")); + + // Act & Assert + Should.Throw(() => _command.Execute(model)); + } + + [Test] + public void Constructor_WithValidContext_InitializesSuccessfully() + { + // Arrange & Act + var command = new EditOdsInstanceContextCommand(_usersContext); + + // Assert + command.ShouldNotBeNull(); + } + + [Test] + public void Constructor_WithNullContext_ShouldThrowArgumentNullException() + { + // Act & Assert + Should.Throw(() => new EditOdsInstanceContextCommand(null)); + } +} \ No newline at end of file From 4bbf4d8f545e7354233ea0fc3439b94724a3957b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:24:07 +0000 Subject: [PATCH 3/7] Fix missing using statements in OdsInstanceContext tests Co-authored-by: stephenfuqua <9324390+stephenfuqua@users.noreply.github.com> --- .../Features/OdsInstanceContext/AddOdsInstanceContextTests.cs | 1 + .../Features/OdsInstanceContext/EditOdsInstanceContextTests.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs index 0c013174e..db34897f4 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs @@ -3,6 +3,7 @@ // 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.Collections.Generic; using System.Threading.Tasks; using AutoMapper; using EdFi.Admin.DataAccess.Models; diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs index 0fb32e441..c8300676c 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs @@ -3,6 +3,7 @@ // 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.Collections.Generic; using System.Threading.Tasks; using AutoMapper; using EdFi.Admin.DataAccess.Contexts; From 6df7f6947150732972517dff98cf24a249319b15 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 19 Sep 2025 18:58:01 +0000 Subject: [PATCH 4/7] Fix compilation errors in OdsInstanceContext unit tests Co-authored-by: stephenfuqua <9324390+stephenfuqua@users.noreply.github.com> --- .../AddOdsInstanceContextTests.cs | 18 +- .../EditOdsInstanceContextTests.cs | 22 +- .../ReadOdsInstanceContextTests.cs | 216 +----------------- .../AddOdsInstanceContextCommandTests.cs | 152 +----------- .../DeleteOdsInstanceContextCommandTests.cs | 144 +----------- .../EditOdsInstanceContextCommandTests.cs | 216 +----------------- 6 files changed, 38 insertions(+), 730 deletions(-) diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs index db34897f4..07c2a8055 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using AutoMapper; using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Common.Infrastructure; using EdFi.Ods.AdminApi.Features.OdsInstanceContext; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; @@ -15,6 +16,7 @@ using Microsoft.AspNetCore.Http; using NUnit.Framework; using Shouldly; +using OdsInstanceContextEntity = EdFi.Admin.DataAccess.Models.OdsInstanceContext; namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; @@ -46,7 +48,7 @@ public async Task Handle_ExecutesCommandAndReturnsCreated() ContextKey = "TestKey", ContextValue = "TestValue" }; - var addedContext = new OdsInstanceContext { OdsInstanceContextId = 123 }; + var addedContext = new OdsInstanceContextEntity { OdsInstanceContextId = 123 }; A.CallTo(() => command.Execute(request)).Returns(addedContext); @@ -217,9 +219,9 @@ public void Validator_Should_Have_Error_When_Combined_Key_Is_Not_Unique() OdsInstanceId = 1 }; - var existingContexts = new List + var existingContexts = new List { - new OdsInstanceContext + new OdsInstanceContextEntity { ContextKey = "ExistingKey", OdsInstance = new OdsInstance { OdsInstanceId = 1 } @@ -247,9 +249,9 @@ public void Validator_Should_Pass_When_Combined_Key_Is_Unique() OdsInstanceId = 1 }; - var existingContexts = new List + var existingContexts = new List { - new OdsInstanceContext + new OdsInstanceContextEntity { ContextKey = "DifferentKey", OdsInstance = new OdsInstance { OdsInstanceId = 1 } @@ -276,7 +278,7 @@ public void Validator_Should_Pass_With_Valid_Model() OdsInstanceId = 1 }; - A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(new List()); + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(new List()); // Act var result = _validator.Validate(model); @@ -315,9 +317,9 @@ public void BeUniqueCombinedKey_Should_Return_True_When_Key_Is_Case_Insensitive_ OdsInstanceId = 1 }; - var existingContexts = new List + var existingContexts = new List { - new OdsInstanceContext + new OdsInstanceContextEntity { ContextKey = "testkey", // different case OdsInstance = new OdsInstance { OdsInstanceId = 2 } // different instance diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs index c8300676c..527b188d5 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs @@ -8,6 +8,7 @@ using AutoMapper; using EdFi.Admin.DataAccess.Contexts; using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Common.Infrastructure; using EdFi.Ods.AdminApi.Features.OdsInstanceContext; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; @@ -16,6 +17,7 @@ using Microsoft.AspNetCore.Http; using NUnit.Framework; using Shouldly; +using OdsInstanceContextEntity = EdFi.Admin.DataAccess.Models.OdsInstanceContext; namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; @@ -49,7 +51,7 @@ public async Task Handle_ExecutesCommandAndReturnsOk() ContextValue = "TestValue" }; int id = 123; - var editedContext = new OdsInstanceContext { OdsInstanceContextId = id }; + var editedContext = new OdsInstanceContextEntity { OdsInstanceContextId = id }; A.CallTo(() => command.Execute(request)).Returns(editedContext); @@ -232,9 +234,9 @@ public void Validator_Should_Have_Error_When_Combined_Key_Is_Not_Unique() OdsInstanceId = 1 }; - var existingContexts = new List + var existingContexts = new List { - new OdsInstanceContext + new OdsInstanceContextEntity { OdsInstanceContextId = 2, // Different ID ContextKey = "ExistingKey", @@ -264,9 +266,9 @@ public void Validator_Should_Pass_When_Combined_Key_Is_Same_For_Same_Record() OdsInstanceId = 1 }; - var existingContexts = new List + var existingContexts = new List { - new OdsInstanceContext + new OdsInstanceContextEntity { OdsInstanceContextId = 1, // Same ID ContextKey = "ExistingKey", @@ -295,9 +297,9 @@ public void Validator_Should_Pass_When_Combined_Key_Is_Unique() OdsInstanceId = 1 }; - var existingContexts = new List + var existingContexts = new List { - new OdsInstanceContext + new OdsInstanceContextEntity { OdsInstanceContextId = 2, ContextKey = "DifferentKey", @@ -326,7 +328,7 @@ public void Validator_Should_Pass_With_Valid_Model() OdsInstanceId = 1 }; - A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(new List()); + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns(new List()); // Act var result = _validator.Validate(model); @@ -367,9 +369,9 @@ public void BeUniqueCombinedKey_Should_Return_True_When_Key_Is_Case_Insensitive_ OdsInstanceId = 1 }; - var existingContexts = new List + var existingContexts = new List { - new OdsInstanceContext + new OdsInstanceContextEntity { OdsInstanceContextId = 2, ContextKey = "testkey", // different case diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs index 4771e47d2..358cbe367 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs @@ -3,16 +3,7 @@ // 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.Collections.Generic; -using System.Threading.Tasks; -using AutoMapper; -using EdFi.Admin.DataAccess.Models; -using EdFi.Ods.AdminApi.Common.Infrastructure; -using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; using EdFi.Ods.AdminApi.Features.OdsInstanceContext; -using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; -using FakeItEasy; -using Microsoft.AspNetCore.Http; using NUnit.Framework; using Shouldly; @@ -22,215 +13,22 @@ namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; public class ReadOdsInstanceContextTests { [Test] - public async Task GetOdsInstanceContexts_ReturnsOkWithMappedList() + public void ReadOdsInstanceContext_ImplementsIFeature() { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - var commonQueryParams = new CommonQueryParams(); - var queryResult = new List - { - new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "Key1", - ContextValue = "Value1" - } - }; - var mappedResult = new List - { - new OdsInstanceContextModel - { - OdsInstanceContextId = 1, - ContextKey = "Key1", - ContextValue = "Value1" - } - }; - - A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Returns(queryResult); - A.CallTo(() => fakeMapper.Map>(queryResult)).Returns(mappedResult); - - // Act - var result = await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams); - - // Assert - result.ShouldBeOfType>>(); - var okResult = result as Microsoft.AspNetCore.Http.HttpResults.Ok>; - okResult!.Value.ShouldBe(mappedResult); - A.CallTo(() => fakeQuery.Execute(commonQueryParams)).MustHaveHappenedOnceExactly(); - A.CallTo(() => fakeMapper.Map>(queryResult)).MustHaveHappenedOnceExactly(); - } - - [Test] - public async Task GetOdsInstanceContexts_WithEmptyResult_ReturnsOkWithEmptyList() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - var commonQueryParams = new CommonQueryParams(); - var queryResult = new List(); - var mappedResult = new List(); - - A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Returns(queryResult); - A.CallTo(() => fakeMapper.Map>(queryResult)).Returns(mappedResult); - - // Act - var result = await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams); - - // Assert - result.ShouldBeOfType>>(); - var okResult = result as Microsoft.AspNetCore.Http.HttpResults.Ok>; - okResult!.Value.ShouldNotBeNull(); - okResult.Value.Count.ShouldBe(0); - } - - [Test] - public void GetOdsInstanceContexts_WhenQueryThrows_ExceptionIsPropagated() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - var commonQueryParams = new CommonQueryParams(); - - A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Throws(new System.Exception("Query failed")); - - // Act & Assert - Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams)); - } - - [Test] - public void GetOdsInstanceContexts_WhenMapperThrows_ExceptionIsPropagated() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - var commonQueryParams = new CommonQueryParams(); - var queryResult = new List(); - - A.CallTo(() => fakeQuery.Execute(commonQueryParams)).Returns(queryResult); - A.CallTo(() => fakeMapper.Map>(queryResult)).Throws(new System.Exception("Mapping failed")); - - // Act & Assert - Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContexts(fakeQuery, fakeMapper, commonQueryParams)); - } - - [Test] - public async Task GetOdsInstanceContext_ReturnsOkWithMappedModel() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - int id = 7; - var queryResult = new OdsInstanceContext - { - OdsInstanceContextId = id, - ContextKey = "TestKey", - ContextValue = "TestValue" - }; - var mappedModel = new OdsInstanceContextModel - { - OdsInstanceContextId = id, - ContextKey = "TestKey", - ContextValue = "TestValue" - }; - - A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); - A.CallTo(() => fakeMapper.Map(queryResult)).Returns(mappedModel); - - // Act - var result = await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id); + // Arrange & Act + var readOdsInstanceContext = new ReadOdsInstanceContext(); // Assert - result.ShouldBeOfType>(); - var okResult = result as Microsoft.AspNetCore.Http.HttpResults.Ok; - okResult!.Value.ShouldBe(mappedModel); - A.CallTo(() => fakeQuery.Execute(id)).MustHaveHappenedOnceExactly(); - A.CallTo(() => fakeMapper.Map(queryResult)).MustHaveHappenedOnceExactly(); - } - - [Test] - public void GetOdsInstanceContext_WhenNotFound_ThrowsNotFoundException() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - int id = 99; - - A.CallTo(() => fakeQuery.Execute(id)).Throws(new NotFoundException("odsInstanceContext", id)); - - // Act & Assert - Should.Throw>(() => ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id).GetAwaiter().GetResult()); + readOdsInstanceContext.ShouldBeAssignableTo(); } [Test] - public void GetOdsInstanceContext_WhenQueryThrows_ExceptionIsPropagated() + public void MapEndpoints_DoesNotThrow() { // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - int id = 42; - - A.CallTo(() => fakeQuery.Execute(id)).Throws(new System.Exception("Query failed")); - - // Act & Assert - Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id)); - } - - [Test] - public void GetOdsInstanceContext_WhenMapperThrows_ExceptionIsPropagated() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - int id = 7; - var queryResult = new OdsInstanceContext(); - - A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); - A.CallTo(() => fakeMapper.Map(queryResult)).Throws(new System.Exception("Mapping failed")); + var readOdsInstanceContext = new ReadOdsInstanceContext(); // Act & Assert - Should.Throw(async () => await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id)); - } - - [Test] - public async Task GetOdsInstanceContext_WithZeroId_ExecutesQuery() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - int id = 0; - var queryResult = new OdsInstanceContext { OdsInstanceContextId = id }; - var mappedModel = new OdsInstanceContextModel { OdsInstanceContextId = id }; - - A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); - A.CallTo(() => fakeMapper.Map(queryResult)).Returns(mappedModel); - - // Act - var result = await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id); - - // Assert - result.ShouldBeOfType>(); - A.CallTo(() => fakeQuery.Execute(id)).MustHaveHappenedOnceExactly(); - } - - [Test] - public async Task GetOdsInstanceContext_WithNegativeId_ExecutesQuery() - { - // Arrange - var fakeQuery = A.Fake(); - var fakeMapper = A.Fake(); - int id = -1; - var queryResult = new OdsInstanceContext { OdsInstanceContextId = id }; - var mappedModel = new OdsInstanceContextModel { OdsInstanceContextId = id }; - - A.CallTo(() => fakeQuery.Execute(id)).Returns(queryResult); - A.CallTo(() => fakeMapper.Map(queryResult)).Returns(mappedModel); - - // Act - var result = await ReadOdsInstanceContext.GetOdsInstanceContext(fakeQuery, fakeMapper, id); - - // Assert - result.ShouldBeOfType>(); - A.CallTo(() => fakeQuery.Execute(id)).MustHaveHappenedOnceExactly(); + Should.NotThrow(() => readOdsInstanceContext.MapEndpoints(null!)); } } \ No newline at end of file diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs index 6d781073e..e72e7f0de 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs @@ -3,6 +3,7 @@ // 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.Linq; using EdFi.Admin.DataAccess.Contexts; using EdFi.Admin.DataAccess.Models; using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; @@ -17,159 +18,12 @@ namespace EdFi.Ods.AdminApi.UnitTests.Infrastructure.Database.Commands; [TestFixture] public class AddOdsInstanceContextCommandTests { - private IUsersContext _usersContext; - private AddOdsInstanceContextCommand _command; - private DbSet _odsInstances; - private DbSet _odsInstanceContexts; - - [SetUp] - public void SetUp() - { - _usersContext = A.Fake(); - _odsInstances = A.Fake>(); - _odsInstanceContexts = A.Fake>(); - - A.CallTo(() => _usersContext.OdsInstances).Returns(_odsInstances); - A.CallTo(() => _usersContext.OdsInstanceContexts).Returns(_odsInstanceContexts); - - _command = new AddOdsInstanceContextCommand(_usersContext); - } - - [Test] - public void Execute_WithValidModel_CreatesAndReturnsOdsInstanceContext() - { - // Arrange - var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; - var model = A.Fake(); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns("TestKey"); - A.CallTo(() => model.ContextValue).Returns("TestValue"); - - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(odsInstance); - - // Act - var result = _command.Execute(model); - - // Assert - result.ShouldNotBeNull(); - result.ContextKey.ShouldBe("TestKey"); - result.ContextValue.ShouldBe("TestValue"); - result.OdsInstance.ShouldBe(odsInstance); - - A.CallTo(() => _odsInstanceContexts.Add(A.That.Matches(x => - x.ContextKey == "TestKey" && - x.ContextValue == "TestValue" && - x.OdsInstance == odsInstance))) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); - } - - [Test] - public void Execute_WhenOdsInstanceNotFound_ThrowsNotFoundException() - { - // Arrange - var model = A.Fake(); - A.CallTo(() => model.OdsInstanceId).Returns(999); - A.CallTo(() => model.ContextKey).Returns("TestKey"); - A.CallTo(() => model.ContextValue).Returns("TestValue"); - - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(null); - - // Act & Assert - var exception = Should.Throw>(() => _command.Execute(model)); - exception.ResourceName.ShouldBe("odsInstance"); - exception.Id.ShouldBe(999); - } - - [Test] - public void Execute_WithNullContextKey_CreatesOdsInstanceContextWithNullKey() - { - // Arrange - var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; - var model = A.Fake(); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns(null); - A.CallTo(() => model.ContextValue).Returns("TestValue"); - - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(odsInstance); - - // Act - var result = _command.Execute(model); - - // Assert - result.ShouldNotBeNull(); - result.ContextKey.ShouldBeNull(); - result.ContextValue.ShouldBe("TestValue"); - result.OdsInstance.ShouldBe(odsInstance); - } - - [Test] - public void Execute_WithNullContextValue_CreatesOdsInstanceContextWithNullValue() - { - // Arrange - var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; - var model = A.Fake(); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns("TestKey"); - A.CallTo(() => model.ContextValue).Returns(null); - - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(odsInstance); - - // Act - var result = _command.Execute(model); - - // Assert - result.ShouldNotBeNull(); - result.ContextKey.ShouldBe("TestKey"); - result.ContextValue.ShouldBeNull(); - result.OdsInstance.ShouldBe(odsInstance); - } - - [Test] - public void Execute_WithZeroOdsInstanceId_ThrowsNotFoundException() - { - // Arrange - var model = A.Fake(); - A.CallTo(() => model.OdsInstanceId).Returns(0); - A.CallTo(() => model.ContextKey).Returns("TestKey"); - A.CallTo(() => model.ContextValue).Returns("TestValue"); - - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(null); - - // Act & Assert - var exception = Should.Throw>(() => _command.Execute(model)); - exception.ResourceName.ShouldBe("odsInstance"); - exception.Id.ShouldBe(0); - } - - [Test] - public void Execute_WhenSaveChangesFails_ExceptionIsPropagated() - { - // Arrange - var odsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; - var model = A.Fake(); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns("TestKey"); - A.CallTo(() => model.ContextValue).Returns("TestValue"); - - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(odsInstance); - A.CallTo(() => _usersContext.SaveChanges()).Throws(new System.Exception("Database error")); - - // Act & Assert - Should.Throw(() => _command.Execute(model)); - } - [Test] public void Constructor_WithValidContext_InitializesSuccessfully() { // Arrange & Act - var command = new AddOdsInstanceContextCommand(_usersContext); + var context = A.Fake(); + var command = new AddOdsInstanceContextCommand(context); // Assert command.ShouldNotBeNull(); diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs index 7496fbb3c..af1f357e3 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs @@ -3,6 +3,7 @@ // 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.Linq; using EdFi.Admin.DataAccess.Contexts; using EdFi.Admin.DataAccess.Models; using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; @@ -17,151 +18,12 @@ namespace EdFi.Ods.AdminApi.UnitTests.Infrastructure.Database.Commands; [TestFixture] public class DeleteOdsInstanceContextCommandTests { - private IUsersContext _usersContext; - private DeleteOdsInstanceContextCommand _command; - private DbSet _odsInstanceContexts; - - [SetUp] - public void SetUp() - { - _usersContext = A.Fake(); - _odsInstanceContexts = A.Fake>(); - - A.CallTo(() => _usersContext.OdsInstanceContexts).Returns(_odsInstanceContexts); - - _command = new DeleteOdsInstanceContextCommand(_usersContext); - } - - [Test] - public void Execute_WithValidId_RemovesOdsInstanceContext() - { - // Arrange - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "TestKey", - ContextValue = "TestValue" - }; - - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - - // Act - _command.Execute(1); - - // Assert - A.CallTo(() => _odsInstanceContexts.Remove(existingContext)).MustHaveHappenedOnceExactly(); - A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); - } - - [Test] - public void Execute_WhenOdsInstanceContextNotFound_ThrowsNotFoundException() - { - // Arrange - int nonExistentId = 999; - - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(null); - - // Act & Assert - var exception = Should.Throw>(() => _command.Execute(nonExistentId)); - exception.ResourceName.ShouldBe("odsInstanceContext"); - exception.Id.ShouldBe(nonExistentId); - } - - [Test] - public void Execute_WithZeroId_ThrowsNotFoundException() - { - // Arrange - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(null); - - // Act & Assert - var exception = Should.Throw>(() => _command.Execute(0)); - exception.ResourceName.ShouldBe("odsInstanceContext"); - exception.Id.ShouldBe(0); - } - - [Test] - public void Execute_WithNegativeId_ThrowsNotFoundException() - { - // Arrange - int negativeId = -1; - - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(null); - - // Act & Assert - var exception = Should.Throw>(() => _command.Execute(negativeId)); - exception.ResourceName.ShouldBe("odsInstanceContext"); - exception.Id.ShouldBe(negativeId); - } - - [Test] - public void Execute_WhenRemoveSucceeds_CallsSaveChanges() - { - // Arrange - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 123, - ContextKey = "TestKey", - ContextValue = "TestValue" - }; - - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - - // Act - _command.Execute(123); - - // Assert - A.CallTo(() => _odsInstanceContexts.Remove(existingContext)).MustHaveHappenedOnceExactly(); - A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); - } - - [Test] - public void Execute_WhenSaveChangesFails_ExceptionIsPropagated() - { - // Arrange - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "TestKey", - ContextValue = "TestValue" - }; - - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - A.CallTo(() => _usersContext.SaveChanges()).Throws(new System.Exception("Database error")); - - // Act & Assert - Should.Throw(() => _command.Execute(1)); - } - - [Test] - public void Execute_WhenRemoveFails_ExceptionIsPropagated() - { - // Arrange - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "TestKey", - ContextValue = "TestValue" - }; - - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - A.CallTo(() => _odsInstanceContexts.Remove(existingContext)).Throws(new System.Exception("Remove failed")); - - // Act & Assert - Should.Throw(() => _command.Execute(1)); - } - [Test] public void Constructor_WithValidContext_InitializesSuccessfully() { // Arrange & Act - var command = new DeleteOdsInstanceContextCommand(_usersContext); + var context = A.Fake(); + var command = new DeleteOdsInstanceContextCommand(context); // Assert command.ShouldNotBeNull(); diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs index 1c1565385..16d79ee4a 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs @@ -3,6 +3,7 @@ // 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.Linq; using EdFi.Admin.DataAccess.Contexts; using EdFi.Admin.DataAccess.Models; using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; @@ -17,223 +18,12 @@ namespace EdFi.Ods.AdminApi.UnitTests.Infrastructure.Database.Commands; [TestFixture] public class EditOdsInstanceContextCommandTests { - private IUsersContext _usersContext; - private EditOdsInstanceContextCommand _command; - private DbSet _odsInstances; - private DbSet _odsInstanceContexts; - - [SetUp] - public void SetUp() - { - _usersContext = A.Fake(); - _odsInstances = A.Fake>(); - _odsInstanceContexts = A.Fake>(); - - A.CallTo(() => _usersContext.OdsInstances).Returns(_odsInstances); - A.CallTo(() => _usersContext.OdsInstanceContexts).Returns(_odsInstanceContexts); - - _command = new EditOdsInstanceContextCommand(_usersContext); - } - - [Test] - public void Execute_WithValidModel_UpdatesAndReturnsOdsInstanceContext() - { - // Arrange - var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Original Instance" }; - var newOdsInstance = new OdsInstance { OdsInstanceId = 2, Name = "New Instance" }; - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "OriginalKey", - ContextValue = "OriginalValue", - OdsInstance = existingOdsInstance - }; - - var model = A.Fake(); - A.CallTo(() => model.Id).Returns(1); - A.CallTo(() => model.OdsInstanceId).Returns(2); - A.CallTo(() => model.ContextKey).Returns("UpdatedKey"); - A.CallTo(() => model.ContextValue).Returns("UpdatedValue"); - - A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) - .Returns(_odsInstanceContexts); - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(newOdsInstance); - - // Act - var result = _command.Execute(model); - - // Assert - result.ShouldNotBeNull(); - result.ShouldBe(existingContext); - result.ContextKey.ShouldBe("UpdatedKey"); - result.ContextValue.ShouldBe("UpdatedValue"); - result.OdsInstance.ShouldBe(newOdsInstance); - - A.CallTo(() => _usersContext.SaveChanges()).MustHaveHappenedOnceExactly(); - } - - [Test] - public void Execute_WhenOdsInstanceContextNotFound_ThrowsNotFoundException() - { - // Arrange - var model = A.Fake(); - A.CallTo(() => model.Id).Returns(999); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns("TestKey"); - A.CallTo(() => model.ContextValue).Returns("TestValue"); - - A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) - .Returns(_odsInstanceContexts); - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(null); - - // Act & Assert - var exception = Should.Throw>(() => _command.Execute(model)); - exception.ResourceName.ShouldBe("odsInstanceContext"); - exception.Id.ShouldBe(999); - } - - [Test] - public void Execute_WhenOdsInstanceNotFound_ThrowsNotFoundException() - { - // Arrange - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "OriginalKey", - ContextValue = "OriginalValue" - }; - - var model = A.Fake(); - A.CallTo(() => model.Id).Returns(1); - A.CallTo(() => model.OdsInstanceId).Returns(999); - A.CallTo(() => model.ContextKey).Returns("TestKey"); - A.CallTo(() => model.ContextValue).Returns("TestValue"); - - A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) - .Returns(_odsInstanceContexts); - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(null); - - // Act & Assert - var exception = Should.Throw>(() => _command.Execute(model)); - exception.ResourceName.ShouldBe("odsInstance"); - exception.Id.ShouldBe(999); - } - - [Test] - public void Execute_WithNullContextKey_UpdatesContextWithNullKey() - { - // Arrange - var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "OriginalKey", - ContextValue = "OriginalValue", - OdsInstance = existingOdsInstance - }; - - var model = A.Fake(); - A.CallTo(() => model.Id).Returns(1); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns(null); - A.CallTo(() => model.ContextValue).Returns("UpdatedValue"); - - A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) - .Returns(_odsInstanceContexts); - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(existingOdsInstance); - - // Act - var result = _command.Execute(model); - - // Assert - result.ShouldNotBeNull(); - result.ContextKey.ShouldBeNull(); - result.ContextValue.ShouldBe("UpdatedValue"); - result.OdsInstance.ShouldBe(existingOdsInstance); - } - - [Test] - public void Execute_WithNullContextValue_UpdatesContextWithNullValue() - { - // Arrange - var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "OriginalKey", - ContextValue = "OriginalValue", - OdsInstance = existingOdsInstance - }; - - var model = A.Fake(); - A.CallTo(() => model.Id).Returns(1); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns("UpdatedKey"); - A.CallTo(() => model.ContextValue).Returns(null); - - A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) - .Returns(_odsInstanceContexts); - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(existingOdsInstance); - - // Act - var result = _command.Execute(model); - - // Assert - result.ShouldNotBeNull(); - result.ContextKey.ShouldBe("UpdatedKey"); - result.ContextValue.ShouldBeNull(); - result.OdsInstance.ShouldBe(existingOdsInstance); - } - - [Test] - public void Execute_WhenSaveChangesFails_ExceptionIsPropagated() - { - // Arrange - var existingOdsInstance = new OdsInstance { OdsInstanceId = 1, Name = "Test Instance" }; - var existingContext = new OdsInstanceContext - { - OdsInstanceContextId = 1, - ContextKey = "OriginalKey", - ContextValue = "OriginalValue", - OdsInstance = existingOdsInstance - }; - - var model = A.Fake(); - A.CallTo(() => model.Id).Returns(1); - A.CallTo(() => model.OdsInstanceId).Returns(1); - A.CallTo(() => model.ContextKey).Returns("UpdatedKey"); - A.CallTo(() => model.ContextValue).Returns("UpdatedValue"); - - A.CallTo(() => _odsInstanceContexts.Include(A>>.Ignored)) - .Returns(_odsInstanceContexts); - A.CallTo(() => _odsInstanceContexts.SingleOrDefault(A>>.Ignored)) - .Returns(existingContext); - A.CallTo(() => _odsInstances.SingleOrDefault(A>>.Ignored)) - .Returns(existingOdsInstance); - A.CallTo(() => _usersContext.SaveChanges()).Throws(new System.Exception("Database error")); - - // Act & Assert - Should.Throw(() => _command.Execute(model)); - } - [Test] public void Constructor_WithValidContext_InitializesSuccessfully() { // Arrange & Act - var command = new EditOdsInstanceContextCommand(_usersContext); + var context = A.Fake(); + var command = new EditOdsInstanceContextCommand(context); // Assert command.ShouldNotBeNull(); From 58887bddef6259990b081b83812ecc6f44a33a43 Mon Sep 17 00:00:00 2001 From: "Stephen A. Fuqua" Date: Thu, 2 Oct 2025 08:59:28 -0500 Subject: [PATCH 5/7] Refined Copilot instructions --- .github/copilot-instructions.md | 4 +++- .../Database/Commands/AddOdsInstanceContextCommandTests.cs | 6 +----- .../Commands/DeleteOdsInstanceContextCommandTests.cs | 6 +----- .../Database/Commands/EditOdsInstanceContextCommandTests.cs | 6 +----- 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index bb067b12b..15f86ae2c 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,7 +3,7 @@ * Make only high confidence suggestions when reviewing code changes. * Never change NuGet.config files unless explicitly asked to. -## Formatting +## Formatting and Style * Apply code-formatting style defined in `.editorconfig`. * Prefer file-scoped namespace declarations and single-line using directives. @@ -11,6 +11,7 @@ * Ensure that the final return statement of a method is on its own line. * Use pattern matching and switch expressions wherever possible. * Use `nameof` instead of string literals when referring to member names. +* Delete unused `using` directives ### Nullable Reference Types @@ -27,4 +28,5 @@ ## Running tests +* Copilot should build and run unit tests after making changes * To build and run tests in the repo, use the command `./build.ps1 UnitTest` diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs index e72e7f0de..a6a434a7b 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/AddOdsInstanceContextCommandTests.cs @@ -3,13 +3,9 @@ // 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.Linq; using EdFi.Admin.DataAccess.Contexts; -using EdFi.Admin.DataAccess.Models; -using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using FakeItEasy; -using Microsoft.EntityFrameworkCore; using NUnit.Framework; using Shouldly; @@ -35,4 +31,4 @@ public void Constructor_WithNullContext_ShouldThrowArgumentNullException() // Act & Assert Should.Throw(() => new AddOdsInstanceContextCommand(null)); } -} \ No newline at end of file +} diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs index af1f357e3..5ef92c323 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommandTests.cs @@ -3,13 +3,9 @@ // 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.Linq; using EdFi.Admin.DataAccess.Contexts; -using EdFi.Admin.DataAccess.Models; -using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using FakeItEasy; -using Microsoft.EntityFrameworkCore; using NUnit.Framework; using Shouldly; @@ -35,4 +31,4 @@ public void Constructor_WithNullContext_ShouldThrowArgumentNullException() // Act & Assert Should.Throw(() => new DeleteOdsInstanceContextCommand(null)); } -} \ No newline at end of file +} diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs index 16d79ee4a..3ec09df76 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Infrastructure/Database/Commands/EditOdsInstanceContextCommandTests.cs @@ -3,13 +3,9 @@ // 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.Linq; using EdFi.Admin.DataAccess.Contexts; -using EdFi.Admin.DataAccess.Models; -using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using FakeItEasy; -using Microsoft.EntityFrameworkCore; using NUnit.Framework; using Shouldly; @@ -35,4 +31,4 @@ public void Constructor_WithNullContext_ShouldThrowArgumentNullException() // Act & Assert Should.Throw(() => new EditOdsInstanceContextCommand(null)); } -} \ No newline at end of file +} From 0f9028f42e6453cd2e4d9c05cb416b04bbe7d427 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:53:31 +0000 Subject: [PATCH 6/7] Corrections to the unit tests - Should not create fake validators - Need to setup the fake database calls --- .../AddOdsInstanceContextTests.cs | 41 ++++---- .../EditOdsInstanceContextTests.cs | 79 +++++++-------- .../ReadOdsInstanceContextTests.cs | 7 +- .../AddOdsInstanceContext.cs | 82 +++++++++++----- .../EditOdsInstanceContext.cs | 61 ++++++++---- .../Commands/AddOdsInstanceContextCommand.cs | 95 ++++++++++--------- .../DeleteOdsInstanceContextCommand.cs | 6 +- 7 files changed, 216 insertions(+), 155 deletions(-) diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs index 07c2a8055..834a74b7e 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/AddOdsInstanceContextTests.cs @@ -8,6 +8,7 @@ using AutoMapper; using EdFi.Admin.DataAccess.Models; using EdFi.Ods.AdminApi.Common.Infrastructure; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; using EdFi.Ods.AdminApi.Features.OdsInstanceContext; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; @@ -39,7 +40,7 @@ public void SetUp() public async Task Handle_ExecutesCommandAndReturnsCreated() { // Arrange - var validator = A.Fake(); + var command = A.Fake(); var mapper = A.Fake(); var request = new AddOdsInstanceContext.AddOdsInstanceContextRequest @@ -53,35 +54,34 @@ public async Task Handle_ExecutesCommandAndReturnsCreated() A.CallTo(() => command.Execute(request)).Returns(addedContext); // Act - var result = await AddOdsInstanceContext.Handle(validator, command, mapper, request); + var result = await AddOdsInstanceContext.Handle(_validator, command, mapper, request); // Assert - A.CallTo(() => validator.GuardAsync(request)).MustHaveHappenedOnceExactly(); A.CallTo(() => command.Execute(request)).MustHaveHappenedOnceExactly(); result.ShouldNotBeNull(); - result.ShouldBeOfType>(); + result.ShouldBeOfType(); } [Test] public void Handle_WhenValidationFails_ThrowsValidationException() { // Arrange - var validator = A.Fake(); + var command = A.Fake(); var mapper = A.Fake(); var request = new AddOdsInstanceContext.AddOdsInstanceContextRequest(); - A.CallTo(() => validator.GuardAsync(request)).Throws(new ValidationException("Validation failed")); - // Act & Assert - Should.Throw(async () => await AddOdsInstanceContext.Handle(validator, command, mapper, request)); + Should.Throw( + async () => await AddOdsInstanceContext.Handle(_validator, command, mapper, request) + ); } [Test] public void Handle_WhenCommandThrows_ExceptionIsPropagated() { // Arrange - var validator = A.Fake(); + var command = A.Fake(); var mapper = A.Fake(); var request = new AddOdsInstanceContext.AddOdsInstanceContextRequest(); @@ -89,7 +89,9 @@ public void Handle_WhenCommandThrows_ExceptionIsPropagated() A.CallTo(() => command.Execute(request)).Throws(new System.Exception("Command failed")); // Act & Assert - Should.Throw(async () => await AddOdsInstanceContext.Handle(validator, command, mapper, request)); + Should.Throw( + async () => await AddOdsInstanceContext.Handle(_validator, command, mapper, request) + ); } [Test] @@ -198,7 +200,8 @@ public void Validator_Should_Have_Error_When_OdsInstance_Does_Not_Exist() OdsInstanceId = 999 }; - A.CallTo(() => _getOdsInstanceQuery.Execute(999)).Throws(new System.Exception("OdsInstance not found")); + A.CallTo(() => _getOdsInstanceQuery.Execute(999)) + .Throws(new NotFoundException("odsInstance", 999)); // Act var result = _validator.Validate(model); @@ -295,12 +298,14 @@ public void BeAnExistingOdsInstance_Should_Return_True_When_OdsInstance_Exists() A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).Returns(new OdsInstance()); // Act - var result = _validator.Validate(new AddOdsInstanceContext.AddOdsInstanceContextRequest - { - ContextKey = "TestKey", - ContextValue = "TestValue", - OdsInstanceId = odsInstanceId - }); + var result = _validator.Validate( + new AddOdsInstanceContext.AddOdsInstanceContextRequest + { + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = odsInstanceId + } + ); // Assert - The validation should pass (assuming other fields are valid) A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).MustHaveHappenedOnceExactly(); @@ -334,4 +339,4 @@ public void BeUniqueCombinedKey_Should_Return_True_When_Key_Is_Case_Insensitive_ // Assert result.IsValid.ShouldBeTrue(); } -} \ No newline at end of file +} diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs index 527b188d5..59ff99a08 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/EditOdsInstanceContextTests.cs @@ -4,6 +4,7 @@ // See the LICENSE and NOTICES files in the project root for more information. using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using AutoMapper; using EdFi.Admin.DataAccess.Contexts; @@ -40,7 +41,6 @@ public void SetUp() public async Task Handle_ExecutesCommandAndReturnsOk() { // Arrange - var validator = A.Fake(); var command = A.Fake(); var mapper = A.Fake(); var db = A.Fake(); @@ -54,40 +54,49 @@ public async Task Handle_ExecutesCommandAndReturnsOk() var editedContext = new OdsInstanceContextEntity { OdsInstanceContextId = id }; A.CallTo(() => command.Execute(request)).Returns(editedContext); + A.CallTo(() => _getOdsInstanceQuery.Execute(request.OdsInstanceId)) + .Returns(new OdsInstance { OdsInstanceId = request.OdsInstanceId }); + A.CallTo(() => _getOdsInstanceContextsQuery.Execute()).Returns([]); // Act - var result = await EditOdsInstanceContext.Handle(validator, command, mapper, db, request, id); - - // Assert - request.Id.ShouldBe(id); - A.CallTo(() => validator.GuardAsync(request)).MustHaveHappenedOnceExactly(); - A.CallTo(() => command.Execute(request)).MustHaveHappenedOnceExactly(); - result.ShouldNotBeNull(); - result.ShouldBeOfType(); + try + { + var result = await EditOdsInstanceContext.Handle(_validator, command, mapper, db, request, id); + + // Assert + request.Id.ShouldBe(id); + A.CallTo(() => command.Execute(request)).MustHaveHappenedOnceExactly(); + result.ShouldNotBeNull(); + result.ShouldBeOfType(); + } + catch (ValidationException vex) + { + Assert.Fail(string.Join(";", vex.Errors.Select(e => e.ErrorMessage))); + } } [Test] public void Handle_WhenValidationFails_ThrowsValidationException() { // Arrange - var validator = A.Fake(); var command = A.Fake(); var mapper = A.Fake(); var db = A.Fake(); var request = new EditOdsInstanceContext.EditOdsInstanceContextRequest(); int id = 123; - A.CallTo(() => validator.GuardAsync(request)).Throws(new ValidationException("Validation failed")); + A.CallTo(() => _getOdsInstanceQuery.Execute(request.OdsInstanceId)).Returns(new OdsInstance()); // Act & Assert - Should.Throw(async () => await EditOdsInstanceContext.Handle(validator, command, mapper, db, request, id)); + Should.Throw( + async () => await EditOdsInstanceContext.Handle(_validator, command, mapper, db, request, id) + ); } [Test] public void Handle_WhenCommandThrows_ExceptionIsPropagated() { // Arrange - var validator = A.Fake(); var command = A.Fake(); var mapper = A.Fake(); var db = A.Fake(); @@ -97,7 +106,9 @@ public void Handle_WhenCommandThrows_ExceptionIsPropagated() A.CallTo(() => command.Execute(request)).Throws(new System.Exception("Command failed")); // Act & Assert - Should.Throw(async () => await EditOdsInstanceContext.Handle(validator, command, mapper, db, request, id)); + Should.Throw( + async () => await EditOdsInstanceContext.Handle(_validator, command, mapper, db, request, id) + ); } [Test] @@ -200,28 +211,6 @@ public void Validator_Should_Have_Error_When_OdsInstanceId_Is_Zero() result.Errors.ShouldContain(x => x.PropertyName == nameof(model.OdsInstanceId)); } - [Test] - public void Validator_Should_Have_Error_When_OdsInstance_Does_Not_Exist() - { - // Arrange - var model = new EditOdsInstanceContext.EditOdsInstanceContextRequest - { - Id = 1, - ContextKey = "TestKey", - ContextValue = "TestValue", - OdsInstanceId = 999 - }; - - A.CallTo(() => _getOdsInstanceQuery.Execute(999)).Throws(new System.Exception("OdsInstance not found")); - - // Act - var result = _validator.Validate(model); - - // Assert - result.IsValid.ShouldBeFalse(); - result.Errors.ShouldContain(x => x.PropertyName == nameof(model.OdsInstanceId)); - } - [Test] public void Validator_Should_Have_Error_When_Combined_Key_Is_Not_Unique() { @@ -345,13 +334,15 @@ public void BeAnExistingOdsInstance_Should_Return_True_When_OdsInstance_Exists() A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).Returns(new OdsInstance()); // Act - var result = _validator.Validate(new EditOdsInstanceContext.EditOdsInstanceContextRequest - { - Id = 1, - ContextKey = "TestKey", - ContextValue = "TestValue", - OdsInstanceId = odsInstanceId - }); + var result = _validator.Validate( + new EditOdsInstanceContext.EditOdsInstanceContextRequest + { + Id = 1, + ContextKey = "TestKey", + ContextValue = "TestValue", + OdsInstanceId = odsInstanceId + } + ); // Assert - The validation should pass (assuming other fields are valid) A.CallTo(() => _getOdsInstanceQuery.Execute(odsInstanceId)).MustHaveHappenedOnceExactly(); @@ -387,4 +378,4 @@ public void BeUniqueCombinedKey_Should_Return_True_When_Key_Is_Case_Insensitive_ // Assert result.IsValid.ShouldBeTrue(); } -} \ No newline at end of file +} diff --git a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs index 358cbe367..6ea228be5 100644 --- a/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs +++ b/Application/EdFi.Ods.AdminApi.UnitTests/Features/OdsInstanceContext/ReadOdsInstanceContextTests.cs @@ -6,6 +6,7 @@ using EdFi.Ods.AdminApi.Features.OdsInstanceContext; using NUnit.Framework; using Shouldly; +using System; namespace EdFi.Ods.AdminApi.UnitTests.Features.OdsInstanceContext; @@ -23,12 +24,12 @@ public void ReadOdsInstanceContext_ImplementsIFeature() } [Test] - public void MapEndpoints_DoesNotThrow() + public void MapEndpoints_DoesNotExceptNull() { // Arrange var readOdsInstanceContext = new ReadOdsInstanceContext(); // Act & Assert - Should.NotThrow(() => readOdsInstanceContext.MapEndpoints(null!)); + Should.Throw(() => readOdsInstanceContext.MapEndpoints(null!)); } -} \ No newline at end of file +} diff --git a/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/AddOdsInstanceContext.cs b/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/AddOdsInstanceContext.cs index dab366bfd..35bd862c4 100644 --- a/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/AddOdsInstanceContext.cs +++ b/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/AddOdsInstanceContext.cs @@ -4,13 +4,15 @@ // See the LICENSE and NOTICES files in the project root for more information. using AutoMapper; -using FluentValidation; -using Swashbuckle.AspNetCore.Annotations; +using EdFi.Ods.AdminApi.Common.Features; +using EdFi.Ods.AdminApi.Common.Infrastructure; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; using EdFi.Ods.AdminApi.Infrastructure; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; -using EdFi.Ods.AdminApi.Common.Features; -using EdFi.Ods.AdminApi.Common.Infrastructure; +using FluentValidation; +using Swashbuckle.AspNetCore.Annotations; + namespace EdFi.Ods.AdminApi.Features.OdsInstanceContext; public class AddOdsInstanceContext : IFeature @@ -18,28 +20,43 @@ public class AddOdsInstanceContext : IFeature public void MapEndpoints(IEndpointRouteBuilder endpoints) { AdminApiEndpointBuilder - .MapPost(endpoints, "/odsInstanceContexts", Handle) - .WithDefaultSummaryAndDescription() - .WithRouteOptions(b => b.WithResponseCode(201)) - .BuildForVersions(AdminApiVersions.V2); + .MapPost(endpoints, "/odsInstanceContexts", Handle) + .WithDefaultSummaryAndDescription() + .WithRouteOptions(b => b.WithResponseCode(201)) + .BuildForVersions(AdminApiVersions.V2); } - public static async Task Handle(Validator validator, IAddOdsInstanceContextCommand addOdsInstanceContextCommand, IMapper mapper, AddOdsInstanceContextRequest request) + public static async Task Handle( + Validator validator, + IAddOdsInstanceContextCommand addOdsInstanceContextCommand, + IMapper mapper, + AddOdsInstanceContextRequest request + ) { await validator.GuardAsync(request); var addedOdsInstanceContext = addOdsInstanceContextCommand.Execute(request); return Results.Created($"/odsInstanceContexts/{addedOdsInstanceContext.OdsInstanceContextId}", null); } - [SwaggerSchema(Title = "AddOdsInstanceContextRequest")] public class AddOdsInstanceContextRequest : IAddOdsInstanceContextModel { - [SwaggerSchema(Description = FeatureConstants.OdsInstanceContextOdsInstanceIdDescription, Nullable = false)] + [SwaggerSchema( + Description = FeatureConstants.OdsInstanceContextOdsInstanceIdDescription, + Nullable = false + )] public int OdsInstanceId { get; set; } - [SwaggerSchema(Description = FeatureConstants.OdsInstanceContextContextKeyDescription, Nullable = false)] + + [SwaggerSchema( + Description = FeatureConstants.OdsInstanceContextContextKeyDescription, + Nullable = false + )] public string? ContextKey { get; set; } - [SwaggerSchema(Description = FeatureConstants.OdsInstanceContextContextValueDescription, Nullable = false)] + + [SwaggerSchema( + Description = FeatureConstants.OdsInstanceContextContextValueDescription, + Nullable = false + )] public string? ContextValue { get; set; } } @@ -48,7 +65,10 @@ public class Validator : AbstractValidator private readonly IGetOdsInstanceQuery _getOdsInstanceQuery; private readonly IGetOdsInstanceContextsQuery _getOdsInstanceContextsQuery; - public Validator(IGetOdsInstanceQuery getOdsInstanceQuery, IGetOdsInstanceContextsQuery getOdsInstanceContextsQuery) + public Validator( + IGetOdsInstanceQuery getOdsInstanceQuery, + IGetOdsInstanceContextsQuery getOdsInstanceContextsQuery + ) { _getOdsInstanceQuery = getOdsInstanceQuery; _getOdsInstanceContextsQuery = getOdsInstanceContextsQuery; @@ -60,27 +80,39 @@ public Validator(IGetOdsInstanceQuery getOdsInstanceQuery, IGetOdsInstanceContex .NotEqual(0) .WithMessage(FeatureConstants.OdsInstanceIdValidationMessage); - RuleFor(m => m.OdsInstanceId) - .Must(BeAnExistingOdsInstance) - .When(m => !m.OdsInstanceId.Equals(0)); + RuleFor(m => m.OdsInstanceId).Must(BeAnExistingOdsInstance).When(m => !m.OdsInstanceId.Equals(0)); RuleFor(odsContext => odsContext) - .Must(BeUniqueCombinedKey) - .WithMessage(FeatureConstants.OdsInstanceContextCombinedKeyMustBeUnique); - + .Must(BeUniqueCombinedKey) + .WithMessage(FeatureConstants.OdsInstanceContextCombinedKeyMustBeUnique); } private bool BeAnExistingOdsInstance(int id) { - _getOdsInstanceQuery.Execute(id); - return true; + try + { + _getOdsInstanceQuery.Execute(id); + return true; + } + catch (NotFoundException) + { + return false; + } + catch (Exception ex) + { + System.Console.WriteLine(ex); + return false; + } } private bool BeUniqueCombinedKey(AddOdsInstanceContextRequest request) { - return !_getOdsInstanceContextsQuery.Execute().Exists - (x => x.OdsInstance?.OdsInstanceId == request.OdsInstanceId && - x.ContextKey.Equals(request.ContextKey, StringComparison.OrdinalIgnoreCase)); + return !_getOdsInstanceContextsQuery + .Execute() + .Exists(x => + x.OdsInstance?.OdsInstanceId == request.OdsInstanceId + && x.ContextKey.Equals(request.ContextKey, StringComparison.OrdinalIgnoreCase) + ); } } } diff --git a/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/EditOdsInstanceContext.cs b/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/EditOdsInstanceContext.cs index 34a7d63cd..0d7f63350 100644 --- a/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/EditOdsInstanceContext.cs +++ b/Application/EdFi.Ods.AdminApi/Features/OdsInstanceContext/EditOdsInstanceContext.cs @@ -7,7 +7,7 @@ using EdFi.Admin.DataAccess.Contexts; using EdFi.Ods.AdminApi.Common.Features; using EdFi.Ods.AdminApi.Common.Infrastructure; -using EdFi.Ods.AdminApi.Infrastructure; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; using EdFi.Ods.AdminApi.Infrastructure.Database.Commands; using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; using EdFi.Ods.AdminApi.Infrastructure.Documentation; @@ -27,7 +27,14 @@ public void MapEndpoints(IEndpointRouteBuilder endpoints) .BuildForVersions(AdminApiVersions.V2); } - public static async Task Handle(Validator validator, IEditOdsInstanceContextCommand editOdsInstanceContextCommand, IMapper mapper, IUsersContext db, EditOdsInstanceContextRequest request, int id) + public static async Task Handle( + Validator validator, + IEditOdsInstanceContextCommand editOdsInstanceContextCommand, + IMapper mapper, + IUsersContext db, + EditOdsInstanceContextRequest request, + int id + ) { request.Id = id; await validator.GuardAsync(request); @@ -35,18 +42,29 @@ public static async Task Handle(Validator validator, IEditOdsInstanceCo return Results.Ok(); } - [SwaggerSchema(Title = "EditOdsInstanceContextRequest")] public class EditOdsInstanceContextRequest : IEditOdsInstanceContextModel { [SwaggerExclude] [SwaggerSchema(Description = FeatureConstants.OdsInstanceContextIdDescription, Nullable = false)] public int Id { get; set; } - [SwaggerSchema(Description = FeatureConstants.OdsInstanceContextOdsInstanceIdDescription, Nullable = false)] + + [SwaggerSchema( + Description = FeatureConstants.OdsInstanceContextOdsInstanceIdDescription, + Nullable = false + )] public int OdsInstanceId { get; set; } - [SwaggerSchema(Description = FeatureConstants.OdsInstanceContextContextKeyDescription, Nullable = false)] + + [SwaggerSchema( + Description = FeatureConstants.OdsInstanceContextContextKeyDescription, + Nullable = false + )] public string? ContextKey { get; set; } - [SwaggerSchema(Description = FeatureConstants.OdsInstanceContextContextValueDescription, Nullable = false)] + + [SwaggerSchema( + Description = FeatureConstants.OdsInstanceContextContextValueDescription, + Nullable = false + )] public string? ContextValue { get; set; } } @@ -55,7 +73,10 @@ public class Validator : AbstractValidator private readonly IGetOdsInstanceQuery _getOdsInstanceQuery; private readonly IGetOdsInstanceContextsQuery _getOdsInstanceContextsQuery; - public Validator(IGetOdsInstanceQuery getOdsInstanceQuery, IGetOdsInstanceContextsQuery getOdsInstanceContextsQuery) + public Validator( + IGetOdsInstanceQuery getOdsInstanceQuery, + IGetOdsInstanceContextsQuery getOdsInstanceContextsQuery + ) { _getOdsInstanceQuery = getOdsInstanceQuery; _getOdsInstanceContextsQuery = getOdsInstanceContextsQuery; @@ -67,9 +88,7 @@ public Validator(IGetOdsInstanceQuery getOdsInstanceQuery, IGetOdsInstanceContex .NotEqual(0) .WithMessage(FeatureConstants.OdsInstanceIdValidationMessage); - RuleFor(m => m.OdsInstanceId) - .Must(BeAnExistingOdsInstance) - .When(m => !m.OdsInstanceId.Equals(0)); + RuleFor(m => m.OdsInstanceId).Must(BeAnExistingOdsInstance).When(m => !m.OdsInstanceId.Equals(0)); RuleFor(odsContext => odsContext) .Must(BeUniqueCombinedKey) @@ -78,16 +97,26 @@ public Validator(IGetOdsInstanceQuery getOdsInstanceQuery, IGetOdsInstanceContex private bool BeAnExistingOdsInstance(int id) { - _getOdsInstanceQuery.Execute(id); - return true; + try + { + _getOdsInstanceQuery.Execute(id); + return true; + } + catch (NotFoundException) + { + return false; + } } private bool BeUniqueCombinedKey(EditOdsInstanceContextRequest request) { - return !_getOdsInstanceContextsQuery.Execute().Exists - (x => x.OdsInstance?.OdsInstanceId == request.OdsInstanceId && - x.ContextKey.Equals(request.ContextKey, StringComparison.OrdinalIgnoreCase) && - x.OdsInstanceContextId != request.Id); + return !_getOdsInstanceContextsQuery + .Execute() + .Exists(x => + x.OdsInstance?.OdsInstanceId == request.OdsInstanceId + && x.ContextKey.Equals(request.ContextKey, StringComparison.OrdinalIgnoreCase) + && x.OdsInstanceContextId != request.Id + ); } } } diff --git a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/AddOdsInstanceContextCommand.cs b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/AddOdsInstanceContextCommand.cs index 7af2cebe4..b09048b2a 100644 --- a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/AddOdsInstanceContextCommand.cs +++ b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/AddOdsInstanceContextCommand.cs @@ -1,48 +1,49 @@ -// 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 EdFi.Admin.DataAccess.Contexts; -using EdFi.Admin.DataAccess.Models; -using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; - -namespace EdFi.Ods.AdminApi.Infrastructure.Database.Commands; - -public interface IAddOdsInstanceContextCommand -{ - OdsInstanceContext Execute(IAddOdsInstanceContextModel newOdsInstanceContext); -} - -public class AddOdsInstanceContextCommand : IAddOdsInstanceContextCommand -{ - private readonly IUsersContext _context; - - public AddOdsInstanceContextCommand(IUsersContext context) - { - _context = context; - } - - public OdsInstanceContext Execute(IAddOdsInstanceContextModel newOdsInstanceContext) +// 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 EdFi.Admin.DataAccess.Contexts; +using EdFi.Admin.DataAccess.Models; +using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; + +namespace EdFi.Ods.AdminApi.Infrastructure.Database.Commands; + +public interface IAddOdsInstanceContextCommand +{ + OdsInstanceContext Execute(IAddOdsInstanceContextModel newOdsInstanceContext); +} + +public class AddOdsInstanceContextCommand : IAddOdsInstanceContextCommand +{ + private readonly IUsersContext _context; + + public AddOdsInstanceContextCommand(IUsersContext context) { - var odsInstance = _context.OdsInstances.SingleOrDefault(v => v.OdsInstanceId == newOdsInstanceContext.OdsInstanceId) ?? - throw new NotFoundException("odsInstance", newOdsInstanceContext.OdsInstanceId); - - var odsInstanceContext = new OdsInstanceContext - { - ContextKey = newOdsInstanceContext.ContextKey, - ContextValue = newOdsInstanceContext.ContextValue, - OdsInstance = odsInstance - }; - _context.OdsInstanceContexts.Add(odsInstanceContext); - _context.SaveChanges(); - return odsInstanceContext; - } -} - -public interface IAddOdsInstanceContextModel -{ - public int OdsInstanceId { get; set; } - public string? ContextKey { get; set; } - public string? ContextValue { get; set; } -} + _context = context ?? throw new ArgumentNullException(nameof(context)); + } + + public OdsInstanceContext Execute(IAddOdsInstanceContextModel newOdsInstanceContext) + { + var odsInstance = + _context.OdsInstances.SingleOrDefault(v => v.OdsInstanceId == newOdsInstanceContext.OdsInstanceId) + ?? throw new NotFoundException("odsInstance", newOdsInstanceContext.OdsInstanceId); + + var odsInstanceContext = new OdsInstanceContext + { + ContextKey = newOdsInstanceContext.ContextKey, + ContextValue = newOdsInstanceContext.ContextValue, + OdsInstance = odsInstance + }; + _context.OdsInstanceContexts.Add(odsInstanceContext); + _context.SaveChanges(); + return odsInstanceContext; + } +} + +public interface IAddOdsInstanceContextModel +{ + int OdsInstanceId { get; set; } + string? ContextKey { get; set; } + string? ContextValue { get; set; } +} diff --git a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommand.cs b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommand.cs index d37d7d2cd..8832d774c 100644 --- a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommand.cs +++ b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/DeleteOdsInstanceContextCommand.cs @@ -19,12 +19,14 @@ public class DeleteOdsInstanceContextCommand : IDeleteOdsInstanceContextCommand public DeleteOdsInstanceContextCommand(IUsersContext context) { - _context = context; + _context = context ?? throw new ArgumentNullException(nameof(context)); } public void Execute(int id) { - var odsInstanceContext = _context.OdsInstanceContexts.SingleOrDefault(v => v.OdsInstanceContextId == id) ?? throw new NotFoundException("odsInstanceContext", id); + var odsInstanceContext = + _context.OdsInstanceContexts.SingleOrDefault(v => v.OdsInstanceContextId == id) + ?? throw new NotFoundException("odsInstanceContext", id); _context.OdsInstanceContexts.Remove(odsInstanceContext); _context.SaveChanges(); } From f44bb084b151b60f620da1882f73614e2b1d38b4 Mon Sep 17 00:00:00 2001 From: "Stephen A. Fuqua" Date: Thu, 2 Oct 2025 14:43:07 -0500 Subject: [PATCH 7/7] Add a null check and reformat --- .../Commands/EditOdsInstanceContextCommand.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/EditOdsInstanceContextCommand.cs b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/EditOdsInstanceContextCommand.cs index 2fa77e3b5..a54be3aa2 100644 --- a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/EditOdsInstanceContextCommand.cs +++ b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Commands/EditOdsInstanceContextCommand.cs @@ -6,8 +6,8 @@ using EdFi.Admin.DataAccess.Contexts; using EdFi.Admin.DataAccess.Models; using EdFi.Ods.AdminApi.Common.Infrastructure.ErrorHandling; -using Microsoft.EntityFrameworkCore; - +using Microsoft.EntityFrameworkCore; + namespace EdFi.Ods.AdminApi.Infrastructure.Database.Commands; public interface IEditOdsInstanceContextCommand @@ -21,17 +21,20 @@ public class EditOdsInstanceContextCommand : IEditOdsInstanceContextCommand public EditOdsInstanceContextCommand(IUsersContext context) { - _context = context; + _context = context ?? throw new ArgumentNullException(nameof(context)); } public OdsInstanceContext Execute(IEditOdsInstanceContextModel changedOdsInstanceContextData) { - var odsInstanceContext = _context.OdsInstanceContexts - .Include(oid => oid.OdsInstance) - .SingleOrDefault(v => v.OdsInstanceContextId == changedOdsInstanceContextData.Id) ?? - throw new NotFoundException("odsInstanceContext", changedOdsInstanceContextData.Id); - var odsInstance = _context.OdsInstances.SingleOrDefault(v => v.OdsInstanceId == changedOdsInstanceContextData.OdsInstanceId) ?? - throw new NotFoundException("odsInstance", changedOdsInstanceContextData.OdsInstanceId); + var odsInstanceContext = + _context + .OdsInstanceContexts.Include(oid => oid.OdsInstance) + .SingleOrDefault(v => v.OdsInstanceContextId == changedOdsInstanceContextData.Id) + ?? throw new NotFoundException("odsInstanceContext", changedOdsInstanceContextData.Id); + var odsInstance = + _context.OdsInstances.SingleOrDefault(v => + v.OdsInstanceId == changedOdsInstanceContextData.OdsInstanceId + ) ?? throw new NotFoundException("odsInstance", changedOdsInstanceContextData.OdsInstanceId); odsInstanceContext.ContextKey = changedOdsInstanceContextData.ContextKey; odsInstanceContext.OdsInstance = odsInstance; @@ -44,8 +47,8 @@ public OdsInstanceContext Execute(IEditOdsInstanceContextModel changedOdsInstanc public interface IEditOdsInstanceContextModel { - public int Id { get; set; } - public int OdsInstanceId { get; set; } - public string? ContextKey { get; set; } - public string? ContextValue { get; set; } + int Id { get; set; } + int OdsInstanceId { get; set; } + string? ContextKey { get; set; } + string? ContextValue { get; set; } }