Repository: mirumee/ariadne-codegen (python)

Issue:
First comment in auto-generated files causes conflicts in git
The first comment in auto-generated files, e.g.: `# Generated by ariadne-codegen on 2023-09-18 16:52`, causes problems when two people auto-generate the same file with different content. Changes that could easily be merged in git without the comment, result in conflicts when the comments are there. Setting `include_comments` to `false` in config is not ideal, as then there is no info for code reviewers etc. that the code in a given file was auto-generated. It should be possible to keep the comments, but without the date (anyway, the approximate date of the change can be found in git).

Pull request description:
Stable comments
This pr:
- changes `include_comments` option to accept `"stable"`, `"timestamp"` or `"none"`
- deprecates using boolean, `false` is mapped to `"none"` and `true` to `"timestamp"`
- changes default to `"stable"`
- adds `get_comment` plugin hook

resolves #215 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26bfd50..d51a02e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
 ## UNRELEASED
 
 - Fixed generating results for nullable fields with nullable directives.
-
+- Changed `include_comments` option to accept enum value, changed default to `"stable"`, deprecated boolean support. Added `get_file_comment` plugin hook. 
 
 ## 0.9.0 (2023-09-11)
 
diff --git a/PLUGINS.md b/PLUGINS.md
index 1b99b6e..2ac2a52 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -285,6 +285,17 @@ Hook executed on creating `GraphQLSchema` object from path or url provided in se
 To ensure all plugins have current version of schema, result of this hook is propagated to all plugins, updating their `schema` field. 
 
 
+### get_file_comment
+
+```py
+def get_file_comment(
+    self, comment: str, code: str, source: Optional[str] = None
+) -> str:
+```
+
+Hook executed on generating comment included at the beginning of a generated code.
+
+
 ## Example
 
 This example plugin adds `__version__ = "..."` to generated `__init__.py` file.
diff --git a/README.md b/README.md
index 5dc7594..297e236 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,7 @@ Optional settings:
 - `enums_module_name` (defaults to `"enums"`) - name of file with generated enums models
 - `input_types_module_name` (defaults to `"input_types"`) - name of file with generated input types models
 - `fragments_module_name` (defaults to `"fragments"`) - name of file with generated fragments models
-- `include_comments` (defaults to `true`) - a flag that specifies whether to include comments in generated files
+- `include_comments` (defaults to `"stable"`) - option which sets content of comments included at the top of every generated file. Valid choices are: `"none"` (no comments), `"timestamp"` (comment with generation timestamp), `"stable"` (comment contains a message that this is a generated file)
 - `convert_to_snake_case` (defaults to `true`) - a flag that specifies whether to convert fields and arguments names to snake case
 - `async_client` (defaults to `true`) - default generated client is `async`, change this to option `false` to generate synchronous client instead
 - `files_to_include` (defaults to `[]`) - list of files which will be copied into generated package
diff --git a/ariadne_codegen/client_generators/comments.py b/ariadne_codegen/client_generators/comments.py
new file mode 100644
index 0000000..a5f7c0a
--- /dev/null
+++ b/ariadne_codegen/client_generators/comments.py
@@ -0,0 +1,40 @@
+from datetime import datetime
+from typing import Callable, Optional
+
+from ..settings import CommentsStrategy
+from .constants import (
+    COMMENT_DATETIME_FORMAT,
+    SOURCE_COMMENT,
+    STABLE_COMMENT,
+    TIMESTAMP_COMMENT,
+)
+
+
+def get_comment(strategy: CommentsStrategy, source: Optional[str] = None) -> str:
+    # pylint: disable=unused-argument
+    def empty_comment_function(source: Optional[str] = None) -> str:
+        return ""
+
+    get_comment_function: Callable[[Optional[str]], str] = {
+        CommentsStrategy.NONE: empty_comment_function,
+        CommentsStrategy.TIMESTAMP: get_timestamp_comment,
+        CommentsStrategy.STABLE: get_stable_comment,
+    }.get(strategy, empty_comment_function)
+
+    return get_comment_function(source)
+
+
+def get_timestamp_comment(source: Optional[str] = None) -> str:
+    comment = TIMESTAMP_COMMENT.format(datetime.now().strftime(COMMENT_DATETIME_FORMAT))
+    if source:
+        comment += "\n" + SOURCE_COMMENT.format(source)
+
+    return comment
+
+
+def get_stable_comment(source: Optional[str] = None) -> str:
+    comment = STABLE_COMMENT
+    if source:
+        comment += "\n" + SOURCE_COMMENT.format(source)
+
+    return comment
diff --git a/ariadne_codegen/client_generators/constants.py b/ariadne_codegen/client_generators/constants.py
index fedd770..fbccbbe 100644
--- a/ariadne_codegen/client_generators/constants.py
+++ b/ariadne_codegen/client_generators/constants.py
@@ -21,8 +21,9 @@ ANNOTATED = "Annotated"
 LITERAL = "Literal"
 ASYNC_ITERATOR = "AsyncIterator"
 
