fixed and tested subschema promotions for beat processing

This commit is contained in:
2026-04-14 06:09:00 -04:00
parent a53e89df52
commit 0b4607b7d4
12 changed files with 618 additions and 23 deletions

View File

@ -508,21 +508,24 @@ impl Schema {
to_insert: &mut Vec<(String, Arc<Schema>)>,
errors: &mut Vec<crate::drop::Error>,
) {
let mut should_push = false;
// Push ad-hoc inline composition into the addressable registry
if schema_arc.obj.properties.is_some()
|| schema_arc.obj.items.is_some()
|| schema_arc.obj.family.is_some()
|| schema_arc.obj.one_of.is_some()
{
should_push = true;
}
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &schema_arc.obj.type_ {
if !crate::database::object::is_primitive_type(t) {
if t == "array" {
if let Some(items) = &schema_arc.obj.items {
if let Some(crate::database::object::SchemaTypeOrArray::Single(it)) = &items.obj.type_ {
if !crate::database::object::is_primitive_type(it) {
if items.obj.properties.is_some() || items.obj.cases.is_some() {
to_insert.push((path.clone(), Arc::clone(schema_arc)));
}
}
}
}
} else if !crate::database::object::is_primitive_type(t) {
Self::validate_identifier(t, "type", root_id, &path, errors);
should_push = true;
// Is this an explicit inline ad-hoc composition?
if schema_arc.obj.properties.is_some() || schema_arc.obj.cases.is_some() {
to_insert.push((path.clone(), Arc::clone(schema_arc)));
}
}
}
@ -530,10 +533,6 @@ impl Schema {
Self::validate_identifier(family, "$family", root_id, &path, errors);
}
if should_push {
to_insert.push((path.clone(), Arc::clone(schema_arc)));
}
Self::collect_child_schemas(schema_arc, root_id, path, to_insert, errors);
}
@ -575,7 +574,9 @@ impl Schema {
let mut map_opt = |opt: &Option<Arc<Schema>>, pass_path: bool, sub: &str| {
if let Some(v) = opt {
if pass_path {
Self::collect_schemas(v, root_id, format!("{}/{}", path, sub), to_insert, errors);
// Arrays explicitly push their wrapper natively.
// 'items' becomes a transparent conduit, bypassing self-promotion and skipping the '/items' suffix.
Self::collect_child_schemas(v, root_id, path.clone(), to_insert, errors);
} else {
Self::collect_child_schemas(v, root_id, format!("{}/{}", path, sub), to_insert, errors);
}

View File

@ -3683,6 +3683,12 @@ fn test_database_4_0() {
crate::tests::runner::run_test_case(&path, 4, 0).unwrap();
}
#[test]
fn test_database_5_0() {
let path = format!("{}/fixtures/database.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 5, 0).unwrap();
}
#[test]
fn test_cases_0_0() {
let path = format!("{}/fixtures/cases.json", env!("CARGO_MANIFEST_DIR"));

View File

@ -107,10 +107,6 @@ fn test_library_api() {
}
}
},
"source_schema/target": {
"type": "target_schema",
"compiledProperties": ["value"]
},
"target_schema": {
"type": "object",
"properties": {

View File

@ -49,7 +49,13 @@ impl Case {
Err(d) => d.clone(),
};
expect.assert_drop(&result)
expect.assert_drop(&result)?;
if let Ok(db) = db_res {
expect.assert_schemas(db)?;
}
Ok(())
}
pub fn run_validate(&self, db: Arc<Database>) -> Result<(), String> {

View File

@ -1,6 +1,7 @@
pub mod pattern;
pub mod sql;
pub mod drop;
pub mod schema;
use serde::Deserialize;
@ -18,4 +19,6 @@ pub struct Expect {
pub errors: Option<Vec<serde_json::Value>>,
#[serde(default)]
pub sql: Option<Vec<SqlExpectation>>,
#[serde(default)]
pub schemas: Option<Vec<String>>,
}

View File

@ -0,0 +1,27 @@
use super::Expect;
use std::sync::Arc;
impl Expect {
pub fn assert_schemas(&self, db: &Arc<crate::database::Database>) -> Result<(), String> {
if let Some(expected_schemas) = &self.schemas {
// Collect actual schemas and sort
let mut actual: Vec<String> = db.schemas.keys().cloned().collect();
actual.sort();
// Collect expected schemas and sort
let mut expected: Vec<String> = expected_schemas.clone();
expected.sort();
if actual != expected {
return Err(format!(
"Schema Promotion Mismatch!\nExpected Schemas ({}):\n{:#?}\n\nActual Promoted Schemas ({}):\n{:#?}",
expected.len(),
expected,
actual.len(),
actual
));
}
}
Ok(())
}
}