diff --git a/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/GroupsControllerTests.cs b/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/GroupsControllerTests.cs index 898e12b42..6742c6e0a 100644 --- a/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/GroupsControllerTests.cs +++ b/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/GroupsControllerTests.cs @@ -17,7 +17,6 @@ public class GroupsControllerTests : IClassFixture, IAsy public GroupsControllerTests(ScimApplicationFactory factory) { _factory = factory; - _factory.DatabaseName = "test_database_groups"; } public Task InitializeAsync() diff --git a/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/UsersControllerTests.cs b/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/UsersControllerTests.cs index 5a025a2c8..c0e4f3eb7 100644 --- a/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/UsersControllerTests.cs +++ b/bitwarden_license/test/Scim.IntegrationTest/Controllers/v2/UsersControllerTests.cs @@ -17,7 +17,6 @@ public class UsersControllerTests : IClassFixture, IAsyn public UsersControllerTests(ScimApplicationFactory factory) { _factory = factory; - _factory.DatabaseName = "test_database_users"; } public Task InitializeAsync() diff --git a/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json b/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json index e1d3b4b09..f3b46cc55 100644 --- a/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json +++ b/bitwarden_license/test/Scim.IntegrationTest/packages.lock.json @@ -655,14 +655,6 @@ "resolved": "7.0.5", "contentHash": "yMLM/aK1MikVqpjxd7PJ1Pjgztd3VAd26ZHxyjxG3RPeM9cHjvS5tCg9kAAayR6eHmBg0ffZsHdT28WfA5tTlA==" }, - "Microsoft.EntityFrameworkCore.InMemory": { - "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "y3S/A/0uJX7KOhppC3xqyta6Z0PRz0qPLngH5GFu4GZ7/+Sw2u/amf7MavvR5GfZjGabGcohMpsRSahMmpF9gA==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.5" - } - }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", "resolved": "7.0.5", @@ -3072,7 +3064,6 @@ "Common": "[2023.9.0, )", "Identity": "[2023.9.0, )", "Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )", - "Microsoft.EntityFrameworkCore.InMemory": "[7.0.5, )", "Microsoft.Extensions.Configuration": "[6.0.1, )" } }, diff --git a/test/Api.IntegrationTest/Factories/ApiApplicationFactory.cs b/test/Api.IntegrationTest/Factories/ApiApplicationFactory.cs index b0d9594bc..dc27ad506 100644 --- a/test/Api.IntegrationTest/Factories/ApiApplicationFactory.cs +++ b/test/Api.IntegrationTest/Factories/ApiApplicationFactory.cs @@ -2,17 +2,22 @@ using Bit.IntegrationTestCommon.Factories; using IdentityServer4.AccessTokenValidation; using Microsoft.AspNetCore.TestHost; +using Microsoft.Data.Sqlite; namespace Bit.Api.IntegrationTest.Factories; public class ApiApplicationFactory : WebApplicationFactoryBase { private readonly IdentityApplicationFactory _identityApplicationFactory; + private const string _connectionString = "DataSource=:memory:"; public ApiApplicationFactory() { + SqliteConnection = new SqliteConnection(_connectionString); + SqliteConnection.Open(); + _identityApplicationFactory = new IdentityApplicationFactory(); - _identityApplicationFactory.DatabaseName = DatabaseName; + _identityApplicationFactory.SqliteConnection = SqliteConnection; } protected override void ConfigureWebHost(IWebHostBuilder builder) @@ -53,4 +58,10 @@ public class ApiApplicationFactory : WebApplicationFactoryBase { return await _identityApplicationFactory.TokenFromPasswordAsync(email, masterPasswordHash); } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + SqliteConnection.Dispose(); + } } diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs index 006bcc2c2..c5873b12b 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/AccessPoliciesControllerTests.cs @@ -5,6 +5,7 @@ using Bit.Api.IntegrationTest.SecretsManager.Enums; using Bit.Api.Models.Response; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.SecretsManager.Models.Response; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; @@ -661,16 +662,15 @@ public class AccessPoliciesControllerTests : IClassFixture CreateProjectAndServiceAccountAsync(Guid organizationId, bool misMatchOrganization = false) { + var newOrg = new Organization(); + if (misMatchOrganization) + { + newOrg = await _organizationHelper.CreateSmOrganizationAsync(); + } + var project = await _projectRepository.CreateAsync(new Project { - OrganizationId = misMatchOrganization ? Guid.NewGuid() : organizationId, + OrganizationId = misMatchOrganization ? newOrg.Id : organizationId, Name = _mockEncryptedString, }); @@ -1127,7 +1132,7 @@ public class AccessPoliciesControllerTests : IClassFixture SetupUserServiceAccountAccessPolicyRequestAsync( - PermissionType permissionType, Guid organizationId, Guid userId, Guid serviceAccountId) + PermissionType permissionType, Guid userId, Guid serviceAccountId) { if (permissionType == PermissionType.RunAsUserWithPermission) { diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs index 8cea05c5c..4932ad9b9 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/SecretsControllerTests.cs @@ -189,15 +189,17 @@ public class SecretsControllerTests : IClassFixture, IAsy { var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); + var anotherOrg = await _organizationHelper.CreateSmOrganizationAsync(); - var project = await _projectRepository.CreateAsync(new Project { Name = "123" }); + var project = + await _projectRepository.CreateAsync(new Project { Name = "123", OrganizationId = anotherOrg.Id }); var request = new SecretCreateRequestModel { - ProjectIds = new Guid[] { project.Id }, + ProjectIds = new[] { project.Id }, Key = _mockEncryptedString, Value = _mockEncryptedString, - Note = _mockEncryptedString, + Note = _mockEncryptedString }; var response = await _client.PostAsJsonAsync($"/organizations/{org.Id}/secrets", request); @@ -594,8 +596,9 @@ public class SecretsControllerTests : IClassFixture, IAsy { var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); + var anotherOrg = await _organizationHelper.CreateSmOrganizationAsync(); - var project = await _projectRepository.CreateAsync(new Project { Name = "123" }); + var project = await _projectRepository.CreateAsync(new Project { Name = "123", OrganizationId = anotherOrg.Id }); var secret = await _secretRepository.CreateAsync(new Secret { @@ -698,7 +701,7 @@ public class SecretsControllerTests : IClassFixture, IAsy var (org, _) = await _organizationHelper.Initialize(true, true, true); await LoginAsync(_email); - var (project, secretIds) = await CreateSecretsAsync(org.Id, 3); + var (project, secretIds) = await CreateSecretsAsync(org.Id); if (permissionType == PermissionType.RunAsUserWithPermission) { @@ -709,24 +712,22 @@ public class SecretsControllerTests : IClassFixture, IAsy { new UserProjectAccessPolicy { - GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true, - }, + GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true + } }; await _accessPolicyRepository.CreateManyAsync(accessPolicies); } - var response = await _client.PostAsJsonAsync($"/secrets/delete", secretIds); + var response = await _client.PostAsJsonAsync("/secrets/delete", secretIds); response.EnsureSuccessStatusCode(); var results = await response.Content.ReadFromJsonAsync>(); - Assert.NotNull(results); - - var index = 0; + Assert.NotNull(results?.Data); + Assert.Equal(secretIds.Count, results!.Data.Count()); foreach (var result in results!.Data) { - Assert.Equal(secretIds[index], result.Id); + Assert.Contains(result.Id, secretIds); Assert.Null(result.Error); - index++; } var secrets = await _secretRepository.GetManyByIds(secretIds); diff --git a/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs b/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs index 8150dced5..86e8c81b1 100644 --- a/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs +++ b/test/Api.IntegrationTest/SecretsManager/Controllers/ServiceAccountsControllerTests.cs @@ -704,14 +704,14 @@ public class ServiceAccountsControllerTests : IClassFixture CreateSmOrganizationAsync() + { + var email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; + await _factory.LoginWithNewAccount(email); + var (organization, owner) = + await OrganizationTestHelpers.SignUpAsync(_factory, ownerEmail: email, billingEmail: email); + return organization; + } + public async Task<(string email, OrganizationUser orgUser)> CreateNewUser(OrganizationUserType userType, bool accessSecrets) { var email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; diff --git a/test/Api.IntegrationTest/packages.lock.json b/test/Api.IntegrationTest/packages.lock.json index 4dacbaa00..96501c1ad 100644 --- a/test/Api.IntegrationTest/packages.lock.json +++ b/test/Api.IntegrationTest/packages.lock.json @@ -747,14 +747,6 @@ "resolved": "7.0.5", "contentHash": "yMLM/aK1MikVqpjxd7PJ1Pjgztd3VAd26ZHxyjxG3RPeM9cHjvS5tCg9kAAayR6eHmBg0ffZsHdT28WfA5tTlA==" }, - "Microsoft.EntityFrameworkCore.InMemory": { - "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "y3S/A/0uJX7KOhppC3xqyta6Z0PRz0qPLngH5GFu4GZ7/+Sw2u/amf7MavvR5GfZjGabGcohMpsRSahMmpF9gA==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.5" - } - }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", "resolved": "7.0.5", @@ -3255,7 +3247,6 @@ "Common": "[2023.9.0, )", "Identity": "[2023.9.0, )", "Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )", - "Microsoft.EntityFrameworkCore.InMemory": "[7.0.5, )", "Microsoft.Extensions.Configuration": "[6.0.1, )" } }, diff --git a/test/Identity.IntegrationTest/packages.lock.json b/test/Identity.IntegrationTest/packages.lock.json index 49e914b2d..4d207ddc8 100644 --- a/test/Identity.IntegrationTest/packages.lock.json +++ b/test/Identity.IntegrationTest/packages.lock.json @@ -655,14 +655,6 @@ "resolved": "7.0.5", "contentHash": "yMLM/aK1MikVqpjxd7PJ1Pjgztd3VAd26ZHxyjxG3RPeM9cHjvS5tCg9kAAayR6eHmBg0ffZsHdT28WfA5tTlA==" }, - "Microsoft.EntityFrameworkCore.InMemory": { - "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "y3S/A/0uJX7KOhppC3xqyta6Z0PRz0qPLngH5GFu4GZ7/+Sw2u/amf7MavvR5GfZjGabGcohMpsRSahMmpF9gA==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.5" - } - }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", "resolved": "7.0.5", @@ -3072,7 +3064,6 @@ "Common": "[2023.9.0, )", "Identity": "[2023.9.0, )", "Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )", - "Microsoft.EntityFrameworkCore.InMemory": "[7.0.5, )", "Microsoft.Extensions.Configuration": "[6.0.1, )" } }, diff --git a/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs b/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs index 89a6041c3..fe8e9c2f1 100644 --- a/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs +++ b/test/IntegrationTestCommon/Factories/WebApplicationFactoryBase.cs @@ -7,6 +7,7 @@ using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -19,7 +20,6 @@ namespace Bit.IntegrationTestCommon.Factories; public static class FactoryConstants { - public const string DefaultDatabaseName = "test_database"; public const string WhitelistedIp = "1.1.1.1"; } @@ -27,14 +27,16 @@ public abstract class WebApplicationFactoryBase : WebApplicationFactory where T : class { /// - /// The database name to use for this instance of the factory. By default it will use a shared database name so all instances will connect to the same database during it's lifetime. + /// The database to use for this instance of the factory. By default it will use a shared database so all instances will connect to the same database during it's lifetime. /// /// /// This will need to be set BEFORE using the Server property /// - public string DatabaseName { get; set; } = Guid.NewGuid().ToString(); + public SqliteConnection SqliteConnection { get; set; } private readonly List> _configureTestServices = new(); + private bool _handleSqliteDisposal { get; set; } + public void SubstitueService(Action mockService) where TService : class @@ -52,10 +54,17 @@ public abstract class WebApplicationFactoryBase : WebApplicationFactory } /// - /// Configure the web host to use an EF in memory database + /// Configure the web host to use a SQLite in memory database /// protected override void ConfigureWebHost(IWebHostBuilder builder) { + if (SqliteConnection == null) + { + SqliteConnection = new SqliteConnection("DataSource=:memory:"); + SqliteConnection.Open(); + _handleSqliteDisposal = true; + } + builder.ConfigureAppConfiguration(c => { c.SetBasePath(AppContext.BaseDirectory) @@ -89,11 +98,13 @@ public abstract class WebApplicationFactoryBase : WebApplicationFactory services.AddScoped(services => { return new DbContextOptionsBuilder() - .UseInMemoryDatabase(DatabaseName) + .UseSqlite(SqliteConnection) .UseApplicationServiceProvider(services) .Options; }); + MigrateDbContext(services); + // QUESTION: The normal licensing service should run fine on developer machines but not in CI // should we have a fork here to leave the normal service for developers? // TODO: Eventually add the license file to CI @@ -182,4 +193,23 @@ public abstract class WebApplicationFactoryBase : WebApplicationFactory var scope = Services.CreateScope(); return scope.ServiceProvider.GetRequiredService(); } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (_handleSqliteDisposal) + { + SqliteConnection.Dispose(); + } + } + + private static void MigrateDbContext(IServiceCollection serviceCollection) where TContext : DbContext + { + var serviceProvider = serviceCollection.BuildServiceProvider(); + using var scope = serviceProvider.CreateScope(); + var services = scope.ServiceProvider; + var context = services.GetService(); + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + } } diff --git a/test/IntegrationTestCommon/IntegrationTestCommon.csproj b/test/IntegrationTestCommon/IntegrationTestCommon.csproj index f8a2b236a..b4c123682 100644 --- a/test/IntegrationTestCommon/IntegrationTestCommon.csproj +++ b/test/IntegrationTestCommon/IntegrationTestCommon.csproj @@ -6,7 +6,6 @@ - diff --git a/test/IntegrationTestCommon/packages.lock.json b/test/IntegrationTestCommon/packages.lock.json index 6e1296d5c..9e34795cf 100644 --- a/test/IntegrationTestCommon/packages.lock.json +++ b/test/IntegrationTestCommon/packages.lock.json @@ -13,15 +13,6 @@ "Microsoft.Extensions.Hosting": "6.0.1" } }, - "Microsoft.EntityFrameworkCore.InMemory": { - "type": "Direct", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "y3S/A/0uJX7KOhppC3xqyta6Z0PRz0qPLngH5GFu4GZ7/+Sw2u/amf7MavvR5GfZjGabGcohMpsRSahMmpF9gA==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "7.0.5" - } - }, "Microsoft.Extensions.Configuration": { "type": "Direct", "requested": "[6.0.1, )",