Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-14818] Adjust instructions for running EF integration tests #457

Merged
merged 15 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions docs/contributing/testing/database/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Database Integration Testing

Since Bitwarden has multiple database options, automated testing is extremely important to avoid
time-consuming manual testing for each database type. The `Infrastructure.IntegrationTest` project
allows you to write tests that are automatically executed on all supported databases in our testing
pipeline.

## Creating a new test

To create a new database test, add the `[DatabaseTheory]` and `[DatabaseData]` attributes to test.
Then, use the parameters of the test to inject any repository layer services you need. The test will
run for every database that is [configured in the current environment](#configure-the-tests). Since
you inject the interface of the service, some runs of the test will use the Dapper-based repository
implementation targeting Microsoft SQL Server and others will use the Entity Framework Core based
implementations (which we use for MySql, Postgres, and SQLite).

The goal of database tests is to test the business logic that is encapsulated in a given method. For
example, if a stored procedure in SQL Server calls another procedure to update the
`User.AccountRevisionDate` then the corresponding EF implementation should do that as well. By
running the test against all variants, we are ensuring all the variants are feature-equal. to only
run the SQL Server tests along with one EF implementation; SQLite is often the easiest in that
regard. The other supported EF database providers will still run in the pipeline to catch any
differences between them.

## Configure the tests

The tests are configured through
[.NET Configuration](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration). They
are applied in the following order: user secrets, environment variables prefixed with `BW_TEST_`,
and command line args.

```csharp
public class Root
{
public Database[] Databases { get; set; }
}

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;
}
```

The `Type` property is an enum with the supported values being `SqlServer`, `MySql`, `Postgres`, or
`Sqlite`. The `UseEf` property is only utilized if the `Type` is set to `SqlServer`; by default
`SqlServer` will be configured with the Dapper repositories, however by setting `UseEf` to `true` it
will be configured with the Entity Framework Core repositories. `Enabled` allows you to easily
disable one database but not delete the entry; it can be helpful if you are encountering a problem
with just a single database type and want to run the tests just for it instead of for all of them.

### Locally

To set the tests up locally you may want to add the configuration to your `server/dev/secrets.json`
file. You may have already done this during setup and can just run the tests with `dotnet test`. If
not, please refer to
[the getting started guide](/getting-started/server/database/ef/#testing-ef-changes).

You can also configure the tests just like the pipeline.

### Pipeline

The database tests have been pre-configured to run on all supported databases in the
[`test-database.yml`](https://github.com/bitwarden/server/blob/main/.github/workflows/test-database.yml)
file.

The pipeline uses environment variables. An example entry you might add is:

```bash
BW_TEST_DATABASES__0__TYPE: SqlServer
BW_TEST_DATABASES__0__CONNECTIONSTRING: myConnectionString
```
49 changes: 28 additions & 21 deletions docs/getting-started/server/database/ef/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -196,30 +196,37 @@ If you would like to verify that everything worked correctly:

## Testing EF Changes

Since we allow for multiple databases it is important that any changes to EF repositories/models are
tested against all possible databases. You may want to use a database that is different from your
local development database because the tests may add or remove data. To apply migrations to a
database different from your global settings run the following commands from the root of your
repository
In your `server/dev/secrets.json` file find or add this block of secrets in the root of the json
structure:

```bash
# EntityFramework CLI Reference: https://learn.microsoft.com/en-us/ef/core/cli/dotnet

# Migrate Postgres database ex connection string: Host=localhost;Username=postgres;Password=SET_A_PASSWORD_HERE_123;Database=vault_dev_test
dotnet ef database update --startup-project util/PostgresMigrations --connection "[POSTGRES_CONNECTION_STRING]"
```
"databases:0:type": "Postgres",
"databases:0:connectionString": "Host=localhost;Username=postgres;Password=_________;Database=ef_test",
"databases:0:enabled": "true",
"databases:1:type": "Sqlite",
"databases:1:enabled": "true",
"databases:1:connectionString": "Data Source=_________",
"databases:2:type": "MySql",
"databases:2:connectionString": "server=localhost;uid=root;pwd=_________;database=ef_test",
"databases:2:enabled": "true",
"databases:3:type": "SqlServer",
"databases:3:connectionString": "Server=localhost;Database=ef_test;User Id=SA;Password=_________;Encrypt=True;TrustServerCertificate=True;",
"databases:3:enabled": "true"
```

# Migrate MySql database ex connection string: server=localhost;uid=root;pwd=SET_A_PASSWORD_HERE_123;database=vault_dev_test
dotnet ef database update --startup-project util/MySqlMigrations --connection "[MYSQL_CONNECTION_STRING]"
:::info

cd test/Infrastructure.IntegrationTest
The example database index + type combinations are required for the tooling to work, and to support
multiple versions of the same database running tests at the same time.

# https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-6.0&tabs=windows#secret-manager
dotnet user-secrets set "Ef:Postgres" "[POSTGRES_CONNECTION_STRING]"
dotnet user-secrets set "Ef:MySql" "[MYSQL_CONNECTION_STRING]"
:::

# You can also set the connection string for your normal development MS SQL database like below
dotnet user-secrets set "Dapper:SqlServer" "[MSSQL_CONNECTION_STRING]"
```
This block is used for test databases for each supported provider type. These are what integration
tests will connect to. You should update the password for these connection strings to match your
existing databases if you have not already. If these settings are not present at all in your
`server/dev/secrets.json` file just add them to the bottom. These settings _do not_ go in
`globalSettings`. Then run `pwsh setup_secrets.ps1 -clear` to apply them to your local projects.

You can then run just those tests from the `test/Infrastructure.IntegrationTest` folder using
`dotnet test`.
mzieniukbw marked this conversation as resolved.
Show resolved Hide resolved
With connection strings applied to your projects: ensure your databases are all migrated using
`pwsh server/dev/migrate.ps1 --all`. Then you can run EF tests from the
mzieniukbw marked this conversation as resolved.
Show resolved Hide resolved
`test/Infrastructure.IntegrationTest` folder using `dotnet test`.