Repository: wemake-services/wemake-python-styleguide

Issue:
WPS232 triggering for a single-item module
# Bug report

<!--
Hi, thanks for submitting a bug. We appreciate that.

But, we will need some information about what's wrong to help you.
-->

## What's wrong

<!-- Describe what is not working. Please, attach a traceback. -->

Module with just one function reports this violation:

```
  0:1      WPS232 Found module cognitive complexity that is too high: 10.0 > 8
```

This is not right. Because we already have a check for a single funtion and it passes.
Modules should be analyzed only if we have more than one element.


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910?src=pr&el=h1) Report
> Merging [#1910](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910?src=pr&el=desc) (0fe959d) into [master](https://codecov.io/gh/wemake-services/wemake-python-styleguide/commit/07f89a12ee2c7ddfddc318fe7e3358c161218ea3?el=desc) (07f89a1) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/graphs/tree.svg?width=650&height=150&src=pr&token=yMZCKw0D02)](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##            master     #1910   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          116       117    +1     
  Lines         6182      6158   -24     
  Branches      1375      1367    -8     
=========================================
- Hits          6182      6158   -24     
```


| [Impacted Files](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...ake\_python\_styleguide/logic/complexity/overuses.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL2NvbXBsZXhpdHkvb3ZlcnVzZXMucHk=) | `100.00% <ø> (ø)` | |
| [wemake\_python\_styleguide/logic/tree/annotations.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL3RyZWUvYW5ub3RhdGlvbnMucHk=) | `100.00% <100.00%> (ø)` | |
| [...hon\_styleguide/visitors/ast/complexity/function.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9jb21wbGV4aXR5L2Z1bmN0aW9uLnB5) | `100.00% <100.00%> (ø)` | |
| [...python\_styleguide/visitors/ast/complexity/jones.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9jb21wbGV4aXR5L2pvbmVzLnB5) | `100.00% <100.00%> (ø)` | |
| [...hon\_styleguide/visitors/ast/complexity/overuses.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9jb21wbGV4aXR5L292ZXJ1c2VzLnB5) | `100.00% <100.00%> (ø)` | |
| [wemake\_python\_styleguide/visitors/ast/operators.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9vcGVyYXRvcnMucHk=) | `100.00% <100.00%> (ø)` | |
| [wemake\_python\_styleguide/compat/nodes.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2NvbXBhdC9ub2Rlcy5weQ==) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/logic/walrus.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL3dhbHJ1cy5weQ==) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/compat/routing.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2NvbXBhdC9yb3V0aW5nLnB5) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/logic/safe\_eval.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL3NhZmVfZXZhbC5weQ==) | `100.00% <0.00%> (ø)` | |
| ... and [5 more](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910?src=pr&el=footer). Last update [07f89a1...0fe959d](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1910?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f96bc5b..42e28c13 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,11 +16,15 @@ Semantic versioning in our case means:
   But, in the future we might change the configuration names / logic,
   change the client facing API, change code conventions signigicantly, etc.
 
+
 ## 0.15.2
 
 ### Bugfixes
 
 - Fixes `BitwiseAndBooleanMixupViolation` work with PEP 604 union types #1884
+- Fixes `CognitiveModuleComplexityViolation` to not trigger
+  for a single-item modules
+
 
 ## 0.15.1
 
diff --git a/setup.cfg b/setup.cfg
index c1530f82..c74e430f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -54,6 +54,8 @@ per-file-ignores =
   wemake_python_styleguide/options/defaults.py: WPS432
   # Checker has a lot of imports:
   wemake_python_styleguide/checker.py: WPS201
+  # TODO: remove after 0.15.2 release:
+  wemake_python_styleguide/logic/tree/annotations.py: WPS232
   # Allows mypy type hinting, `Ellipsis`` usage, multiple methods:
   wemake_python_styleguide/types.py: D102, WPS214, WPS220, WPS428
   # There are multiple fixtures, `assert`s, and subprocesses in tests:
diff --git a/wemake_python_styleguide/logic/complexity/overuses.py b/wemake_python_styleguide/logic/complexity/overuses.py
index ca1bf34b..ff0d0377 100644
--- a/wemake_python_styleguide/logic/complexity/overuses.py
+++ b/wemake_python_styleguide/logic/complexity/overuses.py
@@ -7,9 +7,6 @@ from wemake_python_styleguide.constants import SPECIAL_ARGUMENT_NAMES_WHITELIST
 from wemake_python_styleguide.logic import nodes, walk
 from wemake_python_styleguide.logic.arguments import call_args
 
-#: Nodes that can be annotated.
-_AnnNodes = (ast.AnnAssign, ast.arg)
-
 
 def is_class_context(node: ast.AST) -> bool:
     """
@@ -73,32 +70,6 @@ def is_self(node: ast.AST) -> bool:
     )
 
 
-def is_annotation(node: ast.AST) -> bool:
-    """
-    Detects if node is an annotation. Or a part of it.
-
-    We use this predicate to allow all types of repetetive
-    function and instance annotations.
-    """
-    if not isinstance(node, (ast.Str, ast.List, ast.Tuple, ast.Subscript)):
-        return False
-
-    annotated = walk.get_closest_parent(node, (*_AnnNodes, *FunctionNodes))
-    if isinstance(annotated, FunctionNodes):
-        contains_node = bool(
-            annotated.returns and
-            walk.is_contained_by(node, annotated.returns),
-        )
-        return node == annotated.returns or contains_node
-    elif isinstance(annotated, _AnnNodes):
-        contains_node = bool(
-            annotated.annotation and
-            walk.is_contained_by(node, annotated.annotation),
-        )
-        return node == annotated.annotation or contains_node
-    return False
-
-
 def is_primitive(node: ast.AST) -> bool:
     """
     Detects if node is a form of a primitive value.
diff --git a/wemake_python_styleguide/logic/tree/annotations.py b/wemake_python_styleguide/logic/tree/annotations.py
new file mode 100644
index 00000000..89a3ea6d
--- /dev/null
+++ b/wemake_python_styleguide/logic/tree/annotations.py
@@ -0,0 +1,46 @@
+import ast
+
+from typing_extensions import Final
+
+from wemake_python_styleguide.compat.aliases import FunctionNodes
+from wemake_python_styleguide.logic import walk
+
+#: Nodes that can be directly annotated.
+_AnnNodes: Final = (ast.AnnAssign, ast.arg)
+
+#: Nodes that can be a part of an annotation.
+_AnnParts: Final = (
+    ast.Name,
+    ast.Attribute,
+    ast.Str,
+    ast.List,
+    ast.Tuple,
+    ast.Subscript,
+    ast.BinOp,  # new styled unions, like: `str | int`
+)
+
+
+def is_annotation(node: ast.AST) -> bool:
+    """
+    Detects if node is an annotation. Or a part of it.
+
+    We use this predicate to allow all types of repetetive
+    function and instance annotations.
+    """
+    if not isinstance(node, _AnnParts):
+        return False
+
+    annotated = walk.get_closest_parent(node, (*_AnnNodes, *FunctionNodes))
+    if isinstance(annotated, FunctionNodes):
+        contains_node = bool(
+            annotated.returns and
+            walk.is_contained_by(node, annotated.returns),
+        )
+        return node == annotated.returns or contains_node
+    elif isinstance(annotated, _AnnNodes):
+        contains_node = bool(
+            annotated.annotation and
+            walk.is_contained_by(node, annotated.annotation),
+        )
+        return node == annotated.annotation or contains_node
+    return False
diff --git a/wemake_python_styleguide/visitors/ast/complexity/function.py b/wemake_python_styleguide/visitors/ast/complexity/function.py
index f856ad3f..3c2140f8 100644
--- a/wemake_python_styleguide/visitors/ast/complexity/function.py
+++ b/wemake_python_styleguide/visitors/ast/complexity/function.py
@@ -259,6 +259,9 @@ class CognitiveComplexityVisitor(BaseNodeVisitor):
                     ),
                 )
 
