diff --git a/tests/api/conftest.py b/tests/api/conftest.py
index c15b58fc6ee34fe384a2e0135272420eaff7eaf5..ce4f0d78fb444982a5c87a93b492bb52fcb120cc 100644
--- a/tests/api/conftest.py
+++ b/tests/api/conftest.py
@@ -20,8 +20,7 @@ from collections.abc import Generator
 
 import pytest
 import pytest_asyncio
-from blacksheep import Application
-from blacksheep import Request
+from blacksheep import Application, Request
 from blacksheep.testing import TestClient
 
 from vigenere_api.api import application
diff --git a/tests/api/helpers/__init__.py b/tests/api/helpers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf8de0773dc8411127d6bbaa8262a9194c6fc4c0
--- /dev/null
+++ b/tests/api/helpers/__init__.py
@@ -0,0 +1,15 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/tests/api/helpers/test_controller.py b/tests/api/helpers/test_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..0cb7857275817abc4279381b0dfa96b66ee84e62
--- /dev/null
+++ b/tests/api/helpers/test_controller.py
@@ -0,0 +1,93 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+import pytest
+from blacksheep.server.controllers import APIController
+
+from vigenere_api.api.helpers.controller import _camel_to_kebab, Controller
+from vigenere_api.api.helpers.errors import NameTypeError
+
+
+class CamelCaseToKebabCaseSuite:
+    @staticmethod
+    def test_camelcase() -> None:
+        converted = _camel_to_kebab("Camel")
+        assert converted == "camel"
+
+        converted = _camel_to_kebab("CamelCase")
+        assert converted == "camel-case"
+
+    @staticmethod
+    def test_kebab() -> None:
+        converted = _camel_to_kebab("camel-case")
+        assert converted == "camel-case"
+
+    @staticmethod
+    def test_snake_case() -> None:
+        converted = _camel_to_kebab("tedede_test")
+        assert converted == "tedede_test"
+
+    @staticmethod
+    @pytest.mark.raises(excpetion=NameTypeError)
+    def test_bad_type_name() -> None:
+        _ignored = _camel_to_kebab(b"tedede")
+
+
+class ControllerSuite:
+    @staticmethod
+    def test_instantiate() -> None:
+        controller = Controller()
+        assert controller is not None
+        assert isinstance(controller, Controller)
+        assert isinstance(controller, APIController)
+
+    @staticmethod
+    def test_class_name() -> None:
+        assert Controller.__name__ == "Controller"
+
+        assert Controller.class_name() == ""
+
+    @staticmethod
+    def test_inheritance_with_basic_name() -> None:
+        class Test(Controller):
+            pass
+
+        test = Test()
+
+        assert isinstance(test, Controller)
+        assert isinstance(test, APIController)
+        assert test.class_name() == "test"
+
+    @staticmethod
+    def test_inheritance_with_controller_name() -> None:
+        class TestController(Controller):
+            pass
+
+        test = TestController()
+
+        assert isinstance(test, Controller)
+        assert isinstance(test, APIController)
+        assert test.class_name() == "test"
+
+    @staticmethod
+    def test_inheritance_with_complex_controller_name() -> None:
+        class ComplexTestController(Controller):
+            pass
+
+        test = ComplexTestController()
+
+        assert isinstance(test, Controller)
+        assert isinstance(test, APIController)
+        assert test.class_name() == "complex-test"
diff --git a/tests/api/test_open_api_handler.py b/tests/api/helpers/test_open_api_handler.py
similarity index 64%
rename from tests/api/test_open_api_handler.py
rename to tests/api/helpers/test_open_api_handler.py
index f86b2de80a8c04fb07ef111729c65eedc32bebb1..d9437ce6ccf37d630125822cc18077d586d2555f 100644
--- a/tests/api/test_open_api_handler.py
+++ b/tests/api/helpers/test_open_api_handler.py
@@ -14,54 +14,38 @@
 #  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
 # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
+import pytest
+
 from openapidocs.common import Format
-from openapidocs.v3 import Contact
-from openapidocs.v3 import ExternalDocs
-from openapidocs.v3 import Info
-from openapidocs.v3 import License
-from openapidocs.v3 import Tag
+from openapidocs.v3 import ExternalDocs, Tag
 from vigenere_api.api import application
-from vigenere_api.api.utils import VigenereAPIOpenAPIHandler
+from vigenere_api.api.helpers import VigenereAPIOpenAPIHandler
+from vigenere_api.api.helpers.errors import VersionTypeError
+from vigenere_api.version import Version
 
 
 def basic_test() -> None:
