Skip to content

Commit

Permalink
test: add integration tests [DEV-4613][DEV-4614] (#4)
Browse files Browse the repository at this point in the history
Changes:
- extend integration tests:
    - create schema
    - create credential definition
- assert credential definition available in credential definitions list
    - assert did available in wallet dids
    - deactivate did
  • Loading branch information
DaevMithran authored Nov 28, 2024
2 parents be0f050 + b686135 commit fc3d5cc
Show file tree
Hide file tree
Showing 3 changed files with 303 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docker/Dockerfile.run
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ RUN mkdir -p acapy_agent && touch acapy_agent/__init__.py
ADD pyproject.toml poetry.lock README.md ./
RUN mkdir -p log && chmod -R ug+rw log

ARG all_extras=0
ARG all_extras
RUN if ! [ -z ${all_extras} ]; then poetry install --all-extras; else poetry install -E "askar didcommv2"; fi

ADD . .
Expand Down
6 changes: 3 additions & 3 deletions scenarios/examples/cheqd/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
--label Issuer
--inbound-transport http 0.0.0.0 3000
--outbound-transport http
--endpoint http://agency:3000
--endpoint http://issuer:3000
--admin 0.0.0.0 3001
--admin-insecure-mode
--wallet-type askar-anoncreds
--wallet-name agency
--wallet-key insecure
--auto-provision
--log-level info
--log-level DEBUG
--debug-webhooks
--no-ledger
healthcheck:
Expand All @@ -41,7 +41,7 @@
--wallet-name holder
--wallet-key insecure
--auto-provision
--log-level info
--log-level DEBUG
--debug-webhooks
--no-ledger
healthcheck:
Expand Down
334 changes: 299 additions & 35 deletions scenarios/examples/cheqd/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,327 @@
"""

import asyncio
import json
from dataclasses import dataclass
from os import getenv
from typing import Tuple, Mapping, Optional

from acapy_controller import Controller
from acapy_controller.logging import logging_to_stdout
from acapy_controller.protocols import didexchange
from acapy_controller.controller import Minimal

ISSUER = getenv("ISSUER", "http://issuer:3001")
HOLDER = getenv("HOLDER", "http://holder:3001")


@dataclass
class V20CredExRecord(Minimal):
"""V2.0 credential exchange record."""

state: str
cred_ex_id: str
connection_id: str
thread_id: str


@dataclass
class V20CredExRecordFormat(Minimal):
"""V2.0 credential exchange record anoncreds."""

rev_reg_id: Optional[str] = None
cred_rev_id: Optional[str] = None


@dataclass
class V20CredExRecordDetail(Minimal):
"""V2.0 credential exchange record detail."""

cred_ex_record: V20CredExRecord
details: Optional[V20CredExRecordFormat] = None


def format_json(json_to_format):
"""Pretty print json."""
return json.dumps(json_to_format, indent=4)


async def create_did(issuer):
"""Create a DID on the Cheqd testnet."""
did_create_result = await issuer.post("/did/cheqd/create")
did = did_create_result.get("did")

assert did, "DID creation failed."
assert did_create_result.get("verkey"), "Verkey is missing in DID creation result."

print(f"Created DID: {did}")
return did


async def resolve_did(issuer, did):
"""Resolve the DID document."""
resolution_result = await issuer.get(f"/resolver/resolve/{did}")
did_document = resolution_result.get("did_document")

assert did_document, "DID document resolution failed."
print(f"Resolved DID Document: {format_json(did_document)}")
return did_document


async def update_did(issuer, did, did_document):
"""Update the DID document by adding a service endpoint."""
service = [
{
"id": f"{did}#service-1",
"type": "MessagingService",
"serviceEndpoint": ["https://example.com/service"],
}
]
did_document["service"] = service
del did_document["@context"]

did_update_result = await issuer.post(
"/did/cheqd/update", json={"did": did, "didDocument": did_document}
)
updated_did_doc = did_update_result.get("didDocument")
updated_did = did_update_result.get("did")

assert updated_did == did, "DID mismatch after update."
assert (
"service" in updated_did_doc
), "Key 'service' is missing in updated DID document."
assert (
updated_did_doc["service"] == service
), "Service does not match the expected value!"

print(f"Updated DID Document: {format_json(updated_did_doc)}")
return updated_did_doc


async def deactivate_did(issuer, did):
"""Deactivate a DID on the Cheqd testnet."""
did_deactivate_result = await issuer.post(
"/did/cheqd/deactivate",
json={
"did": did,
"options": {"network": "testnet"},
},
)

assert did_deactivate_result.get("did") == did, "DID mismatch after deactivation."
assert (
did_deactivate_result.get("did_document_metadata", {}).get("deactivated") is True
), "DID document metadata does not contain deactivated=true."

print(f"Deactivated DID: {format_json(did_deactivate_result) }")


async def create_schema(issuer, did):
"""Create a schema on the Cheqd testnet."""
schema_create_result = await issuer.post(
"/anoncreds/schema",
json={
"schema": {
"attrNames": ["score"],
"issuerId": did,
"name": "Example schema",
"version": "1.0",
}
},
)
print(f"Created schema: {format_json(schema_create_result)}")
schema_state = schema_create_result.get("schema_state")
assert schema_state.get("state") == "finished", "Schema state is not finished."
assert "schema_id" in schema_state, "Key 'schema_id' is missing in schema_state."

schema_id = schema_state.get("schema_id")
assert (
did in schema_id
), f"schema_id does not contain the expected DID. Expected '{did}' in '{schema_id}'."

return schema_id


async def create_credential_definition(issuer, did, schema_id):
"""Create a credential definition on the connected datastore."""
cred_def_create_result = await issuer.post(
"/anoncreds/credential-definition",
json={
"credential_definition": {
"issuerId": did,
"schemaId": schema_id,
"tag": "default",
}
},
)

cred_def_state = cred_def_create_result.get("credential_definition_state", {})
assert cred_def_state.get("state") == "finished", "Cred def state is not finished."
assert (
"credential_definition_id" in cred_def_state
), "Key 'credential_definition_id' is missing in credential_definition_state."

credential_definition_id = cred_def_state.get("credential_definition_id")
assert (
did in credential_definition_id
), "credential_definition_id does not contain the expected DID."

print(f"Created credential definition: {format_json(cred_def_create_result)}")
return credential_definition_id


async def assert_credential_definitions(issuer, credential_definition_id):
"""Retrieve all cred_defs & ensure array contain created credential_definition_id."""
get_result = await issuer.get("/anoncreds/credential-definitions")

credential_definition_ids = get_result.get("credential_definition_ids", [])
assert (
credential_definition_id in credential_definition_ids
), "credential_definition_ids does not contain the expected credential_definition_id."


async def assert_wallet_dids(issuer, did):
"""Retrieve all wallet dids and ensure array contain created did."""
get_result = await issuer.get("/wallet/did?method=cheqd")

dids = get_result.get("results", [])
assert any(obj.get("did") == did for obj in dids), f"DID {did} not found in array"


async def issue_credential_v2(
issuer: Controller,
holder: Controller,
issuer_connection_id: str,
holder_connection_id: str,
cred_def_id: str,
attributes: Mapping[str, str],
) -> Tuple[V20CredExRecordDetail, V20CredExRecordDetail]:
"""Issue an credential using issue-credential/2.0.
Issuer and holder should already be connected.
"""

issuer_cred_ex = await issuer.post(
"/issue-credential-2.0/send",
json={
"auto_issue": False,
"auto_remove": False,
"comment": "Credential from minimal example",
"trace": False,
"connection_id": issuer_connection_id,
"filter": {
"anoncreds": {
"cred_def_id": cred_def_id,
}
},
"credential_preview": {
"type": "issue-credential-2.0/2.0/credential-preview", # pyright: ignore
"attributes": [
{
"name": name,
"value": value,
}
for name, value in attributes.items()
],
},
},
response=V20CredExRecord,
)
issuer_cred_ex_id = issuer_cred_ex.cred_ex_id

holder_cred_ex = await holder.event_with_values(
topic="issue_credential_v2_0",
event_type=V20CredExRecord,
connection_id=holder_connection_id,
state="offer-received",
)
holder_cred_ex_id = holder_cred_ex.cred_ex_id

await holder.post(
f"/issue-credential-2.0/records/{holder_cred_ex_id}/send-request",
response=V20CredExRecord,
)

await issuer.event_with_values(
topic="issue_credential_v2_0",
cred_ex_id=issuer_cred_ex_id,
state="request-received",
)

await holder.event_with_values(
topic="issue_credential_v2_0",
cred_ex_id=holder_cred_ex_id,
state="credential-received",
)

await holder.post(
f"/issue-credential-2.0/records/{holder_cred_ex_id}/store",
json={},
response=V20CredExRecordDetail,
)

issuer_cred_ex = await issuer.event_with_values(
topic="issue_credential_v2_0",
event_type=V20CredExRecord,
cred_ex_id=issuer_cred_ex_id,
state="done",
)

holder_cred_ex = await holder.event_with_values(
topic="issue_credential_v2_0",
event_type=V20CredExRecord,
cred_ex_id=holder_cred_ex_id,
state="done",
)

return (
V20CredExRecordDetail(cred_ex_record=issuer_cred_ex),
V20CredExRecordDetail(cred_ex_record=holder_cred_ex),
)


async def main():
"""Test DID Cheqd workflow."""
async with Controller(base_url=ISSUER) as issuer:
async with Controller(base_url=ISSUER) as issuer, Controller(
base_url=HOLDER
) as holder:
"""
This section of the test script demonstrates the CRUD operations of a did
followed by creating schema, credential definition and credential issuance.
"""
did = await create_did(issuer)

# Creating a did:cheqd on testnet
did_create_result = await issuer.post("/did/cheqd/create")
did = did_create_result.get("did")
assert did
assert did_create_result.get("verkey")
await resolve_did(issuer, did)

print(did)
# updated_did_document = await update_did(issuer, did, did_document)

# Resolve
resolution_result = await issuer.get(
f"/resolver/resolve/{did}",
)
did_document = resolution_result.get("did_document")
assert did_document
print(did_document)

# Update: Add a service endpoint
did_document["service"] = [
{
"id": f"{did}#service-1",
"type": "MessagingService",
"serviceEndpoint": ["https://example.com/service"],
}
]
did_document["@context"] = []
did_update_result = await issuer.post(
"/did/cheqd/update", json={"didDocument": did_document}
schema_id = await create_schema(issuer, did)
print(schema_id)

credential_definition_id = await create_credential_definition(
issuer, did, schema_id
)
updated_did_doc = did_update_result.get("didDocument")
print(updated_did_doc)
updated_did = did_update_result.get("did")
assert did == updated_did
assert "service" in updated_did_doc, "Key 'metadata' is missing"
assert isinstance(updated_did_doc["service"], dict)
print(credential_definition_id)

# Create Schema
await assert_credential_definitions(issuer, credential_definition_id)
await assert_wallet_dids(issuer, did)

# Create Credential Definition
# Connect issuer and holder
issuer_conn_with_anoncreds_holder, holder_anoncreds_conn = await didexchange(
issuer, holder
)

issue_credential_result = await issue_credential_v2(
issuer,
holder,
issuer_conn_with_anoncreds_holder.connection_id,
holder_anoncreds_conn.connection_id,
credential_definition_id,
{"score": "99"},
)
print(issue_credential_result)

# Deactivate the DID
# await deactivate_did(issuer, did)


if __name__ == "__main__":
Expand Down

0 comments on commit fc3d5cc

Please sign in to comment.