Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/libgraphql-core-v1/src/schema/schema_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use crate::type_builders::IntoGraphQLType;
use crate::type_builders::ObjectTypeBuilder;
use crate::type_builders::ScalarTypeBuilder;
use crate::type_builders::UnionTypeBuilder;
use crate::types::DeprecationState;
use crate::types::DirectiveDefinition;
use crate::types::DirectiveDefinitionKind;
use crate::types::DirectiveLocationKind;
Expand Down Expand Up @@ -221,7 +222,7 @@ impl SchemaBuilder {
FieldName::new("reason"),
ParameterDefinition {
default_value: Some(Value::String(
"No longer supported".to_string(),
DeprecationState::DEFAULT_REASON.to_string(),
)),
description: None,
directives: vec![],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1040,3 +1040,135 @@ fn schema_extension_duplicate_root_operation_fails() {
});
assert!(has_error, "expected DuplicateOperationDefinition");
}

// ---------------------------------------------------------
// @deprecated constraints on extension-contributed items
// ---------------------------------------------------------

// Verifies that a `@deprecated` required (non-null, no default
// value) argument contributed by an object type EXTENSION is
// rejected. Regression coverage for the 16.6b extension-merge
// path: validation runs over the merged type, so
// extension-contributed parameters must be subject to the same
// `@deprecated` constraints as parameters defined directly on
// the type.
//
// See https://spec.graphql.org/September2025/#sec--deprecated
//
// Written by Claude Code, reviewed by a human.
#[test]
fn extension_contributed_deprecated_required_argument_fails() {
let result = SchemaBuilder::build_from_str(
"type Query { x: Int }\n\
extend type Query {\n\
search(oldArg: String! @deprecated): String\n\
}",
);
assert!(result.is_err());
let errors = result.unwrap_err();
let has_error = errors.errors().iter().any(|e| {
if let SchemaBuildErrorKind::TypeValidation(tve) = e.kind() {
matches!(
tve.kind(),
TypeValidationErrorKind::DeprecatedRequiredParameter {
field_name,
parameter_name,
type_name,
} if field_name == "search"
&& parameter_name == "oldArg"
&& type_name == "Query",
)
} else {
false
}
});
assert!(
has_error,
"expected DeprecatedRequiredParameter, got: {errors:?}",
);
}

// Verifies that a `@deprecated` required (non-null, no default
// value) input field contributed by an input object type
// EXTENSION is rejected. Regression coverage for the 16.6b
// extension-merge path: validation runs over the merged type,
// so extension-contributed input fields must be subject to the
// same `@deprecated` constraints as input fields defined
// directly on the type.
//
// See https://spec.graphql.org/September2025/#sec--deprecated
//
// Written by Claude Code, reviewed by a human.
#[test]
fn extension_contributed_deprecated_required_input_field_fails() {
let result = SchemaBuilder::build_from_str(
"type Query { x: Int }\n\
input CreateUserInput { name: String }\n\
extend input CreateUserInput {\n\
oldField: String! @deprecated\n\
}",
);
assert!(result.is_err());
let errors = result.unwrap_err();
let has_error = errors.errors().iter().any(|e| {
if let SchemaBuildErrorKind::TypeValidation(tve) = e.kind() {
matches!(
tve.kind(),
TypeValidationErrorKind::DeprecatedRequiredInputField {
field_name,
parent_type_name,
} if field_name == "oldField"
&& parent_type_name == "CreateUserInput",
)
} else {
false
}
});
assert!(
has_error,
"expected DeprecatedRequiredInputField, got: {errors:?}",
);
}

// Verifies that a `@deprecated` field contributed by an object
// type EXTENSION still participates in IsValidImplementation
// step 2.6 against the merged type's interfaces: the extension
// adds a `@deprecated` field implementing a non-deprecated
// interface field, which must be rejected.
//
// See https://spec.graphql.org/September2025/#IsValidImplementation()
//
// Written by Claude Code, reviewed by a human.
#[test]
fn extension_contributed_deprecated_implementing_field_fails() {
let result = SchemaBuilder::build_from_str(
"interface Node { name: String }\n\
type Query implements Node { name: String }\n\
type User { id: ID }\n\
extend type User { name: String @deprecated }\n\
extend type User implements Node",
);
assert!(result.is_err());
let errors = result.unwrap_err();
let has_error = errors.errors().iter().any(|e| {
if let SchemaBuildErrorKind::TypeValidation(tve) = e.kind() {
matches!(
tve.kind(),
TypeValidationErrorKind::DeprecatedFieldImplementingNonDeprecatedInterfaceField {
field_name,
interface_name,
type_name,
} if field_name == "name"
&& interface_name == "Node"
&& type_name == "User",
)
} else {
false
}
});
assert!(
has_error,
"expected DeprecatedFieldImplementingNonDeprecatedInterfaceField, \
got: {errors:?}",
);
}
Loading
Loading