-    docs = VigenereAPIOpenAPIHandler(
-        info=Info(
-            title="Vigenere-API",
-            version="1.0.0",
-            description="""
-            An API to use cipher, decipher and decrypt method with the Vigenere algorithm.
-            The Caesar algorithm is provided for the cipher method and decipher method.
+    docs = VigenereAPIOpenAPIHandler(Version(major=1, minor=0, patch=0))
 
-            It's a JSON-RPC API.
-            Powered by BlackSheep framework: https://www.neoteroi.dev/blacksheep/
-            """,
-            contact=Contact(name="Axel DAVID", email="axel.david@etu.univ-amu.fr"),
-            license=License(name="GPL-3.0", url="TEST/LICENSE.md"),
-        ),
-        ui_path="/api/v1",
-        preferred_format=Format.YAML,
-    )
+    assert docs.version == Version(major=1, minor=0, patch=0)
 
     info = docs.info
     assert info.title == "Vigenere-API"
     assert info.version == "1.0.0"
-    assert info.description == (
-        "\n"
-        "            An API to use cipher, decipher and decrypt method with the "
-        "Vigenere algorithm.\n"
-        "            The Caesar algorithm is provided for the cipher method and "
-        "decipher method.\n"
-        "\n"
-        "            It's a JSON-RPC API.\n"
-        "            Powered by BlackSheep framework: "
-        "https://www.neoteroi.dev/blacksheep/\n"
-        "            "
+    assert (
+        info.description
+        == """
+        An API to use cipher, decipher and decrypt method with the Vigenere algorithm.
+        The Caesar algorithm is provided for the cipher method and decipher method.
+
+        It's a JSON-RPC API.
+        Powered by BlackSheep framework: https://www.neoteroi.dev/blacksheep/
+        """
     )
     assert info.contact.name == "Axel DAVID"
     assert info.contact.email == "axel.david@etu.univ-amu.fr"
     assert info.license.name == "GPL-3.0"
-    assert info.license.url == "TEST/LICENSE.md"
+    assert info.license.url == "http://localhost:8080/LICENSE.md"
 
     assert docs.ui_providers[0].ui_path == "/api/v1"
 
@@ -88,3 +72,8 @@ def basic_test() -> None:
             ),
         ),
     ]
+
+
+@pytest.mark.raises(exception=VersionTypeError)
+def test_bad_type_version() -> None:
+    _ignored = VigenereAPIOpenAPIHandler(b"toto")
diff --git a/tests/api/helpers/test_open_api_route_filter.py b/tests/api/helpers/test_open_api_route_filter.py
new file mode 100644
index 0000000000000000000000000000000000000000..398875c96f9eb2c678a0addcc1a39629e766d1d6
--- /dev/null
+++ b/tests/api/helpers/test_open_api_route_filter.py
@@ -0,0 +1,75 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+from inspect import signature
+
+import pytest
+from blacksheep import Route
+
+from vigenere_api.api.helpers.errors import (
+    ExcludedPathsTypeError,
+    ExcludedPathTypeError,
+    PathTypeError,
+)
+from vigenere_api.api.helpers.open_api_route_filter import get_route_filter
+
+
+def test_get_filter() -> None:
+    excluded = []
+    filter = get_route_filter(excluded)
+
+    assert callable(filter)
+    s = signature(filter)
+
+    assert s.return_annotation == bool
+
+    parameters_type = list(s.parameters.values())
+
+    assert len(parameters_type) == 2
+    assert parameters_type[0].annotation == str
+    assert parameters_type[1].annotation == Route
+
+
+@pytest.mark.raises(exception=ExcludedPathsTypeError)
+def test_bad_type_excluded() -> None:
+    excluded = 10
+    _ignored = get_route_filter(excluded)
+
+
+@pytest.mark.raises(exception=ExcludedPathTypeError)
+def test_bad_type_path_in_excluded() -> None:
+    excluded = [b"test"]
+    _ignored = get_route_filter(excluded)
+
+
+@pytest.mark.raises(exception=PathTypeError)
+def test_filter_bad_type_path() -> None:
+    route_filter = get_route_filter(["/api"])
+    _ignored = route_filter(b"/http", Route("http", {}))
+
+
+def test_filter_bad_type_route() -> None:
+    route_filter = get_route_filter(["/api"])
+    assert route_filter("/http", {})
+
+
+def test_filter_route_excluded() -> None:
+    route_filter = get_route_filter(["/api"])
+    assert not route_filter("/api", Route("http", {}))
+
+
+def test_filter_route_not_excluded() -> None:
+    route_filter = get_route_filter(["/api"])
+    assert route_filter("/http", Route("http", {}))
diff --git a/tests/api/test_index.py b/tests/api/test_index.py
index 90ccf12180aa6885904dc9969ecd738f900db872..74f4522cbd7fe5a1921724e455a46dc9b723a0f7 100644
--- a/tests/api/test_index.py
+++ b/tests/api/test_index.py
@@ -31,3 +31,16 @@ async def test_get_index(test_client: TestClient) -> None:
     first_header = response.headers.values[0]
     assert first_header[0] == b"Location"
     assert first_header[1] == b"/api/v1"
