Skip to content

Commit

Permalink
Improve test_integration internal organization.
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisGariepy committed Dec 1, 2022
1 parent 81e03d6 commit 5d4c244
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 271 deletions.
4 changes: 2 additions & 2 deletions test_integration/fixtures/codegen/benchmarks.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[[codegen]]
[[test]]
name = "Sync"
base_path = "benches/execution/cornucopia_benches"
destination = "generated_sync.rs"
sync = true

[[codegen]]
[[test]]
name = "Async"
base_path = "benches/execution/cornucopia_benches"
destination = "generated_async.rs"
6 changes: 3 additions & 3 deletions test_integration/fixtures/codegen/examples.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[[codegen]]
[[test]]
name = "Auto build"
base_path = "examples/auto_build"
run = true

[[codegen]]
[[test]]
name = "Basic sync"
base_path = "examples/basic_sync"
sync = true
run = true

[[codegen]]
[[test]]
name = "Basic async"
base_path = "examples/basic_async"
run = true
4 changes: 2 additions & 2 deletions test_integration/fixtures/codegen/test_codegen.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[[codegen]]
[[test]]
name = "Sync"
base_path = "test_codegen"
destination = "src/cornucopia_sync.rs"
derive_ser = true
sync = true
run = true

[[codegen]]
[[test]]
name = "Async"
base_path = "test_codegen"
destination = "src/cornucopia_async.rs"
Expand Down
93 changes: 93 additions & 0 deletions test_integration/src/codegen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::{
fixtures::{CodegenTest, TestSuite},
utils::{reset_db, rustfmt_file, rustfmt_string},
};

use cornucopia::{CodegenSettings, Error};
use owo_colors::OwoColorize;
use std::{env::set_current_dir, process::Command};

// Run codegen test, return true if all test are successful
pub(crate) fn run_codegen_test(
client: &mut postgres::Client,
apply: bool,
) -> Result<bool, Box<dyn std::error::Error>> {
let mut successful = true;
let original_pwd = std::env::current_dir()?;
let fixture_path = "fixtures/codegen";

let test_suites = TestSuite::<CodegenTest>::read(fixture_path);
for suite in test_suites {
println!("{}", format!("[codegen] {}", suite.name).magenta());
for test in suite.tests {
set_current_dir(format!("../{}", test.base_path))?;

// Load schema
reset_db(client)?;
cornucopia::load_schema(client, vec!["schema.sql".to_string()])?;

// If `--apply`, then the code will be regenerated.
// Otherwise, it is only checked.
if apply {
// Generate
cornucopia::generate_live(
client,
test.queries_path.to_str().unwrap(), // TODO: Update this once our API accepts paths
Some(test.destination.to_str().unwrap()), // TODO: Update this once our API accepts paths
CodegenSettings::from(&test),
)
.map_err(Error::report)?;
// Format the generated file
rustfmt_file(&test.destination);
} else {
// Get currently checked-in generate file
let old_codegen = std::fs::read_to_string(&test.destination).unwrap();
// Generate new file
let new_codegen = cornucopia::generate_live(
client,
test.queries_path.to_str().unwrap(), // TODO: Update this once our API accepts paths
None,
CodegenSettings::from(&test),
)
.map_err(Error::report)?;
// Format the generated code string by piping to rustfmt
let new_codegen_formatted = rustfmt_string(&new_codegen);

// If the newly generated file differs from
// the currently checked in one, return an error.
if old_codegen != new_codegen_formatted {
Err(format!(
"\"{}\" is outdated",
test.destination.to_str().unwrap()
))?;
}
}
println!("(generate) {} {}", test.name, "OK".green());

if test.run {
// Change current directory
std::env::set_current_dir(&original_pwd)?;
std::env::set_current_dir(&format!("../{}", test.base_path))?;
// Run
let result = Command::new("cargo").arg("run").output()?;
if result.status.success() {
println!("(run) {} {}", test.name, "OK".green());
} else {
successful = false;
println!(
" {}\n{}",
"ERR".red(),
String::from_utf8_lossy(&result.stderr)
.as_ref()
.bright_black()
);
}
}

// Move back to original directory
std::env::set_current_dir(&original_pwd)?;
}
}

Ok(successful)
}
96 changes: 96 additions & 0 deletions test_integration/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use cornucopia::{CodegenSettings, Error};
use owo_colors::OwoColorize;

use crate::{
fixtures::{ErrorTest, TestSuite},
utils::reset_db,
};

