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

file based integration tests #5067

Merged
merged 20 commits into from
May 16, 2024
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
26 changes: 24 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ dependencies = [
"jsonwebtoken",
"lazy_static",
"libc",
"libtest-mimic",
"linkme",
"lru",
"maplit",
Expand Down Expand Up @@ -2515,6 +2516,15 @@ dependencies = [
"windows-sys 0.52.0",
]

[[package]]
name = "escape8259"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba4f4911e3666fcd7826997b4745c8224295a6f3072f1418c3067b97a67557ee"
dependencies = [
"rustversion",
]

[[package]]
name = "event-listener"
version = "2.5.3"
Expand Down Expand Up @@ -3900,6 +3910,18 @@ dependencies = [
"vcpkg",
]

[[package]]
name = "libtest-mimic"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fefdf21230d6143476a28adbee3d930e2b68a3d56443c777cae3fe9340eebff9"
dependencies = [
"clap",
"escape8259",
"termcolor",
"threadpool",
]

[[package]]
name = "libz-ng-sys"
version = "1.1.12"
Expand Down Expand Up @@ -7729,9 +7751,9 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"

[[package]]
name = "walkdir"
version = "2.4.0"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
Expand Down
6 changes: 6 additions & 0 deletions apollo-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ tracing-opentelemetry = "0.21.0"
tracing-test = "0.2.4"
walkdir = "2.4.0"
wiremock = "0.5.22"
libtest-mimic = "0.7.2"

[target.'cfg(target_os = "linux")'.dev-dependencies]
rstack = { version = "0.3.3", features = ["dw"], default-features = false }
Expand All @@ -353,6 +354,11 @@ serde_json.workspace = true
name = "integration_tests"
path = "tests/integration_tests.rs"

[[test]]
name = "samples"
path = "tests/samples_tests.rs"
harness = false

[[bench]]
name = "huge_requests"
harness = false
Expand Down
14 changes: 11 additions & 3 deletions apollo-router/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ use wiremock::ResponseTemplate;
pub struct IntegrationTest {
router: Option<Child>,
test_config_location: PathBuf,
test_schema_location: PathBuf,
router_location: PathBuf,
stdio_tx: tokio::sync::mpsc::Sender<String>,
stdio_rx: tokio::sync::mpsc::Receiver<String>,
collect_stdio: Option<(tokio::sync::oneshot::Sender<String>, regex::Regex)>,
supergraph: PathBuf,
_subgraphs: wiremock::MockServer,
telemetry: Telemetry,

Expand Down Expand Up @@ -317,10 +317,13 @@ impl IntegrationTest {
.await;

let mut test_config_location = std::env::temp_dir();
let mut test_schema_location = test_config_location.clone();
let location = format!("apollo-router-test-{}.yaml", Uuid::new_v4());
test_config_location.push(location);
test_schema_location.push(format!("apollo-router-test-{}.graphql", Uuid::new_v4()));

fs::write(&test_config_location, &config_str).expect("could not write config");
fs::copy(&supergraph, &test_schema_location).expect("could not write schema");

let (stdio_tx, stdio_rx) = tokio::sync::mpsc::channel(2000);
let collect_stdio = collect_stdio.map(|sender| {
Expand All @@ -332,10 +335,10 @@ impl IntegrationTest {
router: None,
router_location: Self::router_location(),
test_config_location,
test_schema_location,
stdio_tx,
stdio_rx,
collect_stdio,
supergraph,
_subgraphs: subgraphs,
_subgraph_overrides: subgraph_overrides,
bind_address: Default::default(),
Expand Down Expand Up @@ -383,7 +386,7 @@ impl IntegrationTest {
"--config",
&self.test_config_location.to_string_lossy(),
"--supergraph",
&self.supergraph.to_string_lossy(),
&self.test_schema_location.to_string_lossy(),
"--log",
"error,apollo_router=info",
])
Expand Down Expand Up @@ -477,6 +480,11 @@ impl IntegrationTest {
.expect("must be able to write config");
}

#[allow(dead_code)]
pub async fn update_schema(&self, supergraph_path: &PathBuf) {
fs::copy(supergraph_path, &self.test_schema_location).expect("could not write schema");
}

#[allow(dead_code)]
pub fn execute_default_query(
&self,
Expand Down
119 changes: 119 additions & 0 deletions apollo-router/tests/samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# File based integration tests

This folder contains a serie of Router integration tests that can be defined entirely through a JSON file. Thos tests are able to start and stop a router, reload its schema or configiration, make requests and check the expected response. While we can make similar tests from inside the Router's code, these tests here are faster to write and modify because they do not require recompilations of the Router, at the cost of a slightly higher runtime cost.

## How to write a test

One test is recognized as a folder containing a `plan.json` file. Any number of subfolders is accepted, and the test name will be the path to the test folder. If the folder contains a `README.md` file, it will be added to the captured output of the test, and displayed if the test failed.

The `plan.json` file contains a top level JSON object with an `actions` field, containing an array of possible actions, that will be executed one by one:

```json
{
"actions": [
{
"type": "Start",
"schema_path": "./supergraph.graphql",
"configuration_path": "./configuration.yaml",
"subgraphs": {}
},
{
"type": "Request",
"request": {
"query": "{ me { name } }"
},
"expected_response": {
"data":{
"me":{
"name":"Ada Lovelace"
}
}
}
},
{
"type": "Stop"
}
]
}
```

If any of those actions fails, the test will stop immediately.

## Possible actions

### Start

```json
{
"type": "Start",
"schema_path": "./supergraph.graphql",
"configuration_path": "./configuration.yaml",
"subgraphs": {
"accounts": {
"requests": [
{
"request": {"query":"{me{name}}"},
"response": {"data": { "me": { "name": "test" } } }
},
{
"request": {"query":"{me{nom:name}}"},
"response": {"data": { "me": { "nom": "test" } } }
}
]
}
}
}
```

the `schema_path` and `configuration_path` field are relative to the test's folder. The `subgraph` field can contain mocked requests and responses for each subgraph. If the Router fails to load with this schema and configuration, then this action will fail the test.

## Reload configuration

Reloads the router with a new configuration file. If the Router fails to load the new configuration, then this action will fail the test.

```json
{
"type": "ReloadConfiguration",
"configuration_path": "./configuration.yaml"
}
```

## Reload schema

Reloads the router with a new schema file. If the Router fails to load the new configuration, then this action will fail the test.

```json
{
"type": "ReloadSchema",
"schema_path": "./supergraph.graphql"
}
```

## Request

Sends a request to the Router, and verifies that the response body matches the expected response. If it does not match or returned any HTTP error, then this action will fail the test.
```json
{
"type": "Request",
"request": {
"query": "{ me { name } }"
},
"expected_response": {
"data":{
"me":{
"name":"Ada Lovelace"
}
}
}
}
```

### Stop

Stops the Router. If the Router does not stop correctly, then this action will fail the test.

```json
{
"type": "Stop"
}
```
3 changes: 3 additions & 0 deletions apollo-router/tests/samples/basic/query1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This is an example test

This file adds some context that will be displayed on test failure
4 changes: 4 additions & 0 deletions apollo-router/tests/samples/basic/query1/configuration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
override_subgraph_url:
products: http://localhost:4005
include_subgraph_errors:
all: true
52 changes: 52 additions & 0 deletions apollo-router/tests/samples/basic/query1/plan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"actions": [
{
"type": "Start",
"schema_path": "./supergraph.graphql",
"configuration_path": "./configuration.yaml",
"subgraphs": {
"accounts": {
"requests": [
{
"request": {"query":"{me{name}}"},
"response": {"data": { "me": { "name": "test" } } }
},
{
"request": {"query":"{me{nom:name}}"},
"response": {"data": { "me": { "nom": "test" } } }
}
]
}
}
},
{
"type": "Request",
"request": {
"query": "{ me { name } }"
},
"expected_response": {
"data":{
"me":{
"name":"test"
}
}
}
},
{
"type": "Request",
"request": {
"query": "{ me { nom: name } }"
},
"expected_response": {
"data": {
"me": {
"nom": "test"
}
}
}
},
{
"type": "Stop"
}
]
}
Loading
Loading