use apollo_compiler::coord;
use insta::assert_snapshot;
use test_log::test;
use super::ServiceDefinition;
use super::assert_composition_errors;
use super::compose_as_fed2_subgraphs;
#[test]
fn enum_types_merges_inconsistent_enum_only_used_as_output() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
e: E!
}
enum E {
V1
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
enum E {
V2
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let supergraph = result.expect("Expected composition to succeed");
let api_schema = supergraph
.to_api_schema(Default::default())
.expect("Expected API schema generation to succeed");
let enum_e = coord!(E)
.lookup(api_schema.schema())
.expect("Enum E should exist");
assert_snapshot!(enum_e, @r#"
enum E {
V1
V2
}
"#);
}
#[test]
fn enum_types_merges_enum_but_skips_inconsistent_values_only_used_as_input() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
f(e: E!): Int
}
enum E {
V1
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
enum E {
V1
V2
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let supergraph = result.expect("Expected composition to succeed");
let api_schema = supergraph
.to_api_schema(Default::default())
.expect("Expected API schema generation to succeed");
let enum_e = coord!(E)
.lookup(api_schema.schema())
.expect("Enum E should exist");
assert_snapshot!(enum_e, @r###"
enum E {
V1
}
"###);
}
#[test]
fn enum_types_does_not_error_if_skipped_inconsistent_value_has_directive() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
f(e: E!): Int
}
enum E {
V1
V2 @deprecated(reason: "use V3 instead")
V3
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
enum E {
V1
V3
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let supergraph = result.expect("Expected composition to succeed");
let api_schema = supergraph
.to_api_schema(Default::default())
.expect("Expected API schema generation to succeed");
let enum_e = coord!(E)
.lookup(api_schema.schema())
.expect("Enum E should exist");
assert_snapshot!(enum_e, @r#"
enum E {
V1
V3
}
"#);
}
#[test]
fn enum_types_errors_if_enum_used_only_as_input_has_no_consistent_values() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
f(e: E!): Int
}
enum E {
V1
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
enum E {
V2
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
assert_composition_errors(
&result,
&[(
"EMPTY_MERGED_ENUM_TYPE",
r#"None of the values of enum type "E" are defined consistently in all the subgraphs defining that type. As only values common to all subgraphs are merged, this would result in an empty type."#,
)],
);
}
#[test]
fn enum_types_errors_when_merging_inconsistent_enum_used_as_both_input_and_output() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
e: E!
f(e: E!): Int
}
enum E {
V1
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
enum E {
V2
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
assert_composition_errors(
&result,
&[
(
"ENUM_VALUE_MISMATCH",
r#"Enum type "E" is used as both input type (for example, as type of "Query.f(e:)") and output type (for example, as type of "Query.e"), but value "V1" is not defined in all the subgraphs defining "E": "V1" is defined in subgraph "subgraphA" but not in subgraph "subgraphB""#,
),
(
"ENUM_VALUE_MISMATCH",
r#"Enum type "E" is used as both input type (for example, as type of "Query.f(e:)") and output type (for example, as type of "Query.e"), but value "V2" is not defined in all the subgraphs defining "E": "V2" is defined in subgraph "subgraphB" but not in subgraph "subgraphA""#,
),
],
);
}
#[test]
fn enum_types_ignores_inaccessible_fields_when_merging_enums_used_as_both_input_and_output() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
e: E!
f(e: E!): Int
}
enum E {
V1
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
enum E {
V1
V2 @inaccessible
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let _supergraph =
result.expect("Expected composition to succeed - should ignore @inaccessible enum values");
}
#[test]
fn enum_types_succeeds_merging_consistent_enum_used_as_both_input_and_output() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
f(e: E!): E!
}
enum E {
V1
V2
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
enum E {
V1
V2
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let supergraph = result.expect("Expected composition to succeed");
let api_schema = supergraph
.to_api_schema(Default::default())
.expect("Expected API schema generation to succeed");
let enum_e = coord!(E)
.lookup(api_schema.schema())
.expect("Enum E should exist");
assert_snapshot!(enum_e, @r#"
enum E {
V1
V2
}
"#);
}
#[test]
fn input_types_only_merges_fields_common_to_all_subgraphs() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
q1(a: A): String
}
input A {
x: Int
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
type Query {
q2(a: A): String
}
input A {
x: Int
y: Int
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let supergraph = result.expect("Expected composition to succeed");
let api_schema = supergraph
.to_api_schema(Default::default())
.expect("Expected API schema generation to succeed");
assert!(
coord!(A.x).lookup_input_field(api_schema.schema()).is_ok(),
"Expected A.x to exist"
);
assert!(
coord!(A.y).lookup_input_field(api_schema.schema()).is_err(),
"Expected A.y to be undefined"
);
}
#[test]
fn input_types_merges_input_field_with_different_but_compatible_types() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
q1(a: A): String
}
input A {
x: Int
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
type Query {
q2(a: A): String
}
input A {
x: Int!
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let supergraph = result.expect("Expected composition to succeed");
let api_schema = supergraph
.to_api_schema(Default::default())
.expect("Expected API schema generation to succeed");
let target = coord!(A.x)
.lookup_input_field(api_schema.schema())
.expect("Expected A.x to exist");
assert_eq!(
target.ty.to_string(),
"Int!",
"Expected A.x to be of type Int! but got {}",
target.ty
);
}
#[test]
fn input_types_errors_when_merging_completely_inconsistent_input_types() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
f(i: MyInput!): Int
}
input MyInput {
x: Int
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
input MyInput {
y: Int
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
assert_composition_errors(
&result,
&[(
"EMPTY_MERGED_INPUT_TYPE",
r#"None of the fields of input object type "MyInput" are consistently defined in all the subgraphs defining that type. As only fields common to all subgraphs are merged, this would result in an empty type."#,
)],
);
}
#[test]
fn input_types_errors_if_mandatory_input_field_not_in_all_subgraphs() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
q1(a: A): String
}
input A {
x: Int
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
type Query {
q2(a: A): String
}
input A {
x: Int
y: Int!
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
assert_composition_errors(
&result,
&[(
"REQUIRED_INPUT_FIELD_MISSING_IN_SOME_SUBGRAPH",
r#"Input object field "A.y" is required in some subgraphs but does not appear in all subgraphs: it is required in subgraph "subgraphB" but does not appear in subgraph "subgraphA""#,
)],
);
}
#[test]
fn union_types_merges_inconsistent_unions() {
let subgraph_a = ServiceDefinition {
name: "subgraphA",
type_defs: r#"
type Query {
u: U!
}
union U = A | B
type A {
a: Int
}
type B {
b: Int
}
"#,
};
let subgraph_b = ServiceDefinition {
name: "subgraphB",
type_defs: r#"
union U = C
type C {
b: Int
}
"#,
};
let result = compose_as_fed2_subgraphs(&[subgraph_a, subgraph_b]);
let supergraph = result.expect("Expected composition to succeed");
let api_schema = supergraph
.to_api_schema(Default::default())
.expect("Expected API schema generation to succeed");
let union_u = coord!(U)
.lookup(api_schema.schema())
.expect("Union U should exist");
assert_snapshot!(union_u, @"union U = A | B | C");
}