mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-07-03 02:51:04 +02:00
Load and type-check stored queries at server boot, refusing breakage
At startup the server now loads each graph's stored-query registry, type-checks every query against that graph's live schema, and refuses to boot if any query references a type/property the schema doesn't have (same posture as bad policy YAML) — so schema drift surfaces at the deploy boundary, not silently at invocation. Non-blocking warnings are logged. The validated registry is attached to the GraphHandle (the two production sites previously held `queries: None`). Loading (parse + key==symbol identity) happens at settings-build time where the config is in scope; the schema type-check happens after each engine opens (single mode in `open_single_with_queries`, multi mode in `open_single_graph`). `open_with_bearer_tokens_and_policy` delegates with an empty registry so its 18 test callers are unchanged; the public `new_*` constructors are unchanged (only the private build path threads the registry). - ServerConfigMode::Single / GraphStartupConfig carry the loaded registry - boot tests: valid registry boots; type-broken query refuses boot + names it Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a16b3c6ac
commit
9aa96cbb4b
2 changed files with 213 additions and 10 deletions
|
|
@ -16,6 +16,7 @@ use omnigraph_server::api::{
|
|||
BranchCreateRequest, BranchMergeRequest, ChangeRequest, ErrorOutput, ExportRequest,
|
||||
IngestRequest, QueryRequest, ReadRequest, SchemaApplyRequest, SchemaOutput,
|
||||
};
|
||||
use omnigraph_server::queries::{QueryRegistry, RegistrySpec};
|
||||
use omnigraph_server::{AppState, build_app};
|
||||
use serde_json::{Value, json};
|
||||
use serial_test::serial;
|
||||
|
|
@ -141,6 +142,73 @@ fn graph_path(root: &Path) -> PathBuf {
|
|||
root.join("server.omni")
|
||||
}
|
||||
|
||||
fn stored_query_registry(specs: &[(&str, &str, bool)]) -> QueryRegistry {
|
||||
QueryRegistry::from_specs(
|
||||
specs
|
||||
.iter()
|
||||
.map(|(name, source, expose)| RegistrySpec {
|
||||
name: name.to_string(),
|
||||
source: source.to_string(),
|
||||
expose: *expose,
|
||||
tool_name: None,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.expect("specs parse and key==symbol")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn server_boots_with_a_valid_stored_query_registry() {
|
||||
// A stored query that type-checks against the fixture schema
|
||||
// (`Person { name, age }`) must let the server boot.
|
||||
let temp = init_loaded_graph().await;
|
||||
let graph = graph_path(temp.path());
|
||||
let registry = stored_query_registry(&[(
|
||||
"find_person",
|
||||
"query find_person($name: String) { match { $p: Person { name: $name } } return { $p.age } }",
|
||||
false,
|
||||
)]);
|
||||
let state = AppState::open_single_with_queries(
|
||||
graph.to_string_lossy().to_string(),
|
||||
vec![],
|
||||
None,
|
||||
registry,
|
||||
)
|
||||
.await;
|
||||
assert!(state.is_ok(), "valid registry should boot: {:?}", state.err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn server_refuses_boot_on_type_broken_stored_query() {
|
||||
// A stored query referencing a type not in the schema (`Widget`)
|
||||
// must abort boot, naming the offending query.
|
||||
let temp = init_loaded_graph().await;
|
||||
let graph = graph_path(temp.path());
|
||||
let registry = stored_query_registry(&[(
|
||||
"ghost",
|
||||
"query ghost() { match { $w: Widget } return { $w.name } }",
|
||||
false,
|
||||
)]);
|
||||
let result = AppState::open_single_with_queries(
|
||||
graph.to_string_lossy().to_string(),
|
||||
vec![],
|
||||
None,
|
||||
registry,
|
||||
)
|
||||
.await;
|
||||
// `AppState` is not `Debug`, so match rather than `expect_err`.
|
||||
let err = match result {
|
||||
Ok(_) => panic!("type-broken stored query must refuse boot"),
|
||||
Err(err) => err,
|
||||
};
|
||||
let msg = err.to_string();
|
||||
assert!(msg.contains("ghost"), "error should name the broken query: {msg}");
|
||||
assert!(
|
||||
msg.contains("schema check"),
|
||||
"error should mention the schema check: {msg}"
|
||||
);
|
||||
}
|
||||
|
||||
fn drifted_test_schema() -> String {
|
||||
fs::read_to_string(fixture("test.pg"))
|
||||
.unwrap()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue