apollo-router 1.61.13

A configurable, high-performance routing runtime for Apollo Federation 🚀
Documentation
use apollo_compiler::Schema;
use test_log::test;

use super::*;
use crate::Configuration;

fn assert_expected_signature(actual: &UsageReporting, expected_sig: &str) {
    assert_eq!(actual.stats_report_key, expected_sig);
}

macro_rules! assert_extended_references {
    ($actual:expr) => {
        insta::with_settings!({sort_maps => true}, {
            insta::assert_yaml_snapshot!($actual, {
                // sort referenced enum value sets
                ".referenced_enums.*" => insta::sorted_redaction()
            });
        });
    };
}

macro_rules! assert_enums_from_response {
    ($actual:expr) => {
        insta::with_settings!({sort_maps => true}, {
            insta::assert_yaml_snapshot!($actual, {
                // sort referenced enum value sets
                ".*" => insta::sorted_redaction()
            });
        });
    };
}

// Generate usage reporting with the same signature and refs doc, and with enhanced normalization algorithm
fn generate_enhanced(
    doc: &ExecutableDocument,
    operation_name: &Option<String>,
    schema: &Valid<Schema>,
) -> UsageReporting {
    generate_usage_reporting(
        doc,
        doc,
        operation_name,
        schema,
        &ApolloSignatureNormalizationAlgorithm::Enhanced,
    )
}

// Generate extended references (input objects and enum values)
fn generate_extended_refs(
    doc: &Valid<ExecutableDocument>,
    operation_name: Option<String>,
    schema: &Valid<Schema>,
    variables: Option<&Object>,
) -> ExtendedReferenceStats {
    let default_vars = Object::new();
    generate_extended_references(
        Arc::new(doc.clone()),
        operation_name,
        schema,
        variables.unwrap_or(&default_vars),
    )
}

fn enums_from_response(
    query_str: &str,
    operation_name: Option<&str>,
    schema_str: &str,
    response_body_str: &str,
) -> ReferencedEnums {
    let config = Configuration::default();
    let schema = crate::spec::Schema::parse(schema_str, &config).unwrap();
    let query = Query::parse(query_str, operation_name, &schema, &config).unwrap();
    let response_body: Object = serde_json::from_str(response_body_str).unwrap();

    let mut result = ReferencedEnums::new();
    extract_enums_from_response(
        Arc::new(query),
        schema.supergraph_schema(),
        &response_body,
        &mut result,
    );
    result
}

#[test(tokio::test)]
async fn test_enhanced_uses_comma_always() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/enhanced_uses_comma_always_query.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_enhanced(&doc, &Some("TestCommaEnhanced".into()), &schema);
    let expected_sig = "# TestCommaEnhanced\nquery TestCommaEnhanced($arg1:String,$arg2:String,$veryMuchUsuallyTooLongName1234567890:String){manyArgsQuery(arg1:$arg1,arg2:$arg2,arg3:\"\",arg4:$veryMuchUsuallyTooLongName1234567890){basicTypes{nullableId}enumResponse id}}";
    assert_expected_signature(&generated, expected_sig);
}

#[test(tokio::test)]
async fn test_enhanced_sorts_fragments() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/enhanced_sorts_fragments_query.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_enhanced(&doc, &Some("EnhancedFragmentQuery".into()), &schema);
    let expected_sig = "# EnhancedFragmentQuery\nfragment ZZZFragment on EverythingResponse{listOfInterfaces{sharedField}}fragment aaaFragment on EverythingResponse{listOfInterfaces{sharedField}}fragment aaaInterfaceFragment on InterfaceImplementation1{sharedField}fragment bbbInterfaceFragment on InterfaceImplementation2{implementation2Field sharedField}fragment zzzFragment on EverythingResponse{listOfInterfaces{sharedField}}query EnhancedFragmentQuery{noInputQuery{interfaceResponse{...aaaInterfaceFragment...bbbInterfaceFragment...{...on InterfaceImplementation1{implementation1Field}}...{...on InterfaceImplementation2{sharedField}}...on InterfaceImplementation1{implementation1Field}...on InterfaceImplementation2{implementation2Field}}listOfBools unionResponse{...on UnionType1{unionType1Field}...on UnionType2{unionType2Field}}...ZZZFragment...aaaFragment...zzzFragment}}";
    assert_expected_signature(&generated, expected_sig);
}

#[test(tokio::test)]
async fn test_enhanced_sorts_directives() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/enhanced_sorts_directives_query.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_enhanced(&doc, &Some("DirectiveQuery".into()), &schema);
    let expected_sig = "# DirectiveQuery\nfragment Fragment1 on InterfaceImplementation1{implementation1Field sharedField}fragment Fragment2 on InterfaceImplementation2@noArgs@withArgs(arg1:\"\",arg2:\"\",arg3:true,arg4:0,arg5:[]){implementation2Field sharedField}query DirectiveQuery@noArgs@withArgs(arg1:\"\",arg2:\"\"){noInputQuery{enumResponse@noArgs@withArgs(arg3:false,arg4:0,arg5:[])interfaceResponse{...Fragment1@noArgs@withArgs(arg1:\"\")...Fragment2}unionResponse{...on UnionType1@noArgs@withArgs(arg1:\"\",arg2:\"\"){unionType1Field}}}}";
    assert_expected_signature(&generated, expected_sig);
}

