feat(repository): Add 1st domain context (core) and entity (Repository)

Description

Abstract

Add the core domain context with the Repository entity

Motivation

Domain Driven Developement is a great way to have separation of concerns.

Bounded contexts are used to separate groups of things that work together but independant of other contexts.

Entities are objects that have a distinct identity.

As we need to start somewhere, this commit introduce DDD with its first context and first entity, and test it using BDD.

Rationale

As the code repositories are at the root of Isshub, this commit introduce the Repository as its first entity, in a core context that will also holds issues, code requests, etc…

An entity must have a specific set of fields and this is defined and tested using BDD, in a describe.feature file.

We make pylint and flake8 ignore test files because: - no need for documentation as the BDD decorators include what is done in a function - fixture usage is “magic” so it generates some warnings - we still enforce black and isort so it’s enough for having good code

And finally we updated how tests are run in CI for built packages, now that tests are not in a main tests directory, but near the things they test. we also do a better exclusion of tests in the final build and added LICENSE and README.rst to it.

Info

Hash

37d8930e4da80b776842d3834d6bf81f860c5692

Date

2019-06-07 21:03:50 +0200

Parents
  • Merge branch ‘feature/twidi/github-templates’ into develop [878b7ed4]2019-06-05 18:19:41 +0200

Children
  • feat(repository): Introduce entities validation (for Repository entity) [86ad5057]2019-06-07 21:03:50 +0200

Branches
Tags

(No tags)

Changes

.circleci/config.yml

Type

Modified

Stats

+10 -4

@@ -201,13 +201,16 @@ jobs:
           command: |
             ls -la
             mv dist _dist
