From 20068c795a186b63a1d0a17c0c9323bffe28eaa3 Mon Sep 17 00:00:00 2001 From: Kent Bull Date: Tue, 21 Jul 2026 13:59:29 -0600 Subject: [PATCH] clean up stale local paths --- docs/releasing.md | 12 +-- scripts/README.md | 2 +- tests/integration/conftest.py | 12 ++- tests/integration/constants.py | 2 + tests/integration/test_untargeted_ipex.py | 88 +++++++++++++++++++ tests/schema/optional-issuee-attestation.json | 45 ++++++++++ 6 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 tests/integration/test_untargeted_ipex.py create mode 100644 tests/schema/optional-issuee-attestation.json diff --git a/docs/releasing.md b/docs/releasing.md index 5448f2d..ab8f956 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -75,7 +75,7 @@ consumed fragment files. Create a fragment for PR or issue `100` using the configured SignifyPy types: ```bash -cd /Users/kbull/code/keri/kentbull/signifypy +cd signifypy ./venv/bin/python -m towncrier create --dir newsfragments \ --content "Documented the repository-secret PyPI publish flow." \ 100.doc.md @@ -84,7 +84,7 @@ cd /Users/kbull/code/keri/kentbull/signifypy Another example for a bug fix: ```bash -cd /Users/kbull/code/keri/kentbull/signifypy +cd signifypy ./venv/bin/python -m towncrier create --dir newsfragments \ --content "Fixed release workflow auth to use the repository secret PYPI_API_TOKEN." \ 101.fixed.md @@ -93,7 +93,7 @@ cd /Users/kbull/code/keri/kentbull/signifypy You can also create the fragment file yourself if that is faster: ```bash -cd /Users/kbull/code/keri/kentbull/signifypy +cd signifypy cat > newsfragments/102.misc.md <<'EOF' Clarified the maintainer release runbook with concrete Towncrier examples. EOF @@ -102,14 +102,14 @@ EOF Preview the unreleased changelog without modifying tracked files: ```bash -cd /Users/kbull/code/keri/kentbull/signifypy +cd signifypy ./venv/bin/python -m towncrier build --draft --version 0.4.1 ``` Build the actual `0.4.1` changelog entry during release preparation: ```bash -cd /Users/kbull/code/keri/kentbull/signifypy +cd signifypy ./venv/bin/python -m towncrier build --yes --version 0.4.1 ``` @@ -117,7 +117,7 @@ Or let the maintained release-prep target do the version bump, lock refresh, Towncrier build, and release commit together: ```bash -cd /Users/kbull/code/keri/kentbull/signifypy +cd signifypy make release-patch ``` diff --git a/scripts/README.md b/scripts/README.md index f751f98..eff7547 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -4,7 +4,7 @@ The old manual workflow scripts that used to live in this directory have been retired. Those scenarios now belong to the pytest-managed integration harness in -[`tests/integration`](/Users/kbull/code/keri/kentbull/signifypy/tests/integration), +[`tests/integration`](/signifypy/tests/integration), which starts witnesses, KERIA, and `vlei-server` itself instead of requiring multiple external terminal sessions. diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index c1143f4..d771384 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -30,6 +30,7 @@ from contextlib import contextmanager import json import os +import shutil import socket import subprocess from pathlib import Path @@ -66,6 +67,7 @@ def _dependency_root(dependency): KERIPY_ROOT = _dependency_root(KERIPY) KERIA_ROOT = _dependency_root(KERIA) VLEI_ROOT = _dependency_root(VLEI) +UNTARGETED_SCHEMA_PATH = SIGNIFYPY_ROOT / "tests" / "schema" / "optional-issuee-attestation.json" SIGNIFYPY_PYTHON = Path(os.getenv("SIGNIFYPY_INTEGRATION_SIGNIFYPY_PYTHON", SIGNIFYPY_ROOT / "venv" / "bin" / "python")).expanduser() KERIA_PYTHON = Path(os.getenv("SIGNIFYPY_INTEGRATION_KERIA_PYTHON", KERIA_ROOT / "venv" / "bin" / "python")).expanduser() VLEI_PYTHON = Path(os.getenv("SIGNIFYPY_INTEGRATION_VLEI_PYTHON", VLEI_ROOT / "venv" / "bin" / "python")).expanduser() @@ -383,6 +385,7 @@ def _launch_live_stack(live_stack: dict): vlei_python = _require_python(VLEI_PYTHON, "vLEI") runtime_root = live_stack["runtime_root"] config_root = live_stack["config_root"] + schema_dir = _prepare_schema_directory(runtime_root) _write_canonical_witness_configs(config_root, live_stack) _write_keria_config(config_root, live_stack) @@ -465,7 +468,7 @@ def _launch_live_stack(live_stack: dict): "-u", str(VLEI_SERVER_SCRIPT), "--schema-dir", - str(VLEI_ROOT / "schema" / "acdc"), + str(schema_dir), "--cred-dir", str(VLEI_ROOT / "samples" / "acdc"), "--oobi-dir", @@ -581,3 +584,10 @@ def isolated_client_factory(isolated_live_stack): boundaries. Most business-workflow tests should stay on `client_factory`. """ return _client_factory(isolated_live_stack) +def _prepare_schema_directory(runtime_root: Path) -> Path: + """Overlay integration-owned schemas on the pinned vLEI schema set.""" + schema_dir = runtime_root / "schemas" + shutil.copytree(VLEI_ROOT / "schema" / "acdc", schema_dir) + shutil.copy2(UNTARGETED_SCHEMA_PATH, schema_dir / UNTARGETED_SCHEMA_PATH.name) + return schema_dir + diff --git a/tests/integration/constants.py b/tests/integration/constants.py index 5bf44d9..a9ceebb 100644 --- a/tests/integration/constants.py +++ b/tests/integration/constants.py @@ -6,12 +6,14 @@ """ QVI_SCHEMA_SAID = "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao" +UNTARGETED_ATTESTATION_SCHEMA_SAID = "EAv8omZ-o3Pk45h72_WnIpt6LTWNzc8hmLjeblpxB9vz" ADDITIONAL_SCHEMA_OOBI_SAIDS = { "legal-entity": "ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY", "ecr-auth": "EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g", "ecr": "EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw", "oor-auth": "EKA57bKBKxr_kN7iN5i7lMUxpMG-s19dRcmov1iDxz-E", "oor": "EBNaNu-M9P5cgrnfl2Fvymy4E_jvxxyjb70PRtiANlJy", + "untargeted-attestation": UNTARGETED_ATTESTATION_SCHEMA_SAID, } WITNESS_AIDS = [ diff --git a/tests/integration/test_untargeted_ipex.py b/tests/integration/test_untargeted_ipex.py new file mode 100644 index 0000000..fbc2b9c --- /dev/null +++ b/tests/integration/test_untargeted_ipex.py @@ -0,0 +1,88 @@ +"""Regression coverage for presenting untargeted ACDCs through IPEX.""" + +from __future__ import annotations + +import pytest + +from .constants import TEST_WITNESS_AIDS, UNTARGETED_ATTESTATION_SCHEMA_SAID +from .helpers import ( + alias, + create_identifier, + create_registry, + resolve_agent_oobi, + resolve_schema_oobi, + send_credential_grant, + submit_admit, + wait_for_credential, + wait_for_notification, + wait_for_operation, +) + + +pytestmark = pytest.mark.integration + + +def test_untargeted_acdc_grant_delivers_artifacts_to_disclosee(client_factory): + """Present an ACDC without an issuee to an independently addressed disclosee. + + The disclosee intentionally does not resolve the issuer's OOBI first. An + IPEX Grant is responsible for delivering the issuer KEL and the credential + artifacts needed to validate the embedded untargeted attestation. + """ + issuer_client = client_factory() + disclosee_client = client_factory() + issuer_name = alias("untargeted-issuer") + disclosee_name = alias("untargeted-disclosee") + registry_name = alias("untargeted-registry") + + issuer = create_identifier(issuer_client, issuer_name, wits=TEST_WITNESS_AIDS) + disclosee = create_identifier(disclosee_client, disclosee_name, wits=TEST_WITNESS_AIDS) + + # The issuer must know where to send the Grant. Do not resolve the reverse + # direction: successful presentation must bootstrap the disclosee with the + # issuer artifacts carried by the Grant workflow. + resolve_agent_oobi(disclosee_client, disclosee_name, issuer_client) + resolve_schema_oobi(issuer_client, UNTARGETED_ATTESTATION_SCHEMA_SAID) + resolve_schema_oobi(disclosee_client, UNTARGETED_ATTESTATION_SCHEMA_SAID) + + create_registry(issuer_client, issuer_name, registry_name) + issued = issuer_client.credentials().issue( + issuer_name, + registry_name, + data={"claim": "An issuer-authored observation addressed to whom it may concern."}, + schema=UNTARGETED_ATTESTATION_SCHEMA_SAID, + recipient=None, + edges={}, + rules={}, + ) + wait_for_operation(issuer_client, issued.op()) + + assert issued.acdc.sad["i"] == issuer["prefix"] + assert "i" not in issued.acdc.sad["a"] + + send_credential_grant( + issuer_client, + issuer_name=issuer_name, + recipient=disclosee["prefix"], + creder=issued.acdc, + iserder=issued.iss, + anc=issued.anc, + sigs=issued.sigs, + ) + + grant_note = wait_for_notification( + disclosee_client, + "/exn/ipex/grant", + timeout=30.0, + ) + submit_admit( + disclosee_client, + holder_name=disclosee_name, + issuer_prefix=issuer["prefix"], + notification=grant_note, + ) + received = wait_for_credential(disclosee_client, issued.acdc.said) + + assert received["sad"]["d"] == issued.acdc.said + assert received["sad"]["i"] == issuer["prefix"] + assert "i" not in received["sad"]["a"] diff --git a/tests/schema/optional-issuee-attestation.json b/tests/schema/optional-issuee-attestation.json new file mode 100644 index 0000000..a2e77f6 --- /dev/null +++ b/tests/schema/optional-issuee-attestation.json @@ -0,0 +1,45 @@ +{ + "$id": "EAv8omZ-o3Pk45h72_WnIpt6LTWNzc8hmLjeblpxB9vz", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Optional Issuee", + "description": "A credential with an optional issuee", + "credentialType": "UntargetedAttestation", + "properties": { + "v": {"type": "string"}, + "d": {"type": "string"}, + "i": {"type": "string"}, + "ri": { + "description": "credential status registry", + "type": "string" + }, + "s": { + "description": "schema SAID", + "type": "string" + }, + "a": { + "properties": { + "d": {"type": "string"}, + "i": {"type": "string"}, + "dt": { + "format": "date-time", + "type": "string" + }, + "claim": {"type": "string"} + }, + "additionalProperties": false, + "required": ["dt", "claim"], + "type": "object" + }, + "e": { + "description": "edges block", + "type": "object" + }, + "r": { + "type": "object", + "description": "rules block" + } + }, + "additionalProperties": false, + "required": ["i", "ri", "s", "d", "e", "r"], + "type": "object" +}