Skip to content

Commit e7f82a8

Browse files
test: resolve the error related to profile context switching
Signed-off-by: Yuki I <[email protected]>
1 parent f26b993 commit e7f82a8

File tree

2 files changed

+344
-0
lines changed

2 files changed

+344
-0
lines changed

acapy_agent/anoncreds/default/legacy_indy/tests/test_registry.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
RevRegDefValue,
4848
)
4949
from ....models.schema import AnonCredsSchema, GetSchemaResult, SchemaResult
50+
from .....core.profile import Profile
51+
from .....wallet.did_info import DIDInfo
5052

5153
B58 = alphabet if isinstance(alphabet, str) else alphabet.decode("ascii")
5254
INDY_DID = rf"^(did:sov:)?[{B58}]{{21,22}}$"
@@ -1215,3 +1217,92 @@ async def test_get_schem_info(self):
12151217
assert result.issuer_id == "XduBsoPyEA4szYMy3pZ8De"
12161218
assert result.name == "minimal-33279d005748b3cc"
12171219
assert result.version == "1.0"
1220+
1221+
@mock.patch.object(
1222+
IndyLedgerRequestsExecutor,
1223+
"get_ledger_for_identifier",
1224+
return_value=(
1225+
"mock_ledger_id",
1226+
mock.MagicMock(
1227+
spec=BaseLedger,
1228+
send_revoc_reg_entry=mock.CoroutineMock(return_value="mock_seq_no"),
1229+
__aenter__=mock.CoroutineMock(return_value=mock.MagicMock()),
1230+
__aexit__=mock.CoroutineMock(return_value=None),
1231+
profile=mock.MagicMock(spec=Profile),
1232+
),
1233+
),
1234+
)
1235+
@mock.patch.object(AskarAnonCredsProfileSession, "handle")
1236+
async def test_register_revocation_list_passes_profile(
1237+
self, mock_askar_handle, mock_get_ledger_for_id
1238+
):
1239+
"""Test register_revocation_list passes profile kwarg via helper."""
1240+
self.profile.inject_or = mock.MagicMock()
1241+
1242+
test_profile = self.profile
1243+
1244+
test_rev_reg_def = RevRegDef(
1245+
tag="tag",
1246+
cred_def_id="IssuerDID:3:CL:1:tag",
1247+
value=RevRegDefValue(
1248+
max_cred_num=100, public_keys={}, tails_hash="", tails_location=""
1249+
),
1250+
issuer_id="IssuerDID",
1251+
type="CL_ACCUM",
1252+
)
1253+
test_rev_list = RevList(
1254+
issuer_id="IssuerDID",
1255+
current_accumulator="dummy_accum_value",
1256+
revocation_list=[0],
1257+
timestamp=1234567890,
1258+
rev_reg_def_id="IssuerDID:4:IssuerDID:3:CL:1:tag:CL_ACCUM:tag",
1259+
)
1260+
1261+
await self.registry.register_revocation_list(
1262+
test_profile,
1263+
test_rev_reg_def,
1264+
test_rev_list,
1265+
{},
1266+
)
1267+
1268+
mock_ledger_instance = mock_get_ledger_for_id.return_value[1]
1269+
1270+
mock_ledger_instance.send_revoc_reg_entry.assert_called_once()
1271+
1272+
_call_args, call_kwargs = mock_ledger_instance.send_revoc_reg_entry.call_args
1273+
1274+
assert "profile" in call_kwargs
1275+
assert call_kwargs["profile"] is test_profile
1276+
assert call_kwargs["write_ledger"] is True
1277+
1278+
async def test_registry_txn_submit_passes_profile(self):
1279+
"""Test registry txn_submit passes profile kwarg to ledger txn_submit."""
1280+
mock_ledger = mock.MagicMock(BaseLedger, autospec=True)
1281+
mock_ledger.txn_submit = mock.CoroutineMock(return_value="mock_ledger_response")
1282+
mock_ledger.__aenter__ = mock.CoroutineMock(return_value=mock_ledger)
1283+
mock_ledger.__aexit__ = mock.CoroutineMock(return_value=None)
1284+
1285+
test_profile = self.profile
1286+
test_txn_data = '{"a": 1}'
1287+
test_sign_did = mock.MagicMock(spec=DIDInfo)
1288+
1289+
await self.registry.txn_submit(
1290+
ledger=mock_ledger,
1291+
ledger_transaction=test_txn_data,
1292+
sign=True,
1293+
taa_accept=True,
1294+
sign_did=test_sign_did,
1295+
write_ledger=True,
1296+
profile=test_profile,
1297+
)
1298+
1299+
mock_ledger.txn_submit.assert_called_once()
1300+
1301+
_call_args, call_kwargs = mock_ledger.txn_submit.call_args
1302+
1303+
assert "profile" in call_kwargs
1304+
assert call_kwargs["profile"] is test_profile
1305+
assert call_kwargs["sign"] is True
1306+
assert call_kwargs["taa_accept"] is True
1307+
assert call_kwargs["sign_did"] is test_sign_did
1308+
assert call_kwargs["write_ledger"] is True