-TIMESTAMP_COMMENT = "# Generated by ariadne-codegen on {}\n"
-SOURCE_COMMENT = "# Source: {}\n"
+TIMESTAMP_COMMENT = "# Generated by ariadne-codegen on {}"
+STABLE_COMMENT = "# Generated by ariadne-codegen"
+SOURCE_COMMENT = "# Source: {}"
 COMMENT_DATETIME_FORMAT = "%Y-%m-%d %H:%M"
 
 BASE_MODEL_CLASS_NAME = "BaseModel"
diff --git a/ariadne_codegen/client_generators/package.py b/ariadne_codegen/client_generators/package.py
index 68d9fc4..d7e66d0 100644
--- a/ariadne_codegen/client_generators/package.py
+++ b/ariadne_codegen/client_generators/package.py
@@ -1,5 +1,4 @@
 import ast
-from datetime import datetime
 from pathlib import Path
 from typing import Dict, List, Optional, Set
 
@@ -8,17 +7,16 @@ from graphql import FragmentDefinitionNode, GraphQLSchema, OperationDefinitionNo
 from ..codegen import generate_import_from
 from ..exceptions import ParsingError
 from ..plugins.manager import PluginManager
+from ..settings import CommentsStrategy
 from ..utils import ast_to_str, process_name, str_to_pascal_case
 from .arguments import ArgumentsGenerator
 from .client import ClientGenerator