-            rm -r isshub
+            find ./isshub/ -type f -not -path '*/tests/*' -not -path '*/features/*' -delete
+            find ./isshub/ -type d -empty -delete
+            find ./isshub/ -type d -not -path '*/tests/*' -not -path '*/features/*' -not -name 'tests' -not -name 'features' -exec touch "{}/__init__.py" \;
+            mv isshub isshub_tests
             make full-clean
             python -m venv ~/venv
             source ~/venv/bin/activate
             pip install --upgrade pip
             pip install $(ls -tr _dist/*.whl | tail -n 1)[tests]
-            make tests
+            make tests-nocov

   # will test that the python tar.gz package is installable and works
   test_python_package_targz:
@@ -226,13 +229,16 @@ jobs:
             source ~/venv/bin/activate
             pip install --upgrade pip
             mv dist _dist
-            rm -r isshub
+            find ./isshub/ -type f -not -path '*/tests/*' -not -path '*/features/*' -delete
+            find ./isshub/ -type d -empty -delete
+            find ./isshub/ -type d -not -path '*/tests/*' -not -path '*/features/*' -not -name 'tests' -not -name 'features' -exec touch "{}/__init__.py" \;
+            mv isshub isshub_tests
             make full-clean
             python -m venv ~/venv
             source ~/venv/bin/activate
             pip install --upgrade pip
             pip install $(ls -tr _dist/*.tar.gz | tail -n 1)[tests]
-            make tests
+            make tests-nocov

 workflows:
   version: 2

MANIFEST.in

Type

Added

Stats

+2 -0

@@ -0,0 +1,2 @@
+include LICENSE
+include README.rst

Makefile

Type

Modified

Stats

+7 -0

@@ -31,6 +31,7 @@ dev-upgrade:  ## Upgrade all default+dev dependencies defined in setup.cfg

 .PHONY: dist
 dist:  ## Build the package
+dist: clean
     @echo "$(BOLD)Building package$(RESET)"
     @python setup.py sdist bdist_wheel

@@ -66,6 +67,12 @@ tests:  ## Run tests for the isshub project.
     @## we ignore error 5 from pytest meaning there is no test to run
     @pytest || ( ERR=$$?; if [ $${ERR} -eq 5 ]; then (exit 0); else (exit $${ERR}); fi )

+.PHONY: tests-nocov
+tests-nocov:  ## Run tests for the isshub project without coverage.
+    @echo "$(BOLD)Running tests (without coverage)$(RESET)"
+    @## we ignore error 5 from pytest meaning there is no test to run
+    @pytest --no-cov || ( ERR=$$?; if [ $${ERR} -eq 5 ]; then (exit 0); else (exit $${ERR}); fi )
+
 .PHONY: lint
 lint:  ## Run all linters (check-isort, check-black, mypy, flake8, pylint)
 lint: check-isort check-black flake8 pylint mypy

README.rst

Type

Modified

Stats

+25 -0

@@ -453,6 +453,31 @@ Coding
 Domain
 ======

+As said previously, I'll use Domain Driven Development, at least some parts, not sure yet.
+
+The first step is to have a `isshub.domain` package.
+
+It will contain some sub-packages:
+
+contexts
+--------
+
+Bounded contexts are used to separate groups of things that work together but independent of other contexts.
+
+The contexts hold some entities, objects that have a distinct identity.
+
+The contexts are:
+
+core
+''''
+
+The `core` context will hold the "core" domaines, around repositories, issues, code requests...
+
+Its entities are:
+
+Repository
+    Repositories are the central entity of the whole isshub project. Everything is tied to them, at one level or another.
+
 Fetching
 ========

isshub/domain/__init__.py

Type

Added

Stats

+1 -0

@@ -0,0 +1 @@
+"""Package to handle isshub domain content."""

isshub/domain/contexts/__init__.py

Type

Added

Stats

+1 -0

@@ -0,0 +1 @@
+"""Package to handle isshub domain contexts."""

isshub/domain/contexts/core/__init__.py

Type

Added

Stats

+10 -0

@@ -0,0 +1,10 @@
+"""Package to handle isshub domain core context.
+
+The "core" context defines every models that are at the core of the project:
+
+- repositories
+- issues
+- commits
+- ...
+
+"""

isshub/domain/contexts/core/entities/__init__.py

Type

Added

Stats

+1 -0

@@ -0,0 +1 @@
+"""Package to handle isshub entities for domain core context."""

isshub/domain/contexts/core/entities/repository/__init__.py

Type

Added

Stats

+25 -0

@@ -0,0 +1,25 @@
+"""Package defining the ``Repository`` entity."""
+
+from dataclasses import dataclass
+
+
+@dataclass
+class Repository:
+    """A repository holds code, issues, code requests...
+
+    Attributes
+    ----------
+    id : int
+        The unique identifier of the repository
+    name : str
+        The name of the repository. Unique in its namespace.
+    namespace : str
+        Where the repository can be found.
+
+    """
+
+    __slots__ = ["id", "name", "namespace"]
+
+    id: int
+    name: str
+    namespace: str

isshub/domain/contexts/core/entities/repository/features/describe.feature

Type

Added

Stats

+13 -0

@@ -0,0 +1,13 @@
+Feature: Describing a Repository
+
+    Scenario: A Repository has an id
+        Given a Repository
+        Then it must have a field named id
+
+    Scenario: A Repository has a name
+        Given a Repository
+        Then it must have a field named name
+
+    Scenario: A Repository has a namespace
+        Given a Repository
+        Then it must have a field named namespace

isshub/domain/contexts/core/entities/repository/tests/__init__.py

Type

Added

Stats

+1 -0

@@ -0,0 +1 @@
+"""Package holding the tests for the ``Repository`` core entity."""

isshub/domain/contexts/core/entities/repository/tests/factories.py

Type

Added

Stats

+18 -0

@@ -0,0 +1,18 @@
+"""Module defining factories for the Repository core entity."""
+
+import factory
+
+from isshub.domain.contexts.core.entities.repository import Repository
+
+
+class RepositoryFactory(factory.Factory):
+    """Factory for the ``Repository`` core entity."""
+
+    class Meta:
+        """Factory config."""
+
+        model = Repository
+
+    id = factory.Faker("pyint", min=1)
+    name = factory.Faker("pystr", min_chars=2)
+    namespace = factory.Faker("pystr", min_chars=2)

isshub/domain/contexts/core/entities/repository/tests/fixtures.py

Type

Added

Stats

+19 -0

@@ -0,0 +1,19 @@
+"""Module defining fixtures for the Repository core entity."""
+
+
+from pytest import fixture
+
+from .factories import RepositoryFactory
+
+
+@fixture
+def repository_factory():
+    """Fixture to return the factory to create a ``Repository``.
+
+    Returns
+    -------
+    Type[RepositoryFactory]
+        The ``RepositoryFactory`` class.
+
+    """
+    return RepositoryFactory

isshub/domain/contexts/core/entities/repository/tests/test_describe.py

Type

Added

Stats

+18 -0

@@ -0,0 +1,18 @@
+"""Module holding BDD tests for isshub Repository core entity."""
+
+from pytest_bdd import given, parsers, scenarios, then
+
+from .fixtures import repository_factory
+
+
+scenarios("../features/describe.feature")
+
+
+@given("a Repository")
+def repository(repository_factory):
+    return repository_factory()
+
+
+@then(parsers.parse("it must have a field named {field_name:w}"))
+def repository_has_field(repository, field_name):
+    assert hasattr(repository, field_name)

pylintrc

Type

Modified

Stats

+1 -1

@@ -7,7 +7,7 @@ extension-pkg-whitelist=

 # Add files or directories to the blacklist. They should be base names, not
 # paths.
-ignore=CVS
+ignore=CVS,tests

 # Add files or directories matching the regex patterns to the blacklist. The
 # regex matches against base names, not paths.

setup.cfg

Type

Modified

Stats

+4 -1

@@ -28,12 +28,15 @@ packages = find:
 [options.packages.find]
 exclude =
     tests
+    *.tests
+    *.tests.*
 [options.extras_require]
 dev =
     ipython
     mypy
     wheel
 tests =
+    factory-boy
     pytest
     pytest-bdd
     pytest-cov
@@ -116,7 +119,7 @@ exclude =
     dist/
     build/
     ci/
-    test_testing.py
+    test_*.py
 per-file-ignores =
     # ignore mypy missing annotations in tests
     test_*:T4