From e8ae285516ffc2d377eefee3dfc588f0ddfe6930 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Mon, 6 Jan 2025 19:54:51 -0500 Subject: [PATCH 01/11] initial tests Signed-off-by: pstlouis --- server/app/models/did_document.py | 2 +- server/app/plugins/askar.py | 1 + server/config.py | 6 +-- server/tests/__init__.py | 0 server/tests/fixtures.py | 62 ++++++++++++++++++++++++++ server/tests/test_core.py | 74 +++++++++++++++++++++++++++++++ 6 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 server/tests/__init__.py create mode 100644 server/tests/fixtures.py create mode 100644 server/tests/test_core.py diff --git a/server/app/models/did_document.py b/server/app/models/did_document.py index c684154..ae0516a 100644 --- a/server/app/models/did_document.py +++ b/server/app/models/did_document.py @@ -50,7 +50,7 @@ def verification_method_controller_validator(cls, value): class JsonWebKey(BaseModel): kty: str = Field("OKP") crv: str = Field("Ed25519") - x: str = Field(example="jihLNQ0eeR8OR-bgVxiUNOTP0tDKs5WKypYN0J5SJ9I") + x: str = Field() class VerificationMethodJwk(VerificationMethod): diff --git a/server/app/plugins/askar.py b/server/app/plugins/askar.py index 53d1dc8..21d2d15 100644 --- a/server/app/plugins/askar.py +++ b/server/app/plugins/askar.py @@ -122,5 +122,6 @@ def verify_proof(self, document, proof): raise HTTPException( status_code=400, detail="Signature was forged or corrupt." ) + return True except: raise HTTPException(status_code=400, detail="Error verifying proof.") diff --git a/server/config.py b/server/config.py index 94fc19c..f836c62 100644 --- a/server/config.py +++ b/server/config.py @@ -11,13 +11,13 @@ class Settings(BaseSettings): PROJECT_TITLE: str = "DID WebVH Server" PROJECT_VERSION: str = "v0" - SECRET_KEY: str = os.environ["SECRET_KEY"] + SECRET_KEY: str = os.environ.get("SECRET_KEY", "s3cret") - DOMAIN: str = os.environ["DOMAIN"] + DOMAIN: str = os.environ.get("DOMAIN", "localhost") DID_WEB_PREFIX: str = "did:web:" DID_WEBVH_PREFIX: str = "did:webvh:" DID_WEB_BASE: str = f"{DID_WEB_PREFIX}{DOMAIN}" - ENDORSER_MULTIKEY: str = os.environ["ENDORSER_MULTIKEY"] + ENDORSER_MULTIKEY: str = os.environ.get("ENDORSER_MULTIKEY", "") POSTGRES_USER: str = os.getenv("POSTGRES_USER", "") POSTGRES_PASSWORD: str = os.getenv("POSTGRES_PASSWORD", "") diff --git a/server/tests/__init__.py b/server/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/tests/fixtures.py b/server/tests/fixtures.py new file mode 100644 index 0000000..d8cba95 --- /dev/null +++ b/server/tests/fixtures.py @@ -0,0 +1,62 @@ +from config import settings + +TEST_DOMAIN = settings.DOMAIN +TEST_DID_NAMESPACE = 'test' +TEST_DID_IDENTIFIER = '01' +TEST_DID = f"{settings.DID_WEB_BASE}:{TEST_DID_NAMESPACE}:{TEST_DID_IDENTIFIER}" +TEST_PROOF_OPTIONS = { + 'type': 'DataIntegrityProof', + 'cryptosuite': 'eddsa-jcs-2022', + 'proofPurpose': 'assertionMethod' +} +TEST_AUTHORISED_KEY = 'z6Mkj8h3kzWZrPiucoyY9LGCTpXhCqBoX3doDmHz5MaPxnvi' +TEST_AUTHORISED_JWK = 'RYirjVOuAh9BXxQaxozaDLK_JqrKPicZeq9bl3Fg8xc' +TEST_DID_DOCUMENT = { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/multikey/v1" + ], + "id": TEST_DID, + "authorisation": [ + f'{TEST_DID}#key-01' + ], + "assertionMethod": [ + f'{TEST_DID}#key-01' + ], + "verificationMethod": [ + { + 'id': f'{TEST_DID}#key-01', + 'type': 'Multikey', + 'controller': TEST_DID, + 'publiKeyMultibase': TEST_AUTHORISED_KEY + } + ], +} +TEST_DID_DOCUMENT_SIGNED = { + "@context": [ + "https://www.w3.org/ns/did/v1", + "https://w3id.org/security/multikey/v1" + ], + "id": TEST_DID, + "authorisation": [ + f'{TEST_DID}#key-01' + ], + "assertionMethod": [ + f'{TEST_DID}#key-01' + ], + "verificationMethod": [ + { + 'id': f'{TEST_DID}#key-01', + 'type': 'Multikey', + 'controller': TEST_DID, + 'publiKeyMultibase': TEST_AUTHORISED_KEY + } + ], + "proof": { + "type": "DataIntegrityProof", + "proofPurpose": "assertionMethod", + "verificationMethod": f"did:key:{TEST_AUTHORISED_KEY}#{TEST_AUTHORISED_KEY}", + "cryptosuite": "eddsa-jcs-2022", + "proofValue": "z3Th9TRHPiwErjPFC9oNEvhsnkEGKEyipR7c6db2gUZxBb6EtgntsXF7ANr1ByC2aUE1igFxtswfpZyTKXCe1ZMs2" + } +} \ No newline at end of file diff --git a/server/tests/test_core.py b/server/tests/test_core.py new file mode 100644 index 0000000..4536c56 --- /dev/null +++ b/server/tests/test_core.py @@ -0,0 +1,74 @@ +from app.routers.identifiers import request_did +from app.routers.resolvers import get_did_document +from app.plugins import AskarStorage, AskarVerifier, DidWebVH +from datetime import datetime, timezone +from tests.fixtures import ( + TEST_DOMAIN, + TEST_DID_NAMESPACE, + TEST_DID_IDENTIFIER, + TEST_DID, + TEST_DID_DOCUMENT, + TEST_DID_DOCUMENT_SIGNED, + TEST_AUTHORISED_KEY, + TEST_PROOF_OPTIONS +) +import asyncio +import json +import uuid + +def test_storage(): + askar = AskarStorage() + asyncio.run(askar.provision(recreate=True)) + + category = 'test' + key = '01' + data = {'value': None} + value_1 = 'value_1' + value_2 = 'value_2' + + data['value'] = value_1 + asyncio.run(askar.store(category, key, data)) + fetched_data = asyncio.run(askar.fetch(category, key)) + assert fetched_data['value'] == value_1 + + data['value'] = value_2 + asyncio.run(askar.update(category, key, data)) + fetched_data = asyncio.run(askar.fetch(category, key)) + assert fetched_data['value'] == value_2 + +def test_request_did(): + did_request = asyncio.run(request_did(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER)) + did_request = json.loads(did_request.body.decode()) + assert did_request.get('didDocument').get('id') == TEST_DID + assert did_request.get('proofOptions').get("type") == TEST_PROOF_OPTIONS['type'] + assert did_request.get('proofOptions').get("cryptosuite") == TEST_PROOF_OPTIONS['cryptosuite'] + assert did_request.get('proofOptions').get("proofPurpose") == TEST_PROOF_OPTIONS['proofPurpose'] + assert did_request.get('proofOptions').get("domain") == TEST_DOMAIN + assert did_request.get('proofOptions').get("challenge") + assert datetime.fromisoformat( + did_request.get('proofOptions').get("expires") + ) > datetime.now(timezone.utc) + +def test_register_did(): + askar = AskarStorage() + asyncio.run(askar.store("didDocument", TEST_DID, TEST_DID_DOCUMENT)) + asyncio.run(askar.store("authorizedKey", TEST_DID, TEST_AUTHORISED_KEY)) + +def test_resolve_did(): + did_doc = asyncio.run(get_did_document(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER)) + did_doc = json.loads(did_doc.body.decode()) + assert did_doc == TEST_DID_DOCUMENT + assert did_doc.get('id') == TEST_DID + +def test_create_log_entry(): + initial_log_entry = DidWebVH().create(TEST_DID_DOCUMENT, TEST_AUTHORISED_KEY) + assert initial_log_entry.get('versionId') + assert initial_log_entry.get('versionTime') + assert initial_log_entry.get('parameters') + assert initial_log_entry.get('state') + +def test_verify_di_proof(): + document = TEST_DID_DOCUMENT_SIGNED + proof = document.pop('proof') + verifier = AskarVerifier() + assert verifier.verify_proof(document, proof) From 63362ffdcba6c77658f152ac24e75ec16787459d Mon Sep 17 00:00:00 2001 From: pstlouis Date: Tue, 7 Jan 2025 11:52:24 -0500 Subject: [PATCH 02/11] improved fixtures with pydantic models Signed-off-by: pstlouis --- server/app/models/did_document.py | 34 +++------------- server/tests/fixtures.py | 67 +++++++++---------------------- 2 files changed, 23 insertions(+), 78 deletions(-) diff --git a/server/app/models/did_document.py b/server/app/models/did_document.py index ae0516a..7dc1a3e 100644 --- a/server/app/models/did_document.py +++ b/server/app/models/did_document.py @@ -96,7 +96,7 @@ def service_endpoint_validator(cls, value): class DidDocument(BaseModel): context: Union[str, List[str]] = Field( - ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/multikey/v1"], + ["https://www.w3.org/ns/did/v1"], alias="@context", ) id: str = Field() @@ -106,9 +106,9 @@ class DidDocument(BaseModel): alsoKnownAs: List[str] = Field(None) verificationMethod: List[ Union[VerificationMethodMultikey, VerificationMethodJwk] - ] = Field() - authentication: List[Union[str, VerificationMethod]] = Field() - assertionMethod: List[Union[str, VerificationMethod]] = Field() + ] = Field(None) + authentication: List[Union[str, VerificationMethod]] = Field(None) + assertionMethod: List[Union[str, VerificationMethod]] = Field(None) keyAgreement: List[Union[str, VerificationMethod]] = Field(None) capabilityInvocation: List[Union[str, VerificationMethod]] = Field(None) capabilityDelegation: List[Union[str, VerificationMethod]] = Field(None) @@ -126,30 +126,6 @@ def id_validator(cls, value): assert DID_WEB_REGEX.match(value), "Expected id to be a DID." return value - @field_validator("authentication") - @classmethod - def authentication_validator(cls, value): - assert len(value) >= 1, "Expected at least one authentication method." - return value - - @field_validator("assertionMethod") - @classmethod - def assertion_method_validator(cls, value): - assert len(value) >= 1, "Expected at least one assertion method." - return value - - @field_validator("verificationMethod") - @classmethod - def verification_method_validator(cls, value): - assert len(value) >= 1, "Expected at least one verification method." - return value - class SecuredDidDocument(DidDocument): - proof: List[DataIntegrityProof] = Field(None) - - @field_validator("proof") - @classmethod - def proof_validator(cls, value): - assert len(value) == 2, "Expected proof set." - return value + proof: Union[DataIntegrityProof, List[DataIntegrityProof]] = Field(None) diff --git a/server/tests/fixtures.py b/server/tests/fixtures.py index d8cba95..f8983e1 100644 --- a/server/tests/fixtures.py +++ b/server/tests/fixtures.py @@ -1,4 +1,6 @@ from config import settings +from app.models.did_document import DidDocument, SecuredDidDocument +from app.models.di_proof import DataIntegrityProof TEST_DOMAIN = settings.DOMAIN TEST_DID_NAMESPACE = 'test' @@ -11,52 +13,19 @@ } TEST_AUTHORISED_KEY = 'z6Mkj8h3kzWZrPiucoyY9LGCTpXhCqBoX3doDmHz5MaPxnvi' TEST_AUTHORISED_JWK = 'RYirjVOuAh9BXxQaxozaDLK_JqrKPicZeq9bl3Fg8xc' -TEST_DID_DOCUMENT = { - "@context": [ - "https://www.w3.org/ns/did/v1", - "https://w3id.org/security/multikey/v1" - ], - "id": TEST_DID, - "authorisation": [ - f'{TEST_DID}#key-01' - ], - "assertionMethod": [ - f'{TEST_DID}#key-01' - ], - "verificationMethod": [ - { - 'id': f'{TEST_DID}#key-01', - 'type': 'Multikey', - 'controller': TEST_DID, - 'publiKeyMultibase': TEST_AUTHORISED_KEY - } - ], -} -TEST_DID_DOCUMENT_SIGNED = { - "@context": [ - "https://www.w3.org/ns/did/v1", - "https://w3id.org/security/multikey/v1" - ], - "id": TEST_DID, - "authorisation": [ - f'{TEST_DID}#key-01' - ], - "assertionMethod": [ - f'{TEST_DID}#key-01' - ], - "verificationMethod": [ - { - 'id': f'{TEST_DID}#key-01', - 'type': 'Multikey', - 'controller': TEST_DID, - 'publiKeyMultibase': TEST_AUTHORISED_KEY - } - ], - "proof": { - "type": "DataIntegrityProof", - "proofPurpose": "assertionMethod", - "verificationMethod": f"did:key:{TEST_AUTHORISED_KEY}#{TEST_AUTHORISED_KEY}", - "cryptosuite": "eddsa-jcs-2022", - "proofValue": "z3Th9TRHPiwErjPFC9oNEvhsnkEGKEyipR7c6db2gUZxBb6EtgntsXF7ANr1ByC2aUE1igFxtswfpZyTKXCe1ZMs2" - } -} \ No newline at end of file + +TEST_DID_DOCUMENT = DidDocument( + context=['https://www.w3.org/ns/did/v1'], + id=TEST_DID +).model_dump() + +TEST_DID_DOCUMENT_PROOF = DataIntegrityProof( + proofValue='z4NCh2bocHncp9SSpCDETSsWN5ueu7eLPFgaVTNvgCk2RxZvFbVHAN8keGqd8XXbSzrxd3q1VMKQrZuqf672WNncK', + verificationMethod=f'did:key:{TEST_AUTHORISED_KEY}#{TEST_AUTHORISED_KEY}' +).model_dump() + +TEST_DID_DOCUMENT_SIGNED = SecuredDidDocument( + context=['https://www.w3.org/ns/did/v1'], + id=TEST_DID, + proof=TEST_DID_DOCUMENT_PROOF +).model_dump() \ No newline at end of file From 2ae81d5f7af4f1c24fd535acb6006bf9954f4df3 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 14:33:20 -0500 Subject: [PATCH 03/11] implement asyncic pytest Signed-off-by: pstlouis --- server/poetry.lock | 79 ++++++++++++++++++++++++++++++++++++++- server/pyproject.toml | 1 + server/tests/test_core.py | 39 ++++++++++--------- 3 files changed, 100 insertions(+), 19 deletions(-) diff --git a/server/poetry.lock b/server/poetry.lock index bd2be5a..d8d2860 100644 --- a/server/poetry.lock +++ b/server/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "annotated-types" @@ -187,6 +187,17 @@ files = [ {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, ] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "jsonlines" version = "4.0.0" @@ -239,6 +250,32 @@ multiformats = "*" [package.extras] dev = ["mypy", "pylint", "pytest", "pytest-cov"] +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "pydantic" version = "2.9.2" @@ -383,6 +420,44 @@ azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0 toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] +[[package]] +name = "pytest" +version = "8.3.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.25.2" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075"}, + {file = "pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "python-dotenv" version = "1.0.1" @@ -512,4 +587,4 @@ crypto-eth-addresses = ["eth-hash[pycryptodome] (>=0.7.0)"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "ce33731bee913e481e55a9fe13eb822cbf441d86cb553d198b15eb5754e3ef5d" +content-hash = "bb8d5f74c5792a6970821d3bfeee53e986a1d067605c8f4d6d7d460d33e263de" diff --git a/server/pyproject.toml b/server/pyproject.toml index cb88eeb..53be429 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -34,6 +34,7 @@ typing-extensions = "^4.12.2" uvicorn = "0.30.6" validators = "^0.34.0" jsonlines = "^4.0.0" +pytest-asyncio = "^0.25.2" [tool.poetry.group.dev.dependencies] ruff = "^0.6.8" diff --git a/server/tests/test_core.py b/server/tests/test_core.py index 4536c56..7525cf9 100644 --- a/server/tests/test_core.py +++ b/server/tests/test_core.py @@ -12,13 +12,13 @@ TEST_AUTHORISED_KEY, TEST_PROOF_OPTIONS ) -import asyncio import json -import uuid +import pytest -def test_storage(): +@pytest.mark.asyncio +async def test_storage(): askar = AskarStorage() - asyncio.run(askar.provision(recreate=True)) + await askar.provision(recreate=True) category = 'test' key = '01' @@ -27,17 +27,18 @@ def test_storage(): value_2 = 'value_2' data['value'] = value_1 - asyncio.run(askar.store(category, key, data)) - fetched_data = asyncio.run(askar.fetch(category, key)) + await askar.store(category, key, data) + fetched_data = await askar.fetch(category, key) assert fetched_data['value'] == value_1 data['value'] = value_2 - asyncio.run(askar.update(category, key, data)) - fetched_data = asyncio.run(askar.fetch(category, key)) + await askar.update(category, key, data) + fetched_data = await askar.fetch(category, key) assert fetched_data['value'] == value_2 -def test_request_did(): - did_request = asyncio.run(request_did(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER)) +@pytest.mark.asyncio +async def test_request_did(): + did_request = await request_did(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER) did_request = json.loads(did_request.body.decode()) assert did_request.get('didDocument').get('id') == TEST_DID assert did_request.get('proofOptions').get("type") == TEST_PROOF_OPTIONS['type'] @@ -49,25 +50,29 @@ def test_request_did(): did_request.get('proofOptions').get("expires") ) > datetime.now(timezone.utc) -def test_register_did(): +@pytest.mark.asyncio +async def test_register_did(): askar = AskarStorage() - asyncio.run(askar.store("didDocument", TEST_DID, TEST_DID_DOCUMENT)) - asyncio.run(askar.store("authorizedKey", TEST_DID, TEST_AUTHORISED_KEY)) + await askar.store("didDocument", TEST_DID, TEST_DID_DOCUMENT) + await askar.store("authorizedKey", TEST_DID, TEST_AUTHORISED_KEY) -def test_resolve_did(): - did_doc = asyncio.run(get_did_document(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER)) +@pytest.mark.asyncio +async def test_resolve_did(): + did_doc = await get_did_document(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER) did_doc = json.loads(did_doc.body.decode()) assert did_doc == TEST_DID_DOCUMENT assert did_doc.get('id') == TEST_DID -def test_create_log_entry(): +@pytest.mark.asyncio +async def test_create_log_entry(): initial_log_entry = DidWebVH().create(TEST_DID_DOCUMENT, TEST_AUTHORISED_KEY) assert initial_log_entry.get('versionId') assert initial_log_entry.get('versionTime') assert initial_log_entry.get('parameters') assert initial_log_entry.get('state') -def test_verify_di_proof(): +@pytest.mark.asyncio +async def test_verify_di_proof(): document = TEST_DID_DOCUMENT_SIGNED proof = document.pop('proof') verifier = AskarVerifier() From 5ee6ec31b33bcfecff63ee07f17234b3fa22c3e7 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 14:57:57 -0500 Subject: [PATCH 04/11] add gh action Signed-off-by: pstlouis --- .github/workflows/pr-tests.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/pr-tests.yaml diff --git a/.github/workflows/pr-tests.yaml b/.github/workflows/pr-tests.yaml new file mode 100644 index 0000000..1e2d41e --- /dev/null +++ b/.github/workflows/pr-tests.yaml @@ -0,0 +1,24 @@ +name: Python tests + +on: + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./server + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - uses: abatilo/actions-poetry@v4 + - name: Install dependencies + run: | + poetry install + - name: Test with pytest + run: | + pytest \ No newline at end of file From 3454bdd20eb16715ae6afb5e2b7cddf4a5f97360 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 14:59:05 -0500 Subject: [PATCH 05/11] fix gh action Signed-off-by: pstlouis --- .github/workflows/pr-tests.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr-tests.yaml b/.github/workflows/pr-tests.yaml index 1e2d41e..3ebe9ca 100644 --- a/.github/workflows/pr-tests.yaml +++ b/.github/workflows/pr-tests.yaml @@ -5,7 +5,7 @@ on: branches: [ main ] jobs: - build: + test: runs-on: ubuntu-latest defaults: @@ -16,9 +16,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - uses: abatilo/actions-poetry@v4 - - name: Install dependencies + - name: Install dependencies and run pytest run: | poetry install - - name: Test with pytest - run: | pytest \ No newline at end of file From 10bf6ba332a94480ee0ce91ee1deb33a61f57434 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 15:01:25 -0500 Subject: [PATCH 06/11] fix gh action Signed-off-by: pstlouis --- .github/workflows/pr-tests.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-tests.yaml b/.github/workflows/pr-tests.yaml index 3ebe9ca..2fbb237 100644 --- a/.github/workflows/pr-tests.yaml +++ b/.github/workflows/pr-tests.yaml @@ -5,7 +5,7 @@ on: branches: [ main ] jobs: - test: + build: runs-on: ubuntu-latest defaults: @@ -16,7 +16,9 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - uses: abatilo/actions-poetry@v4 - - name: Install dependencies and run pytest + - name: Install project run: | poetry install - pytest \ No newline at end of file + - name: Run pytest + run: | + poetry run pytest \ No newline at end of file From 640a69c7d8672d6ab118281d0d8c6f965f3c4300 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 15:06:11 -0500 Subject: [PATCH 07/11] fix pytest Signed-off-by: pstlouis --- server/tests/test_core.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server/tests/test_core.py b/server/tests/test_core.py index 7525cf9..af9f3e5 100644 --- a/server/tests/test_core.py +++ b/server/tests/test_core.py @@ -14,11 +14,16 @@ ) import json import pytest +import asyncio + +askar = AskarStorage() +asyncio.run(askar.provision(recreate=True)) + +verifier = AskarVerifier() +didwebvh = DidWebVH() @pytest.mark.asyncio async def test_storage(): - askar = AskarStorage() - await askar.provision(recreate=True) category = 'test' key = '01' @@ -52,7 +57,6 @@ async def test_request_did(): @pytest.mark.asyncio async def test_register_did(): - askar = AskarStorage() await askar.store("didDocument", TEST_DID, TEST_DID_DOCUMENT) await askar.store("authorizedKey", TEST_DID, TEST_AUTHORISED_KEY) @@ -65,7 +69,7 @@ async def test_resolve_did(): @pytest.mark.asyncio async def test_create_log_entry(): - initial_log_entry = DidWebVH().create(TEST_DID_DOCUMENT, TEST_AUTHORISED_KEY) + initial_log_entry = didwebvh.create(TEST_DID_DOCUMENT, TEST_AUTHORISED_KEY) assert initial_log_entry.get('versionId') assert initial_log_entry.get('versionTime') assert initial_log_entry.get('parameters') @@ -75,5 +79,4 @@ async def test_create_log_entry(): async def test_verify_di_proof(): document = TEST_DID_DOCUMENT_SIGNED proof = document.pop('proof') - verifier = AskarVerifier() assert verifier.verify_proof(document, proof) From 1e07f01d31888c5cc816e543ce17d27612b98ce0 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 15:34:26 -0500 Subject: [PATCH 08/11] fix signed document test Signed-off-by: pstlouis --- server/config.py | 8 ++++---- server/pyproject.toml | 3 +++ server/tests/fixtures.py | 2 +- server/tests/test_core.py | 1 + 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/server/config.py b/server/config.py index f836c62..22c31b2 100644 --- a/server/config.py +++ b/server/config.py @@ -11,13 +11,13 @@ class Settings(BaseSettings): PROJECT_TITLE: str = "DID WebVH Server" PROJECT_VERSION: str = "v0" - SECRET_KEY: str = os.environ.get("SECRET_KEY", "s3cret") + SECRET_KEY: str = os.environ.get("SECRET_KEY", None) or 's3cret' - DOMAIN: str = os.environ.get("DOMAIN", "localhost") + DOMAIN: str = os.environ.get("DOMAIN", None) or 'localhost' DID_WEB_PREFIX: str = "did:web:" DID_WEBVH_PREFIX: str = "did:webvh:" DID_WEB_BASE: str = f"{DID_WEB_PREFIX}{DOMAIN}" - ENDORSER_MULTIKEY: str = os.environ.get("ENDORSER_MULTIKEY", "") + ENDORSER_MULTIKEY: str = os.environ.get("ENDORSER_MULTIKEY", None) POSTGRES_USER: str = os.getenv("POSTGRES_USER", "") POSTGRES_PASSWORD: str = os.getenv("POSTGRES_PASSWORD", "") @@ -41,4 +41,4 @@ class Settings(BaseSettings): SCID_PLACEHOLDER: str = "{SCID}" -settings = Settings() +settings = Settings() \ No newline at end of file diff --git a/server/pyproject.toml b/server/pyproject.toml index 53be429..6d0d288 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -42,3 +42,6 @@ ruff = "^0.6.8" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +asyncio_default_fixture_loop_scope = "function" \ No newline at end of file diff --git a/server/tests/fixtures.py b/server/tests/fixtures.py index f8983e1..175c5b3 100644 --- a/server/tests/fixtures.py +++ b/server/tests/fixtures.py @@ -20,7 +20,7 @@ ).model_dump() TEST_DID_DOCUMENT_PROOF = DataIntegrityProof( - proofValue='z4NCh2bocHncp9SSpCDETSsWN5ueu7eLPFgaVTNvgCk2RxZvFbVHAN8keGqd8XXbSzrxd3q1VMKQrZuqf672WNncK', + proofValue='z5qtizN3eN3nogndvKt2GhLM4pbLceRNJYq7xi1n6yjQmd5yAzDBWrwC9VxbeFXjCoP2i8kLWRFJxGrqjAt1LFLHN', verificationMethod=f'did:key:{TEST_AUTHORISED_KEY}#{TEST_AUTHORISED_KEY}' ).model_dump() diff --git a/server/tests/test_core.py b/server/tests/test_core.py index af9f3e5..6104a16 100644 --- a/server/tests/test_core.py +++ b/server/tests/test_core.py @@ -45,6 +45,7 @@ async def test_storage(): async def test_request_did(): did_request = await request_did(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER) did_request = json.loads(did_request.body.decode()) + # print(json.dumps(did_request, indent=2)) assert did_request.get('didDocument').get('id') == TEST_DID assert did_request.get('proofOptions').get("type") == TEST_PROOF_OPTIONS['type'] assert did_request.get('proofOptions').get("cryptosuite") == TEST_PROOF_OPTIONS['cryptosuite'] From ff0719875cb8273f64e9c56058682f4926a0aa70 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 15:35:42 -0500 Subject: [PATCH 09/11] fix endorser value type Signed-off-by: pstlouis --- server/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/config.py b/server/config.py index 22c31b2..63cfea4 100644 --- a/server/config.py +++ b/server/config.py @@ -17,7 +17,7 @@ class Settings(BaseSettings): DID_WEB_PREFIX: str = "did:web:" DID_WEBVH_PREFIX: str = "did:webvh:" DID_WEB_BASE: str = f"{DID_WEB_PREFIX}{DOMAIN}" - ENDORSER_MULTIKEY: str = os.environ.get("ENDORSER_MULTIKEY", None) + ENDORSER_MULTIKEY: str = os.environ.get("ENDORSER_MULTIKEY", None) or '' POSTGRES_USER: str = os.getenv("POSTGRES_USER", "") POSTGRES_PASSWORD: str = os.getenv("POSTGRES_PASSWORD", "") From a38cf4589b385013e992a406036b1ae61562d0e4 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Wed, 8 Jan 2025 15:38:35 -0500 Subject: [PATCH 10/11] remove printed out comment Signed-off-by: pstlouis --- server/tests/test_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server/tests/test_core.py b/server/tests/test_core.py index 6104a16..af9f3e5 100644 --- a/server/tests/test_core.py +++ b/server/tests/test_core.py @@ -45,7 +45,6 @@ async def test_storage(): async def test_request_did(): did_request = await request_did(TEST_DID_NAMESPACE, TEST_DID_IDENTIFIER) did_request = json.loads(did_request.body.decode()) - # print(json.dumps(did_request, indent=2)) assert did_request.get('didDocument').get('id') == TEST_DID assert did_request.get('proofOptions').get("type") == TEST_PROOF_OPTIONS['type'] assert did_request.get('proofOptions').get("cryptosuite") == TEST_PROOF_OPTIONS['cryptosuite'] From a1c69334d6fb0bdd5c8fae5a7f004201a949f8e2 Mon Sep 17 00:00:00 2001 From: pstlouis Date: Thu, 9 Jan 2025 11:45:06 -0500 Subject: [PATCH 11/11] updated config Signed-off-by: pstlouis --- server/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/config.py b/server/config.py index 63cfea4..c102ef7 100644 --- a/server/config.py +++ b/server/config.py @@ -11,13 +11,13 @@ class Settings(BaseSettings): PROJECT_TITLE: str = "DID WebVH Server" PROJECT_VERSION: str = "v0" - SECRET_KEY: str = os.environ.get("SECRET_KEY", None) or 's3cret' + SECRET_KEY: str = os.environ.get("SECRET_KEY", 's3cret') - DOMAIN: str = os.environ.get("DOMAIN", None) or 'localhost' + DOMAIN: str = os.environ.get("DOMAIN", 'localhost') DID_WEB_PREFIX: str = "did:web:" DID_WEBVH_PREFIX: str = "did:webvh:" DID_WEB_BASE: str = f"{DID_WEB_PREFIX}{DOMAIN}" - ENDORSER_MULTIKEY: str = os.environ.get("ENDORSER_MULTIKEY", None) or '' + ENDORSER_MULTIKEY: str = os.environ.get("ENDORSER_MULTIKEY", '') POSTGRES_USER: str = os.getenv("POSTGRES_USER", "") POSTGRES_PASSWORD: str = os.getenv("POSTGRES_PASSWORD", "")