api_testing_core/graphql/
schema_file.rs1use std::path::{Path, PathBuf};
2
3use crate::{Result, env_file};
4use nils_common::env as shared_env;
5
6const FALLBACK_CANDIDATES: &[&str] = &[
7 "schema.gql",
8 "schema.graphql",
9 "schema.graphqls",
10 "api.graphql",
11 "api.gql",
12];
13
14fn resolve_relative_under_setup(setup_dir: &Path, rel: &Path) -> PathBuf {
15 if rel.is_absolute() {
16 return rel.to_path_buf();
17 }
18
19 let parent = rel.parent().unwrap_or_else(|| Path::new("."));
20 let parent_abs = std::fs::canonicalize(setup_dir.join(parent)).unwrap_or_else(|_| {
21 setup_dir.join(parent)
23 });
24 let filename = rel.file_name().unwrap_or(rel.as_os_str());
25 parent_abs.join(filename)
26}
27
28pub fn resolve_schema_path(setup_dir: &Path, schema_file_arg: Option<&str>) -> Result<PathBuf> {
29 let schema_file = schema_file_arg
30 .map(str::trim)
31 .filter(|s| !s.is_empty())
32 .map(|s| s.to_string())
33 .or_else(|| shared_env::env_non_empty("GQL_SCHEMA_FILE"))
34 .or_else(|| {
35 let schema_local = setup_dir.join("schema.local.env");
36 let schema_env = setup_dir.join("schema.env");
37 let files: Vec<&Path> = vec![&schema_env, &schema_local];
38 env_file::read_var_last_wins("GQL_SCHEMA_FILE", &files)
39 .ok()
40 .flatten()
41 })
42 .or_else(|| {
43 for c in FALLBACK_CANDIDATES {
44 if setup_dir.join(c).is_file() {
45 return Some((*c).to_string());
46 }
47 }
48 None
49 });
50
51 let Some(schema_file) = schema_file else {
52 anyhow::bail!(
53 "Schema file not configured. Set GQL_SCHEMA_FILE in schema.env (or pass --file)."
54 );
55 };
56
57 let schema_path = resolve_relative_under_setup(setup_dir, Path::new(&schema_file));
58 if !schema_path.is_file() {
59 anyhow::bail!("Schema file not found: {}", schema_path.display());
60 }
61
62 Ok(schema_path)
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use nils_test_support::{EnvGuard, GlobalStateLock};
69 use pretty_assertions::assert_eq;
70 use tempfile::TempDir;
71
72 fn write_file(path: &Path, contents: &str) {
73 std::fs::create_dir_all(path.parent().expect("parent")).expect("mkdir");
74 std::fs::write(path, contents).expect("write");
75 }
76
77 #[test]
78 fn schema_file_arg_is_trimmed_and_resolved_under_setup() {
79 let tmp = TempDir::new().expect("tmp");
80 let setup_dir = std::fs::canonicalize(tmp.path()).expect("setup abs");
81
82 write_file(
83 &setup_dir.join("schemas/api.graphql"),
84 "schema { query: Query }\n",
85 );
86
87 let got = resolve_schema_path(&setup_dir, Some(" schemas/api.graphql ")).expect("path");
88 let expected = std::fs::canonicalize(setup_dir.join("schemas/api.graphql")).expect("abs");
89 assert_eq!(got, expected);
90 }
91
92 #[test]
93 fn schema_file_env_var_is_used_when_no_arg() {
94 let lock = GlobalStateLock::new();
95 let _guard = EnvGuard::set(&lock, "GQL_SCHEMA_FILE", " schema.gql ");
96
97 let tmp = TempDir::new().expect("tmp");
98 let setup_dir = std::fs::canonicalize(tmp.path()).expect("setup abs");
99 write_file(&setup_dir.join("schema.gql"), "schema { query: Query }\n");
100
101 let got = resolve_schema_path(&setup_dir, None).expect("path");
102 let expected = std::fs::canonicalize(setup_dir.join("schema.gql")).expect("abs");
103 assert_eq!(got, expected);
104 }
105
106 #[test]
107 fn schema_file_schema_env_is_used_when_no_arg_or_env() {
108 let lock = GlobalStateLock::new();
109 let _guard = EnvGuard::remove(&lock, "GQL_SCHEMA_FILE");
110
111 let tmp = TempDir::new().expect("tmp");
112 let setup_dir = std::fs::canonicalize(tmp.path()).expect("setup abs");
113
114 write_file(
115 &setup_dir.join("schema.env"),
116 "export GQL_SCHEMA_FILE=schemas/schema.graphql\n",
117 );
118 write_file(
119 &setup_dir.join("schemas/schema.graphql"),
120 "schema { query: Query }\n",
121 );
122
123 let got = resolve_schema_path(&setup_dir, None).expect("path");
124 let expected =
125 std::fs::canonicalize(setup_dir.join("schemas/schema.graphql")).expect("abs");
126 assert_eq!(got, expected);
127 }
128
129 #[test]
130 fn schema_file_falls_back_to_candidate_filenames() {
131 let lock = GlobalStateLock::new();
132 let _guard = EnvGuard::remove(&lock, "GQL_SCHEMA_FILE");
133
134 let tmp = TempDir::new().expect("tmp");
135 let setup_dir = std::fs::canonicalize(tmp.path()).expect("setup abs");
136
137 write_file(&setup_dir.join("schema.gql"), "schema { query: Query }\n");
138
139 let got = resolve_schema_path(&setup_dir, None).expect("path");
140 let expected = std::fs::canonicalize(setup_dir.join("schema.gql")).expect("abs");
141 assert_eq!(got, expected);
142 }
143
144 #[test]
145 fn schema_file_errors_when_not_configured() {
146 let lock = GlobalStateLock::new();
147 let _guard = EnvGuard::remove(&lock, "GQL_SCHEMA_FILE");
148
149 let tmp = TempDir::new().expect("tmp");
150 let setup_dir = std::fs::canonicalize(tmp.path()).expect("setup abs");
151
152 let err = resolve_schema_path(&setup_dir, None).unwrap_err();
153 assert!(
154 err.to_string()
155 .contains("Schema file not configured. Set GQL_SCHEMA_FILE")
156 );
157 }
158}