/// Run errors test, return true if all test are successful
pub(crate) fn run_errors_test(
client: &mut postgres::Client,
apply: bool,
) -> Result<bool, Box<dyn std::error::Error>> {
let mut successful = true;
let original_pwd = std::env::current_dir().unwrap();
let test_suites = TestSuite::<ErrorTest>::read("fixtures/errors");

for mut suite in test_suites {
println!("{} {}", "[error]".magenta(), suite.name.magenta());
for test in suite.tests.iter_mut() {
// Generate file tree path
let temp_dir = tempfile::tempdir()?;

// We need to change current dir for error path to always be the same
std::env::set_current_dir(&temp_dir)?;

// Generate schema
std::fs::write(
"schema.sql",
[
"CREATE TABLE author (id SERIAL, name TEXT);\n",
&test.schema,
]
.concat(),
)?;

// Generate queries files
std::fs::create_dir("queries")?;
std::fs::write("queries/test.sql", &test.query)?;

// Reset db
reset_db(client)?;

// Run codegen
let result = cornucopia::load_schema(client, vec!["schema.sql".into()])
.map_err(Error::from)
.and_then(|_| {
cornucopia::generate_live(
client,
"queries",
None,
CodegenSettings::from(&*test),
)
});

let err = result.unwrap_err().report();
let err_trimmed = err.trim();
if err_trimmed == test.error.trim() {
println!("{} {}", test.name, "OK".green());
} else {
let got_msg = if apply {
"Apply:".bright_black()
} else {
"Got:".bright_black()
};
let expected_msg = if apply {
"Previous:".bright_black()
} else {
"Expected:".bright_black()
};
successful = false;
println!(
"{} {}\n{}\n{}\n{}\n{}\n",
test.name,
"ERR".red(),
expected_msg,
test.error,
got_msg,
err,
);
}
if apply {
test.error = err_trimmed.into();
}
std::env::set_current_dir(&original_pwd)?;
}

if apply {
// Format test descriptor and update error message if needed
let edited = toml::to_string_pretty(&suite.tests)?;
std::fs::write(suite.path, edited)?;
}
}

Ok(successful)
}
86 changes: 86 additions & 0 deletions test_integration/src/fixtures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::path::{Path, PathBuf};

use cornucopia::CodegenSettings;
use serde::{de::DeserializeOwned, Deserialize};

#[derive(Deserialize)]
struct TestSuiteDeserializer<T> {
test: Vec<T>,
}

pub struct TestSuite<T> {
pub(crate) name: String,
pub(crate) path: PathBuf,
pub(crate) tests: Vec<T>,
}

impl<T: DeserializeOwned> TestSuite<T> {
pub(crate) fn read<P: AsRef<Path>>(fixtures_path: P) -> impl Iterator<Item = TestSuite<T>> {
std::fs::read_dir(fixtures_path).unwrap().map(|file| {
let file = file.unwrap();
let name = file.file_name().to_string_lossy().to_string();
let path = file.path();
let content = std::fs::read_to_string(&path).unwrap();
let tests: TestSuiteDeserializer<T> = toml::from_str(&content).unwrap();
TestSuite {
name,
tests: tests.test,
path,
}
})
}
}

/// Codegen test case
#[derive(Debug, serde::Deserialize)]
pub(crate) struct CodegenTest {
pub(crate) name: String,
pub(crate) base_path: String,
#[serde(default = "default_queries_path")]
pub(crate) queries_path: PathBuf,
#[serde(default = "default_destination_path")]
pub(crate) destination: PathBuf,
#[serde(default)]
pub(crate) sync: bool,
#[serde(default)]
pub(crate) derive_ser: bool,
#[serde(default)]
pub(crate) run: bool,
}

fn default_queries_path() -> PathBuf {
PathBuf::from("queries")
}

fn default_destination_path() -> PathBuf {
PathBuf::from("src/cornucopia.rs")
}

impl From<&CodegenTest> for CodegenSettings {
fn from(codegen_test: &CodegenTest) -> Self {
Self {
is_async: !codegen_test.sync,
derive_ser: codegen_test.derive_ser,
}
}
}

/// Error test case
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub(crate) struct ErrorTest {
pub(crate) name: String,
#[serde(default)]
pub(crate) query: String,
#[serde(default)]
pub(crate) schema: String,
pub(crate) error: String,
}

impl From<&ErrorTest> for CodegenSettings {
fn from(_error_test: &ErrorTest) -> Self {
Self {
is_async: false,
derive_ser: false,
}
}
}
Loading

0 comments on commit 5d4c244

Please sign in to comment.