Repository: mirumee/ariadne

Issue:
Query cost validation is skipping `InlineFragmentNode`
I've got tipped by @przlada that our query cost validator skips `InlineFragmentNode` when calculating the costs.

`InlineFragmentNode` is a fragment used when querying interfaces and unions:

```graphql
{
  search(query: "lorem ipsum") {
    ... on User {
      id
      username
    }
    ... on Comment {
      id
      content
    }
  }
}
```


Discussion:

Resolving patch:
diff --git a/ariadne/validation/query_cost.py b/ariadne/validation/query_cost.py
--- a/ariadne/validation/query_cost.py
+++ b/ariadne/validation/query_cost.py
@@ -144,14 +144,18 @@ def compute_node_cost(self, node: CostAwareNode, type_def, parent_multipliers=No
                     fragment_type = self.context.schema.get_type(
                         fragment.type_condition.name.value
                     )
-                    node_cost = self.compute_node_cost(fragment, fragment_type)
+                    node_cost = self.compute_node_cost(
+                        fragment, fragment_type, self.operation_multipliers
+                    )
             if isinstance(child_node, InlineFragmentNode):
                 inline_fragment_type = type_def
                 if child_node.type_condition and child_node.type_condition.name:
                     inline_fragment_type = self.context.schema.get_type(
                         child_node.type_condition.name.value
                     )
-                node_cost = self.compute_node_cost(child_node, inline_fragment_type)
+                node_cost = self.compute_node_cost(
+                    child_node, inline_fragment_type, self.operation_multipliers
+                )
             total += node_cost
         return total
 


Test patch:
diff --git a/tests/test_query_cost_validation.py b/tests/test_query_cost_validation.py
--- a/tests/test_query_cost_validation.py
+++ b/tests/test_query_cost_validation.py
@@ -458,3 +458,191 @@ def test_child_field_cost_defined_in_directive_is_multiplied_by_values_from_lite
             extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
         )
     ]
+
+
+def test_child_inline_fragment_cost_defined_in_map_is_multiplied_by_values_from_variables(
+    schema,
+):
+    query = """
+        query testQuery($value: Int!) {
+          child(value: $value) {
+            ... on Child {
+              online
+            }
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3, variables={"value": 5}, cost_map=cost_map)
+    result = validate(schema, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]
+
+
+def test_child_inline_fragment_cost_defined_in_map_is_multiplied_by_values_from_literal(
+    schema,
+):
+    query = """
+        {
+          child(value: 5) {
+            ... on Child{
+                online
+            }
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3, cost_map=cost_map)
+    result = validate(schema, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]
+
+
+def test_child_inline_fragment_cost_defined_in_directive_is_multiplied_by_values_from_variables(
+    schema_with_costs,
+):
+    query = """
+        query testQuery($value: Int!) {
+          child(value: $value) {
+            ... on Child {
+              online
+            }
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3, variables={"value": 5})
+    result = validate(schema_with_costs, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]
+
+
+def test_child_inline_fragment_cost_defined_in_directive_is_multiplied_by_values_from_literal(
+    schema_with_costs,
+):
+    query = """
+        {
+          child(value: 5) {
+            ... on Child{
+                online
+            }
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3)
+    result = validate(schema_with_costs, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]
+
+
+def test_child_fragment_cost_defined_in_map_is_multiplied_by_values_from_variables(
+    schema,
+):
+    query = """
+        fragment child on Child {
+          online
+        }
+        query testQuery($value: Int!) {
+          child(value: $value) {
+            ...child
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3, variables={"value": 5}, cost_map=cost_map)
+    result = validate(schema, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]
+
+
+def test_child_fragment_cost_defined_in_map_is_multiplied_by_values_from_literal(
+    schema,
+):
+    query = """
+        fragment child on Child {
+          online
+        }
+        {
+          child(value: 5) {
+            ...child
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3, cost_map=cost_map)
+    result = validate(schema, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]
+
+
+def test_child_fragment_cost_defined_in_directive_is_multiplied_by_values_from_variables(
+    schema_with_costs,
+):
+    query = """
+        fragment child on Child {
+          online
+        }
+        query testQuery($value: Int!) {
+          child(value: $value) {
+            ...child
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3, variables={"value": 5})
+    result = validate(schema_with_costs, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]
+
+
+def test_child_fragment_cost_defined_in_directive_is_multiplied_by_values_from_literal(
+    schema_with_costs,
+):
+    query = """
+        fragment child on Child {
+          online
+        }
+        {
+          child(value: 5) {
+            ...child
+          }
+        }
+    """
+    ast = parse(query)
+    rule = cost_validator(maximum_cost=3)
+    result = validate(schema_with_costs, ast, [rule])
+    assert result == [
+        GraphQLError(
+            "The query exceeds the maximum cost of 3. Actual cost is 20",
+            extensions={"cost": {"requestedQueryCost": 20, "maximumAvailable": 3}},
+        )
+    ]