diff --git a/.github/ISSUE_TEMPLATE/default-issue-template.md b/.github/ISSUE_TEMPLATE/default-issue-template.md deleted file mode 100644 index f4a0aa1..0000000 --- a/.github/ISSUE_TEMPLATE/default-issue-template.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Default issue template -about: Default issue template -title: '' -labels: '' -assignees: '' - ---- - -**Parent ticket or topic:** _If applicable._ - - - -## Context - - -### Pre-requisites - - -## Task - - -### Acceptance criteria -- [ ] Criterion, if desired - -## Roles -- Owner(s): @someone -- Review: @person, @other-person diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..fd60e93 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,31 @@ +name: Coverage + +on: + workflow_run: + workflows: ["Tests"] + types: + - completed + +jobs: + coverage: + name: Run tests & display coverage + runs-on: ubuntu-latest + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' + permissions: + # Gives the action the necessary permissions for publishing new + # comments in pull requests. + pull-requests: write + # Gives the action the necessary permissions for editing existing + # comments (to avoid publishing multiple comments in the same PR) + contents: write + # Gives the action the necessary permissions for looking up the + # workflow that launched this workflow, and download the related + # artifact that contains the comment to be published + actions: read + steps: + - name: Post comment + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} + verbose: true \ No newline at end of file diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..3387d0b --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,11 @@ +name: Linters + +on: [push] + +jobs: + linting: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: psf/black@stable + - uses: chartboost/ruff-action@v1 \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..6ebb2cd --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,47 @@ +name: Tests + +on: + pull_request: + push: + branches: + - "main" + +jobs: + unit-tests: + runs-on: ubuntu-latest + permissions: + # Gives the action the necessary permissions for publishing new + # comments in pull requests. + pull-requests: write + # Gives the action the necessary permissions for pushing data to the + # python-coverage-comment-action branch, and for editing existing + # comments (to avoid publishing multiple comments in the same PR) + contents: write + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: 3.11 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + poetry config virtualenvs.create false + poetry install --no-root + - name: Launch tests & generate report + run: poetry run pytest + - name: Coverage comment + id: coverage_comment + uses: py-cov-action/python-coverage-comment-action@v3 + with: + GITHUB_TOKEN: ${{ github.token }} + verbose: true + - name: Store Pull Request comment to be posted + uses: actions/upload-artifact@v3 + if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' + with: + # If you use a different name, update COMMENT_ARTIFACT_NAME accordingly + name: python-coverage-comment-action + # If you use a different name, update COMMENT_FILENAME accordingly + path: python-coverage-comment-action.txt diff --git a/pyproject.toml b/pyproject.toml index 70ee33c..fc88790 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,26 @@ asyncio_mode = "auto" pythonpath = [ "src" ] +addopts = [ + "--cov-report=term-missing", + "--cov-branch", + "--cov-report=xml", + "--cov-report=term", + "--cov=src", + "-vv", + "--strict-markers", + "-rfE", +] +testpaths = [ + "tests", +] + +[tool.coverage.run] +relative_files = true +source = ["src"] +[tool.coverage.report] +skip_empty = true [build-system] requires = ["poetry-core"] diff --git a/src/oauth2/oauth2_admin.py b/src/oauth2/oauth2_admin.py index f2cbe3c..0b473d1 100644 --- a/src/oauth2/oauth2_admin.py +++ b/src/oauth2/oauth2_admin.py @@ -30,7 +30,11 @@ def get_claims(self, token: str) -> Dict[str, str] | None: key=self._get_keys(), issuer=os.getenv("KC_REALM_URL"), audience=os.getenv("AUTH_CLIENT"), - options={"verify_at_hash": False, "verify_aud": False, "verify_iss": False}, + options={ + "verify_at_hash": False, + "verify_aud": False, + "verify_iss": False, + }, ) except jose.ExpiredSignatureError: pass diff --git a/tests/api/routers/test_institutions_api.py b/tests/api/routers/test_institutions_api.py index e4d0706..a666e44 100644 --- a/tests/api/routers/test_institutions_api.py +++ b/tests/api/routers/test_institutions_api.py @@ -115,6 +115,7 @@ def test_get_institution_authed( def test_add_domains_unauthed(self, app_fixture: FastAPI, unauthed_user_mock: Mock): client = TestClient(app_fixture) + lei_path = "testLeiPath" res = client.post( f"/v1/institutions/{lei_path}/domains/", json=[{"domain": "testDomain"}] @@ -129,6 +130,7 @@ def test_add_domains_authed( FinancialInstitutionDomainDao(domain="test.bank", lei="TESTBANK123") ] client = TestClient(app_fixture) + lei_path = "testLeiPath" res = client.post( f"/v1/institutions/{lei_path}/domains/", json=[{"domain": "testDomain"}] diff --git a/tests/entities/conftest.py b/tests/entities/conftest.py index d22d248..990d971 100644 --- a/tests/entities/conftest.py +++ b/tests/entities/conftest.py @@ -45,9 +45,19 @@ async def td(): @pytest.fixture(scope="function") -async def session(engine: AsyncEngine): - Session = async_scoped_session( +async def transaction_session(session_generator: async_scoped_session): + async with session_generator() as session: + yield session + + +@pytest.fixture(scope="function") +async def query_session(session_generator: async_scoped_session): + async with session_generator() as session: + yield session + + +@pytest.fixture(scope="function") +def session_generator(engine: AsyncEngine): + return async_scoped_session( async_sessionmaker(engine, expire_on_commit=False), current_task ) - async with Session() as session: - yield session diff --git a/tests/entities/repos/test_institutions_repo.py b/tests/entities/repos/test_institutions_repo.py index 1ac7f50..f042c7f 100644 --- a/tests/entities/repos/test_institutions_repo.py +++ b/tests/entities/repos/test_institutions_repo.py @@ -14,7 +14,7 @@ class TestInstitutionsRepo: @pytest.fixture(scope="function", autouse=True) async def setup( self, - session: AsyncSession, + transaction_session: AsyncSession, ): fi_dao = FinancialInstitutionDao( name="Test Bank 123", @@ -23,48 +23,60 @@ async def setup( FinancialInstitutionDomainDao(domain="test.bank", lei="TESTBANK123") ], ) - session.add(fi_dao) - await session.commit() + transaction_session.add(fi_dao) + await transaction_session.commit() - async def test_get_institutions(self, session: AsyncSession): - res = await repo.get_institutions(session) + async def test_get_institutions(self, query_session: AsyncSession): + res = await repo.get_institutions(query_session) assert len(res) == 1 - async def test_get_institutions_by_domain(self, session: AsyncSession): - res = await repo.get_institutions(session, domain="test.bank") + async def test_get_institutions_by_domain(self, query_session: AsyncSession): + res = await repo.get_institutions(query_session, domain="test.bank") assert len(res) == 1 - async def test_get_institutions_by_domain_not_existing(self, session: AsyncSession): - res = await repo.get_institutions(session, domain="testing.bank") + async def test_get_institutions_by_domain_not_existing( + self, query_session: AsyncSession + ): + res = await repo.get_institutions(query_session, domain="testing.bank") assert len(res) == 0 - async def test_add_institution(self, session: AsyncSession): + async def test_add_institution(self, transaction_session: AsyncSession): await repo.upsert_institution( - session, FinancialInstitutionDao(name="New Bank 123", lei="NEWBANK123") + transaction_session, + FinancialInstitutionDao(name="New Bank 123", lei="NEWBANK123"), ) - res = await repo.get_institutions(session) + res = await repo.get_institutions(transaction_session) assert len(res) == 2 - async def test_update_institution(self, session: AsyncSession): + async def test_update_institution(self, transaction_session: AsyncSession): await repo.upsert_institution( - session, FinancialInstitutionDao(name="Test Bank 234", lei="TESTBANK123") + transaction_session, + FinancialInstitutionDao(name="Test Bank 234", lei="TESTBANK123"), ) - res = await repo.get_institutions(session) + res = await repo.get_institutions(transaction_session) assert len(res) == 1 assert res[0].name == "Test Bank 234" - async def test_add_domains(self, session: AsyncSession): + async def test_add_domains( + self, transaction_session: AsyncSession, query_session: AsyncSession + ): await repo.add_domains( - session, + transaction_session, "TESTBANK123", [FinancialInsitutionDomainCreate(domain="bank.test")], ) - fi = await repo.get_institution(session, "TESTBANK123") + fi = await repo.get_institution(query_session, "TESTBANK123") assert len(fi.domains) == 2 - async def test_domain_allowed(self, session: AsyncSession): + async def test_domain_allowed(self, transaction_session: AsyncSession): denied_domain = DeniedDomainDao(domain="yahoo.com") - session.add(denied_domain) - await session.commit() - assert await repo.is_email_domain_allowed(session, "test@yahoo.com") is False - assert await repo.is_email_domain_allowed(session, "test@gmail.com") is True + transaction_session.add(denied_domain) + await transaction_session.commit() + assert ( + await repo.is_email_domain_allowed(transaction_session, "test@yahoo.com") + is False + ) + assert ( + await repo.is_email_domain_allowed(transaction_session, "test@gmail.com") + is True + )