Skip to content

Commit c3b9421

Browse files
authored
Add MariaDB instructions and clarify EF testing (#687)
1 parent bbd5bb7 commit c3b9421

File tree

2 files changed

+137
-61
lines changed

2 files changed

+137
-61
lines changed

docs/contributing/testing/database/index.md

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,21 @@ pipeline.
77

88
## Creating a new test
99

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

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

2526
## Configure the tests
2627

@@ -51,14 +52,11 @@ will be configured with the Entity Framework Core repositories. `Enabled` allows
5152
disable one database but not delete the entry; it can be helpful if you are encountering a problem
5253
with just a single database type and want to run the tests just for it instead of for all of them.
5354

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

61-
You can also configure the tests just like the pipeline.
57+
To run the tests locally during development (highly recommended), see
58+
[Getting Started - Entity Framework](../../../getting-started/server/database/ef/index.mdx). Once
59+
configured, you can run the tests with `dotnet test` or using your IDE.
6260

6361
### Pipeline
6462

@@ -82,9 +80,10 @@ across all configured database providers.
8280

8381
:::note
8482

85-
This is meant for testing data migrations only. It assumes your database schema is already fully
86-
up-to-date. After setting up your test data, it re-runs the specified migration to verify how it
87-
transforms the data. It will not work for schema-only migrations.
83+
This is meant for testing data migrations only (i.e. migrations that transform data already present
84+
in the database). It assumes your database schema is already fully up-to-date. After setting up your
85+
test data, it re-runs the specified migration to verify how it transforms the data. It will not work
86+
and is not required for schema-only migrations.
8887

8988
:::
9089

@@ -94,7 +93,7 @@ To test a migration, set the `MigrationName` property on the `[DatabaseData]` at
9493
`IMigrationTesterService`:
9594

9695
```csharp
97-
[DatabaseTheory, DatabaseData(MigrationName = "ExampleDataMigration")]
96+
[Theory, DatabaseData(MigrationName = "ExampleDataMigration")]
9897
public async Task TestExampleDataMigration(
9998
IMigrationTesterService migrationTester,
10099
IOrganizationRepository organizationRepository)

docs/getting-started/server/database/ef/index.mdx

Lines changed: 116 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export const providers = [
1212
label: "MySQL",
1313
value: "mysql",
1414
},
15+
{
16+
label: "MariaDB",
17+
value: "mariadb",
18+
},
1519
{
1620
label: "SQLite",
1721
value: "sqlite",
@@ -80,7 +84,20 @@ sure you update the existing values instead of creating new ones
8084
<TabItem value="mysql">
8185

8286
Be sure to change information like root password as needed. If you already have these secrets, make
83-
sure you update the existing values instead of creating new ones
87+
sure you update the existing values instead of creating new ones.
88+
89+
```json
90+
"globalSettings:databaseProvider": "mysql",
91+
"globalSettings:mySql:connectionString": "server=localhost;uid=root;pwd=example;database=vault_dev",
92+
```
93+
94+
</TabItem>
95+
<TabItem value="mariadb">
96+
97+
MariaDB is generally a drop-in alternative to MySQL, so it shares the `mysql` configuration keys.
98+
99+
Be sure to change information like root password as needed. If you already have these secrets, make
100+
sure you update the existing values instead of creating new ones.
84101

85102
```json
86103
"globalSettings:databaseProvider": "mysql",
@@ -109,7 +126,7 @@ that the changes take effect.
109126

110127
:::
111128

112-
### Updating the database
129+
### Start the database server
113130

114131
<Tabs
115132
groupId="provider"
@@ -137,7 +154,7 @@ Docker storage volume and initialize the database from scratch.
137154

138155
1. Confirm that `MYSQL_ROOT_PASSWORD` in `dev/.env` matches the password in `dev/secrets.json`.
139156

140-
2. In the `dev` folder of your server repository, run
157+
2. In the `dev` folder of your server repository, start your database container:
141158

142159
```bash
143160
docker compose --profile mysql up
@@ -151,57 +168,86 @@ Docker storage volume and initialize the database from scratch.
151168

152169
:::
153170

171+
</TabItem>
172+
<TabItem value="mariadb">
173+
174+
1. Confirm that `MYSQL_ROOT_PASSWORD` in `dev/.env` matches the password in `dev/secrets.json`.
175+
176+
2. In the `dev` folder of your server repository, run
177+
178+
```bash
179+
docker compose --profile mariadb up
180+
```
181+
182+
:::tip[Confirm your database connection!]
183+
184+
If you run into connection errors, double check that your `.env` and `secrets.json` files have
185+
matching passwords. If they do, you may have initialized your database incorrectly. Delete the
186+
Docker storage volume and initialize the database from scratch.
187+
188+
:::
189+
154190
</TabItem>
155191
<TabItem value="sqlite">
156192

193+
No additional step is required for SQLite. The migrator will create the database file if it doesn't
194+
exist.
195+
157196
:::tip[Confirm your database path!]
158197

159-
The migrator creates the database file if it doesn't exist, but it does not create folders. If you
160-
get an error that the path doesn't exist, double check that the path exists and that the folder
161-
containing the sqlite database has write and/or create permissions.
198+
The migrator does not create folders. If you get an error that the path doesn't exist, double check
199+
that the path exists and that the folder containing the sqlite database has write and/or create
200+
permissions.
162201

163202
:::
164203

165204
</TabItem>
166205
</Tabs>
167206

168-
### Migrations
207+
:::tip[Start all containers at once]
208+
209+
Use the `ef` profile to start all EntityFramework database containers at once:
210+
211+
```bash
212+
docker compose --profile ef up
213+
```
214+
215+
:::
216+
217+
### Run migrations
218+
219+
In the `dev` folder, run the following command to update the database to the latest migration:
169220

170221
<Tabs
171222
groupId="provider"
172223
values={providers}>
173224
<TabItem value="postgres">
174225

175-
In the `dev` folder run the following to update the database to the latest migration
176-
177226
```bash
178227
pwsh migrate.ps1 -postgres
179228
```
180229

181-
The `-postgres` flag on `migrate.ps1` runs `dotnet ef` commands to perform the migrations.
182-
183230
</TabItem>
184231
<TabItem value="mysql">
185232

186-
In the `dev` folder run the following to update the database to the latest migration
187-
188233
```bash
189234
pwsh migrate.ps1 -mysql
190235
```
191236

192-
The `-mysql` flag on `migrate.ps1` runs `dotnet ef` commands to perform the migrations.
237+
</TabItem>
238+
<TabItem value="mariadb">
239+
240+
```bash
241+
pwsh migrate.ps1 -mariadb
242+
```
193243

194244
</TabItem>
195245
<TabItem value="sqlite">
196246

197-
In the `dev` folder run the following to update the database to the latest migration
198-
199247
```bash
200248
pwsh migrate.ps1 -sqlite
201249
```
202250

203-
The `-sqlite` flag on `migrate.ps1` runs `dotnet ef` commands to perform the migrations.
204-
205251
:::note
206252

207253
The migrator creates the database file if it doesn't exist, but it does not create folders. If you
@@ -212,13 +258,7 @@ get an error that the path doesn't exist, it's referring to missing folders.
212258
</TabItem>
213259
</Tabs>
214260

215-
You can also run migrations for all database providers at once using
216-
217-
```bash
218-
pwsh migrate.ps1 -all
219-
```
220-
221-
### Verifying changes
261+
### Verify changes
222262

223263
If you would like to verify that everything worked correctly:
224264

@@ -227,26 +267,46 @@ If you would like to verify that everything worked correctly:
227267
- Note: this requires a configured MSSQL database. You may also need to set up other EF providers
228268
for tests to pass.
229269

230-
## Testing changes
270+
## Database integration tests
271+
272+
Database integration tests run for each database provider for both Dapper (MSSQL) and Entity
273+
Framework. Developers are not expected to manually test each database provider. Instead, use
274+
integration tests to ensure correctness across all supported databases.
275+
276+
Integration tests have their own connection strings, so that you can use separate databases to the
277+
one used by your local development server. This is recommended because integration tests produce a
278+
lot of test data over time. In the example below, this is done by using `vault_test` as the database
279+
name.
280+
281+
### Configuring test databases
231282

232-
In your `server/dev/secrets.json` file find or add this block of secrets in the root of the json
233-
structure:
283+
1. In your user secrets, find or add this block of secrets in the root of the json structure
284+
(**not** in `GlobalSettings`):
234285

235286
```
236287
"databases:0:type": "Postgres",
237-
"databases:0:connectionString": "Host=localhost;Username=postgres;Password=_________;Database=ef_test",
288+
"databases:0:connectionString": "Host=localhost;Username=postgres;Password=_________;Database=vault_test",
238289
"databases:0:enabled": "true",
239290
"databases:1:type": "Sqlite",
240291
"databases:1:enabled": "true",
241292
"databases:1:connectionString": "Data Source=_________",
242293
"databases:2:type": "MySql",
243-
"databases:2:connectionString": "server=localhost;uid=root;pwd=_________;database=ef_test",
294+
"databases:2:connectionString": "server=localhost;uid=root;pwd=_________;database=vault_test",
244295
"databases:2:enabled": "true",
245296
"databases:3:type": "SqlServer",
246-
"databases:3:connectionString": "Server=localhost;Database=ef_test;User Id=SA;Password=_________;Encrypt=True;TrustServerCertificate=True;",
297+
"databases:3:connectionString": "Server=localhost;Database=vault_test;User Id=SA;Password=_________;Encrypt=True;TrustServerCertificate=True;",
247298
"databases:3:enabled": "true"
299+
"databases:4:type": "MySql",
300+
"databases:4:connectionString": "server=localhost;port=4306;uid=maria;pwd=_________;database=vault_test;AllowUserVariables=true",
301+
"databases:4:enabled": "true",
248302
```
249303

304+
:::note
305+
306+
The second MySql entry refers to MariaDB.
307+
308+
:::
309+
250310
:::info
251311

252312
The example database index + type combinations are required for the tooling to work, and to support
@@ -255,16 +315,33 @@ multiple versions of the same database running tests at the same time.
255315
:::
256316

257317
This block is used for test databases for each supported provider type. These are what integration
258-
tests will connect to. You should update the password for these connection strings to match your
259-
existing databases if you have not already. If these settings are not present at all in your
260-
`server/dev/secrets.json` file just add them to the bottom. These settings _do not_ go in
261-
`globalSettings`. Then run `pwsh setup_secrets.ps1 -clear` to apply them to your local projects.
318+
tests will connect to. Make sure that you fill in the password for each connection string.
319+
320+
2. Run `pwsh setup_secrets.ps1 -clear` to apply the updated user secrets to your local projects.
321+
322+
3. Ensure your databases are all migrated (see instructions above). You can use the `-test` flag to
323+
only migrate the integration test databases, or `-all` to migrate everything.
324+
325+
```
326+
# Migrate a specific integration test database (used by tests)
327+
pwsh migrate.ps1 -postgres -test
328+
329+
# Migrate a specific development database (used by your local server)
330+
pwsh migrate.ps1 -postgres
331+
332+
# Migrate all local databases
333+
pwsh migrate.ps1 -all
334+
```
335+
336+
4. Run integration tests from the `test/Infrastructure.IntegrationTest` folder using `dotnet test`.
337+
338+
### Writing integration tests
262339

263-
With connection strings applied to your projects: ensure your databases are all migrated using
264-
`pwsh server/dev/migrate.ps1 --all`. Then you can run EF tests from the
265-
`test/Infrastructure.IntegrationTest` folder using `dotnet test`.
340+
See
341+
[Contributing - Database Integration Testing](../../../../contributing/testing/database/index.md)
342+
for more information on testing utilities and patterns.
266343

267-
# Modifying the database
344+
## Modifying the database
268345

269346
The process for modifying the database is described in
270347
[Migrations](./../../../../contributing/database-migrations/ef.md).

0 commit comments

Comments
 (0)