+        if len(self._functions) <= 1:
+            return  # We don't check module average for a single function.
+
         average = total / len(self._functions)
         if average > self.options.max_cognitive_average:
             self.add_violation(
diff --git a/wemake_python_styleguide/visitors/ast/complexity/jones.py b/wemake_python_styleguide/visitors/ast/complexity/jones.py
index 331f952e..78e1ab9b 100644
--- a/wemake_python_styleguide/visitors/ast/complexity/jones.py
+++ b/wemake_python_styleguide/visitors/ast/complexity/jones.py
@@ -10,7 +10,7 @@ Original project is licensed under MIT.
 import ast
 from collections import defaultdict
 from statistics import median
-from typing import DefaultDict, List
+from typing import DefaultDict, List, Set
 
 from typing_extensions import final
 
@@ -46,7 +46,7 @@ class JonesComplexityVisitor(BaseNodeVisitor):
         """Initializes line number counter."""
         super().__init__(*args, **kwargs)
         self._lines: DefaultDict[int, List[ast.AST]] = defaultdict(list)
-        self._to_ignore: List[ast.AST] = []
+        self._to_ignore: Set[ast.AST] = set()
 
     def visit(self, node: ast.AST) -> None:
         """
@@ -99,6 +99,5 @@ class JonesComplexityVisitor(BaseNodeVisitor):
 
     def _maybe_ignore_child(self, node: ast.AST) -> bool:
         if isinstance(node, ast.AnnAssign):
-            self._to_ignore.extend(ast.walk(node.annotation))
-
+            self._to_ignore.update(ast.walk(node.annotation))
         return node in self._to_ignore
diff --git a/wemake_python_styleguide/visitors/ast/complexity/overuses.py b/wemake_python_styleguide/visitors/ast/complexity/overuses.py
index 5199ab71..0d6d204e 100644
--- a/wemake_python_styleguide/visitors/ast/complexity/overuses.py
+++ b/wemake_python_styleguide/visitors/ast/complexity/overuses.py
@@ -15,6 +15,7 @@ from typing_extensions import final
 from wemake_python_styleguide.compat.aliases import FunctionNodes
 from wemake_python_styleguide.logic import source, walk
 from wemake_python_styleguide.logic.complexity import overuses
+from wemake_python_styleguide.logic.tree import annotations
 from wemake_python_styleguide.types import AnyNodes, AnyText, AnyTextPrimitive
 from wemake_python_styleguide.violations import complexity
 from wemake_python_styleguide.visitors import base, decorators
@@ -70,7 +71,7 @@ class StringOveruseVisitor(base.BaseNodeVisitor):
         self.generic_visit(node)
 
     def _check_string_constant(self, node: AnyText) -> None:
-        if overuses.is_annotation(node):
+        if annotations.is_annotation(node):
             return
 
         # Some strings are so common, that it makes no sense to check if
@@ -120,7 +121,7 @@ class ExpressionOveruseVisitor(base.BaseNodeVisitor):
     _ignore_predicates: Tuple[Callable[[ast.AST], bool], ...] = (
         overuses.is_decorator,
         overuses.is_self,
-        overuses.is_annotation,
+        annotations.is_annotation,
         overuses.is_class_context,
         overuses.is_super_call,
         overuses.is_primitive,
diff --git a/wemake_python_styleguide/visitors/ast/operators.py b/wemake_python_styleguide/visitors/ast/operators.py
index ca0e80d9..84e3aea1 100644
--- a/wemake_python_styleguide/visitors/ast/operators.py
+++ b/wemake_python_styleguide/visitors/ast/operators.py
@@ -6,7 +6,7 @@ from typing_extensions import final
 from wemake_python_styleguide.compat.aliases import TextNodes
 from wemake_python_styleguide.compat.nodes import NamedExpr
 from wemake_python_styleguide.logic import walk
-from wemake_python_styleguide.logic.nodes import get_parent
+from wemake_python_styleguide.logic.tree.annotations import is_annotation
 from wemake_python_styleguide.logic.tree.operators import (
     count_unary_operator,
     unwrap_unary_node,
@@ -298,24 +298,12 @@ class BitwiseOpVisitor(base.BaseNodeVisitor):
         if not isinstance(node.op, (ast.BitOr, ast.BitAnd)):
             return
 
-        if self._is_type_annotation(node):
-            return
+        if isinstance(node.op, ast.BitOr) and is_annotation(node):
+            return  # We allow new styled union types like: `int | None`
 
         if self._is_bool_like(node.left) or self._is_bool_like(node.right):
             self.add_violation(BitwiseAndBooleanMixupViolation(node))
 
-    # checks either side of the Bitwise operation invalid usage
     def _is_bool_like(self, node: ast.expr) -> bool:
+        """Checks either side of the Bitwise operation invalid usage."""
         return isinstance(node, self._invalid_nodes)
-
-    # checks whether bitwise operator is used in a type annotation
-    def _is_type_annotation(self, node: ast.BinOp) -> bool:
-        parent_node = get_parent(node)
-
-        if isinstance(parent_node, (ast.AnnAssign, ast.arg)):
-            return node is parent_node.annotation
-
-        if isinstance(parent_node, ast.FunctionDef):
-            return node is parent_node.returns
-
-        return False


Test patch:
diff --git a/tests/test_visitors/test_ast/test_compares/test_in_type_check.py b/tests/test_visitors/test_ast/test_compares/test_in_type_check.py
index 60631d43..a6c4971a 100644
--- a/tests/test_visitors/test_ast/test_compares/test_in_type_check.py
+++ b/tests/test_visitors/test_ast/test_compares/test_in_type_check.py
@@ -48,7 +48,7 @@ def test_compare_with_wrong_type(
     assert_errors(
         visitor,
         [WrongInCompareTypeViolation],
-        ignored_types=(InCompareWithSingleItemContainerViolation,),
+        ignored_types=InCompareWithSingleItemContainerViolation,
     )
 
 
diff --git a/tests/test_visitors/test_ast/test_compares/test_in_with_single_item_container.py b/tests/test_visitors/test_ast/test_compares/test_in_with_single_item_container.py
index a01eaafe..61b63d96 100644
--- a/tests/test_visitors/test_ast/test_compares/test_in_with_single_item_container.py
+++ b/tests/test_visitors/test_ast/test_compares/test_in_with_single_item_container.py
@@ -40,7 +40,7 @@ def test_single_item_container(
     assert_errors(
         visitor,
         [InCompareWithSingleItemContainerViolation],
-        ignored_types=(WrongInCompareTypeViolation,),
+        ignored_types=WrongInCompareTypeViolation,
     )
 
 
@@ -70,8 +70,4 @@ def test_multi_item_container(
     visitor = InCompareSanityVisitor(default_options, tree=tree)
     visitor.run()
 
-    assert_errors(
-        visitor,
-        [],
-        ignored_types=(WrongInCompareTypeViolation,),
-    )
+    assert_errors(visitor, [], ignored_types=WrongInCompareTypeViolation)
diff --git a/tests/test_visitors/test_ast/test_compares/test_multiple_in.py b/tests/test_visitors/test_ast/test_compares/test_multiple_in.py
index e29bc4bc..a9d9b70a 100644
--- a/tests/test_visitors/test_ast/test_compares/test_multiple_in.py
+++ b/tests/test_visitors/test_ast/test_compares/test_multiple_in.py
@@ -47,7 +47,7 @@ def test_compare_with_in(
     assert_errors(
         visitor,
         [],
-        ignored_types=(InCompareWithSingleItemContainerViolation,),
+        ignored_types=InCompareWithSingleItemContainerViolation,
     )
 
 
@@ -78,7 +78,7 @@ def test_compare_with_multiple_in(
     assert_errors(
         visitor,
         [MultipleInCompareViolation],
-        ignored_types=(InCompareWithSingleItemContainerViolation,),
+        ignored_types=InCompareWithSingleItemContainerViolation,
     )
 
 
diff --git a/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_average.py b/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_average.py
index 958c26b8..29d1f067 100644
--- a/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_average.py
+++ b/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_average.py
@@ -8,6 +8,18 @@ from wemake_python_styleguide.visitors.ast.complexity.function import (
     CognitiveComplexityVisitor,
 )
 
+# Templates:
+
+single_item = '{0}'
+multiple_items = """
+def other():
+    ...
+
+{0}
+"""
+
+# Complex samples:
+
 complex_function = """
 def literal_eval(node):
     if isinstance(node, (Constant, NameConstant)):
@@ -53,7 +65,7 @@ def test_complex_cognitive_module(
     mode,
 ):
     """Ensures that complex cognitive code does not work."""
-    tree = parse_ast_tree(mode(code))
+    tree = parse_ast_tree(mode(multiple_items.format(code)))
 
     visitor = CognitiveComplexityVisitor(default_options, tree=tree)
     visitor.run()
@@ -61,7 +73,7 @@ def test_complex_cognitive_module(
     assert_errors(
         visitor,
         [CognitiveModuleComplexityViolation],
-        ignored_types=(CognitiveComplexityViolation,),
+        ignored_types=CognitiveComplexityViolation,
     )
 
 
@@ -78,7 +90,7 @@ def test_complex_cognitive_options(
     mode,
 ):
     """Ensures that complex can be modified via settings."""
-    tree = parse_ast_tree(mode(code))
+    tree = parse_ast_tree(mode(multiple_items.format(code)))
 
     option_values = options(max_cognitive_average=0)
     visitor = CognitiveComplexityVisitor(option_values, tree=tree)
@@ -87,13 +99,20 @@ def test_complex_cognitive_options(
     assert_errors(
         visitor,
         [CognitiveModuleComplexityViolation],
-        ignored_types=(CognitiveComplexityViolation,),
+        ignored_types=CognitiveComplexityViolation,
     )
     assert_error_text(
-        visitor, '1.0', option_values.max_cognitive_average, multiple=True,
+        visitor,
+        '0.5',  # manually calculated
+        option_values.max_cognitive_average,
+        multiple=True,
     )
 
 
+@pytest.mark.parametrize('template', [
+    single_item,
+    multiple_items,
+])
 @pytest.mark.parametrize('code', [
     function_example,
     method_example,
@@ -102,12 +121,13 @@ def test_complex_cognitive_options(
 def test_cognitive_average_default_options(
     assert_errors,
     parse_ast_tree,
+    template,
     code,
     default_options,
     mode,
 ):
     """Ensures that simple cognitive code does work."""
-    tree = parse_ast_tree(mode(code))
+    tree = parse_ast_tree(mode(template.format(code)))
 
     visitor = CognitiveComplexityVisitor(default_options, tree=tree)
     visitor.run()
diff --git a/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_score.py b/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_score.py
index 6d0e0ada..1f029e83 100644
--- a/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_score.py
+++ b/tests/test_visitors/test_ast/test_complexity/test_function/test_cognitive/test_cognitive_score.py
@@ -8,6 +8,8 @@ from wemake_python_styleguide.visitors.ast.complexity.function import (
     CognitiveComplexityVisitor,
 )
 
+# Complex sample:
+
 complex_function = """
 def literal_eval(node):
     if isinstance(node, (Constant, NameConstant)):
@@ -24,7 +26,7 @@ def literal_eval(node):
     return _convert_signed_num(node)
 """
 
-# Samples:
+# Regular samples:
 
 function_example = """
 def some():
@@ -62,10 +64,13 @@ def test_complex_cognitive_function(
     assert_errors(
         visitor,
         [CognitiveComplexityViolation],
-        ignored_types=(CognitiveModuleComplexityViolation,),
+        ignored_types=CognitiveModuleComplexityViolation,
     )
     assert_error_text(
-        visitor, '22', default_options.max_cognitive_score, multiple=True,
+        visitor,
+        '22',  # we calculate this manually
+        default_options.max_cognitive_score,
+        multiple=True,
     )
 
 
@@ -91,10 +96,13 @@ def test_complex_cognitive_options(
     assert_errors(
         visitor,
         [CognitiveComplexityViolation],
-        ignored_types=(CognitiveModuleComplexityViolation,),
+        ignored_types=CognitiveModuleComplexityViolation,
     )
     assert_error_text(
-        visitor, '1', option_values.max_cognitive_score, multiple=True,
+        visitor,
+        '1',  # we calculate this manually
+        option_values.max_cognitive_score,
+        multiple=True,
     )
 
 
diff --git a/tests/test_visitors/test_ast/test_operators/test_bitwise_boolean_mixup.py b/tests/test_visitors/test_ast/test_operators/test_bitwise_boolean_mixup.py
index 567fc6c3..cf69303b 100644
--- a/tests/test_visitors/test_ast/test_operators/test_bitwise_boolean_mixup.py
+++ b/tests/test_visitors/test_ast/test_operators/test_bitwise_boolean_mixup.py
@@ -70,6 +70,8 @@ def test_correct_binary(
     'int | None',
     'First | None',
     'First | Second',
+    'List[List[str | None]]',
+    'set[Alias | Other | Third | None]',
 ])
 def test_union_type(
     assert_errors,
diff --git a/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_modifier.py b/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_modifier.py
index 8928006e..611b26c9 100644
--- a/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_modifier.py
+++ b/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_modifier.py
@@ -42,7 +42,7 @@ def test_correct_prefix(
     assert_errors(
         visitor,
         [],
-        ignored_types=(RawStringNotNeededViolation,),
+        ignored_types=RawStringNotNeededViolation,
     )
 
 
@@ -77,7 +77,7 @@ def test_uppercase_prefix(
     assert_errors(
         visitor,
         [UppercaseStringModifierViolation],
-        ignored_types=(RawStringNotNeededViolation,),
+        ignored_types=RawStringNotNeededViolation,
     )
     assert_error_text(visitor, modifier, multiple=True)
 
diff --git a/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_multiline.py b/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_multiline.py
index 2f6be3c1..ad32c107 100644
--- a/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_multiline.py
+++ b/tests/test_visitors/test_tokenize/test_primitives/test_string_tokens/test_string_multiline.py
@@ -78,7 +78,7 @@ def test_incorrect_multiline_strings(
     assert_errors(
         visitor,
         [WrongMultilineStringViolation],
-        ignored_types=(RawStringNotNeededViolation,),
+        ignored_types=RawStringNotNeededViolation,
     )