Skip to content

Commit 0682c85

Browse files
authored
Re-organize README and ONBOARDING docs. (#91)
1 parent 80c6fc3 commit 0682c85

File tree

2 files changed

+74
-47
lines changed

2 files changed

+74
-47
lines changed

ONBOARDING.md

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
1-
## So you want to write Complement tests
1+
# So you want to write Complement tests
22

3-
If you've not run Complement tests yet, please do so. This document will outline how Complement works and how you can add efficient tests and best practices for Complement itself.
3+
Complement is a black box integration testing framework for Matrix homeservers.
44

5-
### Architecture
5+
This document will outline how Complement works and how you can add efficient tests and best practices for Complement itself. If you haven't run Complement tests yet, please see the [README](README.md) and start there!
6+
7+
## Terminology
8+
9+
* `Blueprint`: a human-readable outline for what should be done prior to a test (such as creating users, rooms, etc).
10+
* `Deployment`: controls the lifetime of a Docker container (built from a `Blueprint`). It has functions on it for creating deployment-scoped structs such as Client-Server API clients for interacting with specific homeservers in the deployment.
11+
12+
## Architecture
13+
14+
Each Complement test runs one or more Docker containers for the homeserver(s) involved in the test. These containers are snapshots of the target homeserver at a particular state. The state of each container is determined by the `Blueprint` used. Client-Server and Server-Server API calls can then be made with assertions against the results.
15+
16+
In order to actually write a test, Complement needs to:
17+
18+
* Create `Deployments`, this is done by calling `deployment := Deploy(...)` (and can subsequently be killed via `deployment.Destroy(...)`).
19+
* (Potentially) make API calls against the `Deployments`.
20+
* Make assertions, this can be done via the standard Go testing mechanisms (e.g. `t.Fatalf`), but Complement also provides some helpers in the `must` and `match` packages.
21+
22+
For testing outbound federation, Complement implements a bare-bones Federation server for homeservers to talk to. Each test must explicitly declare the functionality of the homeserver. This is done using [functional options](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis) and looks something like:
623

7-
Complement runs a Docker container every time you call `deployment := Deploy(...)`, which gets killed on `deployment.Destroy(...)`. These containers are snapshots of the target homeserver at a particular state. The state is determined by the `Blueprint`, which is a human-readable outline for what should be done prior to the test (such as creating users, rooms, etc). Coming from Sytest, a `Blueprint` is similar to a `fixture`. A `deployment` has functions on it for creating deployment-scoped structs such as CS-API clients for interacting with specific Homeservers in the deployment. Assertions are done via the `must` and `match` packages. For testing outbound federation, Complement implements a bare-bones Federation server for homeservers to talk to. Unlike Sytest, you have to explicitly opt-in to attaching core functionality to the server so the reader can clearly see what is and is not being handled automatically. This is done using [functional options](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis) and looks something like:
824
```go
925
// A federation server which handles serving up its own keys when requested,
1026
// automatically accepts make_join and send_join requests and deals with
@@ -35,11 +51,11 @@ The high numbered ports are randomly chosen, and are for illustrative purposes o
3551
```
3652
The mapping of `hs1` to `localhost:port` combinations can be done automatically using a `docker.RoundTripper`.
3753

38-
### How do I...
54+
## How do I...
3955

40-
Get a CS API client:
56+
Get a Client-Server API client:
4157
```go
42-
// the user and hs name are from the blueprint
58+
// the user and homeserver name are from the blueprint
4359
// automatically maps localhost:12345 to the right container
4460
alice := deployment.Client(t, "hs1", "@alice:hs1")
4561
```
@@ -65,9 +81,9 @@ Get a Federation client:
6581
fedClient := srv.FederationClient(deployment, "hs1")
6682
```
6783

68-
### FAQ
84+
## FAQ
6985

70-
#### How should I name the test files / test functions?
86+
### How should I name the test files / test functions?
7187

7288
Test files have to have `_test.go` else Go won't run the tests in that file. Other than that, there are no restrictions or naming convention.
7389
If you are converting a sytest be sure to add a comment _anywhere_ in the source code which has the form:
@@ -87,11 +103,11 @@ Adding `// sytest: ...` means `sytest_coverage.go` will know the test is convert
87103
when run! Use `go run sytest_coverage.go -v` to see the exact string to use, as they may be different to the one produced
88104
by an actual sytest run due to parameterised tests.
89105

90-
#### Should I always make a new blueprint for a test?
106+
### Should I always make a new blueprint for a test?
91107

92108
Probably not. Blueprints are costly, and they should only be made if there is a strong case for plenty of reuse among tests. In the same way that we don't always add fixtures to sytest, we should be sparing with adding blueprints.
93109

94-
#### How should I assert JSON objects?
110+
### How should I assert JSON objects?
95111

96112
Use one of the matchers in the `match` package (which uses `gjson`) rather than `json.Unmarshal(...)` into a struct. There's a few reasons for this:
97113
- Removes the temptation to use `gomatrixserverlib` structs.
@@ -100,14 +116,14 @@ Use one of the matchers in the `match` package (which uses `gjson`) rather than
100116

101117
If you want to extract data from objects, just use `gjson` directly.
102118

103-
#### How should I assert HTTP requests/responses?
119+
### How should I assert HTTP requests/responses?
104120

105121
Use the corresponding matcher in the `match` package. This allows you to be as specific or as lax as you like on your checks, and allows you to add JSON matchers on
106122
the HTTP body.
107123

108-
#### I want to run a bunch of tests in parallel, how do I do this?
124+
### I want to run a bunch of tests in parallel, how do I do this?
109125

110-
If you're familiar with Go testing then you already know how. Add `t.Parallel()` to all tests which you want to run in parallel. For a good example of this, see `registration_test.go` which does:
126+
This is done using the standard Go testing mechanisms. Add `t.Parallel()` to all tests which you want to run in parallel. For a good example of this, see `registration_test.go` which does:
111127
```go
112128
// This will block until the 2 subtests have completed
113129
t.Run("parallel", func(t *testing.T) {
@@ -124,7 +140,7 @@ t.Run("parallel", func(t *testing.T) {
124140
})
125141
```
126142

127-
#### How should I do comments in the test?
143+
### How should I do comments in the test?
128144

129145
Add long prose to the start of the function to outline what it is you're testing (and why if it is unclear). For example:
130146
```go
@@ -135,31 +151,31 @@ func TestInboundFederationKeys(t *testing.T) {
135151
}
136152
```
137153

138-
#### I think Complement is doing something weird, can I get more logs?
154+
### I think Complement is doing something weird, can I get more logs?
139155

140156
You can pass `COMPLEMENT_DEBUG=1` to add lots of debug logging. You can also do this via `os.Setenv("COMPLEMENT_DEBUG", "1")` before you make a deployment. This will add trace logging to the clients which logs full HTTP request/responses, amongst other debug info.
141157

142-
#### How do I set up a bunch of stuff before the tests, e.g before each?
158+
### How do I set up a bunch of stuff before the tests, e.g before each?
143159

144160
There is no syntactically pleasing way to do this. Create a separate function which returns a function. See https://stackoverflow.com/questions/42310088/setup-and-teardown-for-each-test-using-std-testing-package?rq=1
145161

146-
#### How do I log messages in tests?
162+
### How do I log messages in tests?
147163

148-
Standard Go testing here, use `t.Logf(...)` which will be logged only if the test fails or if `-v` is set. Note that you will not need to log HTTP requests performed using one of the built in deployment clients as they are already wrapped in loggers. For full HTTP logs, use `COMPLEMENT_DEBUG=1`.
164+
This is done using standard Go testing mechanisms, use `t.Logf(...)` which will be logged only if the test fails or if `-v` is set. Note that you will not need to log HTTP requests performed using one of the built in deployment clients as they are already wrapped in loggers. For full HTTP logs, use `COMPLEMENT_DEBUG=1`.
149165

150-
#### How do I skip a test?
166+
### How do I skip a test?
151167

152168
Use one of `t.Skipf(...)` or `t.SkipNow()`.
153169

154-
#### Why do we use `t.Errorf` sometimes and `t.Fatalf` other times?
170+
### Why do we use `t.Errorf` sometimes and `t.Fatalf` other times?
155171

156172
Error will fail the test but continue execution, where Fatal will fail the test and quit. Use Fatal when continuing to run the test will result in programming errors (e.g nil exceptions).
157173

158-
#### Why do I get the error "Error response from daemon: Conflict. The container name "/complement_rooms_state_alice.hs1_1" is already in use by container "c2d1d90c6cff7b7de2678b56c702bd1ff76ca72b930e8f2ca32eef3f2514ff3b". You have to remove (or rename) that container to be able to reuse that name."?
174+
### Why do I get the error "Error response from daemon: Conflict. The container name "/complement_rooms_state_alice.hs1_1" is already in use by container "c2d1d90c6cff7b7de2678b56c702bd1ff76ca72b930e8f2ca32eef3f2514ff3b". You have to remove (or rename) that container to be able to reuse that name."?
159175

160176
The Docker daemon has a lag time between removing containers and them actually being removed. This means you cannot remove a container called 'foo' and immediately recreate it as 'foo'. To get around this, you need to use a different name. This probably means the namespace you have given the deployment is used by another test. Try changing it to something else e.g `Deploy(t, "rooms_state_2", b.BlueprintAlice.Name)`
161177

162-
#### How do I run tests inside my IDE?
178+
### How do I run tests inside my IDE?
163179

164180
For VSCode, add to `settings.json`:
165181
```
@@ -171,3 +187,9 @@ For VSCode, add to `settings.json`:
171187
For Goland:
172188
* Under "Run"->"Edit Configurations..."->"Templates"->"Go Test", add `COMPLEMENT_BASE_IMAGE=complement-dendrite:latest`
173189
* Then you can right-click on any test file or test case and "Run <test name>".
190+
191+
### What do I need to know if I'm coming from sytest?
192+
193+
Sytest has a concept of a `fixture` to configure the homeserver or test in a particular way, these are replaced with a `Blueprint` in Complement.
194+
195+
Unlike Sytest, each test must opt-in to attaching core functionality to the server so the reader can clearly see what is and is not being handled automatically.

README.md

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,10 @@
11
[![Complement Dev](https://img.shields.io/matrix/complement:matrix.org.svg?label=%23complement%3Amatrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#complement:matrix.org)
22

3-
### Complement
3+
# Complement
44

55
Complement is a black box integration testing framework for Matrix homeservers.
66

7-
8-
#### Getting started
9-
10-
To get started developing, see https://github.com/matrix-org/complement/blob/master/ONBOARDING.md
11-
12-
If you're looking to run Complement against a local dev instance of Synapse, see [`matrix-org/synapse` -> `scripts-dev/complement.sh`](https://github.com/matrix-org/synapse/blob/develop/scripts-dev/complement.sh).
13-
14-
If you want to develop Complement tests while working on a local dev instance of Synapse, use the [`scripts-dev/complement.sh`](https://github.com/matrix-org/synapse/blob/develop/scripts-dev/complement.sh) script and set the `COMPLEMENT_DIR` environment variable to the filepath of your local Complement checkout. A regex that matches against test names can also be supplied as an argument to the script, i.e:
15-
16-
```sh
17-
COMPLEMENT_DIR=/path/to/complement scripts-dev/complement.sh "TestOutboundFederation(Profile|Send)"
18-
```
19-
20-
21-
#### Running
7+
## Running
228

239
You need to have Go and Docker installed, as well as `libolm3` and `libolm-dev`. Then:
2410

@@ -37,6 +23,14 @@ brew install libolm
3723

3824
You can either use your own image, or one of the ones supplied in the [dockerfiles](./dockerfiles) directory.
3925

26+
A full list of config options can be found [in the config file](./internal/config/config.go). All normal Go test config
27+
options will work, so to just run 1 named test and include a timeout for the test run:
28+
```
29+
$ COMPLEMENT_BASE_IMAGE=complement-dendrite:latest go test -timeout 30s -run '^(TestOutboundFederationSend)$' -v ./tests
30+
```
31+
32+
### Running against Dendrite
33+
4034
For instance, for Dendrite:
4135
```
4236
# build a docker image for Dendrite...
@@ -45,13 +39,20 @@ $ (cd dockerfiles && docker build -t complement-dendrite -f Dendrite.Dockerfile
4539
$ COMPLEMENT_BASE_IMAGE=complement-dendrite:latest go test -v ./tests
4640
```
4741

48-
A full list of config options can be found [in the config file](./internal/config/config.go). All normal Go test config
49-
options will work, so to just run 1 named test and include a timeout for the test run:
50-
```
51-
$ COMPLEMENT_BASE_IMAGE=complement-dendrite:latest go test -timeout 30s -run '^(TestOutboundFederationSend)$' -v ./tests
42+
### Running against Synapse
43+
44+
If you're looking to run Complement against a local dev instance of Synapse, see [`matrix-org/synapse` -> `scripts-dev/complement.sh`](https://github.com/matrix-org/synapse/blob/develop/scripts-dev/complement.sh).
45+
46+
If you want to develop Complement tests while working on a local dev instance of Synapse, use the [`scripts-dev/complement.sh`](https://github.com/matrix-org/synapse/blob/develop/scripts-dev/complement.sh) script and set the `COMPLEMENT_DIR` environment variable to the filepath of your local Complement checkout. A regex that matches against test names can also be supplied as an argument to the script, i.e:
47+
48+
```sh
49+
COMPLEMENT_DIR=/path/to/complement scripts-dev/complement.sh "TestOutboundFederation(Profile|Send)"
5250
```
5351

54-
##### Image requirements
52+
### Image requirements
53+
54+
If you're looking to run against a custom Dockerfile, it must meet the following requirements:
55+
5556
- The Dockerfile must `EXPOSE 8008` and `EXPOSE 8448` for client and federation traffic respectively.
5657
- The homeserver should run and listen on these ports.
5758
- The homeserver needs to `200 OK` requests to `GET /_matrix/client/versions`.
@@ -60,11 +61,15 @@ $ COMPLEMENT_BASE_IMAGE=complement-dendrite:latest go test -timeout 30s -run '^(
6061
- The homeserver needs to assume dockerfile `CMD` or `ENTRYPOINT` instructions will be run multiple times.
6162
- The homeserver can use the CA certificate mounted at /ca to create its own TLS cert (see [Complement PKI](README.md#complement-pki)).
6263

63-
#### Why 'Complement'?
64+
## Writing tests
65+
66+
To get started developing Complement tests, see [the onboarding documentation](ONBOARDING.md).
67+
68+
## Why 'Complement'?
6469

6570
Because **M**<sup>*C*</sup> = **1** - **M**
6671

67-
#### Complement PKI
72+
## Complement PKI
6873

6974
As the Matrix federation protocol expects federation endpoints to be served with valid TLS certs,
7075
Complement will create a self-signed CA cert to use for creating valid TLS certs in homeserver containers.
@@ -89,7 +94,7 @@ cp /root/ca.crt /usr/local/share/ca-certificates/complement.crt
8994
update-ca-certificates
9095
```
9196

92-
#### Sytest parity
97+
## Sytest parity
9398

9499
```
95100
$ go run sytest_coverage.go -v

0 commit comments

Comments
 (0)