+
+
+@pytest.mark.asyncio()
+async def test_bad_path(test_client: TestClient) -> None:
+    response = await test_client.get("/zzzzzzzzzz")
+
+    assert response is not None
+
+    assert response.status == 200
+    assert response.content is not None
+    assert response.reason.upper() == "OK"
+
+    assert await response.text() == "OOPS! Nothing was found here!"
diff --git a/tests/api/v1/docs/test_caesar.py b/tests/api/v1/docs/test_caesar.py
index b99670f46df7d768dfa925cb96e59d98f6c1ad72..da998664ffbffc7c74b9765501c7a6f23cc8ff1d 100644
--- a/tests/api/v1/docs/test_caesar.py
+++ b/tests/api/v1/docs/test_caesar.py
@@ -16,15 +16,19 @@
 
 from http import HTTPStatus
 
-from blacksheep.server.openapi.common import ContentInfo
-from blacksheep.server.openapi.common import RequestBodyInfo
-from blacksheep.server.openapi.common import ResponseExample
-from blacksheep.server.openapi.common import ResponseInfo
+from blacksheep.server.openapi.common import (
+    ContentInfo,
+    RequestBodyInfo,
+    ResponseExample,
+    ResponseInfo,
+)
 
-from vigenere_api.api.v1.controllers.docs.caesar import CAESAR_DATA1
-from vigenere_api.api.v1.controllers.docs.caesar import CAESAR_DATA2
-from vigenere_api.api.v1.controllers.docs.caesar import CaesarControllerDocs
-from vigenere_api.api.v1.controllers.docs.caesar import CaesarOperation
+from vigenere_api.api.v1.controllers.caesar.docs import (
+    CAESAR_DATA1,
+    CAESAR_DATA2,
+    CaesarControllerDocs,
+    CaesarOperation,
+)
 from vigenere_api.models import CaesarData
 
 
diff --git a/tests/api/v1/test_api.py b/tests/api/v1/test_api.py
index 151c9561bc8d4210a110306a1077adccf12b8954..686fcf4dafd6dcde578bfaa93877a4677634d1a4 100644
--- a/tests/api/v1/test_api.py
+++ b/tests/api/v1/test_api.py
@@ -25,10 +25,6 @@ async def test_get_api_docs(test_client: TestClient) -> None:
 
     assert response.status == 200
     assert response.content is not None
-    assert (
-        response.content.body
-        == b'<!DOCTYPE html>\n<html>\n<head>\n    <title>Vigenere-API</title>\n    <link rel="icon" href="/favicon.png"/>\n    <link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@3.30.0/swagger-ui.css">\n</head>\n<body>\n    <div id="swagger-ui"></div>\n    <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@3.30.0/swagger-ui-bundle.js"></script>\n    <script>\n    const ui = SwaggerUIBundle({\n        url: \'/openapi.yaml\',\n        oauth2RedirectUrl: window.location.origin + \'/docs/oauth2-redirect\',\n        dom_id: \'#swagger-ui\',\n        presets: [\n            SwaggerUIBundle.presets.apis,\n            SwaggerUIBundle.SwaggerUIStandalonePreset\n        ],\n        layout: "BaseLayout",\n        deepLinking: true,\n        showExtensions: true,\n        showCommonExtensions: true\n    })\n    </script>\n</body>\n</html>\n'
-    )
     assert response.reason.upper() == "OK"
 
 
@@ -40,8 +36,4 @@ async def test_get_api_redocs(test_client: TestClient) -> None:
 
     assert response.status == 200
     assert response.content is not None
-    assert (
-        response.content.body
-        == b'<!DOCTYPE html>\n<html>\n  <head>\n    <title>Vigenere-API</title>\n    <meta charset="utf-8"/>\n    <meta name="viewport" content="width=device-width, initial-scale=1">\n    <link rel="icon" href="/favicon.png"/>\n    <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">\n    <style>\n      body {\n        margin: 0;\n        padding: 0;\n      }\n    </style>\n  </head>\n  <body>\n    <redoc spec-url="/openapi.yaml"></redoc>\n    <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>\n  </body>\n</html>\n'
-    )
     assert response.reason.upper() == "OK"
diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf8de0773dc8411127d6bbaa8262a9194c6fc4c0
--- /dev/null
+++ b/tests/helpers/__init__.py
@@ -0,0 +1,15 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/tests/helpers/test_errors.py b/tests/helpers/test_errors.py
new file mode 100644
index 0000000000000000000000000000000000000000..6fc5348e214eee69c180d2b1ccd592608e5d66bd
--- /dev/null
+++ b/tests/helpers/test_errors.py
@@ -0,0 +1,26 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+import pytest
+
+from vigenere_api.helpers import VigenereAPITypeError
+
+
+@pytest.mark.raises(
+    exception=VigenereAPITypeError, message="The text is 'str'. Please give a bytes."
+)
+def test_throws_VigenereAPITypeError() -> None:
+    raise VigenereAPITypeError("test", "text", "a bytes")
diff --git a/tests/helpers/test_model.py b/tests/helpers/test_model.py
new file mode 100644
index 0000000000000000000000000000000000000000..aacd8bd9b44f98024b46fe671269a73ff4c7608c
--- /dev/null
+++ b/tests/helpers/test_model.py
@@ -0,0 +1,44 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+from vigenere_api.helpers import Model
+
+
+def test_instantiate() -> None:
+    obj = Model()
+
+    assert obj is not None
+
+
+class InheritanceSuite:
+    @staticmethod
+    def test_basic() -> None:
+        class Test(Model):
+            pass
+
+        obj = Test()
+
+        assert obj is not None
+        assert obj == Model()
+
+    @staticmethod
+    def test_with_new_field() -> None:
+        class Test(Model):
+            test: str
+
+        obj = Test(test="TEST")
+
+        assert obj is not None
+        assert obj.test == "TEST"
diff --git a/tests/integration/api/test_index.py b/tests/integration/api/test_index.py
index 6ffebf9d5dfcb074a2db8dc3eed00207a98f182f..3a2f38ddb4fe89231aca5ea32cf2a1ab1676f478 100644
--- a/tests/integration/api/test_index.py
+++ b/tests/integration/api/test_index.py
@@ -34,3 +34,20 @@ def test_get_index(server: str) -> None:
 
     assert response.next is not None
     assert response.next.path_url == "/api/v1"
+
+
+@pytest.mark.integration_test()
+def test_bad_path(server: str) -> None:
+    response = requests.get(
+        url=f"{server}/zzzzzzzzzz",
+        timeout=1,
+        allow_redirects=False,
+    )
+
+    assert response is not None
+
+    assert response.status == 200
+    assert response.content is not None
+    assert response.reason.upper() == "OK"
+
+    assert response.text == "OOPS! Nothing was found here!"
diff --git a/tests/models/test_caesar.py b/tests/models/test_caesar.py
index 637311ee55323d7b191c707116ffa11525bb5915..1ae2967ded88306f42225637395d415522f69805 100644
--- a/tests/models/test_caesar.py
+++ b/tests/models/test_caesar.py
@@ -20,8 +20,7 @@ import pytest
 from pydantic import ValidationError
 
 from vigenere_api.models.caesar import CaesarData
-from vigenere_api.models.errors import AlgorithmKeyTypeError
-from vigenere_api.models.errors import AlgorithmTextTypeError
+from vigenere_api.models.errors import AlgorithmKeyTypeError, AlgorithmTextTypeError
 
 
 class CtorSuite:
diff --git a/tests/models/test_helper.py b/tests/models/test_helper.py
index bc48b2c99d36fdedd90eb1f2ead605d43d2f6129..d1f30f7de7b4733a305a4a74fdb819eab2917e85 100644
--- a/tests/models/test_helper.py
+++ b/tests/models/test_helper.py
@@ -18,13 +18,15 @@
 
 import pytest
 
-from vigenere_api.models.helper import move_char
-from vigenere_api.models.helper.errors import HelperBadCharValueError
-from vigenere_api.models.helper.errors import HelperBadFirstLetterValueError
-from vigenere_api.models.helper.errors import HelperBadLengthCharValueError
-from vigenere_api.models.helper.errors import HelperCharTypeError
-from vigenere_api.models.helper.errors import HelperFirstLetterTypeError
-from vigenere_api.models.helper.errors import HelperKeyTypeError
+from vigenere_api.models.helpers import move_char
+from vigenere_api.models.helpers.errors import (
+    HelperBadCharValueError,
+    HelperBadFirstLetterValueError,
+    HelperBadLengthCharValueError,
+    HelperCharTypeError,
+    HelperFirstLetterTypeError,
+    HelperKeyTypeError,
+)
 
 
 def test_move_lower_letter() -> None:
