add Graphql ci (#4681)

This commit is contained in:
Raphael Darley 2024-09-04 04:30:15 -07:00 committed by GitHub
parent 7ab4d94e93
commit ebe8d49ed9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 238 additions and 4 deletions

View file

@ -310,6 +310,33 @@ jobs:
path: target/llvm-cov/html/
retention-days: 5
graphql-integration:
name: GraphQL integration
runs-on: ubuntu-latest
steps:
- name: Install stable toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Checkout sources
uses: actions/checkout@v4
- name: Setup cache
uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
run: |
sudo apt-get -y update
- name: Install cargo-make
run: cargo install --debug --locked cargo-make
- name: run graphql integration test
run: cargo make ci-graphql-integration
test-sdk-build:
name: Test SDK build
runs-on: ubuntu-latest

View file

@ -45,6 +45,11 @@ command = "cargo"
env = { RUST_BACKTRACE = 1, RUST_LOG = { value = "cli_integration::common=debug", condition = { env_not_set = ["RUST_LOG"] } } }
args = ["test", "--locked", "--features", "storage-mem,ml", "--workspace", "--test", "ml_integration", "--", "ml_integration", "--nocapture"]
[tasks.ci-graphql-integration]
category = "GRAPHQL - INTEGRATION TESTS"
command = "cargo"
env = { RUST_BACKTRACE = 1, RUST_LOG = { value = "cli_integration::common=debug", condition = { env_not_set = ["RUST_LOG"] } }, SURREAL_EXPERIMENTAL_GRAPHQL = "true", RUSTFLAGS = "--cfg surrealdb_unstable" }
args = ["test", "--locked", "--features", "storage-mem", "--workspace", "--test", "graphql_integration", "--", "graphql_integration", "--nocapture"]
[tasks.ci-test-workspace]
category = "CI - INTEGRATION TESTS"

View file

@ -1,4 +1,6 @@
use rand::{thread_rng, Rng};
use std::collections::btree_set::Iter;
use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::path::{Path, PathBuf};
@ -95,7 +97,11 @@ impl Drop for Child {
}
}
pub fn run_internal<P: AsRef<Path>>(args: &str, current_dir: Option<P>) -> Child {
pub fn run_internal<P: AsRef<Path>>(
args: &str,
current_dir: Option<P>,
vars: Option<HashMap<String, String>>,
) -> Child {
let mut path = std::env::current_exe().unwrap();
assert!(path.pop());
if path.ends_with("deps") {
@ -118,6 +124,9 @@ pub fn run_internal<P: AsRef<Path>>(args: &str, current_dir: Option<P>) -> Child
let stderr = Stdio::from(File::create(&stderr_path).unwrap());
cmd.env_clear();
if let Some(v) = vars {
cmd.envs(v);
}
cmd.stdin(Stdio::piped());
cmd.stdout(stdout);
cmd.stderr(stderr);
@ -132,12 +141,12 @@ pub fn run_internal<P: AsRef<Path>>(args: &str, current_dir: Option<P>) -> Child
/// Run the CLI with the given args
pub fn run(args: &str) -> Child {
run_internal::<String>(args, None)
run_internal::<String>(args, None, None)
}
/// Run the CLI with the given args inside a temporary directory
pub fn run_in_dir<P: AsRef<Path>>(args: &str, current_dir: P) -> Child {
run_internal(args, Some(current_dir))
run_internal(args, Some(current_dir), None)
}
pub fn tmp_file(name: &str) -> String {
@ -153,6 +162,7 @@ pub struct StartServerArguments {
pub tick_interval: time::Duration,
pub temporary_directory: Option<String>,
pub args: String,
pub vars: Option<HashMap<String, String>>,
}
impl Default for StartServerArguments {
@ -165,6 +175,7 @@ impl Default for StartServerArguments {
tick_interval: time::Duration::new(1, 0),
temporary_directory: None,
args: "".to_string(),
vars: None,
}
}
}
@ -207,6 +218,18 @@ pub async fn start_server_with_temporary_directory(
.await
}
pub async fn start_server_gql_without_auth() -> Result<(String, Child), Box<dyn Error>> {
start_server(StartServerArguments {
auth: false,
vars: Some(HashMap::from([(
"SURREAL_EXPERIMENTAL_GRAPHQL".to_string(),
"true".to_string(),
)])),
..Default::default()
})
.await
}
pub async fn start_server(
StartServerArguments {
path,
@ -216,6 +239,7 @@ pub async fn start_server(
tick_interval,
temporary_directory,
args,
vars,
}: StartServerArguments,
) -> Result<(String, Child), Box<dyn Error>> {
let mut rng = thread_rng();
@ -257,7 +281,7 @@ pub async fn start_server(
info!("starting server with args: {start_args}");
// Configure where the logs go when running the test
let server = run_internal::<String>(&start_args, None);
let server = run_internal::<String>(&start_args, None, vars.clone());
if !wait_is_ready {
return Ok((addr, server));

View file

@ -0,0 +1,178 @@
mod common;
#[cfg(surrealdb_unstable)]
mod graphql_integration {
use std::time::Duration;
use assert_fs::assert;
use http::header::HeaderValue;
use http::{header, Method};
use reqwest::Client;
use serde_json::json;
use surrealdb::headers::{AUTH_DB, AUTH_NS};
use surrealdb::sql;
use test_log::test;
use ulid::Ulid;
use super::common::{self, StartServerArguments, PASS, USER};
#[test(tokio::test)]
async fn basic() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_gql_without_auth().await.unwrap();
let gql_url = &format!("http://{addr}/graphql");
let sql_url = &format!("http://{addr}/sql");
let mut headers = reqwest::header::HeaderMap::new();
let ns = Ulid::new().to_string();
let db = Ulid::new().to_string();
headers.insert("surreal-ns", ns.parse()?);
headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// check errors with no tables
{
let res = client.post(gql_url).body("").send().await?;
assert_eq!(res.status(), 400);
let body = res.text().await?;
assert!(body.contains("no tables found in database"), "body: {body}")
}
// add schema and data
{
let res = client
.post(sql_url)
.body(
r#"
DEFINE TABLE foo SCHEMAFUL;
DEFINE FIELD val ON foo TYPE int;
CREATE foo:1 set val = 42;
CREATE foo:2 set val = 43;
"#,
)
.send()
.await?;
assert_eq!(res.status(), 200);
}
// fetch data via graphql
{
let res = client
.post(gql_url)
.body(json!({"query": r#"query{foo{id, val}}"#}).to_string())
.send()
.await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
let expected = json!({
"data": {
"foo": [
{
"id": "foo:1",
"val": 42
},
{
"id": "foo:2",
"val": 43
}
]
}
});
assert_eq!(expected.to_string(), body)
}
// test limit
{
let res = client
.post(gql_url)
.body(json!({"query": r#"query{foo(limit: 1){id, val}}"#}).to_string())
.send()
.await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
let expected = json!({
"data": {
"foo": [
{
"id": "foo:1",
"val": 42
}
]
}
});
assert_eq!(expected.to_string(), body)
}
// test start
{
let res = client
.post(gql_url)
.body(json!({"query": r#"query{foo(start: 1){id, val}}"#}).to_string())
.send()
.await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
let expected = json!({
"data": {
"foo": [
{
"id": "foo:2",
"val": 43
}
]
}
});
assert_eq!(expected.to_string(), body)
}
// test order
{
let res = client
.post(gql_url)
.body(json!({"query": r#"query{foo(order: {desc: val}){id}}"#}).to_string())
.send()
.await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
let expected = json!({
"data": {
"foo": [
{
"id": "foo:2",
},
{
"id": "foo:1",
}
]
}
});
assert_eq!(expected.to_string(), body)
}
// test filter
{
let res = client
.post(gql_url)
.body(json!({"query": r#"query{foo(filter: {val: {eq: 42}}){id}}"#}).to_string())
.send()
.await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
let expected = json!({
"data": {
"foo": [
{
"id": "foo:1",
}
]
}
});
assert_eq!(expected.to_string(), body)
}
Ok(())
}
}