mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +01:00
[PM-2444] Add Pipeline for Testing All Database Variants in CI (#2471)
* Add Pipeline * Fix Lint * Added a Change * Update Pipeline * Add Multi-Version Support * Use Profile Switch for each profile * Fix MySql * Debug MySql * Use Proper Seperator * Add Allow User Variables=true * Pipeline Work * Expand Config for Postgres * Change Config Key * Add Debug Step * Fix Debug Step * Fix Tests * Add Sleep * Fix Tests * Fix SQL Server Tests * Add Sqlite * Use Context Property * Fix Tests * Fix Test Logger * Update AccountRevisionDate Check * Fix Postgres Time Issues * Formatting and Pipeline Update * Remove Unneeded SqlServer Setting * Update .github/workflows/infrastructure-tests.yml Co-authored-by: mimartin12 <77340197+mimartin12@users.noreply.github.com> --------- Co-authored-by: mimartin12 <77340197+mimartin12@users.noreply.github.com>
This commit is contained in:
parent
6e6432c1d0
commit
61a0efbdfc
117
.github/workflows/infrastructure-tests.yml
vendored
Normal file
117
.github/workflows/infrastructure-tests.yml
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
name: Run Database Infrastructure Tests
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches-ignore:
|
||||||
|
- 'l10n_master'
|
||||||
|
- 'gh-pages'
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/infrastructure-tests.yml' # This file
|
||||||
|
- 'src/Sql/**' # SQL Server Database Changes
|
||||||
|
- 'util/Migrator/**' # New SQL Server Migrations
|
||||||
|
- 'util/MySqlMigrations/**' # Changes to MySQL
|
||||||
|
- 'util/PostgresMigrations/**' # Changes to Postgres
|
||||||
|
- 'util/SqliteMigrations/**' # Changes to Sqlite
|
||||||
|
- 'src/Infrastructure.Dapper/**' # Changes to SQL Server Dapper Repository Layer
|
||||||
|
- 'src/Infrastructure.EntityFramework/**' # Changes to Entity Framework Repository Layer
|
||||||
|
- 'test/Infrastructure.IntegrationTest/**' # Any changes to the tests
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'rc'
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/infrastructure-tests.yml' # This file
|
||||||
|
- 'src/Sql/**' # SQL Server Database Changes
|
||||||
|
- 'util/Migrator/**' # New SQL Server Migrations
|
||||||
|
- 'util/MySqlMigrations/**' # Changes to MySQL
|
||||||
|
- 'util/PostgresMigrations/**' # Changes to Postgres
|
||||||
|
- 'util/SqliteMigrations/**' # Changes to Sqlite
|
||||||
|
- 'src/Infrastructure.Dapper/**' # Changes to SQL Server Dapper Repository Layer
|
||||||
|
- 'src/Infrastructure.EntityFramework/**' # Changes to Entity Framework Repository Layer
|
||||||
|
- 'test/Infrastructure.IntegrationTest/**' # Any changes to the tests
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: 'Run Infrastructure.IntegrationTest'
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
||||||
|
|
||||||
|
- name: Set up dotnet
|
||||||
|
uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # v3.0.3
|
||||||
|
with:
|
||||||
|
dotnet-version: '6.0.x'
|
||||||
|
|
||||||
|
- name: Restore Tools
|
||||||
|
run: dotnet tool restore
|
||||||
|
|
||||||
|
- name: Compose Databases
|
||||||
|
working-directory: 'dev'
|
||||||
|
# We could think about not using profiles and pulling images directly to cover multiple versions
|
||||||
|
run: |
|
||||||
|
cp .env.example .env
|
||||||
|
docker compose --profile mssql --profile postgres --profile mysql up -d
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
# I've seen the SQL Server container not be ready for commands right after starting up and just needing a bit longer to be ready
|
||||||
|
- name: Sleep
|
||||||
|
run: sleep 15s
|
||||||
|
|
||||||
|
- name: Migrate SQL Server
|
||||||
|
working-directory: 'dev'
|
||||||
|
run: "pwsh ./migrate.ps1"
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Migrate MySQL
|
||||||
|
working-directory: 'util/MySqlMigrations'
|
||||||
|
run: 'dotnet ef database update --connection "$CONN_STR" -- --GlobalSettings:MySql:ConnectionString="$CONN_STR"'
|
||||||
|
env:
|
||||||
|
CONN_STR: "server=localhost;uid=root;pwd=SET_A_PASSWORD_HERE_123;database=vault_dev;Allow User Variables=true"
|
||||||
|
|
||||||
|
- name: Migrate Postgres
|
||||||
|
working-directory: 'util/PostgresMigrations'
|
||||||
|
run: 'dotnet ef database update --connection "$CONN_STR" -- --GlobalSettings:PostgreSql:ConnectionString="$CONN_STR"'
|
||||||
|
env:
|
||||||
|
CONN_STR: "Host=localhost;Username=postgres;Password=SET_A_PASSWORD_HERE_123;Database=vault_dev"
|
||||||
|
|
||||||
|
- name: Migrate Sqlite
|
||||||
|
working-directory: 'util/SqliteMigrations'
|
||||||
|
run: 'dotnet ef database update --connection "$CONN_STR" -- --GlobalSettings:Sqlite:ConnectionString="$CONN_STR"'
|
||||||
|
env:
|
||||||
|
CONN_STR: "Data Source=${{ runner.temp }}/test.db"
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
working-directory: 'test/Infrastructure.IntegrationTest'
|
||||||
|
env:
|
||||||
|
# Default Postgres:
|
||||||
|
BW_TEST_DATABASES__0__TYPE: "Postgres"
|
||||||
|
BW_TEST_DATABASES__0__CONNECTIONSTRING: "Host=localhost;Username=postgres;Password=SET_A_PASSWORD_HERE_123;Database=vault_dev"
|
||||||
|
# Default MySql
|
||||||
|
BW_TEST_DATABASES__1__TYPE: "MySql"
|
||||||
|
BW_TEST_DATABASES__1__CONNECTIONSTRING: "server=localhost;uid=root;pwd=SET_A_PASSWORD_HERE_123;database=vault_dev"
|
||||||
|
# Default Dapper SqlServer
|
||||||
|
BW_TEST_DATABASES__2__TYPE: "SqlServer"
|
||||||
|
BW_TEST_DATABASES__2__CONNECTIONSTRING: "Server=localhost;Database=vault_dev;User Id=SA;Password=SET_A_PASSWORD_HERE_123;Encrypt=True;TrustServerCertificate=True;"
|
||||||
|
# Default Sqlite
|
||||||
|
BW_TEST_DATABASES__3__TYPE: "Sqlite"
|
||||||
|
BW_TEST_DATABASES__3__CONNECTIONSTRING: "Data Source=${{ runner.temp }}/test.db"
|
||||||
|
run: dotnet test --logger "trx;LogFileName=infrastructure-test-results.trx"
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Report test results
|
||||||
|
uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: Test Results
|
||||||
|
path: "**/*-test-results.trx"
|
||||||
|
reporter: dotnet-trx
|
||||||
|
fail-on-error: true
|
||||||
|
|
||||||
|
- name: Docker compose down
|
||||||
|
if: always()
|
||||||
|
working-directory: "dev"
|
||||||
|
run: docker compose down
|
||||||
|
shell: pwsh
|
@ -428,6 +428,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
// Remove all existing ones that are no longer requested
|
// Remove all existing ones that are no longer requested
|
||||||
var requestedUserIds = requestedUsers.Select(u => u.Id);
|
var requestedUserIds = requestedUsers.Select(u => u.Id);
|
||||||
dbContext.CollectionUsers.RemoveRange(existingCollectionUsers.Where(cu => !requestedUserIds.Contains(cu.OrganizationUserId)));
|
dbContext.CollectionUsers.RemoveRange(existingCollectionUsers.Where(cu => !requestedUserIds.Contains(cu.OrganizationUserId)));
|
||||||
|
// Need to save the new collection users before running the bump revision code
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(id, organizationId);
|
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(id, organizationId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
@ -64,9 +64,10 @@ public static class DatabaseContextExtensions
|
|||||||
from cg in cg_g.DefaultIfEmpty()
|
from cg in cg_g.DefaultIfEmpty()
|
||||||
where ou.OrganizationId == organizationId &&
|
where ou.OrganizationId == organizationId &&
|
||||||
ou.Status == OrganizationUserStatusType.Confirmed &&
|
ou.Status == OrganizationUserStatusType.Confirmed &&
|
||||||
cg.CollectionId != null &&
|
(cu.CollectionId != null ||
|
||||||
ou.AccessAll == true &&
|
cg.CollectionId != null ||
|
||||||
g.AccessAll == true
|
ou.AccessAll == true ||
|
||||||
|
g.AccessAll == true)
|
||||||
select u;
|
select u;
|
||||||
|
|
||||||
var users = await query.ToListAsync();
|
var users = await query.ToListAsync();
|
||||||
|
@ -112,6 +112,8 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
|
|||||||
var entity = Mapper.Map<Cipher>((Core.Vault.Entities.Cipher)cipher);
|
var entity = Mapper.Map<Cipher>((Core.Vault.Entities.Cipher)cipher);
|
||||||
await dbContext.AddAsync(entity);
|
await dbContext.AddAsync(entity);
|
||||||
|
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
if (cipher.OrganizationId.HasValue)
|
if (cipher.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
||||||
|
@ -16,7 +16,7 @@ public class EmergencyAccessRepositoriesTests
|
|||||||
var grantorUser = await userRepository.CreateAsync(new User
|
var grantorUser = await userRepository.CreateAsync(new User
|
||||||
{
|
{
|
||||||
Name = "Test Grantor User",
|
Name = "Test Grantor User",
|
||||||
Email = "test+grantor@email.com",
|
Email = $"test+grantor{Guid.NewGuid()}@email.com",
|
||||||
ApiKey = "TEST",
|
ApiKey = "TEST",
|
||||||
SecurityStamp = "stamp",
|
SecurityStamp = "stamp",
|
||||||
});
|
});
|
||||||
@ -24,7 +24,7 @@ public class EmergencyAccessRepositoriesTests
|
|||||||
var granteeUser = await userRepository.CreateAsync(new User
|
var granteeUser = await userRepository.CreateAsync(new User
|
||||||
{
|
{
|
||||||
Name = "Test Grantee User",
|
Name = "Test Grantee User",
|
||||||
Email = "test+grantee@email.com",
|
Email = $"test+grantee{Guid.NewGuid()}@email.com",
|
||||||
ApiKey = "TEST",
|
ApiKey = "TEST",
|
||||||
SecurityStamp = "stamp",
|
SecurityStamp = "stamp",
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,31 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Bit.Core.Enums;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Bit.Infrastructure.IntegrationTest;
|
namespace Bit.Infrastructure.IntegrationTest;
|
||||||
|
|
||||||
|
public class Database
|
||||||
|
{
|
||||||
|
public SupportedDatabaseProviders Type { get; set; }
|
||||||
|
public string ConnectionString { get; set; } = default!;
|
||||||
|
public bool UseEf { get; set; }
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TypedConfig
|
||||||
|
{
|
||||||
|
public Database[] Databases { get; set; } = default!;
|
||||||
|
}
|
||||||
|
|
||||||
public static class ConfigurationExtensions
|
public static class ConfigurationExtensions
|
||||||
{
|
{
|
||||||
public static bool TryGetConnectionString(this IConfiguration config, string key, out string connectionString)
|
public static Database[] GetDatabases(this IConfiguration config)
|
||||||
{
|
{
|
||||||
connectionString = config[key];
|
var typedConfig = config.Get<TypedConfig>();
|
||||||
if (string.IsNullOrEmpty(connectionString))
|
if (typedConfig.Databases == null)
|
||||||
{
|
{
|
||||||
return false;
|
return Array.Empty<Database>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return typedConfig.Databases.Where(d => d.Enabled).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Infrastructure.Dapper;
|
using Bit.Infrastructure.Dapper;
|
||||||
using Bit.Infrastructure.EntityFramework;
|
using Bit.Infrastructure.EntityFramework;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -45,42 +46,39 @@ public class DatabaseDataAttribute : DataAttribute
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (config.TryGetConnectionString(DatabaseTheoryAttribute.DapperSqlServerKey, out var dapperSqlServerConnectionString))
|
var databases = config.GetDatabases();
|
||||||
|
|
||||||
|
foreach (var database in databases)
|
||||||
{
|
{
|
||||||
var dapperSqlServerCollection = new ServiceCollection();
|
if (database.Type == SupportedDatabaseProviders.SqlServer && !database.UseEf)
|
||||||
dapperSqlServerCollection.AddLogging(configureLogging);
|
|
||||||
dapperSqlServerCollection.AddDapperRepositories(SelfHosted);
|
|
||||||
var globalSettings = new GlobalSettings
|
|
||||||
{
|
{
|
||||||
DatabaseProvider = "sqlServer",
|
var dapperSqlServerCollection = new ServiceCollection();
|
||||||
SqlServer = new GlobalSettings.SqlSettings
|
dapperSqlServerCollection.AddLogging(configureLogging);
|
||||||
|
dapperSqlServerCollection.AddDapperRepositories(SelfHosted);
|
||||||
|
var globalSettings = new GlobalSettings
|
||||||
{
|
{
|
||||||
ConnectionString = dapperSqlServerConnectionString,
|
DatabaseProvider = "sqlServer",
|
||||||
},
|
SqlServer = new GlobalSettings.SqlSettings
|
||||||
};
|
{
|
||||||
dapperSqlServerCollection.AddSingleton(globalSettings);
|
ConnectionString = database.ConnectionString,
|
||||||
dapperSqlServerCollection.AddSingleton<IGlobalSettings>(globalSettings);
|
},
|
||||||
yield return dapperSqlServerCollection.BuildServiceProvider();
|
};
|
||||||
}
|
dapperSqlServerCollection.AddSingleton(globalSettings);
|
||||||
|
dapperSqlServerCollection.AddSingleton<IGlobalSettings>(globalSettings);
|
||||||
if (config.TryGetConnectionString(DatabaseTheoryAttribute.EfPostgresKey, out var efPostgresConnectionString))
|
dapperSqlServerCollection.AddSingleton<ITestDatabaseHelper>(_ => new DapperSqlServerTestDatabaseHelper(database));
|
||||||
{
|
dapperSqlServerCollection.AddDataProtection();
|
||||||
var efPostgresCollection = new ServiceCollection();
|
yield return dapperSqlServerCollection.BuildServiceProvider();
|
||||||
efPostgresCollection.AddLogging(configureLogging);
|
}
|
||||||
efPostgresCollection.SetupEntityFramework(efPostgresConnectionString, SupportedDatabaseProviders.Postgres);
|
else
|
||||||
efPostgresCollection.AddPasswordManagerEFRepositories(SelfHosted);
|
{
|
||||||
efPostgresCollection.AddTransient<ITestDatabaseHelper, EfTestDatabaseHelper>();
|
var efCollection = new ServiceCollection();
|
||||||
yield return efPostgresCollection.BuildServiceProvider();
|
efCollection.AddLogging(configureLogging);
|
||||||
}
|
efCollection.SetupEntityFramework(database.ConnectionString, database.Type);
|
||||||
|
efCollection.AddPasswordManagerEFRepositories(SelfHosted);
|
||||||
if (config.TryGetConnectionString(DatabaseTheoryAttribute.EfMySqlKey, out var efMySqlConnectionString))
|
efCollection.AddTransient<ITestDatabaseHelper>(sp => new EfTestDatabaseHelper(sp.GetRequiredService<DatabaseContext>(), database));
|
||||||
{
|
efCollection.AddDataProtection();
|
||||||
var efMySqlCollection = new ServiceCollection();
|
yield return efCollection.BuildServiceProvider();
|
||||||
efMySqlCollection.AddLogging(configureLogging);
|
}
|
||||||
efMySqlCollection.SetupEntityFramework(efMySqlConnectionString, SupportedDatabaseProviders.MySql);
|
|
||||||
efMySqlCollection.AddPasswordManagerEFRepositories(SelfHosted);
|
|
||||||
efMySqlCollection.AddTransient<ITestDatabaseHelper, EfTestDatabaseHelper>();
|
|
||||||
yield return efMySqlCollection.BuildServiceProvider();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,24 +7,18 @@ public class DatabaseTheoryAttribute : TheoryAttribute
|
|||||||
{
|
{
|
||||||
private static IConfiguration? _cachedConfiguration;
|
private static IConfiguration? _cachedConfiguration;
|
||||||
|
|
||||||
public const string DapperSqlServerKey = "Dapper:SqlServer";
|
|
||||||
public const string EfPostgresKey = "Ef:Postgres";
|
|
||||||
public const string EfMySqlKey = "Ef:MySql";
|
|
||||||
|
|
||||||
public DatabaseTheoryAttribute()
|
public DatabaseTheoryAttribute()
|
||||||
{
|
{
|
||||||
if (!HasAnyDatabaseSetup())
|
if (!HasAnyDatabaseSetup())
|
||||||
{
|
{
|
||||||
Skip = "No database connections strings setup.";
|
Skip = "No databases setup.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasAnyDatabaseSetup()
|
private static bool HasAnyDatabaseSetup()
|
||||||
{
|
{
|
||||||
var config = GetConfiguration();
|
var config = GetConfiguration();
|
||||||
return config.TryGetConnectionString(DapperSqlServerKey, out _) ||
|
return config.GetDatabases().Length > 0;
|
||||||
config.TryGetConnectionString(EfPostgresKey, out _) ||
|
|
||||||
config.TryGetConnectionString(EfMySqlKey, out _);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IConfiguration GetConfiguration()
|
public static IConfiguration GetConfiguration()
|
||||||
|
@ -16,7 +16,7 @@ public class OrganizationUserRepositoryTests
|
|||||||
var user = await userRepository.CreateAsync(new User
|
var user = await userRepository.CreateAsync(new User
|
||||||
{
|
{
|
||||||
Name = "Test User",
|
Name = "Test User",
|
||||||
Email = "test@email.com",
|
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||||
ApiKey = "TEST",
|
ApiKey = "TEST",
|
||||||
SecurityStamp = "stamp",
|
SecurityStamp = "stamp",
|
||||||
});
|
});
|
||||||
@ -24,6 +24,8 @@ public class OrganizationUserRepositoryTests
|
|||||||
var organization = await organizationRepository.CreateAsync(new Organization
|
var organization = await organizationRepository.CreateAsync(new Organization
|
||||||
{
|
{
|
||||||
Name = "Test Org",
|
Name = "Test Org",
|
||||||
|
BillingEmail = user.Email, // TODO: EF does not enfore this being NOT NULL
|
||||||
|
Plan = "Test", // TODO: EF does not enforce this being NOT NULl
|
||||||
});
|
});
|
||||||
|
|
||||||
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
@ -50,7 +52,7 @@ public class OrganizationUserRepositoryTests
|
|||||||
var user1 = await userRepository.CreateAsync(new User
|
var user1 = await userRepository.CreateAsync(new User
|
||||||
{
|
{
|
||||||
Name = "Test User 1",
|
Name = "Test User 1",
|
||||||
Email = "test1@email.com",
|
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||||
ApiKey = "TEST",
|
ApiKey = "TEST",
|
||||||
SecurityStamp = "stamp",
|
SecurityStamp = "stamp",
|
||||||
});
|
});
|
||||||
@ -58,7 +60,7 @@ public class OrganizationUserRepositoryTests
|
|||||||
var user2 = await userRepository.CreateAsync(new User
|
var user2 = await userRepository.CreateAsync(new User
|
||||||
{
|
{
|
||||||
Name = "Test User 2",
|
Name = "Test User 2",
|
||||||
Email = "test1@email.com",
|
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||||
ApiKey = "TEST",
|
ApiKey = "TEST",
|
||||||
SecurityStamp = "stamp",
|
SecurityStamp = "stamp",
|
||||||
});
|
});
|
||||||
@ -66,18 +68,22 @@ public class OrganizationUserRepositoryTests
|
|||||||
var organization = await organizationRepository.CreateAsync(new Organization
|
var organization = await organizationRepository.CreateAsync(new Organization
|
||||||
{
|
{
|
||||||
Name = "Test Org",
|
Name = "Test Org",
|
||||||
|
BillingEmail = user1.Email, // TODO: EF does not enforce this being NOT NULl
|
||||||
|
Plan = "Test", // TODO: EF does not enforce this being NOT NULl
|
||||||
});
|
});
|
||||||
|
|
||||||
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
UserId = user1.Id,
|
UserId = user1.Id,
|
||||||
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
});
|
});
|
||||||
|
|
||||||
var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
UserId = user2.Id,
|
UserId = user2.Id,
|
||||||
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
});
|
});
|
||||||
|
|
||||||
helper.ClearTracker();
|
helper.ClearTracker();
|
||||||
|
@ -4,6 +4,7 @@ namespace Bit.Infrastructure.IntegrationTest;
|
|||||||
|
|
||||||
public interface ITestDatabaseHelper
|
public interface ITestDatabaseHelper
|
||||||
{
|
{
|
||||||
|
Database Info { get; }
|
||||||
void ClearTracker();
|
void ClearTracker();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,11 +12,14 @@ public class EfTestDatabaseHelper : ITestDatabaseHelper
|
|||||||
{
|
{
|
||||||
private readonly DatabaseContext _databaseContext;
|
private readonly DatabaseContext _databaseContext;
|
||||||
|
|
||||||
public EfTestDatabaseHelper(DatabaseContext databaseContext)
|
public EfTestDatabaseHelper(DatabaseContext databaseContext, Database database)
|
||||||
{
|
{
|
||||||
_databaseContext = databaseContext;
|
_databaseContext = databaseContext;
|
||||||
|
Info = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Database Info { get; }
|
||||||
|
|
||||||
public void ClearTracker()
|
public void ClearTracker()
|
||||||
{
|
{
|
||||||
_databaseContext.ChangeTracker.Clear();
|
_databaseContext.ChangeTracker.Clear();
|
||||||
@ -24,11 +28,13 @@ public class EfTestDatabaseHelper : ITestDatabaseHelper
|
|||||||
|
|
||||||
public class DapperSqlServerTestDatabaseHelper : ITestDatabaseHelper
|
public class DapperSqlServerTestDatabaseHelper : ITestDatabaseHelper
|
||||||
{
|
{
|
||||||
public DapperSqlServerTestDatabaseHelper()
|
public DapperSqlServerTestDatabaseHelper(Database database)
|
||||||
{
|
{
|
||||||
|
Info = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Database Info { get; }
|
||||||
|
|
||||||
public void ClearTracker()
|
public void ClearTracker()
|
||||||
{
|
{
|
||||||
// There are no tracked entities in Dapper SQL Server
|
// There are no tracked entities in Dapper SQL Server
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Vault.Entities;
|
using Bit.Core.Vault.Entities;
|
||||||
using Bit.Core.Vault.Enums;
|
using Bit.Core.Vault.Enums;
|
||||||
@ -20,7 +21,7 @@ public class CipherRepositoryTests
|
|||||||
var user = await userRepository.CreateAsync(new User
|
var user = await userRepository.CreateAsync(new User
|
||||||
{
|
{
|
||||||
Name = "Test User",
|
Name = "Test User",
|
||||||
Email = "test@email.com",
|
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||||
ApiKey = "TEST",
|
ApiKey = "TEST",
|
||||||
SecurityStamp = "stamp",
|
SecurityStamp = "stamp",
|
||||||
});
|
});
|
||||||
@ -29,6 +30,7 @@ public class CipherRepositoryTests
|
|||||||
{
|
{
|
||||||
Type = CipherType.Login,
|
Type = CipherType.Login,
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
|
Data = "", // TODO: EF does not enforce this as NOT NULL
|
||||||
});
|
});
|
||||||
|
|
||||||
helper.ClearTracker();
|
helper.ClearTracker();
|
||||||
@ -55,21 +57,27 @@ public class CipherRepositoryTests
|
|||||||
var user = await userRepository.CreateAsync(new User
|
var user = await userRepository.CreateAsync(new User
|
||||||
{
|
{
|
||||||
Name = "Test User",
|
Name = "Test User",
|
||||||
Email = "test@email.com",
|
Email = $"test+{Guid.NewGuid()}@email.com",
|
||||||
ApiKey = "TEST",
|
ApiKey = "TEST",
|
||||||
SecurityStamp = "stamp",
|
SecurityStamp = "stamp",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
helper.ClearTracker();
|
||||||
|
|
||||||
|
user = await userRepository.GetByIdAsync(user.Id);
|
||||||
|
|
||||||
var organization = await organizationRepository.CreateAsync(new Organization
|
var organization = await organizationRepository.CreateAsync(new Organization
|
||||||
{
|
{
|
||||||
Name = "Test Organization",
|
Name = "Test Organization",
|
||||||
|
BillingEmail = user.Email,
|
||||||
|
Plan = "Test" // TODO: EF does not enforce this as NOT NULL
|
||||||
});
|
});
|
||||||
|
|
||||||
await organizationUserRepository.CreateAsync(new OrganizationUser
|
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
Status = OrganizationUserStatusType.Accepted,
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
Type = OrganizationUserType.Owner,
|
Type = OrganizationUserType.Owner,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,12 +87,27 @@ public class CipherRepositoryTests
|
|||||||
OrganizationId = organization.Id
|
OrganizationId = organization.Id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await Task.Delay(100);
|
||||||
|
|
||||||
|
await collectionRepository.UpdateUsersAsync(collection.Id, new[]
|
||||||
|
{
|
||||||
|
new CollectionAccessSelection
|
||||||
|
{
|
||||||
|
Id = orgUser.Id,
|
||||||
|
HidePasswords = true,
|
||||||
|
ReadOnly = true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
helper.ClearTracker();
|
helper.ClearTracker();
|
||||||
|
|
||||||
|
await Task.Delay(100);
|
||||||
|
|
||||||
await cipherRepository.CreateAsync(new CipherDetails
|
await cipherRepository.CreateAsync(new CipherDetails
|
||||||
{
|
{
|
||||||
Type = CipherType.Login,
|
Type = CipherType.Login,
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
|
Data = "", // TODO: EF does not enforce this as NOT NULL
|
||||||
}, new List<Guid>
|
}, new List<Guid>
|
||||||
{
|
{
|
||||||
collection.Id,
|
collection.Id,
|
||||||
@ -92,7 +115,8 @@ public class CipherRepositoryTests
|
|||||||
|
|
||||||
var updatedUser = await userRepository.GetByIdAsync(user.Id);
|
var updatedUser = await userRepository.GetByIdAsync(user.Id);
|
||||||
|
|
||||||
Assert.NotEqual(updatedUser.AccountRevisionDate, user.AccountRevisionDate);
|
Assert.True(updatedUser.AccountRevisionDate - user.AccountRevisionDate > TimeSpan.Zero,
|
||||||
|
"The AccountRevisionDate is expected to be changed");
|
||||||
|
|
||||||
var collectionCiphers = await collectionCipherRepository.GetManyByOrganizationIdAsync(organization.Id);
|
var collectionCiphers = await collectionCipherRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||||
Assert.NotEmpty(collectionCiphers);
|
Assert.NotEmpty(collectionCiphers);
|
||||||
|
@ -7,15 +7,20 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace Bit.MySqlMigrations;
|
namespace Bit.MySqlMigrations;
|
||||||
|
|
||||||
public static class GlobalSettingsFactory
|
public class GlobalSettingsFactory
|
||||||
{
|
{
|
||||||
public static GlobalSettings GlobalSettings { get; } = new GlobalSettings();
|
public GlobalSettings GlobalSettings { get; }
|
||||||
static GlobalSettingsFactory()
|
|
||||||
|
public GlobalSettingsFactory(string[] args)
|
||||||
{
|
{
|
||||||
|
GlobalSettings = new GlobalSettings();
|
||||||
// UserSecretsId here should match what is in Api.csproj
|
// UserSecretsId here should match what is in Api.csproj
|
||||||
var configBuilder = new ConfigurationBuilder().AddUserSecrets("bitwarden-Api");
|
var config = new ConfigurationBuilder()
|
||||||
var Configuration = configBuilder.Build();
|
.AddUserSecrets("bitwarden-Api")
|
||||||
ConfigurationBinder.Bind(Configuration.GetSection("GlobalSettings"), GlobalSettings);
|
.AddCommandLine(args)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
config.GetSection("GlobalSettings").Bind(GlobalSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +32,9 @@ public class DatabaseContextFactory : IDesignTimeDbContextFactory<DatabaseContex
|
|||||||
services.AddDataProtection();
|
services.AddDataProtection();
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
var globalSettings = GlobalSettingsFactory.GlobalSettings;
|
var globalSettings = new GlobalSettingsFactory(args)
|
||||||
|
.GlobalSettings;
|
||||||
|
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
|
||||||
var connectionString = globalSettings.MySql?.ConnectionString;
|
var connectionString = globalSettings.MySql?.ConnectionString;
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
if (string.IsNullOrWhiteSpace(connectionString))
|
||||||
|
@ -7,15 +7,20 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace Bit.PostgresMigrations;
|
namespace Bit.PostgresMigrations;
|
||||||
|
|
||||||
public static class GlobalSettingsFactory
|
public class GlobalSettingsFactory
|
||||||
{
|
{
|
||||||
public static GlobalSettings GlobalSettings { get; } = new GlobalSettings();
|
public GlobalSettings GlobalSettings { get; }
|
||||||
static GlobalSettingsFactory()
|
|
||||||
|
public GlobalSettingsFactory(string[] args)
|
||||||
{
|
{
|
||||||
|
GlobalSettings = new GlobalSettings();
|
||||||
// UserSecretsId here should match what is in Api.csproj
|
// UserSecretsId here should match what is in Api.csproj
|
||||||
var configBuilder = new ConfigurationBuilder().AddUserSecrets("bitwarden-Api");
|
var config = new ConfigurationBuilder()
|
||||||
var Configuration = configBuilder.Build();
|
.AddUserSecrets("bitwarden-Api")
|
||||||
ConfigurationBinder.Bind(Configuration.GetSection("GlobalSettings"), GlobalSettings);
|
.AddCommandLine(args)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
config.GetSection("GlobalSettings").Bind(GlobalSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +32,8 @@ public class DatabaseContextFactory : IDesignTimeDbContextFactory<DatabaseContex
|
|||||||
services.AddDataProtection();
|
services.AddDataProtection();
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
var globalSettings = GlobalSettingsFactory.GlobalSettings;
|
var globalSettings = new GlobalSettingsFactory(args)
|
||||||
|
.GlobalSettings;
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
|
||||||
var connectionString = globalSettings.PostgreSql?.ConnectionString;
|
var connectionString = globalSettings.PostgreSql?.ConnectionString;
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
if (string.IsNullOrWhiteSpace(connectionString))
|
||||||
|
@ -7,14 +7,20 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace Bit.SqliteMigrations;
|
namespace Bit.SqliteMigrations;
|
||||||
|
|
||||||
public static class GlobalSettingsFactory
|
public class GlobalSettingsFactory
|
||||||
{
|
{
|
||||||
public static GlobalSettings GlobalSettings { get; } = new GlobalSettings();
|
public GlobalSettings GlobalSettings { get; }
|
||||||
static GlobalSettingsFactory()
|
|
||||||
|
public GlobalSettingsFactory(string[] args)
|
||||||
{
|
{
|
||||||
var configBuilder = new ConfigurationBuilder().AddUserSecrets("bitwarden-Api");
|
GlobalSettings = new GlobalSettings();
|
||||||
var Configuration = configBuilder.Build();
|
// UserSecretsId here should match what is in Api.csproj
|
||||||
ConfigurationBinder.Bind(Configuration.GetSection("GlobalSettings"), GlobalSettings);
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddUserSecrets("bitwarden-Api")
|
||||||
|
.AddCommandLine(args)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
config.GetSection("GlobalSettings").Bind(GlobalSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +32,8 @@ public class DatabaseContextFactory : IDesignTimeDbContextFactory<DatabaseContex
|
|||||||
services.AddDataProtection();
|
services.AddDataProtection();
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
var globalSettings = GlobalSettingsFactory.GlobalSettings;
|
var globalSettings = new GlobalSettingsFactory(args)
|
||||||
|
.GlobalSettings;
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
|
||||||
var connectionString = globalSettings.Sqlite?.ConnectionString ?? "Data Source=:memory:";
|
var connectionString = globalSettings.Sqlite?.ConnectionString ?? "Data Source=:memory:";
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
if (string.IsNullOrWhiteSpace(connectionString))
|
||||||
|
Loading…
Reference in New Issue
Block a user