diff --git a/tests/version/__init__.py b/tests/version/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf8de0773dc8411127d6bbaa8262a9194c6fc4c0
--- /dev/null
+++ b/tests/version/__init__.py
@@ -0,0 +1,15 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/tests/version/test_version.py b/tests/version/test_version.py
new file mode 100644
index 0000000000000000000000000000000000000000..35bafec88b3ce74ed5bf41fe43eabaf1dfd2b7a7
--- /dev/null
+++ b/tests/version/test_version.py
@@ -0,0 +1,129 @@
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#  Vigenere-API                                                                        +
+#  Copyright (C) 2023 Axel DAVID                                                       +
+#                                                                                      +
+#  This program is free software: you can redistribute it and/or modify it under       +
+#  the terms of the GNU General Public License as published by the Free Software       +
+#  Foundation, either version 3 of the License, or (at your option) any later version. +
+#                                                                                      +
+#  This program is distributed in the hope that it will be useful, but WITHOUT ANY     +
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR       +
+#  A PARTICULAR PURPOSE. See the GNU General Public License for more details.          +
+#                                                                                      +
+#  You should have received a copy of the GNU General Public License along with        +
+#  this program.  If not, see <https://www.gnu.org/licenses/>.                         +
+# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+import pytest
+from pydantic import ValidationError
+
+from vigenere_api.version import get_version, Version
+
+
+class CtorSuite:
+    @staticmethod
+    def test_ctor() -> None:
+        v = Version(major=1, minor=0, patch=0)
+
+        assert v.major == 1
+        assert v.minor == 0
+        assert v.patch == 0
+
+    class MissingFieldsSuite:
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="major")
+        def test_without_major() -> None:
+            _ignored = Version(minor=0, patch=0)
+
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="minor")
+        def test_without_minor() -> None:
+            _ignored = Version(major=0, patch=0)
+
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="patch")
+        def test_without_patch() -> None:
+            _ignored = Version(minor=0, major=0)
+
+        @staticmethod
+        def test_with_only_patch() -> None:
+            with pytest.raises(ValidationError) as error:
+                _ignored = Version(patch=0)
+
+            error_msg = str(error)
+            assert "minor" in error_msg
+            assert "major" in error_msg
+
+        @staticmethod
+        def test_with_only_minor() -> None:
+            with pytest.raises(ValidationError) as error:
+                _ignored = Version(minor=0)
+
+            error_msg = str(error)
+            assert "patch" in error_msg
+            assert "major" in error_msg
+
+        @staticmethod
+        def test_with_only_manor() -> None:
+            with pytest.raises(ValidationError) as error:
+                _ignored = Version(major=0)
+
+            error_msg = str(error)
+            assert "minor" in error_msg
+            assert "patch" in error_msg
+
+        @staticmethod
+        def test_with_nothing() -> None:
+            with pytest.raises(ValidationError) as error:
+                _ignored = Version()
+
+            error = error.value.args[0]
+            assert "major" in error[0]._loc
+            assert "minor" in error[1]._loc
+            assert "patch" in error[2]._loc
+
+    class BadTypeSuite:
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="major")
+        def test_major() -> None:
+            _ignored = Version(major=1.0, minor=0, patch=0)
+
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="minor")
+        def test_minor() -> None:
+            _ignored = Version(major=1, minor=0.0, patch=0)
+
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="patch")
+        def test_patch() -> None:
+            _ignored = Version(major=1, minor=0, patch=0.0)
+
+    class BadValueSuite:
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="major")
+        def test_major() -> None:
+            _ignored = Version(major=-10, minor=0, patch=0)
+
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="minor")
+        def test_minor() -> None:
+            _ignored = Version(major=1, minor=-8, patch=0)
+
+        @staticmethod
+        @pytest.mark.raises(exception=ValidationError, message="patch")
+        def test_patch() -> None:
+            _ignored = Version(major=1, minor=0, patch=-9)
+
+
+class OperationSuite:
+    @staticmethod
+    def test_str() -> None:
+        v = Version(major=1, minor=0, patch=0)
+
+        assert str(v) == "1.0.0"
+
+
+def test_get_version() -> None:
+    v = get_version()
+
+    assert isinstance(v, Version)
+    assert v == Version(major=1, minor=0, patch=0)