+from .comments import get_comment
 from .constants import (
     BASE_MODEL_CLASS_NAME,
-    COMMENT_DATETIME_FORMAT,
     DEFAULT_ASYNC_BASE_CLIENT_PATH,
     DEFAULT_BASE_CLIENT_PATH,
     GRAPHQL_CLIENT_EXCEPTIONS_NAMES,
-    SOURCE_COMMENT,
-    TIMESTAMP_COMMENT,
     UNSET_NAME,
     UNSET_TYPE_NAME,
     UPLOAD_CLASS_NAME,
@@ -44,7 +42,7 @@ class PackageGenerator:
         enums_module_name: str = "enums",
         input_types_module_name: str = "input_types",
         fragments_module_name: str = "fragments",
-        include_comments: bool = True,
+        comments_strategy: CommentsStrategy = CommentsStrategy.STABLE,
         queries_source: str = "",
         schema_source: str = "",
         convert_to_snake_case: bool = True,
@@ -93,7 +91,7 @@ class PackageGenerator:
         self.fragments_module_name = fragments_module_name
         self.client_file_name = client_file_name
 
-        self.include_comments = include_comments
+        self.comments_strategy = comments_strategy
         self.queries_source = queries_source
         self.schema_source = schema_source
         self.convert_to_snake_case = convert_to_snake_case
@@ -253,7 +251,7 @@ class PackageGenerator:
     def _generate_client(self):
         client_file_path = self.package_path / f"{self.client_file_name}.py"
         client_module = self.client_generator.generate()
-        code = self._proccess_generated_code(
+        code = self._add_comments_to_code(
             ast_to_str(client_module, multiline_strings=True), self.queries_source
         )
         if self.plugin_manager:
@@ -265,22 +263,20 @@ class PackageGenerator:
             names=[self.client_generator.name], from_=self.client_file_name, level=1
         )
 
-    def _proccess_generated_code(self, code: str, source: str = "") -> str:
-        if self.include_comments:
-            comments = [
-                TIMESTAMP_COMMENT.format(
-                    datetime.now().strftime(COMMENT_DATETIME_FORMAT)
-                )
-            ]
-            if source:
-                comments.append(SOURCE_COMMENT.format(source))
-            return "".join(comments) + "\n" + code
+    def _add_comments_to_code(self, code: str, source: Optional[str] = None) -> str:
+        comment = get_comment(strategy=self.comments_strategy, source=source)
+        if self.plugin_manager:
+            comment = self.plugin_manager.get_file_comment(
+                comment, code=code, source=source
+            )
+        if comment:
+            return comment + "\n\n" + code
 
         return code
 
     def _generate_enums(self):
         module = self.enums_generator.generate()
-        code = self._proccess_generated_code(ast_to_str(module), self.schema_source)
+        code = self._add_comments_to_code(ast_to_str(module), self.schema_source)
         if self.plugin_manager:
             code = self.plugin_manager.generate_enums_code(code)
         enums_file_path = self.package_path / f"{self.enums_module_name}.py"
@@ -293,7 +289,7 @@ class PackageGenerator:
     def _generate_input_types(self):
         module = self.input_types_generator.generate()
         input_types_file_path = self.package_path / f"{self.input_types_module_name}.py"
-        code = self._proccess_generated_code(ast_to_str(module), self.schema_source)
+        code = self._add_comments_to_code(ast_to_str(module), self.schema_source)
         if self.plugin_manager:
             code = self.plugin_manager.generate_inputs_code(code)
         input_types_file_path.write_text(code)
@@ -307,9 +303,7 @@ class PackageGenerator:
     def _generate_result_types(self):
         for file_name, module in self.result_types_files.items():
             file_path = self.package_path / file_name
-            code = self._proccess_generated_code(
-                ast_to_str(module), self.queries_source
-            )
+            code = self._add_comments_to_code(ast_to_str(module), self.queries_source)
             if self.plugin_manager:
                 code = self.plugin_manager.generate_result_types_code(code)
             file_path.write_text(code)
@@ -333,7 +327,7 @@ class PackageGenerator:
         )
         module = generator.generate()
         file_path = self.package_path / f"{self.fragments_module_name}.py"
-        code = self._proccess_generated_code(ast_to_str(module), self.queries_source)
+        code = self._add_comments_to_code(ast_to_str(module), self.queries_source)
         file_path.write_text(code)
         self.generated_files.append(file_path.name)
         self.init_generator.add_import(
@@ -353,7 +347,7 @@ class PackageGenerator:
                 level=1,
             )
         for source_path in files_to_copy:
-            code = self._proccess_generated_code(source_path.read_text())
+            code = self._add_comments_to_code(source_path.read_text())
             if self.plugin_manager:
                 code = self.plugin_manager.copy_code(code)
             target_path = self.package_path / source_path.name
@@ -374,7 +368,7 @@ class PackageGenerator:
     def _generate_init(self):
         init_file_path = self.package_path / "__init__.py"
         init_module = self.init_generator.generate()
-        code = self._proccess_generated_code(ast_to_str(init_module, False))
+        code = self._add_comments_to_code(ast_to_str(init_module, False))
         if self.plugin_manager:
             code = self.plugin_manager.generate_init_code(code)
         init_file_path.write_text(code)
diff --git a/ariadne_codegen/config.py b/ariadne_codegen/config.py
index a0f898a..d6a7b8d 100644
--- a/ariadne_codegen/config.py
+++ b/ariadne_codegen/config.py
@@ -7,7 +7,7 @@ import toml
 
 from .client_generators.scalars import ScalarData
 from .exceptions import ConfigFileNotFound, MissingConfiguration
-from .settings import ClientSettings, GraphQLSchemaSettings
+from .settings import ClientSettings, CommentsStrategy, GraphQLSchemaSettings
 
 simplefilter("default", DeprecationWarning)
 
@@ -52,6 +52,23 @@ def get_client_settings(config_dict: Dict) -> ClientSettings:
         ) from exc
 
     try:
+        if "include_comments" in section and isinstance(
+            section["include_comments"], bool
+        ):
+            section["include_comments"] = (
+                CommentsStrategy.TIMESTAMP.value
+                if section["include_comments"]
+                else CommentsStrategy.NONE.value
+            )
+            options = ", ".join(strategy.value for strategy in CommentsStrategy)
+            warn(
+                "Support for boolean 'include_comments' value has been deprecated "
+                "and will be dropped in future release. "
+                f"Instead use one of following options: {options}",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+
         return ClientSettings(
             **{
                 key: value
diff --git a/ariadne_codegen/main.py b/ariadne_codegen/main.py
index 14462fd..805b885 100644
--- a/ariadne_codegen/main.py
+++ b/ariadne_codegen/main.py
@@ -78,7 +78,7 @@ def client(config_dict):
         fragments_module_name=settings.fragments_module_name,
         queries_source=settings.queries_path,
         schema_source=schema_source,
-        include_comments=settings.include_comments,
+        comments_strategy=settings.include_comments,
         fragments=fragments,
         convert_to_snake_case=settings.convert_to_snake_case,
         async_client=settings.async_client,
diff --git a/ariadne_codegen/plugins/base.py b/ariadne_codegen/plugins/base.py
index f1a9c2a..0792c2f 100644
--- a/ariadne_codegen/plugins/base.py
+++ b/ariadne_codegen/plugins/base.py
@@ -149,3 +149,9 @@ class Plugin:
 
     def process_schema(self, schema: GraphQLSchema) -> GraphQLSchema:
         return schema
+
+    # pylint: disable=unused-argument
+    def get_file_comment(
+        self, comment: str, code: str, source: Optional[str] = None
+    ) -> str:
+        return comment
diff --git a/ariadne_codegen/plugins/manager.py b/ariadne_codegen/plugins/manager.py
index a0c1eda..c6e4e71 100644
--- a/ariadne_codegen/plugins/manager.py
+++ b/ariadne_codegen/plugins/manager.py
@@ -200,3 +200,10 @@ class PluginManager:
                 plugin.schema = processed_schema
 
         return processed_schema
+
+    def get_file_comment(
+        self, comment: str, code: str, source: Optional[str] = None
+    ) -> str:
+        return self._apply_plugins_on_object(
+            "get_file_comment", comment, code=code, source=source
+        )
diff --git a/ariadne_codegen/settings.py b/ariadne_codegen/settings.py
index 3719117..61cccac 100644
--- a/ariadne_codegen/settings.py
+++ b/ariadne_codegen/settings.py
@@ -14,6 +14,12 @@ from .client_generators.scalars import ScalarData
 from .exceptions import InvalidConfiguration
 
 
+class CommentsStrategy(str, enum.Enum):
+    NONE = "none"
+    STABLE = "stable"
+    TIMESTAMP = "timestamp"
+
+
 class Strategy(str, enum.Enum):
     CLIENT = "client"
     GRAPHQL_SCHEMA = "graphqlschema"
@@ -51,7 +57,7 @@ class ClientSettings(BaseSettings):
     enums_module_name: str = "enums"
     input_types_module_name: str = "input_types"
     fragments_module_name: str = "fragments"
-    include_comments: bool = True
+    include_comments: CommentsStrategy = field(default=CommentsStrategy.STABLE)
     convert_to_snake_case: bool = True
     async_client: bool = True
     files_to_include: List[str] = field(default_factory=list)
@@ -62,6 +68,15 @@ class ClientSettings(BaseSettings):
             raise TypeError("__init__ missing 1 required argument: 'queries_path'")
         super().__post_init__()
 
+        try:
+            self.include_comments = CommentsStrategy(self.include_comments)
+        except ValueError as exc:
+            valid_options = ", ".join(strategy.value for strategy in CommentsStrategy)
+            raise InvalidConfiguration(
+                f"'{self.include_comments}' is not a valid choice. "
+                f"Valid options are: {valid_options}"
+            ) from exc
+
         self._set_default_base_client_data()
 
         for name, data in self.scalars.items():
@@ -98,11 +113,6 @@ class ClientSettings(BaseSettings):
 
     @property
     def used_settings_message(self):
-        comments_msg = (
-            "Including comments."
-            if self.include_comments
-            else "Not including comments."
-        )
         snake_case_msg = (
             "Converting fields and arguments name to snake case."
             if self.convert_to_snake_case
@@ -138,7 +148,7 @@ class ClientSettings(BaseSettings):
             Generating enums into '{self.enums_module_name}.py'.
             Generating inputs into '{self.input_types_module_name}.py'.
             Generating fragments into '{self.fragments_module_name}.py'.
-            {comments_msg}
+            Comments type: {self.include_comments.value}
             {snake_case_msg}
             {async_client_msg}
             {files_to_include_msg}


Test patch:
diff --git a/tests/client_generators/test_comments.py b/tests/client_generators/test_comments.py
new file mode 100644
index 0000000..c0b243f
--- /dev/null
+++ b/tests/client_generators/test_comments.py
@@ -0,0 +1,66 @@
+from datetime import datetime
+
+import pytest
+from freezegun import freeze_time
+
+from ariadne_codegen.client_generators.comments import (
+    get_comment,
+    get_stable_comment,
+    get_timestamp_comment,
+)
+from ariadne_codegen.client_generators.constants import (
+    COMMENT_DATETIME_FORMAT,
+    SOURCE_COMMENT,
+    STABLE_COMMENT,
+    TIMESTAMP_COMMENT,
+)
+from ariadne_codegen.settings import CommentsStrategy
+
+
+@pytest.mark.parametrize("source", [None, "source.graphql"])
+def test_get_comment_calls_correct_method_for_stable_strategy(source):
+    assert get_comment(
+        strategy=CommentsStrategy.STABLE, source=source
+    ) == get_stable_comment(source=source)
+
+
+@pytest.mark.parametrize("source", [None, "source.graphql"])
+@freeze_time("24.12.2023 18:00")
+def test_get_comment_calls_correct_method_for_timestamp_strategy(source):
+    assert get_comment(
+        strategy=CommentsStrategy.TIMESTAMP, source=source
+    ) == get_timestamp_comment(source=source)
+
+
+@pytest.mark.parametrize("source", [None, "source.graphql"])
+def test_get_comment_returns_empty_string_for_none_strategy(source):
+    assert get_comment(strategy=CommentsStrategy.NONE, source=source) == ""
+
+
+@freeze_time("24.12.2023 18:00")
+def test_get_timestamp_comment_returns_string_without_source():
+    assert get_timestamp_comment() == TIMESTAMP_COMMENT.format(
+        datetime.now().strftime(COMMENT_DATETIME_FORMAT)
+    )
+
+
+@freeze_time("24.12.2023 18:00")
+def test_get_timestamp_comment_returns_string_witho_source():
+    result = get_timestamp_comment("source.graphql")
+
+    assert (
+        TIMESTAMP_COMMENT.format(datetime.now().strftime(COMMENT_DATETIME_FORMAT))
+        in result
+    )
+    assert SOURCE_COMMENT.format("source.graphql") in result
+
+
+def test_get_stable_comment_returns_string_without_source():
+    assert get_stable_comment() == STABLE_COMMENT
+
+
+def test_get_stable_cooment_returns_string_with_source():
+    result = get_stable_comment(source="source.graphql")
+
+    assert STABLE_COMMENT in result
+    assert SOURCE_COMMENT.format("source.graphql") in result
diff --git a/tests/client_generators/test_package_generator.py b/tests/client_generators/test_package_generator.py
index fd22341..427539a 100644
--- a/tests/client_generators/test_package_generator.py
+++ b/tests/client_generators/test_package_generator.py
@@ -1,5 +1,4 @@
 import ast
-from datetime import datetime
 from textwrap import dedent
 
 import pytest
@@ -7,13 +6,14 @@ from freezegun import freeze_time
 from graphql import GraphQLSchema, build_ast_schema, parse
 
 from ariadne_codegen.client_generators.constants import (
-    COMMENT_DATETIME_FORMAT,
     SOURCE_COMMENT,
+    STABLE_COMMENT,
     TIMESTAMP_COMMENT,
 )
 from ariadne_codegen.client_generators.package import PackageGenerator
 from ariadne_codegen.client_generators.scalars import ScalarData
 from ariadne_codegen.exceptions import ParsingError
+from ariadne_codegen.settings import CommentsStrategy
 
 from ..utils import get_class_def
 
@@ -347,14 +347,24 @@ def test_generate_creates_client_file_with_gql_lambda_definition(tmp_path):
         assert expected_gql_def in client_content
 
 
-@freeze_time("01.12.2022 12:00")
-def test_generate_adds_comment_with_timestamp_to_generated_files(tmp_path):
+@pytest.mark.parametrize(
+    "strategy, expected_comment",
+    [
+        (CommentsStrategy.STABLE, STABLE_COMMENT),
+        (
+            CommentsStrategy.TIMESTAMP,
+            TIMESTAMP_COMMENT.format("2022-01-01 12:00"),
+        ),
+    ],
+)
+@freeze_time("01.01.2022 12:00")
+def test_generate_adds_comment_to_generated_files(tmp_path, strategy, expected_comment):
     package_name = "test_graphql_client"
     generator = PackageGenerator(
         package_name,
         tmp_path.as_posix(),
         build_ast_schema(parse(SCHEMA_STR)),
-        include_comments=True,
+        comments_strategy=strategy,
     )
     query_str = """
     query CustomQuery($val: CustomEnum!) {
@@ -374,9 +384,7 @@ def test_generate_adds_comment_with_timestamp_to_generated_files(tmp_path):
         f"{generator.input_types_module_name}.py",
         "custom_query.py",
     ]
-    expected_comment = TIMESTAMP_COMMENT.format(
-        datetime.now().strftime(COMMENT_DATETIME_FORMAT)
-    )
+
     for file_name in files_names:
         file_path = package_path / file_name
         with file_path.open() as file_:
@@ -384,7 +392,12 @@ def test_generate_adds_comment_with_timestamp_to_generated_files(tmp_path):
             assert expected_comment in content
 
 
-def test_generate_adds_comment_with_correct_source_to_generated_files(tmp_path):
+@pytest.mark.parametrize(
+    "strategy", [CommentsStrategy.STABLE, CommentsStrategy.TIMESTAMP]
+)
+def test_generate_adds_comment_with_correct_source_to_generated_files(
+    tmp_path, strategy
+):
     package_name = "test_graphql_client"
     schema_source = "schema_source.graphql"
     queries_source = "queries_source.graphql"
@@ -392,7 +405,7 @@ def test_generate_adds_comment_with_correct_source_to_generated_files(tmp_path):
         package_name,
         tmp_path.as_posix(),
         build_ast_schema(parse(SCHEMA_STR)),
-        include_comments=True,
+        comments_strategy=strategy,
         schema_source=schema_source,
         queries_source=queries_source,
     )
@@ -425,6 +438,37 @@ def test_generate_adds_comment_with_correct_source_to_generated_files(tmp_path):
         assert expected_queries_source_comment in content
 
 
+@pytest.mark.parametrize(
+    "strategy",
+    [CommentsStrategy.NONE, CommentsStrategy.STABLE, CommentsStrategy.TIMESTAMP],
+)
+def test_generate_calls_get_file_comment_hook_for_every_file(
+    tmp_path, strategy, mocked_plugin_manager
+):
+    package_name = "test_graphql_client"
+    generator = PackageGenerator(
+        package_name,
+        tmp_path.as_posix(),
+        build_ast_schema(parse(SCHEMA_STR)),
+        comments_strategy=strategy,
+        plugin_manager=mocked_plugin_manager,
+    )
+    query_str = """
+    query CustomQuery($val: CustomEnum!) {
+        query3(val: $val) {
+            id
+        }
+    }
+    """
+    generator.add_operation(parse(query_str).definitions[0])
+
+    generator.generate()
+
+    assert len(list(tmp_path.joinpath(package_name).iterdir())) == len(
+        mocked_plugin_manager.get_file_comment.mock_calls
+    )
+
+
 def test_generate_creates_result_types_from_operation_that_uses_fragment(tmp_path):
     package_name = "test_graphql_client"
     query_str = """
diff --git a/tests/conftest.py b/tests/conftest.py
index 0f2a91e..3d9731c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -16,4 +16,5 @@ def mocked_plugin_manager(mocker):
     manager.generate_init_code.side_effect = no_effect_method
     manager.process_name.side_effect = no_effect_method
     manager.generate_fragments_module.side_effect = no_effect_method
+    manager.get_file_comment.side_effect = no_effect_method
     return manager
diff --git a/tests/main/clients/custom_base_client/pyproject.toml b/tests/main/clients/custom_base_client/pyproject.toml
index 94d7134..d208b12 100644
--- a/tests/main/clients/custom_base_client/pyproject.toml
+++ b/tests/main/clients/custom_base_client/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "custom_base_client"
 
 base_client_name = "CustomAsyncBaseClient"
diff --git a/tests/main/clients/custom_config_file/config.toml b/tests/main/clients/custom_config_file/config.toml
index 0db3f02..2aaa0fd 100644
--- a/tests/main/clients/custom_config_file/config.toml
+++ b/tests/main/clients/custom_config_file/config.toml
@@ -2,4 +2,4 @@
 queries_path = "queries.graphql"
 schema_path = "schema.graphql"
 target_package_name = "custom_config_client"
-include_comments = false
+include_comments = "none"
diff --git a/tests/main/clients/custom_files_names/pyproject.toml b/tests/main/clients/custom_files_names/pyproject.toml
index 7129ada..5734a28 100644
--- a/tests/main/clients/custom_files_names/pyproject.toml
+++ b/tests/main/clients/custom_files_names/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "custom_names_client"
 
 client_file_name = "custom_client"
diff --git a/tests/main/clients/custom_scalars/pyproject.toml b/tests/main/clients/custom_scalars/pyproject.toml
index 90acd05..f31dc14 100644
--- a/tests/main/clients/custom_scalars/pyproject.toml
+++ b/tests/main/clients/custom_scalars/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "custom_scalars_client"
 files_to_include = ["custom_scalars.py"]
 
diff --git a/tests/main/clients/example/pyproject.toml b/tests/main/clients/example/pyproject.toml
index cbc0a60..7cfc9d3 100644
--- a/tests/main/clients/example/pyproject.toml
+++ b/tests/main/clients/example/pyproject.toml
@@ -1,5 +1,5 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "example_client"
diff --git a/tests/main/clients/extended_models/pyproject.toml b/tests/main/clients/extended_models/pyproject.toml
index fcf7ed7..32095e1 100644
--- a/tests/main/clients/extended_models/pyproject.toml
+++ b/tests/main/clients/extended_models/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "client_with_extended_models"
 files_to_include = ["common_mixins.py", "mixins_a.py", "mixins_b.py"]
diff --git a/tests/main/clients/fragments_on_abstract_types/pyproject.toml b/tests/main/clients/fragments_on_abstract_types/pyproject.toml
index efd284c..007bf76 100644
--- a/tests/main/clients/fragments_on_abstract_types/pyproject.toml
+++ b/tests/main/clients/fragments_on_abstract_types/pyproject.toml
@@ -1,5 +1,5 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "fragments_on_abstract_types_client"
diff --git a/tests/main/clients/inline_fragments/pyproject.toml b/tests/main/clients/inline_fragments/pyproject.toml
index 9de607c..cdce850 100644
--- a/tests/main/clients/inline_fragments/pyproject.toml
+++ b/tests/main/clients/inline_fragments/pyproject.toml
@@ -1,5 +1,5 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "inline_fragments_client"
diff --git a/tests/main/clients/invalid_pyprojects/no_files.toml b/tests/main/clients/invalid_pyprojects/no_files.toml
index cbc0a60..7cfc9d3 100644
--- a/tests/main/clients/invalid_pyprojects/no_files.toml
+++ b/tests/main/clients/invalid_pyprojects/no_files.toml
@@ -1,5 +1,5 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "example_client"
diff --git a/tests/main/clients/invalid_pyprojects/no_section.toml b/tests/main/clients/invalid_pyprojects/no_section.toml
index 4f29abf..f75ed70 100644
--- a/tests/main/clients/invalid_pyprojects/no_section.toml
+++ b/tests/main/clients/invalid_pyprojects/no_section.toml
@@ -1,5 +1,5 @@
 [other-section]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "example_client"
diff --git a/tests/main/clients/invalid_pyprojects/operation_not_valid_for_schema.toml b/tests/main/clients/invalid_pyprojects/operation_not_valid_for_schema.toml
index b73465e..212de13 100644
--- a/tests/main/clients/invalid_pyprojects/operation_not_valid_for_schema.toml
+++ b/tests/main/clients/invalid_pyprojects/operation_not_valid_for_schema.toml
@@ -1,5 +1,5 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "not_valid_query.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "example_client"
diff --git a/tests/main/clients/multiple_fragments/pyproject.toml b/tests/main/clients/multiple_fragments/pyproject.toml
index bb1d250..81e440d 100644
--- a/tests/main/clients/multiple_fragments/pyproject.toml
+++ b/tests/main/clients/multiple_fragments/pyproject.toml
@@ -1,5 +1,5 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "multiple_fragments_client"
diff --git a/tests/main/clients/remote_schema/pyproject.toml b/tests/main/clients/remote_schema/pyproject.toml
index da20edd..659b308 100644
--- a/tests/main/clients/remote_schema/pyproject.toml
+++ b/tests/main/clients/remote_schema/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.ariadne-codegen]
 queries_path = "queries.graphql"
 target_package_name = "remote_schema_client"
-include_comments = false
+include_comments = "none"
 
 remote_schema_url = "http://test/graphql/"
 remote_schema_headers = { "header1" = "value1", "header2" = "value2" }
diff --git a/tests/main/clients/shorter_results/pyproject.toml b/tests/main/clients/shorter_results/pyproject.toml
index d6e01e5..a9e3586 100644
--- a/tests/main/clients/shorter_results/pyproject.toml
+++ b/tests/main/clients/shorter_results/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.ariadne-codegen]
 schema_path = "schema.graphql"
 queries_path = "queries.graphql"
-include_comments = false
+include_comments = "none"
 target_package_name = "shorter_results"
 files_to_include = ["custom_scalars.py"]
 fragments_module_name = "shorter_results_fragments"
diff --git a/tests/plugins/test_manager.py b/tests/plugins/test_manager.py
index fd211aa..d86e99d 100644
--- a/tests/plugins/test_manager.py
+++ b/tests/plugins/test_manager.py
@@ -334,3 +334,15 @@ def test_process_schema_updates_plugins_schema_field():
     assert dumb_plugin1.schema is not org_schema
     assert dump_plugin2.schema is not org_schema
     assert schema_plugin.schema is not org_schema
+
+
+def test_get_file_comment_calls_plugins_get_file_comment(
+    plugin_manager_with_mocked_plugins,
+):
+    plugin_manager_with_mocked_plugins.get_file_comment(
+        "comment", code="class Xyz:\n    pass", source="queries.graphql"
+    )
+
+    plugin1, plugin2 = plugin_manager_with_mocked_plugins.plugins
+    assert plugin1.get_file_comment.called
+    assert plugin2.get_file_comment.called
diff --git a/tests/test_config.py b/tests/test_config.py
index e4da400..b728af6 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -11,7 +11,11 @@ from ariadne_codegen.config import (
     get_section,
 )
 from ariadne_codegen.exceptions import ConfigFileNotFound, MissingConfiguration
-from ariadne_codegen.settings import ClientSettings, GraphQLSchemaSettings
+from ariadne_codegen.settings import (
+    ClientSettings,
+    CommentsStrategy,
+    GraphQLSchemaSettings,
+)
 
 
 @pytest.fixture
@@ -167,6 +171,33 @@ def test_get_client_settings_returns_client_settings_object_ignoring_extra_field
     assert settings.queries_path == queries_path.as_posix()
 
 
+@pytest.mark.parametrize(
+    "include_comments, expected_result",
+    [(True, CommentsStrategy.TIMESTAMP), (False, CommentsStrategy.NONE)],
+)
+def test_get_client_settings_raises_deprecation_warning_for_boolean_include_comments(
+    tmp_path, include_comments, expected_result
+):
+    schema_path = tmp_path / "schema.graphql"
+    schema_path.touch()
+    queries_path = tmp_path / "queries.graphql"
+    queries_path.touch()
+    config_dict = {
+        "tool": {
+            "ariadne-codegen": {
+                "schema_path": schema_path.as_posix(),
+                "queries_path": queries_path.as_posix(),
+                "include_comments": include_comments,
+            }
+        }
+    }
+
+    with pytest.deprecated_call():
+        settings = get_client_settings(config_dict)
+
+    assert settings.include_comments == expected_result
+
+
 def test_get_section_returns_correct_dictionary_from_tool_key():
     section_dict = {"schema_path": ".", "queries_path": "."}
     config_dict = {"tool": {"ariadne-codegen": section_dict}}