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:
codecov[bot]: ## [Codecov](https://codecov.io/gh/mirumee/ariadne/pull/1080?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) Report
> Merging [#1080](https://codecov.io/gh/mirumee/ariadne/pull/1080?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) (4da8e14) into [master](https://codecov.io/gh/mirumee/ariadne/commit/1f62d2da08abc8ed5dfd50c83befa8f965a840ed?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) (1f62d2d) will **increase** coverage by `0.13%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master    #1080      +/-   ##
==========================================
+ Coverage   98.11%   98.24%   +0.13%     
==========================================
  Files         115      115              
  Lines        7463     7511      +48     
==========================================
+ Hits         7322     7379      +57     
+ Misses        141      132       -9     
```


| [Impacted Files](https://codecov.io/gh/mirumee/ariadne/pull/1080?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee) | Coverage Δ | |
|---|---|---|
| [ariadne/validation/query\_cost.py](https://codecov.io/gh/mirumee/ariadne/pull/1080?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee#diff-YXJpYWRuZS92YWxpZGF0aW9uL3F1ZXJ5X2Nvc3QucHk=) | `88.30% <100.00%> (+4.67%)` | :arrow_up: |
| [tests/test\_query\_cost\_validation.py](https://codecov.io/gh/mirumee/ariadne/pull/1080?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee#diff-dGVzdHMvdGVzdF9xdWVyeV9jb3N0X3ZhbGlkYXRpb24ucHk=) | `100.00% <100.00%> (ø)` | |

... and [1 file with indirect coverage changes](https://codecov.io/gh/mirumee/ariadne/pull/1080/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee)

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mirumee)


Patch:
diff --git a/ariadne/validation/query_cost.py b/ariadne/validation/query_cost.py
index f46775b..d5b29a6 100644
--- a/ariadne/validation/query_cost.py
+++ b/ariadne/validation/query_cost.py
@@ -144,14 +144,18 @@ class CostValidator(ValidationRule):
                     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
index 692743f..79403a8 100644
--- 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}},
+        )
+    ]