acapy_agent/ledger/tests/test_indy_vdr.py

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from ...wallet.did_info import DIDInfo
1515
from ...wallet.did_method import SOV, DIDMethod, DIDMethods, HolderDefinedDid
1616
from ...wallet.did_posture import DIDPosture
17+
from ...wallet.error import WalletNotFoundError
1718
from ...wallet.key_type import ED25519, KeyTypes
1819
from ..endpoint_type import EndpointType
1920
from ..indy_vdr import (
@@ -27,13 +28,23 @@
2728
VdrError,
2829
)
2930

31+
from ...core.profile import Profile
32+
from ...storage.askar import AskarStorage
33+
from ..util import TAA_ACCEPTED_RECORD_TYPE
34+
3035
WEB = DIDMethod(
3136
name="web",
3237
key_types=[ED25519],
3338
rotation=True,
3439
holder_defined_did=HolderDefinedDid.REQUIRED,
3540
)
3641

42+
TEST_TENANT_DID = "WgWxqztrNooG92RXvxSTWv"
43+
TEST_SCHEMA_SEQ_NO = 4935
44+
TEST_CRED_DEF_TAG = "tenant_tag"
45+
46+
TEST_CRED_DEF_ID = f"{TEST_TENANT_DID}:3:CL:{TEST_SCHEMA_SEQ_NO}:{TEST_CRED_DEF_TAG}"
47+
3748