#[test(tokio::test)]
async fn test_enhanced_inline_input_object() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str: &str = include_str!("testdata/enhanced_inline_input_object_query.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_enhanced(&doc, &Some("InputObjectTypeQuery".into()), &schema);
    #[allow(clippy::literal_string_with_formatting_args)]
    let expected_sig = "# InputObjectTypeQuery\nquery InputObjectTypeQuery{inputTypeQuery(input:{inputString:\"\",inputInt:0,inputBoolean:null,nestedType:{someFloat:0},enumInput:SOME_VALUE_1,nestedTypeList:[],listInput:[]}){enumResponse}}";
    assert_expected_signature(&generated, expected_sig);
}

#[test(tokio::test)]
async fn test_enhanced_alias_preservation() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/enhanced_alias_preservation_query.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_enhanced(&doc, &Some("AliasQuery".into()), &schema);
    let expected_sig = "# AliasQuery\nquery AliasQuery{enumInputQuery(enumInput:SOME_VALUE_1){enumResponse nullableId aliasedId:id}ZZAlias:enumInputQuery(enumInput:SOME_VALUE_3){enumResponse}aaAlias:enumInputQuery(enumInput:SOME_VALUE_2){aliasedAgain:enumResponse}xxAlias:enumInputQuery(enumInput:SOME_VALUE_1){aliased:enumResponse}}";
    assert_expected_signature(&generated, expected_sig);
}

#[test(tokio::test)]
async fn test_extended_references_inline_enums() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_inline_enums.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_extended_refs(&doc, Some("EnumInlineQuery".into()), &schema, None);
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_extended_references_var_enums() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_var_enums.graphql");
    let query_vars_str = include_str!("testdata/extended_references_var_enums.json");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();
    let vars: Object = serde_json::from_str(query_vars_str).unwrap();

    let generated = generate_extended_refs(&doc, Some("EnumVarQuery".into()), &schema, Some(&vars));
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_extended_references_fragment_inline_enums() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_fragment_inline_enums.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_extended_refs(
        &doc,
        Some("EnumInlineQueryWithFragment".into()),
        &schema,
        None,
    );
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_extended_references_fragment_var_enums() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_fragment_var_enums.graphql");
    let query_vars_str = include_str!("testdata/extended_references_fragment_var_enums.json");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();
    let vars: Object = serde_json::from_str(query_vars_str).unwrap();

    let generated = generate_extended_refs(
        &doc,
        Some("EnumVarQueryWithFragment".into()),
        &schema,
        Some(&vars),
    );
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_extended_references_inline_type() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_inline_type.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();

    let generated =
        generate_extended_refs(&doc, Some("InputTypeInlineQuery".into()), &schema, None);
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_extended_references_var_type() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_var_type.graphql");
    let query_vars_str = include_str!("testdata/extended_references_var_type.json");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();
    let vars: Object = serde_json::from_str(query_vars_str).unwrap();

    let generated = generate_extended_refs(
        &doc,
        Some("InputTypeVariablesQuery".into()),
        &schema,
        Some(&vars),
    );
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_extended_references_inline_nested_type() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_inline_nested_type.graphql");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();

    let generated = generate_extended_refs(
        &doc,
        Some("NestedInputTypeInlineQuery".into()),
        &schema,
        None,
    );
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_extended_references_var_nested_type() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/extended_references_var_nested_type.graphql");
    let query_vars_str = include_str!("testdata/extended_references_var_nested_type.json");

    let schema = Schema::parse_and_validate(schema_str, "schema.graphql").unwrap();
    let doc = ExecutableDocument::parse_and_validate(&schema, query_str, "query.graphql").unwrap();
    let vars: Object = serde_json::from_str(query_vars_str).unwrap();

    let generated = generate_extended_refs(
        &doc,
        Some("NestedInputTypeVarsQuery".into()),
        &schema,
        Some(&vars),
    );
    assert_extended_references!(&generated);
}

#[test(tokio::test)]
async fn test_enums_from_response_complex_response_type() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/enums_from_response_complex_response_type.graphql");
    let response_str =
        include_str!("testdata/enums_from_response_complex_response_type_response.json");
    let op_name = Some("EnumResponseQuery");

    let generated = enums_from_response(query_str, op_name, schema_str, response_str);
    assert_enums_from_response!(&generated);
}

#[test(tokio::test)]
async fn test_enums_from_response_fragments() {
    let schema_str = include_str!("testdata/schema_interop.graphql");
    let query_str = include_str!("testdata/enums_from_response_fragments.graphql");
    let response_str = include_str!("testdata/enums_from_response_fragments_response.json");
    let op_name = Some("EnumResponseQueryFragments");

    let generated = enums_from_response(query_str, op_name, schema_str, response_str);
    assert_enums_from_response!(&generated);
}