diff --git a/Application/EdFi.Admin.DataAccess/Contexts/SqlServerUsersContext.cs b/Application/EdFi.Admin.DataAccess/Contexts/SqlServerUsersContext.cs index 7b64c56872..67d9a1d135 100644 --- a/Application/EdFi.Admin.DataAccess/Contexts/SqlServerUsersContext.cs +++ b/Application/EdFi.Admin.DataAccess/Contexts/SqlServerUsersContext.cs @@ -10,6 +10,5 @@ namespace EdFi.Admin.DataAccess.Contexts public class SqlServerUsersContext : UsersContext { public SqlServerUsersContext(DbContextOptions options) : base(options) { } - } } diff --git a/Application/EdFi.Admin.DataAccess/Contexts/UsersContext.cs b/Application/EdFi.Admin.DataAccess/Contexts/UsersContext.cs index 376eab2836..20b585a2a9 100644 --- a/Application/EdFi.Admin.DataAccess/Contexts/UsersContext.cs +++ b/Application/EdFi.Admin.DataAccess/Contexts/UsersContext.cs @@ -74,6 +74,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) r => r.HasOne().WithMany().HasForeignKey("ApiClientId")); + modelBuilder.Entity() + .HasMany(a => a.Profiles) + .WithMany(a => a.Applications) + .UsingEntity("ProfileApplications"); + modelBuilder.UseUnderscoredFkColumnNames(); modelBuilder.Model.FindEntityTypes(typeof(ApiClient)).First().GetProperty("CreatorOwnershipTokenId") diff --git a/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.csproj b/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.csproj index 08428eccb7..d066e5586e 100644 --- a/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.csproj +++ b/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.csproj @@ -16,8 +16,9 @@ true - + + diff --git a/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.sln b/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.sln index 1e2c3585fc..076e99bcbb 100644 --- a/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.sln +++ b/Application/EdFi.Admin.DataAccess/EdFi.Admin.DataAccess.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.960 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.Admin.DataAccess", "EdFi.Admin.DataAccess.csproj", "{D2A9D9A2-A4DB-4E65-82C3-C7E9F6DBA1E2}" EndProject @@ -9,6 +9,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.Admin.DataAccess.UnitT EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.Common", "..\EdFi.Common\EdFi.Common.csproj", "{92A9A0CD-0AE6-433D-9673-EB88E52D88C4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.Admin.DataAccess.IntegrationTests", "..\..\tests\EdFi.Admin.DataAccess.IntegrationTests\EdFi.Admin.DataAccess.IntegrationTests.csproj", "{DE3AD0EB-ECDF-47EB-AAE4-C4CA65674652}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test Support", "Test Support", "{A0B2E4A0-067E-4558-9412-79A4EA5E5D30}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdFi.TestFixture", "..\..\tests\EdFi.TestFixture\EdFi.TestFixture.csproj", "{AF2CCF4F-21AA-4680-B2B9-2A96796933AE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,10 +33,21 @@ Global {92A9A0CD-0AE6-433D-9673-EB88E52D88C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {92A9A0CD-0AE6-433D-9673-EB88E52D88C4}.Release|Any CPU.ActiveCfg = Release|Any CPU {92A9A0CD-0AE6-433D-9673-EB88E52D88C4}.Release|Any CPU.Build.0 = Release|Any CPU + {DE3AD0EB-ECDF-47EB-AAE4-C4CA65674652}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE3AD0EB-ECDF-47EB-AAE4-C4CA65674652}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE3AD0EB-ECDF-47EB-AAE4-C4CA65674652}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE3AD0EB-ECDF-47EB-AAE4-C4CA65674652}.Release|Any CPU.Build.0 = Release|Any CPU + {AF2CCF4F-21AA-4680-B2B9-2A96796933AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF2CCF4F-21AA-4680-B2B9-2A96796933AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF2CCF4F-21AA-4680-B2B9-2A96796933AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF2CCF4F-21AA-4680-B2B9-2A96796933AE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AF2CCF4F-21AA-4680-B2B9-2A96796933AE} = {A0B2E4A0-067E-4558-9412-79A4EA5E5D30} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E7C6FBAA-92CE-4FEB-BFCC-F547FF6D40FD} EndGlobalSection diff --git a/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs b/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs index 449b967df3..6c8db048ec 100644 --- a/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs +++ b/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs @@ -16,6 +16,7 @@ namespace EdFi.Admin.DataAccess.Models /// Class representing EdFi client application information persisted in a data store. /// A Client has a list of domains that are valid for access /// + [Table("ApiClients")] public class ApiClient { public ApiClient() @@ -125,9 +126,12 @@ public string SandboxTypeName public int? ActivationRetried { get; set; } /// - /// Have a reference to OwnershipToken table ownershiptokenid for specific apiclient. - /// - public virtual OwnershipToken CreatorOwnershipTokenId { get; set; } + /// Have a reference to OwnershipToken table ownershiptokenid for specific apiclient. + /// + public virtual OwnershipToken CreatorOwnershipToken { get; set; } + + [Column("CreatorOwnershipTokenId_OwnershipTokenId")] + public short? CreatorOwnershipTokenId { get; set; } /// /// Fully namespaced URI reference to the StudentIdentificationSystemDescriptor value to use for identification mapping @@ -150,6 +154,7 @@ public string SandboxTypeName /// EdOrg is the key, Domain Connection information is the value. /// The end-user should never see the Data Connection information /// + [NotMapped] public Dictionary Domains { get; set; } /// diff --git a/Application/EdFi.Admin.DataAccess/Models/ApiClientOwnershipToken.cs b/Application/EdFi.Admin.DataAccess/Models/ApiClientOwnershipToken.cs index cf19d96d1b..64a2b9b8df 100644 --- a/Application/EdFi.Admin.DataAccess/Models/ApiClientOwnershipToken.cs +++ b/Application/EdFi.Admin.DataAccess/Models/ApiClientOwnershipToken.cs @@ -21,12 +21,8 @@ public class ApiClientOwnershipToken [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ApiClientOwnershipTokenId { get; set; } - [Required] - [Column("OwnershipToken_OwnershipTokenId")] - public OwnershipToken OwnershipToken { get; set; } + public virtual ApiClient ApiClient { get; set; } - [Required] - [Column("ApiClient_ApiClientId")] - public ApiClient ApiClient { get; set; } + public virtual OwnershipToken OwnershipToken { get; set; } } -} +} \ No newline at end of file diff --git a/Application/EdFi.Admin.DataAccess/Models/ApplicationEducationOrganization.cs b/Application/EdFi.Admin.DataAccess/Models/ApplicationEducationOrganization.cs index 4c09181bc9..30a5629aae 100644 --- a/Application/EdFi.Admin.DataAccess/Models/ApplicationEducationOrganization.cs +++ b/Application/EdFi.Admin.DataAccess/Models/ApplicationEducationOrganization.cs @@ -10,7 +10,7 @@ namespace EdFi.Admin.DataAccess.Models { - public sealed class ApplicationEducationOrganization + public class ApplicationEducationOrganization { public ApplicationEducationOrganization() { @@ -21,10 +21,10 @@ public ApplicationEducationOrganization() [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ApplicationEducationOrganizationId { get; set; } - public Application Application { get; set; } + public virtual Application Application { get; set; } public int EducationOrganizationId { get; set; } - public ICollection Clients { get; set; } + public virtual ICollection Clients { get; set; } } } diff --git a/Application/EdFi.Admin.DataAccess/Models/ClientAccessToken.cs b/Application/EdFi.Admin.DataAccess/Models/ClientAccessToken.cs index 307f53095b..9966045dd6 100644 --- a/Application/EdFi.Admin.DataAccess/Models/ClientAccessToken.cs +++ b/Application/EdFi.Admin.DataAccess/Models/ClientAccessToken.cs @@ -36,7 +36,7 @@ public Guid Id set { _id = value; } } - public ApiClient ApiClient { get; set; } + public virtual ApiClient ApiClient { get; set; } public DateTime Expiration { get; set; } diff --git a/Application/EdFi.Admin.DataAccess/Models/OwnershipToken.cs b/Application/EdFi.Admin.DataAccess/Models/OwnershipToken.cs index 6adaa9267e..65b27a4a7c 100644 --- a/Application/EdFi.Admin.DataAccess/Models/OwnershipToken.cs +++ b/Application/EdFi.Admin.DataAccess/Models/OwnershipToken.cs @@ -30,6 +30,7 @@ public OwnershipToken() /// [StringLength(50)] public string Description { get; set; } + public virtual ICollection ApiClients { get; set; } public ICollection Clients { get; set; } } diff --git a/Application/EdFi.Admin.DataAccess/Models/VendorNamespacePrefix.cs b/Application/EdFi.Admin.DataAccess/Models/VendorNamespacePrefix.cs index 627b9591f7..1bc8adbde4 100644 --- a/Application/EdFi.Admin.DataAccess/Models/VendorNamespacePrefix.cs +++ b/Application/EdFi.Admin.DataAccess/Models/VendorNamespacePrefix.cs @@ -8,14 +8,14 @@ namespace EdFi.Admin.DataAccess.Models { - public sealed class VendorNamespacePrefix + public class VendorNamespacePrefix { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int VendorNamespacePrefixId { get; set; } [Required] - public Vendor Vendor { get; set; } + public virtual Vendor Vendor { get; set; } [Required] [StringLength(255)] diff --git a/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs b/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs index 0f7cd1e9fd..5d24486937 100644 --- a/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs +++ b/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs @@ -117,8 +117,7 @@ public IEnumerable GetUsers() { using (var context = _contextFactory.CreateContext()) { - return context.Users.Include(u => u.ApiClients.Select(ac => ac.Application)) - .ToList(); + return context.Users.Include(u => u.ApiClients).ThenInclude(ac => ac.Application).ToList(); } } @@ -127,7 +126,7 @@ public User GetUser(int userId) using (var context = _contextFactory.CreateContext()) { return - context.Users.Include(u => u.ApiClients.Select(ac => ac.Application)) + context.Users.Include(u => u.ApiClients).ThenInclude(ac => ac.Application) .FirstOrDefault(u => u.UserId == userId); } } @@ -137,7 +136,7 @@ public User GetUser(string userName) using (var context = _contextFactory.CreateContext()) { return - context.Users.Include(u => u.ApiClients.Select(ac => ac.Application)) + context.Users.Include(u => u.ApiClients).ThenInclude(a => a.Application) .Include(u => u.Vendor) .FirstOrDefault(x => x.Email == userName); } @@ -148,7 +147,7 @@ public void DeleteUser(User userProfile) using (var context = _contextFactory.CreateContext()) { var user = - context.Users.Include(u => u.ApiClients.Select(ac => ac.Application)) + context.Users.Include(u => u.ApiClients).ThenInclude(ac => ac.Application) .FirstOrDefault(x => x.UserId == userProfile.UserId); if (user == null) @@ -172,12 +171,14 @@ public ApiClient GetClient(string key) { using (var context = _contextFactory.CreateContext()) { - return context.Clients.Include(c => c.Application) - .Include(c => c.Application.Vendor) - .Include(c => c.Application.Vendor.VendorNamespacePrefixes) - .Include(c => c.Application.Profiles) + return context.Clients + .Include(c => c.Application) + .ThenInclude(c => c.Vendor) + .ThenInclude(c => c.VendorNamespacePrefixes) + .Include(c => c.Application) + .ThenInclude(c => c.Profiles) .Include(c => c.ApplicationEducationOrganizations) - .Include(c => c.CreatorOwnershipTokenId) + .Include(c => c.CreatorOwnershipToken) .FirstOrDefault(c => c.Key == key); } } @@ -186,10 +187,12 @@ public async Task GetClientAsync(string key) { using (var context = _contextFactory.CreateContext()) { - return await context.Clients.Include(c => c.Application) - .Include(c => c.Application.Vendor) - .Include(c => c.Application.Vendor.VendorNamespacePrefixes) - .Include(c => c.Application.Profiles) + return await context.Clients + .Include(c => c.Application) + .ThenInclude(c => c.Vendor) + .ThenInclude(c => c.VendorNamespacePrefixes) + .Include(c => c.Application) + .ThenInclude(c => c.Profiles) .Include(c => c.ApplicationEducationOrganizations) .Include(c => c.CreatorOwnershipTokenId) .FirstOrDefaultAsync(c => c.Key == key); @@ -220,11 +223,6 @@ public void DeleteClient(string key) { var client = context.Clients.First(x => x.Key == key); - // TODO SF: AA-518 - // Assuming that this is used by Admin App, although that will not actually be clear - // until we are able to start testing Admin App thoroughly. - // Convert this to ANSI SQL for PostgreSql support and don't use a SqlParameter. - // Be sure to write integration tests in project EdFi.Ods.Admin.Models.IntegrationTests. context.ExecuteSqlCommandAsync( @"delete from dbo.ClientAccessTokens where ApiClient_ApiClientId = @p0; delete from dbo.ApiClients where ApiClientId = @p0", client.ApiClientId).Wait(); diff --git a/Application/EdFi.Admin.DataAccess/Utils/DefaultApplicationCreator.cs b/Application/EdFi.Admin.DataAccess/Utils/DefaultApplicationCreator.cs index 8d615ddb0c..056dbbfcaa 100644 --- a/Application/EdFi.Admin.DataAccess/Utils/DefaultApplicationCreator.cs +++ b/Application/EdFi.Admin.DataAccess/Utils/DefaultApplicationCreator.cs @@ -39,7 +39,7 @@ public Application FindOrCreateUpdatedDefaultSandboxApplication(int vendorId, Sa { var vendor = context.Vendors .Where(x => x.VendorId == vendorId) - .Include(x => x.Applications.Select>(a => a.ApplicationEducationOrganizations)) + .Include(x => x.Applications).ThenInclude(x => x.ApplicationEducationOrganizations) .Single(); var defaultAppName = _configuration.GetSection("DefaultApplicationName").Value ?? "Default Sandbox Application"; diff --git a/Application/EdFi.Admin.DataAccess/Utils/EdFiOdsConnectionStringBuilder.cs b/Application/EdFi.Admin.DataAccess/Utils/EdFiOdsConnectionStringBuilder.cs index 6ac3ce0ff5..676049f911 100644 --- a/Application/EdFi.Admin.DataAccess/Utils/EdFiOdsConnectionStringBuilder.cs +++ b/Application/EdFi.Admin.DataAccess/Utils/EdFiOdsConnectionStringBuilder.cs @@ -3,10 +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; using System.Configuration; -using System.Data.SqlClient; using EdFi.Common.Extensions; +using Microsoft.Data.SqlClient; namespace EdFi.Admin.DataAccess.Utils { diff --git a/Application/EdFi.Ods.Tests/EdFi.Ods.Sandbox/Repositories/ClientAppRepoTests.cs b/Application/EdFi.Ods.Tests/EdFi.Ods.Sandbox/Repositories/ClientAppRepoTests.cs deleted file mode 100644 index 7443ea49ee..0000000000 --- a/Application/EdFi.Ods.Tests/EdFi.Ods.Sandbox/Repositories/ClientAppRepoTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Licensed to the Ed-Fi Alliance under one or more agreements. -// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. -// See the LICENSE and NOTICES files in the project root for more information. - -using System; -using EdFi.Admin.DataAccess.Contexts; -using EdFi.Admin.DataAccess.Models; -using EdFi.Admin.DataAccess.Repositories; -using EdFi.TestFixture; -using FakeItEasy; -using NUnit.Framework; -using Microsoft.Extensions.Configuration; -using Shouldly; - -// ReSharper disable once InconsistentNaming -namespace EdFi.Ods.Tests.EdFi.Ods.Sandbox -{ - public class ClientAppRepoTests - { - [TestFixture] - public class When_calling_the_clientAppRepo - { - private ClientAppRepo _clientAppRepo; - private ApiClient _testClient; - - [OneTimeSetUp] - public void Setup() - { - var configValueProviderStub = A.Fake(); - var usersContext = A.Fake(); - - - var usersContextFactory = A.Fake(); - A.CallTo(() => usersContextFactory.CreateContext()) - .Returns(usersContext); - - _clientAppRepo = new ClientAppRepo(usersContextFactory, configValueProviderStub); - - _testClient = new ApiClient(true) - { - Name = "ClientAppRepoTest" + Guid.NewGuid() - .ToString("N") - }; - - var dbSet = new TestDbSet {_testClient}; - - A.CallTo(() => usersContext.Clients) - .Returns(dbSet); - } - - [Test] - public void Should_get_client_with_key_and_secret() - { - var tmpClient = _clientAppRepo.GetClient(_testClient.Key, _testClient.Secret); - tmpClient.ShouldNotBeNull(); - tmpClient.Name.ShouldBe(_testClient.Name); - } - - [Test] - public void Should_get_client_with_key_only() - { - var tmpClient = _clientAppRepo.GetClient(_testClient.Key); - tmpClient.ShouldNotBeNull(); - tmpClient.Name.ShouldBe(_testClient.Name); - } - - [Test] - public void Should_not_get_client_with_key_and_empty_secret() - { - var tmpClient = _clientAppRepo.GetClient(_testClient.Key, string.Empty); - tmpClient.ShouldBeNull(); - } - - [Test] - public void Should_not_get_client_with_key_and_null_secret() - { - var tmpClient = _clientAppRepo.GetClient(_testClient.Key, null); - tmpClient.ShouldBeNull(); - } - } - } -} diff --git a/Application/EdFi.Ods.Tests/EdFi.Ods.Sandbox/_Stubs/TestSandboxProvisioner.cs b/Application/EdFi.Ods.Tests/EdFi.Ods.Sandbox/_Stubs/TestSandboxProvisioner.cs deleted file mode 100644 index 5aafd9caec..0000000000 --- a/Application/EdFi.Ods.Tests/EdFi.Ods.Sandbox/_Stubs/TestSandboxProvisioner.cs +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Licensed to the Ed-Fi Alliance under one or more agreements. -// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. -// See the LICENSE and NOTICES files in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using EdFi.Admin.DataAccess; -using EdFi.Ods.Sandbox.Provisioners; - -namespace EdFi.Ods.Tests.EdFi.Ods.Sandbox._Stubs -{ - public class TestSandboxProvisioner : ISandboxProvisioner - { - private readonly List _sandboxes = new List(); - - public Sandbox[] AddedSandboxes - { - get { return _sandboxes.ToArray(); } - } - - public void AddSandbox(string sandboxKey, SandboxType sandboxType) - { - _sandboxes.Add( - new Sandbox - { - Key = sandboxKey, SandboxType = sandboxType - }); - } - - public void DeleteSandboxes(params string[] deletedClientKeys) - { - throw new NotImplementedException(); - } - - public void RenameSandbox(string oldName, string newName) - { - throw new NotImplementedException(); - } - - public SandboxStatus GetSandboxStatus(string clientKey) - { - throw new NotImplementedException(); - } - - public void ResetDemoSandbox() - { - throw new NotImplementedException(); - } - - public string[] GetSandboxDatabases() - { - throw new NotImplementedException(); - } - - public Task AddSandboxAsync(string sandboxKey, SandboxType sandboxType) => throw new NotImplementedException(); - - public Task DeleteSandboxesAsync(params string[] deletedClientKeys) => throw new NotImplementedException(); - - public Task RenameSandboxAsync(string oldName, string newName) => throw new NotImplementedException(); - - public Task GetSandboxStatusAsync(string clientKey) => throw new NotImplementedException(); - - public Task ResetDemoSandboxAsync() => throw new NotImplementedException(); - - public Task GetSandboxDatabasesAsync() => throw new NotImplementedException(); - - public Task CopySandboxAsync(string originalDatabaseName, string newDatabaseName) => throw new NotImplementedException(); - - public class Sandbox - { - public string Key { get; set; } - - public SandboxType SandboxType { get; set; } - } - } -} \ No newline at end of file diff --git a/Application/Test.Common/Test.Common.csproj b/Application/Test.Common/Test.Common.csproj index 509a9ed693..c1f0da633a 100644 --- a/Application/Test.Common/Test.Common.csproj +++ b/Application/Test.Common/Test.Common.csproj @@ -1,6 +1,6 @@  - net6.0 + net8.0 Test.Common Test.Common Copyright © 2019 Ed-Fi Alliance, LLC and Contributors @@ -14,18 +14,17 @@ true - - - + - - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive + + @@ -33,4 +32,4 @@ - + \ No newline at end of file diff --git a/build.githubactions.ps1 b/build.githubactions.ps1 index d1e169d460..f312c9ae33 100644 --- a/build.githubactions.ps1 +++ b/build.githubactions.ps1 @@ -165,7 +165,7 @@ function Publish { function Test { if(-not $TestFilter) { - Invoke-Execute { dotnet test $solution -c $Configuration --no-build -v normal --filter 'FullyQualifiedName!~DataAccess.IntegrationTests'} + Invoke-Execute { dotnet test $solution -c $Configuration --no-build -v normal --filter 'FullyQualifiedName!~EdFi.Admin.DataAccess.IntegrationTests'} } else { Invoke-Execute { dotnet test $solution -c $Configuration --no-build -v normal --filter TestCategory!~"$TestFilter" } } diff --git a/tests/EdFi.Admin.DataAccess.IntegrationTests/EdFi.Admin.DataAccess.IntegrationTests.csproj b/tests/EdFi.Admin.DataAccess.IntegrationTests/EdFi.Admin.DataAccess.IntegrationTests.csproj index 831d4594ff..c7ac79415d 100644 --- a/tests/EdFi.Admin.DataAccess.IntegrationTests/EdFi.Admin.DataAccess.IntegrationTests.csproj +++ b/tests/EdFi.Admin.DataAccess.IntegrationTests/EdFi.Admin.DataAccess.IntegrationTests.csproj @@ -1,6 +1,6 @@ - net6.0 + net8.0 Ed-Fi Alliance EdFi.Admin.DataAccess.IntegrationTests EdFi.Admin.DataAccess.IntegrationTests @@ -16,24 +16,22 @@ true - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + - + + + + + + + + + + + @@ -45,4 +43,4 @@ appsettings.json - + \ No newline at end of file diff --git a/tests/EdFi.Admin.DataAccess.IntegrationTests/Models/UserContextMappingTests.cs b/tests/EdFi.Admin.DataAccess.IntegrationTests/Models/UserContextMappingTests.cs deleted file mode 100644 index 78898bf269..0000000000 --- a/tests/EdFi.Admin.DataAccess.IntegrationTests/Models/UserContextMappingTests.cs +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Licensed to the Ed-Fi Alliance under one or more agreements. -// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. -// See the LICENSE and NOTICES files in the project root for more information. - -using System; -using System.Data.Entity; -using System.Data.Entity.Migrations; -using System.Linq; -using EdFi.Admin.DataAccess.Models; -using EdFi.TestFixture; -using Microsoft.Extensions.Configuration; -using NCrunch.Framework; -using NUnit.Framework; -using Shouldly; -using Test.Common; - -namespace EdFi.Ods.Admin.DataAccess.IntegrationTests.Models -{ - public class UserContextMappingTests - { - [TestFixture] - [ExclusivelyUses(TestSingletons.EmptyAdminDatabase)] - public class When_creating_a_user : UserContextTestBase - { - private string emailAddress; - - [OneTimeSetUp] - public new void Setup() - { - emailAddress = string.Format("{0}@{1}.com", DateTime.Now.Ticks, DateTime.Now.Ticks + 1); - } - - [Test] - public void Should_persist_the_user_to_the_database() - { - using (var context = GetUsersContextTest()) - { - //Arrange - var user = new User {Email = emailAddress}; - - //Act - context.Users.Add(user); - context.SaveChangesForTest(); - - //Assert - context.Users.Count(x => x.Email == emailAddress) - .ShouldBe(1); - } - } - } - - [TestFixture] - [ExclusivelyUses(TestSingletons.EmptyAdminDatabase)] - public class When_adding_an_lea_mapping_to_a_client : UserContextTestBase - { - private string clientName; - private int leaId; - - [OneTimeSetUp] - public new void Setup() - { - clientName = string.Format("{0}_TestData", DateTime.Now.Ticks); - leaId = int.MaxValue - 1; - } - - [Test] - public void Should_persist_the_lea_mapping_without_explicitly_adding_that_mapping_to_the_databaseContext() - { - using (var context = GetUsersContextTest()) - { - //Arrange - var lea = new ApplicationEducationOrganization {EducationOrganizationId = leaId}; - - var client = new ApiClient(true) {Name = clientName}; - - client.ApplicationEducationOrganizations.Add(lea); - - //Act - context.Clients.Add(client); - context.SaveChangesForTest(); - - //Assert - var clientFromDb = context.Clients.Where(x => x.Name == clientName) - .Include(x => x.ApplicationEducationOrganizations) - .Single(); - - int[] leas = clientFromDb.ApplicationEducationOrganizations.Select(x => x.EducationOrganizationId) - .ToArray(); - - leas.ShouldBe( - new[] {leaId}); - } - } - } - - [TestFixture] - [ExclusivelyUses(TestSingletons.EmptyAdminDatabase)] - public class When_adding_an_lea_mapping_to_an_application : UserContextTestBase - { - private string appName; - private int leaId; - - [OneTimeSetUp] - public new void Setup() - { - appName = string.Format("{0}_TestData", DateTime.Now.Ticks); - leaId = int.MaxValue - 1; - } - - [Test] - public void Should_persist_the_lea_mapping_without_explicitly_adding_that_mapping_to_the_databaseContext() - { - using (var context = GetUsersContextTest()) - { - //Arrange - var lea = new ApplicationEducationOrganization {EducationOrganizationId = leaId}; - - var application = new Application {ApplicationName = appName}; - - application.ApplicationEducationOrganizations.Add(lea); - - application.OperationalContextUri = "uri://ed-fi-api-host.org"; - - //Act - context.Applications.Add(application); - context.SaveChangesForTest(); - - //Assert - var applicationFromDb = context.Applications.Where(x => x.ApplicationName == appName) - .Include(x => x.ApplicationEducationOrganizations) - .Single(); - - int[] leas = applicationFromDb.ApplicationEducationOrganizations.Select(x => x.EducationOrganizationId) - .ToArray(); - - leas.ShouldBe( - new[] {leaId}); - } - } - } - - [TestFixture] - [ExclusivelyUses(TestSingletons.EmptyAdminDatabase)] - public class When_adding_an_application_to_a_vendor : UserContextTestBase - { - private string vendorName; - private string appName; - private const string ClaimSetName = "ClaimSet"; - - [OneTimeSetUp] - public new void Setup() - { - vendorName = string.Format("{0}_TestData", DateTime.Now.Ticks); - appName = string.Format("{0}_TestData", DateTime.Now.Ticks); - } - - [Test] - public void Should_create_application() - { - //Arrange - var vendor = new Vendor {VendorName = vendorName}; - - vendor.CreateApplication(appName, ClaimSetName); - using (var context = GetUsersContextTest()) - { - vendor.Applications.AsEnumerable() - .ElementAt(0) - .OperationalContextUri = "uri://ed-fi-api-host.org"; - - context.Vendors.Add(vendor); - context.SaveChangesForTest(); - - //Act - var vendorFromDb = context.Vendors.Where(v => v.VendorName == vendorName) - .Include(x => x.Applications) - .Single(); - - //Assert - vendorFromDb.ShouldNotBeNull(); - vendorFromDb.Applications.Count.ShouldBe(1); - - vendorFromDb.Applications.ToList()[0] - .ApplicationName.ShouldBe(appName); - } - } - } - - [TestFixture] - public class When_adding_a_local_education_agency_to_an_application : UserContextTestBase - { - private string vendorName; - private string appName; - private int leaId; - private const string ClaimSetName = "ClaimSet"; - - [OneTimeSetUp] - public new void Setup() - { - vendorName = string.Format("{0}_TestData", DateTime.Now.Ticks); - appName = string.Format("{0}_TestData", DateTime.Now.Ticks); - leaId = int.MaxValue - 1; - } - - [Test] - public void Should_create_lea_association() - { - //Arrange - var vendor = new Vendor {VendorName = vendorName}; - - vendor.CreateApplication(appName, ClaimSetName); - - var educationOrganizationAssociation = vendor.Applications.AsEnumerable() - .ElementAt(0) - .CreateApplicationEducationOrganization(leaId); - - using (var context = GetUsersContextTest()) - { - vendor.Applications.AsEnumerable() - .ElementAt(0) - .OperationalContextUri = "uri://ed-fi-api-host.org"; - - context.ApplicationEducationOrganizations.AddOrUpdate(educationOrganizationAssociation); - context.Vendors.Add(vendor); - context.SaveChangesForTest(); - - //Act - var application = context.Applications.Where(app => app.ApplicationName == appName) - .Include(x => x.ApplicationEducationOrganizations) - .Single(); - - var applicationLocalEducationAgencies = application.ApplicationEducationOrganizations.ToArray(); - applicationLocalEducationAgencies.Length.ShouldBe(1); - - applicationLocalEducationAgencies[0] - .EducationOrganizationId.ShouldBe(leaId); - } - } - } - } -} diff --git a/tests/EdFi.Admin.DataAccess.IntegrationTests/Models/UserContextTestBase.cs b/tests/EdFi.Admin.DataAccess.IntegrationTests/Models/UserContextTestBase.cs deleted file mode 100644 index 288f48a215..0000000000 --- a/tests/EdFi.Admin.DataAccess.IntegrationTests/Models/UserContextTestBase.cs +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Licensed to the Ed-Fi Alliance under one or more agreements. -// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. -// See the LICENSE and NOTICES files in the project root for more information. - -using System; -using System.Data.Entity; -using System.Linq; -using System.Transactions; -using EdFi.Admin.DataAccess.Contexts; -using EdFi.Admin.DataAccess.Providers; -using EdFi.Ods.Api.Configuration; -using EdFi.Common.Configuration; -using EdFi.TestFixture; -using Microsoft.Extensions.Configuration; -using NUnit.Framework; - -namespace EdFi.Ods.Admin.DataAccess.IntegrationTests.Models -{ - public abstract class UserContextTestBase : TestFixtureBase - { - private TransactionScope _transaction; - private UsersContext _userContext; - - protected DatabaseEngine TestDatabaseEngine { get; private set; } - - [OneTimeSetUp] - public void OneTimeSetUp() - { - AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); - - var config = new ConfigurationBuilder() - .SetBasePath(TestContext.CurrentContext.TestDirectory) - .AddJsonFile("appsettings.json", false) - .AddJsonFile("appsettings.Development.json", true) - .AddEnvironmentVariables() - .Build(); - - var engine = config.GetSection("ApiSettings").GetValue("Engine"); - TestDatabaseEngine = DatabaseEngine.TryParseEngine(engine); - - var connectionStringProvider = new AdminDatabaseConnectionStringProvider(new ConfigConnectionStringsProvider(config)); - - DbConfiguration.SetConfiguration(new DatabaseEngineDbConfiguration(TestDatabaseEngine)); - var userContextFactory = new UsersContextFactory(connectionStringProvider, TestDatabaseEngine); - _userContext = userContextFactory.CreateContext() as UsersContext; - } - - protected UsersContext GetUsersContextTest() - { - return _userContext; - } - - [SetUp] - public void Setup() - { - _transaction = new TransactionScope(); - } - - [TearDown] - public void TearDown() - { - _transaction.Dispose(); - } - } -} diff --git a/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/MSSQL/ClientAppRepoTests.cs b/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/MSSQL/ClientAppRepoTests.cs new file mode 100644 index 0000000000..c5f236cb67 --- /dev/null +++ b/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/MSSQL/ClientAppRepoTests.cs @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +using System; +using EdFi.Admin.DataAccess.Contexts; +using EdFi.Admin.DataAccess.Models; +using EdFi.Admin.DataAccess.Repositories; +using EdFi.TestFixture; +using FakeItEasy; +using NUnit.Framework; +using Microsoft.Extensions.Configuration; +using Shouldly; +using System.Transactions; +using Microsoft.EntityFrameworkCore; +using System.Linq; + +// ReSharper disable once InconsistentNaming +namespace EdFi.Admin.DataAccess.IntegrationTests.Repositories.MSSQL +{ + [TestFixture] + public class ClientAppRepoTests + { + private const int BearerTokenTimeoutMinutes = 10; + private const string DefaultOperationalContextUri = "uri://ed-fi.org"; + private const string DefaultApplicationName = "Integration Test"; + private const string DefaultClaimSetName = "SIS Vendor"; + + protected SqlServerUsersContext Context; + protected SqlServerUsersContext OpenContext; + protected TransactionScope Transaction; + + [SetUp] + public void Setup() + { + // Read settings + var builder = new ConfigurationBuilder() + .AddJsonFile($"appSettings.json", true, true) + .AddJsonFile($"appSettings.development.json", true, true); + + var config = builder.Build(); + + // Setup SQL Server + var connectionString = config.GetConnectionString("MSSQL"); + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(connectionString); + Context = new SqlServerUsersContext(optionsBuilder.Options); + OpenContext = new SqlServerUsersContext(optionsBuilder.Options); + + // Startup a transaction so we can dispose of any changes after running the tests + Transaction = new TransactionScope(); + + // Configure settings mocks + var usersContextFactory = A.Fake(); + A.CallTo(() => usersContextFactory.CreateContext()) + .Returns(Context); + + var configValueProviderStub = A.Fake(); + + var tokenTimeoutSection = A.Fake(); + A.CallTo(() => tokenTimeoutSection.Value).Returns(BearerTokenTimeoutMinutes.ToString()); + A.CallTo(() => configValueProviderStub.GetSection("BearerTokenTimeoutMinutes")).Returns(tokenTimeoutSection); + + var contextSection = A.Fake(); + A.CallTo(() => contextSection.Value).Returns(DefaultOperationalContextUri); + A.CallTo(() => configValueProviderStub.GetSection("DefaultOperationalContextUri")).Returns(contextSection); + + var appNameSection = A.Fake(); + A.CallTo(() => appNameSection.Value).Returns(DefaultApplicationName); + A.CallTo(() => configValueProviderStub.GetSection("DefaultApplicationName")).Returns(appNameSection); + + var claimSetSection = A.Fake(); + A.CallTo(() => claimSetSection.Value).Returns(DefaultClaimSetName); + A.CallTo(() => configValueProviderStub.GetSection("DefaultClaimSetName")).Returns(claimSetSection); + + // Create the system under test + _clientAppRepo = new ClientAppRepo(usersContextFactory, configValueProviderStub); + + // Load a fake API client + _testClient = new ApiClient(true) + { + Name = "ClientAppRepoTest" + Guid.NewGuid().ToString("N") + }; + Context.Clients.Add(_testClient); + Context.SaveChangesForTest(); + } + + [TearDown] + public void Teardown() + { + Transaction.Dispose(); + } + + private ClientAppRepo _clientAppRepo; + private ApiClient _testClient; + + [TestFixture] + public class When_getting_client_by_key_and_secret : ClientAppRepoTests + { + [Test] + public void Should_get_client_with_key_and_secret() + { + var tmpClient = _clientAppRepo.GetClient(_testClient.Key, _testClient.Secret); + tmpClient.ShouldNotBeNull(); + tmpClient.Name.ShouldBe(_testClient.Name); + } + + [Test] + public void Should_not_get_client_with_key_and_empty_secret() + { + var tmpClient = _clientAppRepo.GetClient(_testClient.Key, string.Empty); + tmpClient.ShouldBeNull(); + } + + [Test] + public void Should_not_get_client_with_key_and_null_secret() + { + var tmpClient = _clientAppRepo.GetClient(_testClient.Key, null); + tmpClient.ShouldBeNull(); + } + } + + [TestFixture] + public class When_getting_a_client_and_all_related_properties : ClientAppRepoTests + { + private const string ClientName = "zsdfasdf"; + private ApiClient _result; + + [SetUp] + public void SetUp() + { + // Arrange + // Adding vendor, namespace prefix, profiles, education organizations, + // and ownership token in order to test the Entity Framework + // hydration of those properties on the client + var client = new ApiClient + { + Name = ClientName, + Key = "987sal;ifasd", + Secret = "987asdkif" + }; + OpenContext.Clients.Add(client); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + OpenContext.Vendors.Add(vendor); + + var namespacePrefix = new VendorNamespacePrefix + { + NamespacePrefix = "abc", + Vendor = vendor + }; + vendor.VendorNamespacePrefixes.Add(namespacePrefix); + OpenContext.VendorNamespacePrefixes.Add(namespacePrefix); + + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + client.Application = application; + application.ApiClients.Add(client); + OpenContext.Applications.Add(application); + + var profile = new Profile + { + ProfileName = "profile" + }; + application.Profiles.Add(profile); + profile.Applications.Add(application); + OpenContext.Profiles.Add(profile); + + var token = new OwnershipToken + { + Description = "sddsdsf" + }; + token.ApiClients.Add(client); + client.CreatorOwnershipToken = token; + OpenContext.OwnershipTokens.Add(token); + + OpenContext.SaveChangesForTest(); + + // Act + _result = _clientAppRepo.GetClient(client.Key); + } + + [Test] + public void Then_result_not_be_null() + { + _result.ShouldNotBeNull(); + } + + [Test] + public void Then_result_should_be_the_correct_client() + { + _result.Name.ShouldBe(ClientName); + } + + [Test] + public void Then_result_should_have_a_hydrated_application() + { + _result.Application.ShouldNotBeNull(); + } + + [Test] + public void Then_result_should_have_a_hydrated_vendor() + { + _result.Application.Vendor.ShouldNotBeNull(); + } + + [Test] + public void Then_result_should_have_a_hydrated_vendor_namespace() + { + _result.Application.Vendor.VendorNamespacePrefixes.ShouldHaveSingleItem(); + } + + [Test] + public void Then_result_should_have_a_hydrated_profile() + { + _result.Application.Profiles.ShouldHaveSingleItem(); + } + + [Test] + public void Then_result_should_have_a_hydrated_ownership_token() + { + _result.CreatorOwnershipToken.ShouldNotBeNull(); + } + } + + + [TestFixture] + public class When_getting_client_by_key : ClientAppRepoTests + { + [Test] + public void Given_key_and_secret_should_get_client() + { + var tmpClient = _clientAppRepo.GetClientByKey(_testClient.Key); + tmpClient.ShouldNotBeNull(); + tmpClient.Name.ShouldBe(_testClient.Name); + } + + [Test] + public void Given_Invalid_key_should_return_null() + { + var tmpClient = _clientAppRepo.GetClient("does not exist"); + tmpClient.ShouldBeNull(); + } + } + + [TestFixture] + public class When_updating_a_client :ClientAppRepoTests + { + [Test] + public void Given_valid_client_should_save_update() + { + const string tempName = "new name"; + _testClient.Name = tempName; + + _clientAppRepo.UpdateClient(_testClient); + + OpenContext.Clients.Count(x => x.Name == tempName).ShouldBe(1); + } + + [Test] + public void Given_new_client_should_upsert() + { + const string tempName = "new name"; + var client = new ApiClient + { + Key = "a", + Secret = "b", + Name = tempName + }; + + _clientAppRepo.UpdateClient(client); + + OpenContext.Clients.Count(x => x.Name == tempName).ShouldBe(1); + } + } + + [TestFixture] + public class When_deleting_a_client : ClientAppRepoTests + { + [Test] + public void Given_valid_client_it_should_delete() + { + _clientAppRepo.DeleteClient(_testClient.Key); + + OpenContext.Clients.Count(x => x.Name == _testClient.Name).ShouldBe(0); + } + } + + [TestFixture] + public class When_getting_vendor_applications : ClientAppRepoTests + { + [Test] + public void Given_a_vendor_has_an_application_it_should_return_that_application() + { + // Arrange + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + OpenContext.SaveChangesForTest(); + + // Act + var result = _clientAppRepo.GetVendorApplications(vendor.VendorId); + + // Assert + result.ShouldNotBeNull(); + result.Length.ShouldBe(1); + result.Count(x => x.ApplicationName == application.ApplicationName).ShouldBe(1); + } + + [Test] + public void Given_a_vendor_has_no_applications_it_should_return_an_empty_array() + { + // Arrange + var vendor = new Vendor + { + VendorName = "vendor name" + }; + + OpenContext.Vendors.Add(vendor); + OpenContext.SaveChangesForTest(); + + // Act + var result = _clientAppRepo.GetVendorApplications(vendor.VendorId); + + // Assert + result.ShouldNotBeNull(); + result.Length.ShouldBe(0); + } + } + + + [TestFixture] + public class When_adding_api_client_to_user : ClientAppRepoTests + { + [Test] + public void Given_user_does_not_exist_it_should_do_nothing() + { + _clientAppRepo.AddApiClientToUserWithVendorApplication(7777777, _testClient); + } + + [Test] + public void Given_user_exists_but_vendor_does_not_it_should_save_the_new_client() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + OpenContext.SaveChangesForTest(); + + const string tempName = "Given_user_exists_but_vendor_does_not"; + var client = new ApiClient + { + Key = "a", + Secret = "b", + Name = tempName + }; + + // Act + _clientAppRepo.AddApiClientToUserWithVendorApplication(user.UserId, client); + + // Assert + OpenContext.Clients.Count(x => x.Name == tempName).ShouldBe(1); + } + + [Test] + public void Given_user_and_vendor_exist_it_should_attach_first_application_to_the_client() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + + user.Vendor = vendor; + vendor.Users.Add(user); + + OpenContext.SaveChangesForTest(); + + const string tempName = "Given_user_and_vendor_exi..."; + var client = new ApiClient + { + Key = "a", + Secret = "b", + Name = tempName + }; + + // Act + _clientAppRepo.AddApiClientToUserWithVendorApplication(user.UserId, client); + + // Assert + var saved = OpenContext.Clients.FirstOrDefault(x => x.Name == tempName); + saved.ShouldNotBeNull(); + + saved.Application + .ApplicationName + .ShouldBe(application.ApplicationName); + } + } + + [TestFixture] + public class When_creating_an_api_client : ClientAppRepoTests + { + [Test] + public void Given_user_exists_it_should_save_the_new_client_for_sandbox() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + OpenContext.SaveChangesForTest(); + + // Act + const string clientName = "Given_user_exists_it_should_save..."; + const string key = "key"; + const string secret = "secret"; + + _clientAppRepo.CreateApiClient(user.UserId, clientName, key, secret); + + // Assert + var saved = OpenContext.Clients.FirstOrDefault(x => x.Name == clientName); + saved.ShouldNotBeNull(); + saved.UseSandbox.ShouldBeTrue(); + saved.IsApproved.ShouldBeTrue(); + saved.SandboxType.ShouldBe(SandboxType.Sample); + } + + // No negative testing here, because the code does not have proper safe guards + // when the user does not exist. Will throw an exception. + } + + } +} diff --git a/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/PostgreSQL/ClientAppRepoTests.cs b/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/PostgreSQL/ClientAppRepoTests.cs new file mode 100644 index 0000000000..e95139382c --- /dev/null +++ b/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/PostgreSQL/ClientAppRepoTests.cs @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +using System; +using EdFi.Admin.DataAccess.Contexts; +using EdFi.Admin.DataAccess.Models; +using EdFi.Admin.DataAccess.Repositories; +using EdFi.TestFixture; +using FakeItEasy; +using NUnit.Framework; +using Microsoft.Extensions.Configuration; +using Shouldly; +using System.Transactions; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using Microsoft.IdentityModel.Tokens; + +// ReSharper disable once InconsistentNaming +namespace EdFi.Admin.DataAccess.IntegrationTests.Repositories.PostgreSQL +{ + [TestFixture] + public class ClientAppRepoTests + { + private const int BearerTokenTimeoutMinutes = 10; + private const string DefaultOperationalContextUri = "uri://ed-fi.org"; + private const string DefaultApplicationName = "Integration Test"; + private const string DefaultClaimSetName = "SIS Vendor"; + + protected PostgresUsersContext Context; + protected PostgresUsersContext OpenContext; + protected TransactionScope Transaction; + + [SetUp] + public void Setup() + { + // Read settings + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + + var builder = new ConfigurationBuilder() + .AddJsonFile($"appSettings.json", true, true) + .AddJsonFile($"appSettings.development.json", true, true); + + var config = builder.Build(); + + // Setup PostgreSQL + var connectionString = config.GetConnectionString("PostgreSQL"); + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseNpgsql(connectionString); + Context = new PostgresUsersContext(optionsBuilder.Options); + OpenContext = new PostgresUsersContext(optionsBuilder.Options); + + // Startup a transaction so we can dispose of any changes after running the tests + Transaction = new TransactionScope(); + + // Configure settings mocks + var usersContextFactory = A.Fake(); + A.CallTo(() => usersContextFactory.CreateContext()) + .Returns(Context); + + var configValueProviderStub = A.Fake(); + + var tokenTimeoutSection = A.Fake(); + A.CallTo(() => tokenTimeoutSection.Value).Returns(BearerTokenTimeoutMinutes.ToString()); + A.CallTo(() => configValueProviderStub.GetSection("BearerTokenTimeoutMinutes")).Returns(tokenTimeoutSection); + + var contextSection = A.Fake(); + A.CallTo(() => contextSection.Value).Returns(DefaultOperationalContextUri); + A.CallTo(() => configValueProviderStub.GetSection("DefaultOperationalContextUri")).Returns(contextSection); + + var appNameSection = A.Fake(); + A.CallTo(() => appNameSection.Value).Returns(DefaultApplicationName); + A.CallTo(() => configValueProviderStub.GetSection("DefaultApplicationName")).Returns(appNameSection); + + var claimSetSection = A.Fake(); + A.CallTo(() => claimSetSection.Value).Returns(DefaultClaimSetName); + A.CallTo(() => configValueProviderStub.GetSection("DefaultClaimSetName")).Returns(claimSetSection); + + // Create the system under test + _clientAppRepo = new ClientAppRepo(usersContextFactory, configValueProviderStub); + + // Load a fake API client + _testClient = new ApiClient(true) + { + Name = "ClientAppRepoTest" + Guid.NewGuid().ToString("N") + }; + Context.Clients.Add(_testClient); + Context.SaveChangesForTest(); + } + + [TearDown] + public void Teardown() + { + Transaction.Dispose(); + } + + private ClientAppRepo _clientAppRepo; + private ApiClient _testClient; + + + [TestFixture] + public class When_getting_client_by_key_and_secret : ClientAppRepoTests + { + [Test] + public void Should_get_client_with_key_and_secret() + { + var tmpClient = _clientAppRepo.GetClient(_testClient.Key, _testClient.Secret); + tmpClient.ShouldNotBeNull(); + tmpClient.Name.ShouldBe(_testClient.Name); + } + + [Test] + public void Should_not_get_client_with_key_and_empty_secret() + { + var tmpClient = _clientAppRepo.GetClient(_testClient.Key, string.Empty); + tmpClient.ShouldBeNull(); + } + + [Test] + public void Should_not_get_client_with_key_and_null_secret() + { + var tmpClient = _clientAppRepo.GetClient(_testClient.Key, null); + tmpClient.ShouldBeNull(); + } + } + + [TestFixture] + public class When_getting_a_client_and_all_related_properties : ClientAppRepoTests + { + private const string ClientName = "zsdfasdf"; + private ApiClient _result; + + [SetUp] + public void SetUp() + { + // Arrange + // Adding vendor, namespace prefix, profiles, education organizations, + // and ownership token in order to test the Entity Framework + // hydration of those properties on the client + var client = new ApiClient + { + Name = ClientName, + Key = "987sal;ifasd", + Secret = "987asdkif" + }; + OpenContext.Clients.Add(client); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + OpenContext.Vendors.Add(vendor); + + var namespacePrefix = new VendorNamespacePrefix + { + NamespacePrefix = "abc", + Vendor = vendor + }; + vendor.VendorNamespacePrefixes.Add(namespacePrefix); + OpenContext.VendorNamespacePrefixes.Add(namespacePrefix); + + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + client.Application = application; + application.ApiClients.Add(client); + + vendor.Applications.Add(application); + OpenContext.Applications.Add(application); + + var profile = new Profile + { + ProfileName = "profile" + }; + application.Profiles.Add(profile); + profile.Applications.Add(application); + OpenContext.Profiles.Add(profile); + + var token = new OwnershipToken + { + Description = "sddsdsf" + }; + token.ApiClients.Add(client); + client.CreatorOwnershipToken = token; + OpenContext.OwnershipTokens.Add(token); + + OpenContext.SaveChangesForTest(); + + // Uncomment next two lines only for debugging. + // PostgreSQL does not have a "read uncommitted" isolation level, therefore + // this is the only way to manually query the database for inspection while + // debugging the test. + //Transaction.Complete(); + //Transaction = new TransactionScope(); + + // Act + _result = _clientAppRepo.GetClient(client.Key); + } + + [Test] + public void Then_result_not_be_null() + { + _result.ShouldNotBeNull(); + } + + [Test] + public void Then_result_should_be_the_correct_client() + { + _result.Name.ShouldBe(ClientName); + } + + [Test] + public void Then_result_should_have_a_hydrated_application() + { + _result.Application.ShouldNotBeNull(); + } + + [Test] + public void Then_result_should_have_a_hydrated_vendor() + { + _result.Application.Vendor.ShouldNotBeNull(); + } + + [Test] + public void Then_result_should_have_a_hydrated_vendor_namespace() + { + _result.Application.Vendor.VendorNamespacePrefixes.ShouldHaveSingleItem(); + } + + [Test] + public void Then_result_should_have_a_hydrated_profile() + { + _result.Application.Profiles.ShouldHaveSingleItem(); + } + + [Test] + public void Then_result_should_have_a_hydrated_ownership_token() + { + _result.CreatorOwnershipToken.ShouldNotBeNull(); + } + } + + [TestFixture] + public class When_getting_client_by_key : ClientAppRepoTests + { + [Test] + public void Given_key_and_secret_should_get_client() + { + var tmpClient = _clientAppRepo.GetClientByKey(_testClient.Key); + tmpClient.ShouldNotBeNull(); + tmpClient.Name.ShouldBe(_testClient.Name); + } + + [Test] + public void Given_Invalid_key_should_return_null() + { + var tmpClient = _clientAppRepo.GetClient("does not exist"); + tmpClient.ShouldBeNull(); + } + } + + [TestFixture] + public class When_updating_a_client : ClientAppRepoTests + { + [Test] + public void Given_valid_client_should_save_update() + { + const string tempName = "new name"; + _testClient.Name = tempName; + _clientAppRepo.UpdateClient(_testClient); + + OpenContext.Clients.Count(x => x.Name == tempName).ShouldBe(1); + } + + [Test] + public void Given_new_client_should_upsert() + { + const string tempName = "new name"; + var client = new ApiClient + { + Key = "a", + Secret = "b", + Name = tempName + }; + _clientAppRepo.UpdateClient(client); + + OpenContext.Clients.Count(x => x.Name == tempName).ShouldBe(1); + } + } + + [TestFixture] + public class When_deleting_a_client : ClientAppRepoTests + { + [Test] + public void Given_valid_client_it_should_delete() + { + _clientAppRepo.DeleteClient(_testClient.Key); + + OpenContext.Clients.Count(x => x.Name == _testClient.Name).ShouldBe(0); + } + } + + [TestFixture] + public class When_getting_vendor_applications : ClientAppRepoTests + { + [Test] + public void Given_a_vendor_has_an_application_it_should_return_that_application() + { + // Arrange + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + OpenContext.SaveChangesForTest(); + + // Act + var result = _clientAppRepo.GetVendorApplications(vendor.VendorId); + + // Assert + result.ShouldNotBeNull(); + result.Length.ShouldBe(1); + result.Count(x => x.ApplicationName == application.ApplicationName).ShouldBe(1); + } + + [Test] + public void Given_a_vendor_has_no_applications_it_should_return_an_empty_array() + { + // Arrange + var vendor = new Vendor + { + VendorName = "vendor name" + }; + + OpenContext.Vendors.Add(vendor); + OpenContext.SaveChangesForTest(); + + // Act + var result = _clientAppRepo.GetVendorApplications(vendor.VendorId); + + // Assert + result.ShouldNotBeNull(); + result.Length.ShouldBe(0); + } + } + + + [TestFixture] + public class When_adding_api_client_to_user : ClientAppRepoTests + { + [Test] + public void Given_user_does_not_exist_it_should_do_nothing() + { + _clientAppRepo.AddApiClientToUserWithVendorApplication(7777777, _testClient); + } + + [Test] + public void Given_user_exists_but_vendor_does_not_it_should_save_the_new_client() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + OpenContext.SaveChangesForTest(); + + const string tempName = "Given_user_exists_but_vendor_does_not"; + var client = new ApiClient + { + Key = "a", + Secret = "b", + Name = tempName + }; + + // Act + _clientAppRepo.AddApiClientToUserWithVendorApplication(user.UserId, client); + + // Assert + OpenContext.Clients.Count(x => x.Name == tempName).ShouldBe(1); + } + + [Test] + public void Given_user_and_vendor_exist_it_should_attach_first_application_to_the_client() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + + user.Vendor = vendor; + vendor.Users.Add(user); + + OpenContext.SaveChangesForTest(); + + const string tempName = "Given_user_and_vendor_exi..."; + var client = new ApiClient + { + Key = "a", + Secret = "b", + Name = tempName + }; + + // Act + _clientAppRepo.AddApiClientToUserWithVendorApplication(user.UserId, client); + + // Assert + var saved = OpenContext.Clients.FirstOrDefault(x => x.Name == tempName); + saved.ShouldNotBeNull(); + + saved.Application + .ApplicationName + .ShouldBe(application.ApplicationName); + } + } + + [TestFixture] + public class When_creating_an_api_client : ClientAppRepoTests + { + [Test] + public void Given_user_exists_it_should_save_the_new_client_for_sandbox() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + OpenContext.SaveChangesForTest(); + + // Act + const string clientName = "Given_user_exists_it_should_save..."; + const string key = "key"; + const string secret = "secret"; + + _clientAppRepo.CreateApiClient(user.UserId, clientName, key, secret); + + // Assert + var saved = OpenContext.Clients.FirstOrDefault(x => x.Name == clientName); + saved.ShouldNotBeNull(); + saved.UseSandbox.ShouldBeTrue(); + saved.IsApproved.ShouldBeTrue(); + saved.SandboxType.ShouldBe(SandboxType.Sample); + } + + // No negative testing here, because the code does not have proper safe guards + // when the user does not exist. Will throw an exception. + } + + [TestFixture] + public class When_getting_users : ClientAppRepoTests + { + [Test] + public void Given_there_are_no_users_it_should_return_an_empty_list() + { + _clientAppRepo.GetUsers().ShouldBeEmpty(); + } + + [Test] + public void Given_there_is_a_user_with_an_application() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + + user.Vendor = vendor; + vendor.Users.Add(user); + + var client = new ApiClient + { + Name = "a", + Application = application, + Key = "key", + Secret = "secret" + }; + application.ApiClients.Add(client); + user.ApiClients.Add(client); + + OpenContext.SaveChangesForTest(); + + // Act + var result = _clientAppRepo.GetUsers(); + + result.ShouldNotBeEmpty(); + result.First() + .ApiClients + .First() + .Application + .ApplicationName + .ShouldBe(application.ApplicationName); + } + } + + + [TestFixture] + public class When_getting_user_by_id : ClientAppRepoTests + { + [Test] + public void Given_there_are_no_users_it_should_return_null() + { + _clientAppRepo.GetUser(-45678).ShouldBeNull(); + } + + [Test] + public void Given_there_is_a_user_with_an_application() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + + user.Vendor = vendor; + vendor.Users.Add(user); + + var client = new ApiClient + { + Name = "a", + Application = application, + Key = "key", + Secret = "secret" + }; + application.ApiClients.Add(client); + user.ApiClients.Add(client); + + OpenContext.SaveChangesForTest(); + + // Act + var result = _clientAppRepo.GetUser(user.UserId); + + result.ShouldNotBeNull(); + result.ApiClients + .First() + .Application + .ApplicationName + .ShouldBe(application.ApplicationName); + } + } + + [TestFixture] + public class When_getting_user_by_name : ClientAppRepoTests + { + [Test] + public void Given_there_are_no_users_it_should_return_null() + { + _clientAppRepo.GetUser("-45678").ShouldBeNull(); + } + + [Test] + public void Given_there_is_a_user_with_an_application() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + + user.Vendor = vendor; + vendor.Users.Add(user); + + var client = new ApiClient + { + Name = "a", + Application = application, + Key = "key", + Secret = "secret" + }; + application.ApiClients.Add(client); + user.ApiClients.Add(client); + + OpenContext.SaveChangesForTest(); + + // Act + var result = _clientAppRepo.GetUser(user.Email); + + result.ShouldNotBeNull(); + result.ApiClients + .First() + .Application + .ApplicationName + .ShouldBe(application.ApplicationName); + } + } + + [TestFixture] + public class When_deleting_a_user : ClientAppRepoTests + { + [Test] + public void Given_there_are_no_users_it_should_not_do_anything() + { + _clientAppRepo.DeleteUser(new User { Email = "-45678" } ); + // Sufficient to show that no exceptions occur + } + + [Test] + public void Given_there_is_a_user_with_an_application_it_should_delet_user_and_clients() + { + // Arrange + var user = new User + { + FullName = "a", + Email = "b@example.com" + }; + OpenContext.Users.Add(user); + + var vendor = new Vendor + { + VendorName = "vendor name" + }; + var application = new Application + { + Vendor = vendor, + ApplicationName = "a", + OperationalContextUri = "uri://ed-fi.org" + }; + vendor.Applications.Add(application); + OpenContext.Vendors.Add(vendor); + OpenContext.Applications.Add(application); + + user.Vendor = vendor; + vendor.Users.Add(user); + + var client = new ApiClient + { + Name = "a", + Application = application, + Key = "key", + Secret = "secret" + }; + application.ApiClients.Add(client); + user.ApiClients.Add(client); + + OpenContext.SaveChangesForTest(); + + // Act + _clientAppRepo.DeleteUser(user); + + // Assert + + OpenContext.Users.Count(x => x.Email == user.Email).ShouldBe(0); + OpenContext.Clients.Count(x => x.Name == client.Name).ShouldBe(0); + } + } + } +} diff --git a/tests/EdFi.Admin.DataAccess.IntegrationTests/appsettings.json b/tests/EdFi.Admin.DataAccess.IntegrationTests/appsettings.json index aa2275fb8c..e3810089f8 100644 --- a/tests/EdFi.Admin.DataAccess.IntegrationTests/appsettings.json +++ b/tests/EdFi.Admin.DataAccess.IntegrationTests/appsettings.json @@ -1,8 +1,6 @@ { - "ApiSettings": { - "Engine": "SQLServer" - }, - "ConnectionStrings": { - "EdFi_Admin": "Server=(local); Database=EdFi_Admin_Test; Trusted_Connection=True; Application Name=EdFi.Admin.DataAccess.IntegrationTests;" - } + "ConnectionStrings": { + "MSSQL": "server=(local);trusted_connection=True;database=EdFi_Admin;persist security info=True;encrypt=false;", + "PostgreSQL": "Host=localhost;port=5432;username=postgres;password=P@ssW0rd;database=EdFi_Admin;" + } } \ No newline at end of file diff --git a/tests/EdFi.Admin.DataAccess.UnitTests/EdFi.Admin.DataAccess.UnitTests.csproj b/tests/EdFi.Admin.DataAccess.UnitTests/EdFi.Admin.DataAccess.UnitTests.csproj index 06945ef47c..e1aab54f44 100644 --- a/tests/EdFi.Admin.DataAccess.UnitTests/EdFi.Admin.DataAccess.UnitTests.csproj +++ b/tests/EdFi.Admin.DataAccess.UnitTests/EdFi.Admin.DataAccess.UnitTests.csproj @@ -16,7 +16,7 @@ true - + diff --git a/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/AccessTokenClientRepoTests.cs b/tests/EdFi.Ods.Api.IntegrationTests/Security/Authentication/AccessTokenSecurityTests.cs similarity index 96% rename from tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/AccessTokenClientRepoTests.cs rename to tests/EdFi.Ods.Api.IntegrationTests/Security/Authentication/AccessTokenSecurityTests.cs index 64188729d7..a3bf9e0bdc 100644 --- a/tests/EdFi.Admin.DataAccess.IntegrationTests/Repositories/AccessTokenClientRepoTests.cs +++ b/tests/EdFi.Ods.Api.IntegrationTests/Security/Authentication/AccessTokenSecurityTests.cs @@ -22,7 +22,7 @@ // ReSharper disable InconsistentNaming -namespace EdFi.Ods.Admin.DataAccess.IntegrationTests.Repositories +namespace EdFi.Ods.Api.IntegrationTests.Security.Authentication { [TestFixture] public class AccessTokenClientRepoTests : TestFixtureBase @@ -44,7 +44,6 @@ protected override void Arrange() AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); _transaction = new TransactionScope(); - Factory = Stub(); var config = new ConfigurationBuilder() .SetBasePath(TestContext.CurrentContext.TestDirectory) @@ -183,7 +182,7 @@ protected override void Arrange() var vendor = LoadAVendor(); var application = LoadAnApplication(vendor, "whatever"); - var apiClient = LoadAnApiClient(application); + var apiClient = LoadAnApiClient(application, 1); _accessToken = LoadAnAccessToken(apiClient, DateTime.UtcNow.AddSeconds(-10)); } @@ -210,7 +209,7 @@ protected override void Arrange() { base.Arrange(); - Client = LoadAnApiClient(null); + Client = LoadAnApiClient(null, 0); AccessToken = LoadAnAccessToken(Client, DateTime.UtcNow.AddSeconds(100)); } @@ -349,13 +348,16 @@ protected override void Arrange() base.Arrange(); var application = LoadAnApplication(null, "Sandbox"); - Client = LoadAnApiClient(application); + Client = LoadAnApiClient(application, 5); AccessToken = LoadAnAccessToken(Client, DateTime.UtcNow.AddSeconds(100)); } protected override void Act() { - Result = SystemUnderTest.GetClientForTokenAsync(AccessToken.Id).Result; + Result = RawApiClientDetailsProvider.GetRawClientDetailsDataAsync(AccessToken.Id) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); ; } [TestFixture] diff --git a/tests/EdFi.TestFixture/DbContextExtensions.cs b/tests/EdFi.TestFixture/DbContextExtensions.cs index 76b191afce..5275e6915e 100644 --- a/tests/EdFi.TestFixture/DbContextExtensions.cs +++ b/tests/EdFi.TestFixture/DbContextExtensions.cs @@ -4,9 +4,8 @@ // See the LICENSE and NOTICES files in the project root for more information. using System; -using System.Data.Entity; -using System.Data.Entity.Validation; -using System.Linq; +using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; namespace EdFi.TestFixture { @@ -23,16 +22,11 @@ public static void CatchAndWriteDbValidationExceptions(Action action) { action(); } - catch (DbEntityValidationException exception) + catch (ValidationException exception) { - var errorTexts = - exception.EntityValidationErrors.SelectMany(x => x.ValidationErrors) - .Select(x => x.ErrorMessage); + var errorTexts = exception.ValidationResult.ErrorMessage; - foreach (var error in errorTexts) - { - Console.WriteLine(error); - } + Console.WriteLine(errorTexts); throw; } diff --git a/tests/EdFi.TestFixture/EdFi.TestFixture.csproj b/tests/EdFi.TestFixture/EdFi.TestFixture.csproj index 02b02307d6..667cf6d0ee 100644 --- a/tests/EdFi.TestFixture/EdFi.TestFixture.csproj +++ b/tests/EdFi.TestFixture/EdFi.TestFixture.csproj @@ -12,8 +12,8 @@ DEBUG;TRACE - - - + + + diff --git a/tests/EdFi.TestFixture/TestDbSet.cs b/tests/EdFi.TestFixture/TestDbSet.cs deleted file mode 100644 index da6d1a0321..0000000000 --- a/tests/EdFi.TestFixture/TestDbSet.cs +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Licensed to the Ed-Fi Alliance under one or more agreements. -// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. -// See the LICENSE and NOTICES files in the project root for more information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; - -namespace EdFi.TestFixture -{ - /// - /// This class may used as a double for any DBSet. - /// Use it with a context interface - /// - /// public class TestContext : IBloggingContext - /// { - /// public TestContext() - /// { - /// this.Blogs = new TestDbSet<Blog>(); - /// this.Posts = new TestDbSet<Post>(); - /// } - /// - /// public DbSet<Blog> Blogs { get; set; } - /// public DbSet<Post> Posts { get; set; } - /// public int SaveChangesCount { get; private set; } - /// public int SaveChanges() - /// { - /// this.SaveChangesCount++; - /// return 1; - /// } - /// } - /// - /// - /// - public class TestDbSet : DbSet, IQueryable, IEnumerable, IDbAsyncEnumerable - where TEntity : class - { - private readonly ObservableCollection _data; - private readonly IQueryable _query; - - public TestDbSet() - { - _data = new ObservableCollection(); - _query = _data.AsQueryable(); - } - - public override ObservableCollection Local - { - get { return _data; } - } - - IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() - { - return new TestDbAsyncEnumerator(_data.GetEnumerator()); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _data.GetEnumerator(); - } - - Type IQueryable.ElementType - { - get { return _query.ElementType; } - } - - Expression IQueryable.Expression - { - get { return _query.Expression; } - } - - IQueryProvider IQueryable.Provider - { - get { return new TestDbAsyncQueryProvider(_query.Provider); } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _data.GetEnumerator(); - } - - public override TEntity Add(TEntity item) - { - _data.Add(item); - return item; - } - - public override IEnumerable AddRange(IEnumerable entities) - { - var tmpEntities = entities.ToArray(); - - foreach (var entity in tmpEntities) - { - _data.Add(entity); - } - - return tmpEntities; - } - - public override TEntity Remove(TEntity item) - { - _data.Remove(item); - return item; - } - - public override TEntity Attach(TEntity item) - { - _data.Add(item); - return item; - } - - public override TEntity Create() - { - return Activator.CreateInstance(); - } - - public override TDerivedEntity Create() - { - return Activator.CreateInstance(); - } - } - - internal class TestDbAsyncQueryProvider : IDbAsyncQueryProvider - { - private readonly IQueryProvider _inner; - - internal TestDbAsyncQueryProvider(IQueryProvider inner) - { - _inner = inner; - } - - public IQueryable CreateQuery(Expression expression) - { - return new TestDbAsyncEnumerable(expression); - } - - public IQueryable CreateQuery(Expression expression) - { - return new TestDbAsyncEnumerable(expression); - } - - public object Execute(Expression expression) - { - return _inner.Execute(expression); - } - - public TResult Execute(Expression expression) - { - return _inner.Execute(expression); - } - - public Task ExecuteAsync(Expression expression, CancellationToken cancellationToken) - { - return Task.FromResult(Execute(expression)); - } - - public Task ExecuteAsync(Expression expression, CancellationToken cancellationToken) - { - return Task.FromResult(Execute(expression)); - } - } - - internal class TestDbAsyncEnumerable : EnumerableQuery, IDbAsyncEnumerable, IQueryable - { - public TestDbAsyncEnumerable(IEnumerable enumerable) - : base(enumerable) { } - - public TestDbAsyncEnumerable(Expression expression) - : base(expression) { } - - public IDbAsyncEnumerator GetAsyncEnumerator() - { - return new TestDbAsyncEnumerator( - this.AsEnumerable() - .GetEnumerator()); - } - - IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() - { - return GetAsyncEnumerator(); - } - - IQueryProvider IQueryable.Provider - { - get { return new TestDbAsyncQueryProvider(this); } - } - } - - internal class TestDbAsyncEnumerator : IDbAsyncEnumerator - { - private readonly IEnumerator _inner; - - public TestDbAsyncEnumerator(IEnumerator inner) - { - _inner = inner; - } - - public void Dispose() - { - _inner.Dispose(); - } - - public Task MoveNextAsync(CancellationToken cancellationToken) - { - return Task.FromResult(_inner.MoveNext()); - } - - public T Current - { - get { return _inner.Current; } - } - - object IDbAsyncEnumerator.Current - { - get { return Current; } - } - } -}