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

chore: convert cli tests to md #2735

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 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
2 changes: 2 additions & 0 deletions src/cli/generator/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#[serde(skip_serializing_if = "Option::is_none")]
pub preset: Option<PresetConfig>,
pub schema: Schema,
#[serde(default, skip_serializing_if = "TemplateString::is_empty")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary changes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

existing tests don't even work rn.. the config in tests is either outdated or we need to add the default tag on secret

pub secret: TemplateString,
#[serde(skip_serializing_if = "Option::is_none")]
pub llm: Option<LLMConfig>,
}
Expand Down Expand Up @@ -288,7 +290,7 @@
LLMConfig { model: llm.model, secret }
});

Ok(Config {

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Check Examples

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Formatter and Lint Check

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-x64-gnu

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-x64-musl

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-arm64-gnu

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-arm64-musl

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on linux-ia32-gnu

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on darwin-arm64

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on darwin-x64

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on win32-x64-msvc

missing field `secret` in initializer of `cli::generator::config::Config<_>`

Check failure on line 293 in src/cli/generator/config.rs

View workflow job for this annotation

GitHub Actions / Run Tests on win32-ia32-msvc

missing field `secret` in initializer of `cli::generator::config::Config<_>`
inputs,
output,
schema: self.schema,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
```json @config
{
"inputs": [
{
Expand Down Expand Up @@ -63,3 +64,4 @@
"query": "Query"
}
}
```
ssddOnTop marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
```json @config
{
"inputs": [
{
Expand All @@ -8,7 +9,7 @@
},
{
"proto": {
"src": "../../../../../../tailcall-fixtures/fixtures/protobuf/news.proto"
"src": "tailcall-fixtures/fixtures/protobuf/news.proto"
}
}
],
Expand All @@ -26,3 +27,4 @@
"query": "Query"
}
}
```
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
```json @config
{
"inputs": [
{
Expand Down Expand Up @@ -81,3 +82,4 @@
"query": "Query"
}
}
```
281 changes: 167 additions & 114 deletions tests/cli/gen.rs
Original file line number Diff line number Diff line change
@@ -1,115 +1,165 @@
pub mod test {
use std::path::Path;
mod parser;

pub mod cacache_manager {
use std::io::{Read, Write};
use std::path::PathBuf;

mod cacache_manager {
use std::io::{Read, Write};
use std::path::PathBuf;
use flate2::write::GzEncoder;
use flate2::Compression;
use http_cache_reqwest::{CacheManager, HttpResponse};
use http_cache_semantics::CachePolicy;
use serde::{Deserialize, Serialize};

use flate2::write::GzEncoder;
use flate2::Compression;
use http_cache_reqwest::{CacheManager, HttpResponse};
use http_cache_semantics::CachePolicy;
use serde::{Deserialize, Serialize};
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
pub type Result<T> = std::result::Result<T, BoxError>;

pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
pub type Result<T> = std::result::Result<T, BoxError>;
pub struct CaCacheManager {
path: PathBuf,
}

#[derive(Clone, Deserialize, Serialize)]
pub struct Store {
response: HttpResponse,
policy: CachePolicy,
}

pub struct CaCacheManager {
path: PathBuf,
impl Default for CaCacheManager {
fn default() -> Self {
Self { path: PathBuf::from("./.cache") }
}
}

#[derive(Clone, Deserialize, Serialize)]
pub struct Store {
#[async_trait::async_trait]
impl CacheManager for CaCacheManager {
async fn put(
&self,
cache_key: String,
response: HttpResponse,
policy: CachePolicy,
) -> Result<HttpResponse> {
let data = Store { response: response.clone(), policy };
let bytes = bincode::serialize(&data)?;

let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder.write_all(&bytes)?;
let compressed_bytes = encoder.finish()?;

cacache::write(&self.path, cache_key, compressed_bytes).await?;
Ok(response)
}

impl Default for CaCacheManager {
fn default() -> Self {
Self { path: PathBuf::from("./.cache") }
async fn get(&self, cache_key: &str) -> Result<Option<(HttpResponse, CachePolicy)>> {
match cacache::read(&self.path, cache_key).await {
Ok(compressed_data) => {
let mut decoder = flate2::read::GzDecoder::new(compressed_data.as_slice());
let mut serialized_data = Vec::new();
decoder.read_to_end(&mut serialized_data)?;
let store: Store = bincode::deserialize(&serialized_data)?;
Ok(Some((store.response, store.policy)))
}
Err(_) => Ok(None),
}
}

#[async_trait::async_trait]
impl CacheManager for CaCacheManager {
async fn put(
&self,
cache_key: String,
response: HttpResponse,
policy: CachePolicy,
) -> Result<HttpResponse> {
let data = Store { response: response.clone(), policy };
let bytes = bincode::serialize(&data)?;

let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder.write_all(&bytes)?;
let compressed_bytes = encoder.finish()?;

cacache::write(&self.path, cache_key, compressed_bytes).await?;
Ok(response)
}
async fn delete(&self, cache_key: &str) -> Result<()> {
Ok(cacache::remove(&self.path, cache_key).await?)
}
}
}

async fn get(&self, cache_key: &str) -> Result<Option<(HttpResponse, CachePolicy)>> {
match cacache::read(&self.path, cache_key).await {
Ok(compressed_data) => {
let mut decoder = flate2::read::GzDecoder::new(compressed_data.as_slice());
let mut serialized_data = Vec::new();
decoder.read_to_end(&mut serialized_data)?;
let store: Store = bincode::deserialize(&serialized_data)?;
Ok(Some((store.response, store.policy)))
}
Err(_) => Ok(None),
}
}
pub mod file {
use std::collections::HashMap;
use std::sync::Arc;

use async_trait::async_trait;
use tailcall::core::FileIO;
use tokio::sync::RwLock;

#[derive(Clone, Default)]
pub struct NativeFileTest(Arc<RwLock<HashMap<String, String>>>);
#[async_trait]
impl FileIO for NativeFileTest {
async fn write<'a>(&'a self, path: &'a str, content: &'a [u8]) -> anyhow::Result<()> {
self.0.write().await.insert(
path.to_string(),
String::from_utf8_lossy(content).to_string(),
);
Ok(())
}

async fn delete(&self, cache_key: &str) -> Result<()> {
Ok(cacache::remove(&self.path, cache_key).await?)
}
async fn read<'a>(&'a self, path: &'a str) -> anyhow::Result<String> {
let val = if let Some(val) = self.0.read().await.get(path).cloned() {
val
} else {
std::fs::read_to_string(path)?
};
Ok(val)
}
}
}

mod http {
use anyhow::Result;
use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions};
use hyper::body::Bytes;
use reqwest::Client;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use tailcall::core::http::Response;
use tailcall::core::HttpIO;
pub mod http {
use anyhow::Result;
use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions};
use hyper::body::Bytes;
use reqwest::Client;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use tailcall::core::http::Response;
use tailcall::core::HttpIO;

use super::cacache_manager::CaCacheManager;
use super::cacache_manager::CaCacheManager;

#[derive(Clone)]
pub struct NativeHttpTest {
client: ClientWithMiddleware,
#[derive(Clone)]
pub struct NativeHttpTest {
client: ClientWithMiddleware,
}

impl Default for NativeHttpTest {
fn default() -> Self {
let mut client = ClientBuilder::new(Client::new());
client = client.with(Cache(HttpCache {
mode: CacheMode::ForceCache,
manager: CaCacheManager::default(),
options: HttpCacheOptions::default(),
}));
Self { client: client.build() }
}
}

impl Default for NativeHttpTest {
fn default() -> Self {
let mut client = ClientBuilder::new(Client::new());
client = client.with(Cache(HttpCache {
mode: CacheMode::ForceCache,
manager: CaCacheManager::default(),
options: HttpCacheOptions::default(),
}));
Self { client: client.build() }
}
#[async_trait::async_trait]
impl HttpIO for NativeHttpTest {
#[allow(clippy::blocks_in_conditions)]
async fn execute(&self, request: reqwest::Request) -> Result<Response<Bytes>> {
let response = self.client.execute(request).await;
Ok(Response::from_reqwest(
response?
.error_for_status()
.map_err(|err| err.without_url())?,
)
.await?)
}
}
}
pub mod env {
use std::borrow::Cow;
use std::collections::HashMap;

#[async_trait::async_trait]
impl HttpIO for NativeHttpTest {
#[allow(clippy::blocks_in_conditions)]
async fn execute(&self, request: reqwest::Request) -> Result<Response<Bytes>> {
let response = self.client.execute(request).await;
Ok(Response::from_reqwest(
response?
.error_for_status()
.map_err(|err| err.without_url())?,
)
.await?)
}
use tailcall::core::EnvIO;

#[derive(Clone)]
pub struct Env(pub HashMap<String, String>);

impl EnvIO for Env {
fn get(&self, key: &str) -> Option<Cow<'_, str>> {
self.0.get(key).map(Cow::from)
}
}
}

pub mod test {
use std::path::Path;

use crate::parser::ExecutionSpec;

mod generator_spec {
use std::path::Path;
Expand All @@ -120,25 +170,31 @@ pub mod test {
use tailcall::core::config::{self, ConfigModule};
use tailcall::core::generator::Generator as ConfigGenerator;
use tailcall::core::valid::{ValidateInto, Validator};
use tokio::runtime::Runtime;

use super::http::NativeHttpTest;
use super::super::http::NativeHttpTest;
use crate::env::Env;
use crate::parser::{ExecutionSpec, IO};

pub fn run_config_generator_spec(path: &Path) -> datatest_stable::Result<()> {
let path = path.to_path_buf();
let runtime = Runtime::new().unwrap();
runtime.block_on(async move {
run_test(&path.to_string_lossy()).await?;
Ok(())
})
}
pub async fn run_test(original_path: &Path, spec: ExecutionSpec) -> anyhow::Result<()> {
let snapshot_name = original_path.to_string_lossy().to_string();

let IO { fs, paths } = spec.configs.into_io().await;
let path = paths.first().unwrap().as_str();

async fn run_test(path: &str) -> anyhow::Result<()> {
let mut runtime = tailcall::cli::runtime::init(&Blueprint::default());
runtime.http = Arc::new(NativeHttpTest::default());
runtime.file = Arc::new(fs);
if let Some(env) = spec.env {
runtime.env = Arc::new(Env(env))
}

let generator = Generator::new(path, runtime);
let config = generator.read().await?;
if spec.debug_assert_config {
insta::assert_debug_snapshot!(snapshot_name, config);
return Ok(());
}

let query_type = config.schema.query.clone().unwrap_or("Query".into());
let mutation_type_name = config.schema.mutation.clone();
let preset: config::transformer::Preset = config
Expand All @@ -164,28 +220,25 @@ pub mod test {

let config = ConfigModule::from(base_config);

insta::assert_snapshot!(path, config.to_sdl());
insta::assert_snapshot!(snapshot_name, config.to_sdl());
Ok(())
}
}
pub fn test_generator(path: &Path) -> datatest_stable::Result<()> {
if let Some(extension) = path.extension() {
if extension == "json"
&& path
.file_name()
.and_then(|v| v.to_str())
.map(|v| v.starts_with("gen"))
.unwrap_or_default()
{
let _ = generator_spec::run_config_generator_spec(path);
}
async fn test_generator(path: &Path) -> datatest_stable::Result<()> {
if path
.file_name()
.and_then(|v| v.to_str())
.map(|v| v.starts_with("gen"))
.unwrap_or_default()
{
let spec = ExecutionSpec::from_source(path, std::fs::read_to_string(path)?)?;
generator_spec::run_test(path, spec).await?;
}
Ok(())
}
pub fn run(path: &Path) -> datatest_stable::Result<()> {
tokio_test::block_on(test_generator(path))
}
}

datatest_stable::harness!(
test::test_generator,
"tests/cli/fixtures/generator",
r"^.*\.json"
);
datatest_stable::harness!(test::run, "tests/cli/fixtures/generator", r"^.*\.md");
Loading
Loading