3849
@pytest_asyncio.fixture
3950
async def ledger():
@@ -1266,3 +1277,245 @@ async def test_rotate_did_keypair(self, ledger: IndyVdrLedger):
12661277
):
12671278
ledger.profile.context.injector.bind_instance(DIDMethods, DIDMethods())
12681279
await ledger.rotate_public_did_keypair()
1280+
1281+
async def _create_tenant_profile(
1282+
self, main_profile: Profile, name: str = "tenant"
1283+
) -> Profile:
1284+
"""Helper to create a secondary profile instance for testing."""
1285+
tenant_settings = {
1286+
"wallet.type": main_profile.settings.get("wallet.type", "askar-anoncreds"),
1287+
"auto_provision": True,
1288+
"wallet.name": f"{name}_wallet",
1289+
"wallet.key": f"test_tenant_key_for_{name}",
1290+
"wallet.key_derivation_method": "RAW",
1291+
"default_label": name,
1292+
}
1293+
tenant_profile = await create_test_profile(settings=tenant_settings)
1294+
tenant_profile.context.injector.bind_instance(
1295+
DIDMethods, main_profile.context.injector.inject(DIDMethods)
1296+
)
1297+
tenant_profile.context.injector.bind_instance(BaseCache, InMemoryCache())
1298+
tenant_profile.context.injector.bind_instance(
1299+
KeyTypes, main_profile.context.injector.inject(KeyTypes)
1300+
)
1301+
await tenant_profile.session()
1302+
return tenant_profile
1303+
1304+
@pytest.mark.asyncio
1305+
async def test_submit_signing_uses_passed_profile_context(
1306+
self, ledger: IndyVdrLedger
1307+
):
1308+
"""Test _submit calls sign_message using the passed profile context."""
1309+
1310+
tenant_profile = await self._create_tenant_profile(
1311+
ledger.profile, "submit_tenant"
1312+
)
1313+
1314+
mock_signing_did = DIDInfo("tenant_signer", "tenant_signer_vk", {}, SOV, ED25519)
1315+
1316+
mock_request_obj = mock.MagicMock(spec=indy_vdr.Request)
1317+
mock_request_obj.signature_input = b"data_to_be_signed"
1318+
mock_request_obj.body = json.dumps({"req": "data"})
1319+
mock_request_obj.set_signature = mock.Mock()
1320+
mock_request_obj.set_txn_author_agreement_acceptance = mock.Mock()
1321+
1322+
with mock.patch(
1323+
"acapy_agent.wallet.askar.AskarWallet.sign_message",
1324+
new_callable=mock.CoroutineMock,
1325+
return_value=b"mock_signature_from_patch",
1326+
) as mock_sign_message_patch:
1327+
ledger.get_wallet_public_did = mock.CoroutineMock(
1328+
return_value=mock_signing_did
1329+
)
1330+
ledger.get_latest_txn_author_acceptance = mock.CoroutineMock(return_value={})
1331+
1332+
async with ledger:
1333+
await ledger._submit(
1334+
mock_request_obj,
1335+
sign=True,
1336+
sign_did=mock_signing_did,
1337+
taa_accept=False,
1338+
write_ledger=False,
1339+
profile=tenant_profile,
1340+
)
1341+
1342+
mock_sign_message_patch.assert_awaited_once_with(
1343+
message=b"data_to_be_signed", from_verkey=mock_signing_did.verkey
1344+
)
1345+
mock_request_obj.set_signature.assert_called_once_with(
1346+
b"mock_signature_from_patch"
1347+
)
1348+
1349+
@pytest.mark.asyncio
1350+
async def test_get_wallet_public_did_uses_passed_profile(self, ledger: IndyVdrLedger):
1351+
"""Test get_wallet_public_did uses the explicitly passed profile."""
1352+
tenant_profile = await self._create_tenant_profile(
1353+
ledger.profile, "get_did_tenant"
1354+
)
1355+
mock_tenant_did = DIDInfo("did:sov:tenant_pub", "vk_pub", {}, SOV, ED25519)
1356+
1357+
with mock.patch(
1358+
"acapy_agent.wallet.askar.AskarWallet.get_public_did",
1359+
new_callable=mock.CoroutineMock,
1360+
return_value=mock_tenant_did,
1361+
) as mock_get_public_patch:
1362+
result_did = await ledger.get_wallet_public_did(profile=tenant_profile)
1363+
1364+
assert result_did is mock_tenant_did
1365+
mock_get_public_patch.assert_awaited_once()
1366+
1367+
@pytest.mark.asyncio
1368+
async def test_get_latest_taa_uses_passed_profile(self, ledger: IndyVdrLedger):
1369+
"""Test get_latest_txn_author_acceptance uses the explicitly passed profile."""
1370+
tenant_profile = await self._create_tenant_profile(
1371+
ledger.profile, "get_taa_tenant"
1372+
)
1373+
tenant_profile.context.injector.bind_instance(BaseCache, InMemoryCache())
1374+
1375+
with mock.patch.object(
1376+
AskarStorage, "find_all_records", return_value=[]
1377+
) as mock_find_records_patch:
1378+
result_taa = await ledger.get_latest_txn_author_acceptance(
1379+
profile=tenant_profile
1380+
)
1381+
1382+
mock_find_records_patch.assert_awaited_once()
1383+
call_args, _call_kwargs = mock_find_records_patch.call_args
1384+
assert call_args[0] == TAA_ACCEPTED_RECORD_TYPE
1385+
assert call_args[1] == {"pool_name": ledger.pool_name}
1386+
1387+
assert result_taa == {}
1388+
1389+
@pytest.mark.asyncio
1390+
async def test_send_revoc_reg_entry_uses_passed_profile(self, ledger: IndyVdrLedger):
1391+
"""Test send_revoc_reg_entry passes the correct profile to _submit."""
1392+
tenant_profile = await self._create_tenant_profile(
1393+
ledger.profile, "rev_entry_tenant"
1394+
)
1395+
1396+
async with tenant_profile.session() as session:
1397+
wallet = session.inject(BaseWallet)
1398+
tenant_did = await wallet.create_public_did(SOV, ED25519)
1399+
1400+
test_rev_reg_id = f"{tenant_did.did}:4:{TEST_CRED_DEF_ID}:CL_ACCUM:0"
1401+
test_reg_entry = {"ver": "1.0", "value": {"accum": "test_accum"}}
1402+
1403+
with (
1404+
mock.patch.object(
1405+
IndyVdrLedger,
1406+
"get_revoc_reg_def",
1407+
new=mock.CoroutineMock(
1408+
return_value={"txn": {"data": {"revocDefType": "CL_ACCUM"}}}
1409+
),
1410+
),
1411+
mock.patch.object(
1412+
IndyVdrLedger,
1413+
"_create_revoc_reg_entry_request",
1414+
new=mock.CoroutineMock(
1415+
return_value=mock.MagicMock(spec=indy_vdr.Request)
1416+
),
1417+
),
1418+
mock.patch.object(
1419+
IndyVdrLedger,
1420+
"_submit",
1421+
new=mock.CoroutineMock(return_value={"result": "mock_ok"}),
1422+
) as mock_submit,
1423+
):
1424+
async with ledger:
1425+
await ledger.send_revoc_reg_entry(
1426+
revoc_reg_id=test_rev_reg_id,
1427+
revoc_def_type="CL_ACCUM",
1428+
revoc_reg_entry=test_reg_entry,
1429+
write_ledger=True,
1430+
profile=tenant_profile,
1431+
)
1432+
1433+
mock_submit.assert_awaited_once()
1434+
_, submit_kwargs = mock_submit.call_args
1435+
assert submit_kwargs.get("profile") is tenant_profile
1436+
assert submit_kwargs.get("sign_did").did == tenant_did.did
1437+
1438+
@pytest.mark.asyncio
1439+
async def test_ledger_txn_submit_uses_passed_profile(self, ledger: IndyVdrLedger):
1440+
"""Test ledger txn_submit passes profile kwarg to _submit."""
1441+
tenant_profile = await self._create_tenant_profile(
1442+
ledger.profile, "txn_submit_tenant"
1443+
)
1444+
1445+
ledger._submit = mock.CoroutineMock(
1446+
return_value={"op": "REPLY", "result": {"status": "ok"}}
1447+
)
1448+
1449+
test_txn_data = '{"req": "data"}'
1450+
test_sign_did = mock.MagicMock(spec=DIDInfo)
1451+
1452+
await ledger.txn_submit(
1453+
test_txn_data,
1454+
sign=True,
1455+
sign_did=test_sign_did,
1456+
write_ledger=True,
1457+
profile=tenant_profile,
1458+
)
1459+
1460+
ledger._submit.assert_awaited_once()
1461+
_submit_args, submit_kwargs = ledger._submit.call_args
1462+
assert "profile" in submit_kwargs
1463+
assert submit_kwargs["profile"] is tenant_profile
1464+
assert _submit_args[0] == test_txn_data
1465+
assert submit_kwargs["sign"] is True
1466+
assert submit_kwargs["sign_did"] is test_sign_did
1467+
assert submit_kwargs["write_ledger"] is True
1468+
1469+
@pytest.mark.asyncio
1470+
async def test_submit_wallet_not_found_error(self, ledger: IndyVdrLedger):
1471+
async with ledger.profile.session() as session:
1472+
wallet = session.inject(BaseWallet)
1473+
test_did = await wallet.create_public_did(SOV, ED25519)
1474+
# Create a DID not present in the wallet
1475+
invalid_did = DIDInfo(
1476+
did="did:sov:invalid",
1477+
verkey="invalid_verkey",
1478+
metadata={},
1479+
method=SOV,
1480+
key_type=ED25519,
1481+
)
1482+
async with ledger:
1483+
test_msg = indy_vdr.ledger.build_get_txn_request(test_did.did, 1, 1)
1484+
with pytest.raises(WalletNotFoundError):
1485+
await ledger._submit(
1486+
test_msg, sign=True, sign_did=invalid_did, write_ledger=False
1487+
)
1488+
1489+
@pytest.mark.asyncio
1490+
async def test_submit_unexpected_error(self, ledger: IndyVdrLedger):
1491+
async with ledger.profile.session() as session:
1492+
wallet = session.inject(BaseWallet)
1493+
test_did = await wallet.create_public_did(SOV, ED25519)
1494+
async with ledger:
1495+
test_msg = indy_vdr.ledger.build_get_txn_request(test_did.did, 1, 1)
1496+
ledger.pool_handle.submit_request.side_effect = ValueError("Unexpected error")
1497+
with pytest.raises(LedgerTransactionError) as exc_info:
1498+
await ledger._submit(test_msg)
1499+
assert "Unexpected error during ledger submission" in str(exc_info.value)
1500+
assert isinstance(exc_info.value.__cause__, ValueError)
1501+
1502+
@pytest.mark.asyncio
1503+
async def test_txn_submit_passes_profile(self, ledger: IndyVdrLedger):
1504+
tenant_profile = await self._create_tenant_profile(
1505+
ledger.profile, "submit_tenant"
1506+
)
1507+
test_txn_data = '{"req": "data"}'
1508+
mock_sign_did = mock.MagicMock(spec=DIDInfo)
1509+
ledger._submit = mock.CoroutineMock(return_value={"result": "ok"})
1510+
1511+
await ledger.txn_submit(
1512+
test_txn_data,
1513+
sign=True,
1514+
sign_did=mock_sign_did,
1515+
write_ledger=True,
1516+
profile=tenant_profile,
1517+
)
1518+
1519+
ledger._submit.assert_awaited_once()
1520+
_, kwargs = ledger._submit.call_args
1521+
assert kwargs.get("profile") == tenant_profile

0 commit comments

Comments
 (0)