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 9 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
82 changes: 82 additions & 0 deletions docs/contributing/testing/database/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Database Integration Testing

Since Bitwarden has multiple database options, testing them all automatically is incredibly
important so that we don't have to have a full QA process for each database type. This is where the
`Infrastructure.IntegrationTest` project comes in, allowing the setup of tests similarly to how the
databases are consumed in the applications through their common interfaces, generally an
`I*Repository`. These tests are automatically executed on all supported databases in our testing
pipeline.
addisonbeck marked this conversation as resolved.
Show resolved Hide resolved

## Creating a new test

To create a new database test just add the `[DatabaseTheory]` and `[DatabaseData]` attributes to
test. Now, in the parameters of the test you are able to "inject" any repository layer services
directly into the test. 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.
addisonbeck marked this conversation as resolved.
Show resolved Hide resolved

The goal of database tests is to test the business logic that is encapsulated in a given method. If
the stored procedure in SQL Server then calls another procedure to update the
`User.AccountRevisionDate` then the same EF implementation should do that as well. By running the
test against all variants we are ensuring all the variants are feature-equal. Locally, you may want
addisonbeck marked this conversation as resolved.
Show resolved Hide resolved
to only run the SQL Server tests along with one EF implementation; SQLite is often the easiest in
that situation. This may work well for a very long time and save you some time overall but there are
differences between the EF database providers such that you will one day get errors in the CI
pipeline.
addisonbeck marked this conversation as resolved.
Show resolved Hide resolved

## Configure the databases

The databases are expected to have the latest migrations applied.
eliykat marked this conversation as resolved.
Show resolved Hide resolved

## Configure the tests

The tests are configured through
[.NET Configuration](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration) in the
order they are applied: user secrets, environment variables prefixed with `BW_TEST_`, and command
line args.
addisonbeck marked this conversation as resolved.
Show resolved Hide resolved

```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`.