diff --git a/crates/wip-protocol/src/validation.rs b/crates/wip-protocol/src/validation.rs index 7852806..e38eaa8 100644 --- a/crates/wip-protocol/src/validation.rs +++ b/crates/wip-protocol/src/validation.rs @@ -315,6 +315,8 @@ pub(crate) fn validate_root_value( ) -> Result<(), ValidationError> { descriptor.validate()?; let declarations = declaration_map(descriptor)?; + validate_type_expr(ty, &declarations) + .map_err(|error| error.prepend(PathSegment::Interface(descriptor.name.clone())))?; validate_value(ty, value, &declarations) .map_err(|error| error.prepend(PathSegment::Interface(descriptor.name.clone()))) } @@ -369,7 +371,12 @@ fn validate_value( _ => expect_kind(value, ValueKind::Json), }, TypeExpr::Named(name) => { - validate_declared_value(declarations[name.as_str()], value, declarations) + let Some(declaration) = declarations.get(name.as_str()) else { + return Err(ValidationError::new(ValidationErrorKind::UnknownType { + name: name.clone(), + })); + }; + validate_declared_value(declaration, value, declarations) } } } diff --git a/crates/wip-protocol/tests/validation.rs b/crates/wip-protocol/tests/validation.rs index 9495df6..3af79f1 100644 --- a/crates/wip-protocol/tests/validation.rs +++ b/crates/wip-protocol/tests/validation.rs @@ -233,6 +233,44 @@ fn named_references_must_exist_and_must_not_cycle() { ] ); + let valid = descriptor("catalog"); + let error = valid + .validate_value(&TypeExpr::Named("Elsewhere".into()), &Value::Record(vec![])) + .unwrap_err(); + assert_eq!( + error.kind, + ValidationErrorKind::UnknownType { + name: "Elsewhere".into() + } + ); + assert_eq!(error.path, vec![PathSegment::Interface("catalog".into())]); + + let error = valid + .validate_value( + &TypeExpr::Entry { + key: Box::new(TypeExpr::String), + value: Box::new(TypeExpr::List(Box::new(TypeExpr::Named("Nested".into())))), + }, + &Value::Entry { + key: Box::new(Value::String("key".into())), + value: Box::new(Value::List(vec![])), + }, + ) + .unwrap_err(); + assert_eq!( + error.kind, + ValidationErrorKind::UnknownType { + name: "Nested".into() + } + ); + assert_eq!( + error.path, + vec![ + PathSegment::Interface("catalog".into()), + PathSegment::EntryValue, + ] + ); + let cyclic = InterfaceDescriptor { name: "cyclic".into(), documentation: None,