diff --git a/Cargo.lock b/Cargo.lock index 4c25a12026..37029102bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2366,6 +2366,7 @@ dependencies = [ "serde_json", "simd-doc", "socket2", + "sources", "tempfile", "time 0.3.36", "tokio", @@ -2382,6 +2383,7 @@ dependencies = [ "typestate", "unseal", "url", + "validation-tests", "webpki", ] @@ -8043,6 +8045,36 @@ dependencies = [ [[package]] name = "validation" version = "0.0.0" +dependencies = [ + "anyhow", + "assemble", + "bytes", + "doc", + "extractors", + "futures", + "itertools 0.10.5", + "json", + "labels", + "lazy_static", + "models", + "pbjson-types", + "proto-flow", + "regex", + "rusqlite", + "serde", + "serde_json", + "sources", + "strsim 0.10.0", + "superslice", + "tables", + "thiserror", + "tracing", + "url", +] + +[[package]] +name = "validation-tests" +version = "0.0.0" dependencies = [ "anyhow", "assemble", @@ -8072,6 +8104,7 @@ dependencies = [ "thiserror", "tracing", "url", + "validation", ] [[package]] diff --git a/crates/dekaf/Cargo.toml b/crates/dekaf/Cargo.toml index 945d4a4be4..073d1fa421 100644 --- a/crates/dekaf/Cargo.toml +++ b/crates/dekaf/Cargo.toml @@ -86,6 +86,8 @@ apache-avro = { workspace = true } async-process = { path = "../async-process" } flowctl = { path = "../flowctl" } locate-bin = { path = "../locate-bin" } +sources = { path = "../sources" } +validation-tests = { path = "../validation-tests" } apache-avro = { workspace = true } insta = { workspace = true } diff --git a/crates/dekaf/src/connector.rs b/crates/dekaf/src/connector.rs index 805e648de2..ad889e6496 100644 --- a/crates/dekaf/src/connector.rs +++ b/crates/dekaf/src/connector.rs @@ -172,7 +172,7 @@ pub async fn unary_materialize( // Largely lifted from materialize-kafka // TODO(jshearer): Expose this logic somewhere that materialize-kafka can use it -fn constraint_for_projection( +pub fn constraint_for_projection( projection: &flow::Projection, resource_config: &DekafResourceConfig, endpoint_config: &DekafConfig, diff --git a/crates/dekaf/src/lib.rs b/crates/dekaf/src/lib.rs index 5f2b09b608..181f3e91f8 100644 --- a/crates/dekaf/src/lib.rs +++ b/crates/dekaf/src/lib.rs @@ -13,9 +13,10 @@ mod topology; use topology::{Collection, Partition}; mod read; +pub use read::extract_and_encode; use read::Read; -mod utils; +pub mod utils; mod session; pub use session::Session; diff --git a/crates/dekaf/src/read.rs b/crates/dekaf/src/read.rs index 38d5dd33bf..bd92de1f47 100644 --- a/crates/dekaf/src/read.rs +++ b/crates/dekaf/src/read.rs @@ -297,7 +297,7 @@ impl Read { tmp.push(0); tmp.extend(self.value_schema_id.to_be_bytes()); - self.extract_and_encode(root.get(), &mut tmp)?; + extract_and_encode(self.extractors.as_slice(), root.get(), &mut tmp)?; record_bytes += tmp.len(); buf.extend_from_slice(&tmp); @@ -404,37 +404,6 @@ impl Read { }, )) } - - /// Handles extracting and avro-encoding a particular field. - /// Note that since avro encoding can happen piecewise, there's never a need to - /// put together the whole extracted document, and instead we can build up the - /// encoded output iteratively - fn extract_and_encode<'a>( - &'a self, - original: &'a doc::ArchivedNode, - buf: &mut Vec, - ) -> anyhow::Result<()> { - self.extractors - .iter() - .try_fold(buf, |buf, (schema, extractor)| { - // This is the value extracted from the original doc - if let Err(e) = match extractor.extract(original) { - Ok(value) => avro::encode(buf, schema, value), - Err(default) => avro::encode(buf, schema, &default.into_owned()), - } - .context(format!( - "Extracting field {extractor:#?}, schema: {schema:?}" - )) { - let debug_serialized = serde_json::to_string(&original.to_debug_json_value())?; - tracing::debug!(extractor=?extractor, ?schema, debug_serialized, ?e, "Failed to encode"); - return Err(e); - } - - Ok::<_, anyhow::Error>(buf) - })?; - - Ok(()) - } } fn compressor( @@ -461,3 +430,34 @@ fn compressor( }; Ok(()) } + +/// Handles extracting and avro-encoding a particular field. +/// Note that since avro encoding can happen piecewise, there's never a need to +/// put together the whole extracted document, and instead we can build up the +/// encoded output iteratively +pub fn extract_and_encode<'a, N: AsNode>( + extractors: &'a [(avro::Schema, utils::CustomizableExtractor)], + original: &'a N, + buf: &mut Vec, +) -> anyhow::Result<()> { + extractors + .iter() + .try_fold(buf, |buf, (schema, extractor)| { + // This is the value extracted from the original doc + if let Err(e) = match extractor.extract(original) { + Ok(value) => avro::encode(buf, schema, value), + Err(default) => avro::encode(buf, schema, &default.into_owned()), + } + .context(format!( + "Extracting field {extractor:#?}, schema: {schema:?}" + )) { + let debug_serialized = serde_json::to_string(&original.to_debug_json_value())?; + tracing::debug!(extractor=?extractor, ?schema, debug_serialized, ?e, "Failed to encode"); + return Err(e); + } + + Ok::<_, anyhow::Error>(buf) + })?; + + Ok(()) +} diff --git a/crates/dekaf/tests/dekaf_schema_test.rs b/crates/dekaf/tests/dekaf_schema_test.rs new file mode 100644 index 0000000000..a69b5b8aa4 --- /dev/null +++ b/crates/dekaf/tests/dekaf_schema_test.rs @@ -0,0 +1,397 @@ +use anyhow::Context; +use dekaf::connector; +use itertools::Itertools; +use proto_flow::{flow::materialization_spec::ConnectorType, materialize}; +use serde_json::json; +use validation_tests::Outcome; + +fn run_validation(fixture: &str) -> anyhow::Result { + let outcome = validation_tests::run(fixture, "{}"); + + let mut errors = outcome + .errors + .iter() + .chain(outcome.errors_draft.iter()) + .peekable(); + + if errors.peek().is_some() { + let formatted = errors.format("\n"); + anyhow::bail!("Validation errors: {formatted:?}") + } + + Ok(outcome) +} + +fn json_schema_to_shape(schema: &str) -> anyhow::Result { + let json_schema = doc::validation::build_bundle(schema)?; + let validator = doc::Validator::new(json_schema)?; + Ok(doc::Shape::infer( + &validator.schemas()[0], + validator.schema_index(), + )) +} + +fn build_test_fixture( + schema: serde_json::Value, + field_selection: serde_json::Value, + config: connector::DekafConfig, + bindings: Option>, +) -> String { + let materialization = if let Some(bindings) = bindings { + json!({ + "connectorType": "DEKAF", + "config": { + "variant": "foo", + "config": config + }, + "bindings": bindings + }) + } else { + json!({ + "connectorType": "DEKAF", + "config": { + "variant": "foo", + "config": config + }, + "bindings": [{ + "constraints": {}, + "resourcePath": ["anything"] + }] + }) + }; + + serde_json::to_string_pretty(&json!({ + "test://example/catalog.yaml": { + "collections": { + "test/collection": schema + }, + "materializations": { + "test/materialization":{ + "endpoint": { + "dekaf": { + "variant": "foo", + "config": config + }, + }, + "bindings": [ + { + "source": "test/collection", + "resource": { + "topic_name": "foo" + }, + "fields": field_selection + } + ] + } + } + }, + "driver": { + "dataPlanes": { + "1d:1d:1d:1d:1d:1d:1d:1d": {"default": true} + }, + "materializations": { + "test/materialization": materialization + } + } + })) + .unwrap() +} + +/// Helper function to run validation and get the components needed for field extraction +async fn get_extraction_components( + schema: serde_json::Value, + field_selection: serde_json::Value, + config: connector::DekafConfig, +) -> anyhow::Result<( + doc::Shape, + proto_flow::flow::FieldSelection, + Vec, +)> { + // First run to get validated bindings + let fixture = build_test_fixture( + schema.clone(), + // Just need something that passes validation, we'll pass in the real + // field selection in the second pass once we have the bindings + json!({"recommended": true}), + config.clone(), + None, + ); + let outcome = run_validation(fixture.as_ref())?; + let built_materializations = outcome.built_materializations; + + let materialization_spec = built_materializations + .first() + .context("No materializations built")? + .spec + .as_ref() + .context("No spec")?; + + let validate_req = materialize::request::Validate { + name: "what".to_string(), + connector_type: ConnectorType::Dekaf as i32, + config_json: materialization_spec.config_json.to_owned(), + bindings: materialization_spec + .bindings + .iter() + .map( + |binding| proto_flow::materialize::request::validate::Binding { + resource_config_json: binding.resource_config_json.clone(), + collection: binding.collection.clone(), + field_config_json_map: binding + .field_selection + .as_ref() + .expect("No field selection") + .field_config_json_map + .clone(), + backfill: 0, + }, + ) + .collect_vec(), + last_materialization: None, + last_version: "foo".to_owned(), + }; + + let validate_resp = connector::unary_materialize(materialize::Request { + validate: Some(validate_req), + ..Default::default() + }) + .await?; + + let bindings = validate_resp + .validated + .as_ref() + .context("No validated response")? + .bindings + .clone(); + + // Second run with validated bindings to get final components + let fixture = build_test_fixture(schema, field_selection, config, Some(bindings)); + let outcome = run_validation(fixture.as_ref())?; + let built_materializations = outcome.built_materializations; + let built_collections = outcome.built_collections; + + let collection = built_collections.first().context("No collections built")?; + let collection_spec = collection.spec.as_ref().context("No collection spec")?; + let materialization = built_materializations + .first() + .context("No materializations built")?; + let materialization_spec = materialization.spec.as_ref().context("No spec")?; + + let schema = if collection_spec.read_schema_json.len() > 0 { + &collection_spec.read_schema_json + } else { + &collection_spec.write_schema_json + }; + + let shape = json_schema_to_shape(schema)?; + let field_selection = materialization_spec + .bindings + .first() + .context("No bindings")? + .field_selection + .as_ref() + .context("No field selection")? + .clone(); + + Ok((shape, field_selection, collection_spec.projections.clone())) +} + +async fn roundtrip( + endpoint_config: connector::DekafConfig, + schema: serde_json::Value, + field_selection: serde_json::Value, + docs: Vec, +) -> anyhow::Result>> { + let (shape, field_selection, projections) = + get_extraction_components(schema, field_selection, endpoint_config.clone()).await?; + + let (avro_schema, extractors) = dekaf::utils::build_field_extractors( + shape, + field_selection, + projections, + endpoint_config.deletions, + )?; + + let avro::Schema::Record(root_schema) = &avro_schema else { + anyhow::bail!("Invalid schema"); + }; + + let field_schemas = root_schema.fields.iter().cloned().map(|f| f.schema); + let extractors = field_schemas + .zip(extractors.clone().into_iter()) + .collect_vec(); + + docs.into_iter() + .map(|doc| { + // Extract and encode document + let mut encoded = Vec::new(); + dekaf::extract_and_encode(extractors.as_slice(), &doc, &mut encoded)?; + + // Now decode it back into a Value representation + Ok(apache_avro::from_avro_datum( + &avro_schema, + &mut encoded.as_slice(), + None, + )) + }) + .collect::, _>>() +} + +#[tokio::test] +async fn test_allof_with_null_default() -> anyhow::Result<()> { + for output in roundtrip( + connector::DekafConfig { + deletions: connector::DeletionMode::Kafka, + token: "1234".to_string(), + strict_topic_names: false, + }, + json!({ + "schema": { + "allOf": [ + { + "properties": { + "id": { + "title": "Id", + "type": "integer" + }, + "conflicts": { + "type": ["integer", "null"], + "default": null, + "title": "Updatedbyuserid" + } + }, + "required": ["id"], + "type": "object" + }, + { + "properties": { + "id": { + "title": "Id", + "type": "integer" + }, + "conflicts": { + "type": "integer" + } + }, + "required": ["id"], + "type": "object" + } + ] + }, + "key": ["/id"] + }), + json!({ + "recommended": true + }), + vec![json!({ + "id": 671963468 + })], + ) + .await? + { + insta::assert_debug_snapshot!(output?); + } + + Ok(()) +} + +#[tokio::test] +async fn test_field_selection_specific_fields() -> anyhow::Result<()> { + for output in roundtrip( + dekaf::connector::DekafConfig { + deletions: dekaf::connector::DeletionMode::Kafka, + strict_topic_names: false, + token: "1234".to_string(), + }, + json!({ + "schema": { + "properties": { + "key": { + "type": "string" + }, + "field_a": { + "type": "string", + }, + "field_b": { + "type": "string", + } + }, + "type": "object", + "required": [ + "key", + "field_a", + "field_b" + ], + }, + "key": [ + "/key" + ] + }), + json!({ + "include": { + "field_a": {} + }, + "recommended": false + }), + vec![json!({ + "key": "first", + "field_a": "foo", + "field_b": "bar" + })], + ) + .await? + { + insta::assert_debug_snapshot!(output?); + } + + Ok(()) +} + +#[tokio::test] +async fn test_field_selection_recommended_fields() -> anyhow::Result<()> { + for output in roundtrip( + dekaf::connector::DekafConfig { + deletions: dekaf::connector::DeletionMode::Kafka, + strict_topic_names: false, + token: "1234".to_string(), + }, + json!({ + "schema": { + "properties": { + "key": { + "type": "string" + }, + "field_a": { + "type": "string", + }, + "field_b": { + "type": "string", + } + }, + "type": "object", + "required": [ + "key", + "field_a", + "field_b" + ], + }, + "key": [ + "/key" + ] + }), + json!({ + "recommended": true + }), + vec![json!({ + "key": "first", + "field_a": "foo", + "field_b": "bar" + })], + ) + .await? + { + insta::assert_debug_snapshot!(output?); + } + + Ok(()) +} diff --git a/crates/dekaf/tests/snapshots/dekaf_schema_test__allof_with_null_default.snap b/crates/dekaf/tests/snapshots/dekaf_schema_test__allof_with_null_default.snap new file mode 100644 index 0000000000..ac6a0316f9 --- /dev/null +++ b/crates/dekaf/tests/snapshots/dekaf_schema_test__allof_with_null_default.snap @@ -0,0 +1,37 @@ +--- +source: crates/dekaf/tests/dekaf_schema_test.rs +expression: output? +--- +Record( + [ + ( + "id", + Long( + 671963468, + ), + ), + ( + "conflicts", + Union( + 1, + Null, + ), + ), + ( + "flow_published_at", + Union( + 0, + Record( + [ + ( + "json", + String( + "null", + ), + ), + ], + ), + ), + ), + ], +) diff --git a/crates/dekaf/tests/snapshots/dekaf_schema_test__field_selection_recommended_fields.snap b/crates/dekaf/tests/snapshots/dekaf_schema_test__field_selection_recommended_fields.snap new file mode 100644 index 0000000000..c91bf69b26 --- /dev/null +++ b/crates/dekaf/tests/snapshots/dekaf_schema_test__field_selection_recommended_fields.snap @@ -0,0 +1,42 @@ +--- +source: crates/dekaf/tests/dekaf_schema_test.rs +expression: output? +--- +Record( + [ + ( + "key", + String( + "first", + ), + ), + ( + "field_a", + String( + "foo", + ), + ), + ( + "field_b", + String( + "bar", + ), + ), + ( + "flow_published_at", + Union( + 0, + Record( + [ + ( + "json", + String( + "null", + ), + ), + ], + ), + ), + ), + ], +) diff --git a/crates/dekaf/tests/snapshots/dekaf_schema_test__field_selection_specific_fields.snap b/crates/dekaf/tests/snapshots/dekaf_schema_test__field_selection_specific_fields.snap new file mode 100644 index 0000000000..71d86f1a35 --- /dev/null +++ b/crates/dekaf/tests/snapshots/dekaf_schema_test__field_selection_specific_fields.snap @@ -0,0 +1,14 @@ +--- +source: crates/dekaf/tests/dekaf_schema_test.rs +expression: output? +--- +Record( + [ + ( + "field_a", + String( + "foo", + ), + ), + ], +) diff --git a/crates/doc/src/shape/intersect.rs b/crates/doc/src/shape/intersect.rs index 696117559c..d93a4ec97a 100644 --- a/crates/doc/src/shape/intersect.rs +++ b/crates/doc/src/shape/intersect.rs @@ -2,6 +2,7 @@ // Intersected Shapes impose *all* of their constraints, // like a JSON Schema `allOf` keyword. use super::*; +use crate::FailedValidation; use itertools::{EitherOrBoth, Itertools}; impl Reduction { @@ -235,7 +236,7 @@ impl Shape { let description = lhs.description.or(rhs.description); let reduction = lhs.reduction.intersect(rhs.reduction); let provenance = lhs.provenance.intersect(rhs.provenance); - let default = lhs.default.or(rhs.default); + let default = intersect_default(type_, lhs.default, rhs.default); let secret = lhs.secret.or(rhs.secret); let mut annotations = rhs.annotations; @@ -310,6 +311,32 @@ pub fn intersect_enum( } } +pub fn intersect_default( + type_: types::Set, + lhs: Option)>>, + rhs: Option)>>, +) -> Option)>> { + match (lhs, rhs) { + (None, None) => None, + (Some(l), None) | (None, Some(l)) => { + if type_.overlaps(types::Set::for_value(&l.as_ref().0)) { + Some(l) + } else { + None + } + } + (Some(l), Some(r)) => { + if type_.overlaps(types::Set::for_value(&l.as_ref().0)) { + Some(l) + } else if type_.overlaps(types::Set::for_value(&r.as_ref().0)) { + Some(r) + } else { + None + } + } + } +} + fn intersect_additional(lhs: Option>, rhs: Option>) -> Option> { match (lhs, rhs) { (Some(lhs), Some(rhs)) => Some(Box::new(Shape::intersect( @@ -392,4 +419,32 @@ mod test { actual ); } + + #[test] + fn test_default_intersection() { + let shape_with_reasonable_default = shape_from( + r#" + allOf: + - type: ["string", "null"] + default: "hello" + - type: "string" + "#, + ); + + assert_eq!( + shape_with_reasonable_default.default.unwrap().as_ref().0, + serde_json::json!("hello") + ); + + let shape = shape_from( + r#" + allOf: + - type: ["string", "null"] + default: null + - type: "string" + "#, + ); + + assert_eq!(shape.default, None); + } } diff --git a/crates/validation-tests/Cargo.toml b/crates/validation-tests/Cargo.toml new file mode 100644 index 0000000000..5ddd3bbc5f --- /dev/null +++ b/crates/validation-tests/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "validation-tests" +version.workspace = true +rust-version.workspace = true +edition.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +assemble = { path = "../assemble" } +doc = { path = "../doc" } +extractors = { path = "../extractors" } +json = { path = "../json" } +labels = { path = "../labels" } +models = { path = "../models" } +proto-flow = { path = "../proto-flow" } +proto-gazette = { path = "../proto-gazette" } +sources = { path = "../sources" } +tables = { path = "../tables", features = ["persist"] } +validation = { path = "../validation" } + +anyhow = { workspace = true } +bytes = { workspace = true } +futures = { workspace = true } +insta = { workspace = true } +itertools = { workspace = true } +json-patch = { workspace = true } +lazy_static = { workspace = true } +pbjson-types = { workspace = true } +regex = { workspace = true } +rusqlite = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +serde_yaml = { workspace = true } +strsim = { workspace = true } +superslice = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } +url = { workspace = true } diff --git a/crates/validation/tests/common.rs b/crates/validation-tests/src/lib.rs similarity index 100% rename from crates/validation/tests/common.rs rename to crates/validation-tests/src/lib.rs diff --git a/crates/validation/tests/disabled_bindings_are_ignored.yaml b/crates/validation-tests/tests/disabled_bindings_are_ignored.yaml similarity index 100% rename from crates/validation/tests/disabled_bindings_are_ignored.yaml rename to crates/validation-tests/tests/disabled_bindings_are_ignored.yaml diff --git a/crates/validation/tests/materialization_constraints_on_excluded_fields.yaml b/crates/validation-tests/tests/materialization_constraints_on_excluded_fields.yaml similarity index 100% rename from crates/validation/tests/materialization_constraints_on_excluded_fields.yaml rename to crates/validation-tests/tests/materialization_constraints_on_excluded_fields.yaml diff --git a/crates/validation/tests/model.yaml b/crates/validation-tests/tests/model.yaml similarity index 100% rename from crates/validation/tests/model.yaml rename to crates/validation-tests/tests/model.yaml diff --git a/crates/validation/tests/scenario_tests.rs b/crates/validation-tests/tests/scenario_tests.rs similarity index 99% rename from crates/validation/tests/scenario_tests.rs rename to crates/validation-tests/tests/scenario_tests.rs index 6c15b44253..59fe9619fa 100644 --- a/crates/validation/tests/scenario_tests.rs +++ b/crates/validation-tests/tests/scenario_tests.rs @@ -1,4 +1,4 @@ -mod common; +use validation_tests as common; const MODEL_YAML: &str = include_str!("model.yaml"); diff --git a/crates/validation/tests/snapshots/scenario_tests__capture_driver_returns_error.snap b/crates/validation-tests/tests/snapshots/scenario_tests__capture_driver_returns_error.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__capture_driver_returns_error.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__capture_driver_returns_error.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__capture_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__capture_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__capture_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__capture_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__capture_target_not_found.snap b/crates/validation-tests/tests/snapshots/scenario_tests__capture_target_not_found.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__capture_target_not_found.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__capture_target_not_found.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__collection_key_empty.snap b/crates/validation-tests/tests/snapshots/scenario_tests__collection_key_empty.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__collection_key_empty.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__collection_key_empty.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__collection_projections_contains_truncation_sentinel.snap b/crates/validation-tests/tests/snapshots/scenario_tests__collection_projections_contains_truncation_sentinel.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__collection_projections_contains_truncation_sentinel.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__collection_projections_contains_truncation_sentinel.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__collection_schema_contains_flow_document.snap b/crates/validation-tests/tests/snapshots/scenario_tests__collection_schema_contains_flow_document.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__collection_schema_contains_flow_document.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__collection_schema_contains_flow_document.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__collection_schema_contains_truncation_sentinel.snap b/crates/validation-tests/tests/snapshots/scenario_tests__collection_schema_contains_truncation_sentinel.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__collection_schema_contains_truncation_sentinel.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__collection_schema_contains_truncation_sentinel.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__collection_schema_string.snap b/crates/validation-tests/tests/snapshots/scenario_tests__collection_schema_string.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__collection_schema_string.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__collection_schema_string.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__connector_validation_is_skipped_when_shards_are_disabled.snap b/crates/validation-tests/tests/snapshots/scenario_tests__connector_validation_is_skipped_when_shards_are_disabled.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__connector_validation_is_skipped_when_shards_are_disabled.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__connector_validation_is_skipped_when_shards_are_disabled.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__cross_entity_name_prefixes_and_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__cross_entity_name_prefixes_and_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__cross_entity_name_prefixes_and_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__cross_entity_name_prefixes_and_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__data_plane_not_found.snap b/crates/validation-tests/tests/snapshots/scenario_tests__data_plane_not_found.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__data_plane_not_found.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__data_plane_not_found.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__derivation_not_before_after_ordering.snap b/crates/validation-tests/tests/snapshots/scenario_tests__derivation_not_before_after_ordering.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__derivation_not_before_after_ordering.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__derivation_not_before_after_ordering.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__derive_driver_returns_error.snap b/crates/validation-tests/tests/snapshots/scenario_tests__derive_driver_returns_error.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__derive_driver_returns_error.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__derive_driver_returns_error.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap b/crates/validation-tests/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__disabled_bindings_are_ignored.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__golden_all_visits.snap b/crates/validation-tests/tests/snapshots/scenario_tests__golden_all_visits.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__golden_all_visits.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__golden_all_visits.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__golden_error.snap b/crates/validation-tests/tests/snapshots/scenario_tests__golden_error.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__golden_error.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__golden_error.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_and_duplicate_storage_mappings.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_and_duplicate_storage_mappings.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_and_duplicate_storage_mappings.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_and_duplicate_storage_mappings.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_capture_names_and_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_capture_names_and_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_capture_names_and_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_capture_names_and_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_collection_names_prefixes_and_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_collection_names_prefixes_and_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_collection_names_prefixes_and_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_collection_names_prefixes_and_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_generated_file_url.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_generated_file_url.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_generated_file_url.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_generated_file_url.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_materialization_names_and_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_materialization_names_and_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_materialization_names_and_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_materialization_names_and_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_partition_names_and_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_partition_names_and_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_partition_names_and_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_partition_names_and_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_test_names_and_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_test_names_and_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_test_names_and_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_test_names_and_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__invalid_transform_names_and_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__invalid_transform_names_and_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__invalid_transform_names_and_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__invalid_transform_names_and_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__key_not_defined_in_write_schema.snap b/crates/validation-tests/tests/snapshots/scenario_tests__key_not_defined_in_write_schema.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__key_not_defined_in_write_schema.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__key_not_defined_in_write_schema.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__keyed_location_pointer_is_malformed.snap b/crates/validation-tests/tests/snapshots/scenario_tests__keyed_location_pointer_is_malformed.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__keyed_location_pointer_is_malformed.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__keyed_location_pointer_is_malformed.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__keyed_location_wrong_type.snap b/crates/validation-tests/tests/snapshots/scenario_tests__keyed_location_wrong_type.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__keyed_location_wrong_type.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__keyed_location_wrong_type.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_constraints_on_excluded_fields.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_driver_conflicts.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_driver_conflicts.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_driver_conflicts.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_driver_conflicts.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_driver_returns_error.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_driver_returns_error.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_driver_returns_error.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_driver_returns_error.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_driver_unknown_constraint.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_driver_unknown_constraint.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_driver_unknown_constraint.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_driver_unknown_constraint.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_duplicates.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_duplicates.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_duplicates.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_duplicates.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_field_errors.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_field_errors.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_field_errors.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_field_errors.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_not_before_after_ordering.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_not_before_after_ordering.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_not_before_after_ordering.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_not_before_after_ordering.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_selector.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_selector.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_selector.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_selector.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__materialization_source_not_found.snap b/crates/validation-tests/tests/snapshots/scenario_tests__materialization_source_not_found.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__materialization_source_not_found.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__materialization_source_not_found.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__non_canonical_schema_ref.snap b/crates/validation-tests/tests/snapshots/scenario_tests__non_canonical_schema_ref.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__non_canonical_schema_ref.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__non_canonical_schema_ref.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__partition_not_defined_in_write_schema.snap b/crates/validation-tests/tests/snapshots/scenario_tests__partition_not_defined_in_write_schema.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__partition_not_defined_in_write_schema.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__partition_not_defined_in_write_schema.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__partition_selections.snap b/crates/validation-tests/tests/snapshots/scenario_tests__partition_selections.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__partition_selections.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__partition_selections.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__projection_not_created_for_empty_properties.snap b/crates/validation-tests/tests/snapshots/scenario_tests__projection_not_created_for_empty_properties.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__projection_not_created_for_empty_properties.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__projection_not_created_for_empty_properties.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__schema_fragment_not_found.snap b/crates/validation-tests/tests/snapshots/scenario_tests__schema_fragment_not_found.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__schema_fragment_not_found.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__schema_fragment_not_found.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__schema_reference_verification.snap b/crates/validation-tests/tests/snapshots/scenario_tests__schema_reference_verification.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__schema_reference_verification.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__schema_reference_verification.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__shape_inspections.snap b/crates/validation-tests/tests/snapshots/scenario_tests__shape_inspections.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__shape_inspections.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__shape_inspections.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__shuffle_is_missing.snap b/crates/validation-tests/tests/snapshots/scenario_tests__shuffle_is_missing.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__shuffle_is_missing.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__shuffle_is_missing.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__shuffle_key_empty.snap b/crates/validation-tests/tests/snapshots/scenario_tests__shuffle_key_empty.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__shuffle_key_empty.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__shuffle_key_empty.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__shuffle_key_length_mismatch.snap b/crates/validation-tests/tests/snapshots/scenario_tests__shuffle_key_length_mismatch.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__shuffle_key_length_mismatch.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__shuffle_key_length_mismatch.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__shuffle_key_types_mismatch.snap b/crates/validation-tests/tests/snapshots/scenario_tests__shuffle_key_types_mismatch.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__shuffle_key_types_mismatch.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__shuffle_key_types_mismatch.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__shuffle_needs_explicit_types.snap b/crates/validation-tests/tests/snapshots/scenario_tests__shuffle_needs_explicit_types.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__shuffle_needs_explicit_types.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__shuffle_needs_explicit_types.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__storage_mappings_not_found.snap b/crates/validation-tests/tests/snapshots/scenario_tests__storage_mappings_not_found.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__storage_mappings_not_found.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__storage_mappings_not_found.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__storage_mappings_without_prefix.snap b/crates/validation-tests/tests/snapshots/scenario_tests__storage_mappings_without_prefix.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__storage_mappings_without_prefix.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__storage_mappings_without_prefix.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__test_not_before_after.snap b/crates/validation-tests/tests/snapshots/scenario_tests__test_not_before_after.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__test_not_before_after.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__test_not_before_after.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__test_step_ingest_schema_error.snap b/crates/validation-tests/tests/snapshots/scenario_tests__test_step_ingest_schema_error.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__test_step_ingest_schema_error.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__test_step_ingest_schema_error.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__test_step_unknown_collection.snap b/crates/validation-tests/tests/snapshots/scenario_tests__test_step_unknown_collection.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__test_step_unknown_collection.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__test_step_unknown_collection.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__test_step_verify_key_order.snap b/crates/validation-tests/tests/snapshots/scenario_tests__test_step_verify_key_order.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__test_step_verify_key_order.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__test_step_verify_key_order.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__test_step_verify_selector.snap b/crates/validation-tests/tests/snapshots/scenario_tests__test_step_verify_selector.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__test_step_verify_selector.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__test_step_verify_selector.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__transform_source_not_found.snap b/crates/validation-tests/tests/snapshots/scenario_tests__transform_source_not_found.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__transform_source_not_found.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__transform_source_not_found.snap diff --git a/crates/validation/tests/snapshots/scenario_tests__unknown_locations.snap b/crates/validation-tests/tests/snapshots/scenario_tests__unknown_locations.snap similarity index 100% rename from crates/validation/tests/snapshots/scenario_tests__unknown_locations.snap rename to crates/validation-tests/tests/snapshots/scenario_tests__unknown_locations.snap diff --git a/crates/validation/tests/snapshots/transition_tests__cronut_migration_errors.snap b/crates/validation-tests/tests/snapshots/transition_tests__cronut_migration_errors.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__cronut_migration_errors.snap rename to crates/validation-tests/tests/snapshots/transition_tests__cronut_migration_errors.snap diff --git a/crates/validation/tests/snapshots/transition_tests__deletion_but_does_not_exist.snap b/crates/validation-tests/tests/snapshots/transition_tests__deletion_but_does_not_exist.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__deletion_but_does_not_exist.snap rename to crates/validation-tests/tests/snapshots/transition_tests__deletion_but_does_not_exist.snap diff --git a/crates/validation/tests/snapshots/transition_tests__deletion_of_used_collection.snap b/crates/validation-tests/tests/snapshots/transition_tests__deletion_of_used_collection.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__deletion_of_used_collection.snap rename to crates/validation-tests/tests/snapshots/transition_tests__deletion_of_used_collection.snap diff --git a/crates/validation/tests/snapshots/transition_tests__deletions.snap b/crates/validation-tests/tests/snapshots/transition_tests__deletions.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__deletions.snap rename to crates/validation-tests/tests/snapshots/transition_tests__deletions.snap diff --git a/crates/validation/tests/snapshots/transition_tests__insert_but_already_exists.snap b/crates/validation-tests/tests/snapshots/transition_tests__insert_but_already_exists.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__insert_but_already_exists.snap rename to crates/validation-tests/tests/snapshots/transition_tests__insert_but_already_exists.snap diff --git a/crates/validation/tests/snapshots/transition_tests__inserts.snap b/crates/validation-tests/tests/snapshots/transition_tests__inserts.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__inserts.snap rename to crates/validation-tests/tests/snapshots/transition_tests__inserts.snap diff --git a/crates/validation/tests/snapshots/transition_tests__live_last_build_id_is_larger_than_current_build_id.snap b/crates/validation-tests/tests/snapshots/transition_tests__live_last_build_id_is_larger_than_current_build_id.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__live_last_build_id_is_larger_than_current_build_id.snap rename to crates/validation-tests/tests/snapshots/transition_tests__live_last_build_id_is_larger_than_current_build_id.snap diff --git a/crates/validation/tests/snapshots/transition_tests__live_last_pub_id_is_larger_then_current_pub_id.snap b/crates/validation-tests/tests/snapshots/transition_tests__live_last_pub_id_is_larger_then_current_pub_id.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__live_last_pub_id_is_larger_then_current_pub_id.snap rename to crates/validation-tests/tests/snapshots/transition_tests__live_last_pub_id_is_larger_then_current_pub_id.snap diff --git a/crates/validation/tests/snapshots/transition_tests__update_but_does_not_exist.snap b/crates/validation-tests/tests/snapshots/transition_tests__update_but_does_not_exist.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__update_but_does_not_exist.snap rename to crates/validation-tests/tests/snapshots/transition_tests__update_but_does_not_exist.snap diff --git a/crates/validation/tests/snapshots/transition_tests__update_collection_becomes_derivation.snap b/crates/validation-tests/tests/snapshots/transition_tests__update_collection_becomes_derivation.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__update_collection_becomes_derivation.snap rename to crates/validation-tests/tests/snapshots/transition_tests__update_collection_becomes_derivation.snap diff --git a/crates/validation/tests/snapshots/transition_tests__updates.snap b/crates/validation-tests/tests/snapshots/transition_tests__updates.snap similarity index 100% rename from crates/validation/tests/snapshots/transition_tests__updates.snap rename to crates/validation-tests/tests/snapshots/transition_tests__updates.snap diff --git a/crates/validation/tests/transition_tests.rs b/crates/validation-tests/tests/transition_tests.rs similarity index 99% rename from crates/validation/tests/transition_tests.rs rename to crates/validation-tests/tests/transition_tests.rs index 15dbbcde60..416d65c610 100644 --- a/crates/validation/tests/transition_tests.rs +++ b/crates/validation-tests/tests/transition_tests.rs @@ -1,4 +1,4 @@ -mod common; +use validation_tests as common; const MODEL_YAML: &str = include_str!("transitions.yaml"); diff --git a/crates/validation/tests/transitions.yaml b/crates/validation-tests/tests/transitions.yaml similarity index 100% rename from crates/validation/tests/transitions.yaml rename to crates/validation-tests/tests/transitions.yaml diff --git a/crates/validation/tests/validation_skipped_when_disabled.yaml b/crates/validation-tests/tests/validation_skipped_when_disabled.yaml similarity index 100% rename from crates/validation/tests/validation_skipped_when_disabled.yaml rename to crates/validation-tests/tests/validation_skipped_when_disabled.yaml diff --git a/crates/validation/Cargo.toml b/crates/validation/Cargo.toml index 1a61d959c3..b28f3d65b2 100644 --- a/crates/validation/Cargo.toml +++ b/crates/validation/Cargo.toml @@ -34,12 +34,3 @@ superslice = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } url = { workspace = true } - -[dev-dependencies] -proto-gazette = { path = "../proto-gazette" } -tables = { path = "../tables", features = ["persist"] } - -insta = { workspace = true } -json-patch = { workspace = true } -serde = { workspace = true } -serde_yaml = { workspace = true }