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¶
isshub/domain/contexts/core/entities/repository/features/describe.feature
isshub/domain/contexts/core/entities/repository/tests/__init__.py
isshub/domain/contexts/core/entities/repository/tests/factories.py
isshub/domain/contexts/core/entities/repository/tests/fixtures.py
isshub/domain/contexts/core/entities/repository/tests/test_describe.py
.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
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