From 529b468ac391f0f5bfedb96f2b6d21a67fb0bc5b Mon Sep 17 00:00:00 2001 From: Fergal Date: Tue, 3 Sep 2024 20:16:18 +0100 Subject: [PATCH] feat: use SAID for long running operation oid instead of identifier prefix (#289) * feat: use SAID for op oid instead of pre * test: coverage on query endpoint and op metadata validation * fix: revert optypes.submit (code shouldnt be included and also doesnt make sense given the impl in longrunning.py) --- src/keria/app/agenting.py | 11 ++-- src/keria/app/aiding.py | 31 +++++---- src/keria/app/credentialing.py | 6 +- src/keria/app/delegating.py | 6 +- src/keria/app/grouping.py | 2 +- src/keria/app/ipexing.py | 20 +++--- src/keria/core/longrunning.py | 71 +++++++++++---------- tests/app/test_agenting.py | 46 ++++++++++++++ tests/app/test_grouping.py | 4 +- tests/app/test_ipexing.py | 10 +-- tests/core/test_longrunning.py | 111 +++++++++++++++++++++++++++++++-- 11 files changed, 237 insertions(+), 81 deletions(-) diff --git a/src/keria/app/agenting.py b/src/keria/app/agenting.py index 9dfba897..e046abd6 100644 --- a/src/keria/app/agenting.py +++ b/src/keria/app/agenting.py @@ -1218,20 +1218,21 @@ def on_post(req, rep): pre = httping.getRequiredParam(body, "pre") qry = dict(pre=pre) - meta = dict() + oid = pre if "anchor" in body: - meta["anchor"] = body["anchor"] + qry["anchor"] = body["anchor"] + oid = f"{pre}.{body["anchor"]["d"]}" elif "sn" in body: - meta["sn"] = body["sn"] + qry["sn"] = body["sn"] + oid = f"{pre}.{body["sn"]}" else: # Must reset key state so we know when we have a new update. for (keys, saider) in agent.hby.db.knas.getItemIter(keys=(pre,)): agent.hby.db.knas.rem(keys) agent.hby.db.ksns.rem((saider.qb64,)) agent.hby.db.ksns.rem((saider.qb64,)) - qry.update(meta) agent.queries.append(qry) - op = agent.monitor.submit(pre, longrunning.OpTypes.query, metadata=meta) + op = agent.monitor.submit(oid, longrunning.OpTypes.query, metadata=qry) rep.status = falcon.HTTP_202 rep.content_type = "application/json" diff --git a/src/keria/app/aiding.py b/src/keria/app/aiding.py index 2a8f08d9..9b337dfb 100644 --- a/src/keria/app/aiding.py +++ b/src/keria/app/aiding.py @@ -552,7 +552,7 @@ def on_post(req, rep): ) ) op = agent.monitor.submit( - serder.pre, longrunning.OpTypes.group, metadata=dict(sn=0) + serder.pre, longrunning.OpTypes.group, metadata=dict(pre=hab.pre, sn=0) ) rep.content_type = "application/json" @@ -622,7 +622,7 @@ def on_post(req, rep): op = agent.monitor.submit( hab.kever.prefixer.qb64, longrunning.OpTypes.witness, - metadata=dict(sn=0), + metadata=dict(pre=hab.pre, sn=0), ) rep.status = falcon.HTTP_202 rep.data = op.to_json().encode("utf-8") @@ -632,7 +632,7 @@ def on_post(req, rep): op = agent.monitor.submit( hab.kever.prefixer.qb64, longrunning.OpTypes.done, - metadata=dict(response=serder.ked), + metadata=dict(pre=hab.pre, response=serder.ked), ) rep.data = op.to_json().encode("utf-8") @@ -887,7 +887,7 @@ def rotate(agent, name, body): ) ) op = agent.monitor.submit( - serder.pre, longrunning.OpTypes.group, metadata=dict(sn=serder.sn) + serder.said, longrunning.OpTypes.group, metadata=dict(pre=hab.pre, sn=serder.sn) ) return op @@ -895,7 +895,7 @@ def rotate(agent, name, body): if hab.kever.delpre: agent.anchors.append(dict(alias=name, pre=hab.pre, sn=serder.sn)) op = agent.monitor.submit( - hab.kever.prefixer.qb64, + serder.said, longrunning.OpTypes.delegation, metadata=dict(pre=hab.pre, sn=serder.sn), ) @@ -904,14 +904,14 @@ def rotate(agent, name, body): if hab.kever.wits: agent.witners.append(dict(serder=serder)) op = agent.monitor.submit( - hab.kever.prefixer.qb64, + serder.said, longrunning.OpTypes.witness, - metadata=dict(sn=serder.sn), + metadata=dict(pre=hab.pre, sn=serder.sn), ) return op op = agent.monitor.submit( - hab.kever.prefixer.qb64, + serder.said, longrunning.OpTypes.done, metadata=dict(response=serder.ked), ) @@ -945,7 +945,7 @@ def interact(agent, name, body): if "group" in body: agent.groups.append(dict(pre=hab.pre, serder=serder, sigers=sigers)) op = agent.monitor.submit( - serder.pre, longrunning.OpTypes.group, metadata=dict(sn=serder.sn) + serder.said, longrunning.OpTypes.group, metadata=dict(pre=hab.pre, sn=serder.sn) ) return op @@ -953,20 +953,19 @@ def interact(agent, name, body): if hab.kever.wits: agent.witners.append(dict(serder=serder)) op = agent.monitor.submit( - hab.kever.prefixer.qb64, + serder.said, longrunning.OpTypes.witness, - metadata=dict(sn=serder.sn), + metadata=dict(pre=hab.pre, sn=serder.sn), ) return op op = agent.monitor.submit( - hab.kever.prefixer.qb64, + serder.said, longrunning.OpTypes.done, metadata=dict(response=serder.ked), ) return op - - + @staticmethod def submit_id(agent, name, body): hab = agent.hby.habByName(name) @@ -976,9 +975,9 @@ def submit_id(agent, name, body): code = body.get("code") if hab.kever.wits: - agent.submits.append(dict(alias=name,code=code)) + agent.submits.append(dict(alias=name, code=code)) op = agent.monitor.submit(hab.kever.prefixer.qb64, longrunning.OpTypes.submit, - metadata=dict(alias=name,sn=hab.kever.sn)) + metadata=dict(alias=name, sn=hab.kever.sn)) return op raise falcon.HTTPBadRequest(title=f"invalid identifier submitted, {name} has no witnesses") diff --git a/src/keria/app/credentialing.py b/src/keria/app/credentialing.py index acc7230c..2ab85c16 100644 --- a/src/keria/app/credentialing.py +++ b/src/keria/app/credentialing.py @@ -183,8 +183,8 @@ def on_post(self, req, rep, name): seqner = coring.Seqner(sn=ixn.sn) prefixer = coring.Prefixer(qb64=ixn.pre) agent.registrar.incept(hab, registry, prefixer=prefixer, seqner=seqner, saider=coring.Saider(qb64=ixn.said)) - op = agent.monitor.submit(hab.kever.prefixer.qb64, longrunning.OpTypes.registry, - metadata=dict(anchor=anchor, depends=op)) + op = agent.monitor.submit(registry.regk, longrunning.OpTypes.registry, + metadata=dict(pre=hab.kever.prefixer.qb64, anchor=anchor, depends=op)) rep.status = falcon.HTTP_202 rep.data = op.to_json().encode("utf-8") @@ -604,7 +604,7 @@ def on_post(self, req, rep, name): agent.credentialer.validate(creder) agent.registrar.issue(regk, iserder, anc) agent.credentialer.issue(creder=creder, serder=iserder) - op = agent.monitor.submit(hab.kever.prefixer.qb64, longrunning.OpTypes.credential, + op = agent.monitor.submit(creder.said, longrunning.OpTypes.credential, metadata=dict(ced=creder.sad, depends=op)) except kering.ConfigurationError as e: diff --git a/src/keria/app/delegating.py b/src/keria/app/delegating.py index 797d19b3..843cfc04 100644 --- a/src/keria/app/delegating.py +++ b/src/keria/app/delegating.py @@ -216,9 +216,9 @@ def on_post(self, req, rep, name): # successful approval returns the delegatee prefix teepre = approveDelegation(hab, anc) - adop = agent.monitor.submit(hab.kever.prefixer.qb64, longrunning.OpTypes.delegation, - metadata=dict(teepre=teepre, anchor=anc, depends=op)) - + adop = agent.monitor.submit(anc["d"], longrunning.OpTypes.delegation, + metadata=dict(pre=hab.kever.prefixer.qb64, teepre=teepre, anchor=anc, depends=op)) + try: rep.status = falcon.HTTP_200 rep.content_type = "application/json" diff --git a/src/keria/app/grouping.py b/src/keria/app/grouping.py index cfa34722..1ffb6ed8 100644 --- a/src/keria/app/grouping.py +++ b/src/keria/app/grouping.py @@ -195,7 +195,7 @@ def on_post(req, rep, name): agent.inceptGroup(pre=gid, mpre=mhab.pre, verfers=verfers, digers=digers) agent.groups.append(dict(pre=hab.pre, serder=serder, sigers=sigers, smids=smids, rmids=rmids)) - op = agent.monitor.submit(serder.pre, longrunning.OpTypes.group, metadata=dict(sn=serder.sn)) + op = agent.monitor.submit(serder.said, longrunning.OpTypes.group, metadata=dict(pre=serder.pre, sn=serder.sn)) rep.content_type = "application/json" rep.status = falcon.HTTP_202 diff --git a/src/keria/app/ipexing.py b/src/keria/app/ipexing.py index 6c77c654..2c70533b 100644 --- a/src/keria/app/ipexing.py +++ b/src/keria/app/ipexing.py @@ -101,7 +101,7 @@ def sendAdmit(agent, hab, ked, sigs, rec): agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) agent.admits.append(dict(said=ked['d'], pre=hab.pre)) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) @staticmethod def sendMultisigExn(agent, hab, ked, sigs, atc, rec): @@ -151,7 +151,7 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[admitked['a']['i']], topic="credential")) agent.admits.append(dict(said=admitked['d'], pre=hab.pre)) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) class IpexGrantCollectionEnd: @@ -226,7 +226,7 @@ def sendGrant(agent, hab, ked, sigs, atc, rec): agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) agent.grants.append(dict(said=ked['d'], pre=hab.pre, rec=rec)) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) @staticmethod def sendMultisigExn(agent, hab, ked, sigs, atc, rec): @@ -273,7 +273,7 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[grantRec], topic="credential")) agent.grants.append(dict(said=grant['d'], pre=hab.pre, rec=[grantRec])) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) class IpexApplyCollectionEnd: @@ -343,7 +343,7 @@ def sendApply(agent, hab, ked, sigs, rec): agent.hby.psr.parseOne(ims=bytearray(ims)) agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) @staticmethod def sendMultisigExn(agent, hab, ked, sigs, atc, rec): @@ -387,7 +387,7 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): agent.hby.psr.parseOne(ims=ims) agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[applyRec], topic="credential")) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) class IpexOfferCollectionEnd: @@ -457,7 +457,7 @@ def sendOffer(agent, hab, ked, sigs, atc, rec): agent.hby.psr.parseOne(ims=bytearray(ims)) agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) @staticmethod def sendMultisigExn(agent, hab, ked, sigs, atc, rec): @@ -505,7 +505,7 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): agent.hby.psr.parseOne(ims=ims) agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[offerRec], topic="credential")) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) class IpexAgreeCollectionEnd: @@ -574,7 +574,7 @@ def sendAgree(agent, hab, ked, sigs, rec): agent.hby.psr.parseOne(ims=bytearray(ims)) agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=rec, topic='credential')) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) @staticmethod def sendMultisigExn(agent, hab, ked, sigs, atc, rec): @@ -622,4 +622,4 @@ def sendMultisigExn(agent, hab, ked, sigs, atc, rec): agent.hby.psr.parseOne(ims=ims) agent.exchanges.append(dict(said=serder.said, pre=hab.pre, rec=[agreeRec], topic="credential")) - return agent.monitor.submit(serder.pre, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) + return agent.monitor.submit(serder.said, longrunning.OpTypes.exchange, metadata=dict(said=serder.said)) diff --git a/src/keria/core/longrunning.py b/src/keria/core/longrunning.py index 16a21071..e29be54a 100644 --- a/src/keria/core/longrunning.py +++ b/src/keria/core/longrunning.py @@ -191,14 +191,15 @@ def status(self, op): ) if op.type in (OpTypes.witness,): - if op.oid not in self.hby.kevers: - raise kering.ValidationError(f"long running {op.type} operation identifier {op.oid} not found") + if "pre" not in op.metadata or "sn" not in op.metadata: + raise kering.ValidationError(f"invalid long running {op.type} operation, metadata missing required fields ('pre', 'sn')") - if "sn" not in op.metadata: - raise kering.ValidationError(f"invalid long running {op.type} operation, metadata missing 'sn' field") + pre = op.metadata["pre"] + if pre not in self.hby.kevers: + raise kering.ValidationError(f"identifier {pre} not found for long running {op.oid} ({op.type})") sn = op.metadata["sn"] - kever = self.hby.kevers[op.oid] + kever = self.hby.kevers[pre] sdig = self.hby.db.getKeLast(key=dbing.snKey(pre=kever.prefixer.qb64b, sn=sn)) if sdig is not None: @@ -217,7 +218,7 @@ def status(self, op): if (dtnow - start) > datetime.timedelta(seconds=eventing.Kevery.TimeoutPWE): operation.done = True operation.error = Status(code=408, # Using HTTP error codes here for lack of a better alternative - message=f"long running {op.type} for {op.oid} operation timed out before " + message=f"long running {op.type} for {op.oid} (pre: {pre}) operation timed out before " f"receiving sufficient witness receipts") else: operation.done = False @@ -249,10 +250,14 @@ def status(self, op): operation.done = False elif op.type in (OpTypes.delegation, ): - if op.oid not in self.hby.kevers: - raise kering.ValidationError(f"long running {op.type} operation identifier {op.oid} not found") + if "pre" not in op.metadata: + raise kering.ValidationError(f"invalid long running {op.type} operation, metadata missing required field 'pre'") - kever = self.hby.kevers[op.oid] + pre = op.metadata["pre"] + if pre not in self.hby.kevers: + raise kering.ValidationError(f"long running {op.type} operation identifier {pre} not found (oid: {op.oid})") + + kever = self.hby.kevers[pre] reqsn = "sn" reqtee = "teepre" @@ -261,7 +266,7 @@ def status(self, op): if reqsn in op.metadata: #delegatee detects successful delegation sn = op.metadata["sn"] seqner = coring.Seqner(sn=sn) - sdig = self.hby.db.getKeLast(key=dbing.snKey(pre=op.oid, sn=sn)) + sdig = self.hby.db.getKeLast(key=dbing.snKey(pre=pre, sn=sn)) if self.swain.complete(kever.prefixer, seqner): evt = self.hby.db.getEvt(dbing.dgKey(pre=kever.prefixer.qb64, dig=bytes(sdig))) @@ -285,14 +290,15 @@ def status(self, op): raise falcon.HTTPBadRequest(description=f"longrunning operation type {op.type} requires one of {required}, but are missing from request") elif op.type in (OpTypes.group, ): - if "sn" not in op.metadata: - raise kering.ValidationError(f"invalid long running {op.type} operation, metadata missing 'sn' field") + if "pre" not in op.metadata or "sn" not in op.metadata: + raise kering.ValidationError(f"invalid long running {op.type} operation, metadata missing required fields ('pre', 'sn')") - prefixer = coring.Prefixer(qb64=op.oid) + pre = op.metadata["pre"] + prefixer = coring.Prefixer(qb64=pre) seqner = coring.Seqner(sn=op.metadata["sn"]) if self.counselor.complete(prefixer, seqner): - sdig = self.hby.db.getKeLast(key=dbing.snKey(pre=op.oid, sn=seqner.sn)) + sdig = self.hby.db.getKeLast(key=dbing.snKey(pre=pre, sn=seqner.sn)) evt = self.hby.db.getEvt(dbing.dgKey(pre=prefixer.qb64, dig=bytes(sdig))) serder = serdering.SerderKERI(raw=bytes(evt)) @@ -302,11 +308,15 @@ def status(self, op): operation.done = False elif op.type in (OpTypes.query, ): - if op.oid not in self.hby.kevers: + if "pre" not in op.metadata: + raise kering.ValidationError(f"invalid long running {op.type} operation, metadata missing required field 'pre'") + + pre = op.metadata["pre"] + if pre not in self.hby.kevers: operation.done = False else: - kever = self.hby.kevers[op.oid] + kever = self.hby.kevers[pre] if "sn" in op.metadata: sn = int(op.metadata['sn'], 16) if kever.sn >= sn: @@ -316,14 +326,14 @@ def status(self, op): operation.done = False elif "anchor" in op.metadata: anchor = op.metadata["anchor"] - if self.hby.db.findAnchoringSealEvent(op.oid, seal=anchor) is not None: + if self.hby.db.findAnchoringSealEvent(pre, seal=anchor) is not None: operation.done = True operation.response = asdict(kever.state()) else: operation.done = False else: ksn = None - for (_, saider) in self.hby.db.knas.getItemIter(keys=(op.oid,)): + for (_, saider) in self.hby.db.knas.getItemIter(keys=(pre,)): ksn = self.hby.db.ksns.get(keys=(saider.qb64,)) break @@ -334,15 +344,17 @@ def status(self, op): operation.done = False elif op.type in (OpTypes.registry, ): - if op.oid not in self.hby.kevers: + if "pre" not in op.metadata or "anchor" not in op.metadata: raise kering.ValidationError( - f"long running {op.type} operation identifier {op.oid} not found") - if "anchor" not in op.metadata: + f"invalid long running {op.type} operation, metadata missing required fields ('pre', 'anchor')") + + pre = op.metadata["pre"] + if pre not in self.hby.kevers: raise kering.ValidationError( - f"invalid long running {op.type} operation, metadata missing 'anchor' field") + f"identifier {pre} for long running {op.type} operation {op.oid} not found") anchor = op.metadata["anchor"] - if self.hby.db.findAnchoringSealEvent(op.oid, seal=anchor) is not None: + if self.hby.db.findAnchoringSealEvent(pre, seal=anchor) is not None: operation.done = True operation.response = dict(anchor=anchor) else: @@ -361,21 +373,16 @@ def status(self, op): operation.done = False elif op.type in (OpTypes.exchange,): - if "said" not in op.metadata: - raise kering.ValidationError( - f"invalid long running {op.type} operation, metadata missing 'said' field") - - said = op.metadata["said"] - if self.exchanger.complete(said): + if self.exchanger.complete(op.oid): operation.done = True - operation.response = dict(said=said) + operation.response = dict(said=op.oid) else: operation.done = False elif op.type in (OpTypes.endrole, ): if "cid" not in op.metadata or "role" not in op.metadata or "eid" not in op.metadata: raise kering.ValidationError( - f"invalid long running {op.type} operation, metadata missing 'ced' field") + f"invalid long running {op.type} operation, metadata missing required fields ('cid', 'role', 'eid')") cid = op.metadata['cid'] role = op.metadata['role'] @@ -396,7 +403,7 @@ def status(self, op): if "words" not in op.metadata: raise kering.ValidationError( - f"invalid long running {op.type} operation, metadata missing 'ced' field") + f"invalid long running {op.type} operation, metadata missing 'words' field") found = False words = op.metadata["words"] diff --git a/tests/app/test_agenting.py b/tests/app/test_agenting.py index 3782595e..7cc253d0 100644 --- a/tests/app/test_agenting.py +++ b/tests/app/test_agenting.py @@ -448,6 +448,52 @@ def test_querier(helpers): assert qryDoer.pre == "EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9" +def test_query_ends(helpers): + with helpers.openKeria() as (agency, agent, app, client): + queryEnd = agenting.QueryCollectionEnd() + app.add_route("/queries", queryEnd) + + result = client.simulate_post(path="/queries") + assert result.status == falcon.HTTP_400 + + result = client.simulate_post(path="/queries", body=json.dumps(dict())) + assert result.status == falcon.HTTP_400 + + body = dict(pre="EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9") + result = client.simulate_post(path="/queries", body=json.dumps(body)) + assert result.status == falcon.HTTP_202 + assert result.json == {'done': False, + 'error': None, + 'metadata': {'pre': 'EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9'}, + 'name': 'query.EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9', + 'response': None} + assert len(agent.queries) == 1 + + snbody = dict(pre="EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9", sn="2") + result = client.simulate_post(path="/queries", body=json.dumps(snbody)) + assert result.status == falcon.HTTP_202 + assert result.json == {'done': False, + 'error': None, + 'metadata': {'pre': 'EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9', 'sn': '2'}, + 'name': 'query.EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9.2', + 'response': None} + assert len(agent.queries) == 2 + + ancbody = dict( + pre="EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9", + anchor={"i": "EKQSWRXh_JHX61NdrL6wJ8ELMwG4zFY8y-sU1nymYzXZ", "s": "1", "d": "EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY"} + ) + result = client.simulate_post(path="/queries", body=json.dumps(ancbody)) + assert result.status == falcon.HTTP_202 + assert result.json == {'done': False, + 'error': None, + 'metadata': {'pre': 'EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9', + 'anchor': {'i': 'EKQSWRXh_JHX61NdrL6wJ8ELMwG4zFY8y-sU1nymYzXZ', 's': '1', 'd': 'EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY'}}, + 'name': 'query.EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'response': None} + assert len(agent.queries) == 3 + + class MockServerTls: def __init__(self, certify, keypath, certpath, cafilepath, port): pass diff --git a/tests/app/test_grouping.py b/tests/app/test_grouping.py index aa022e76..f15f8501 100644 --- a/tests/app/test_grouping.py +++ b/tests/app/test_grouping.py @@ -290,8 +290,8 @@ def make(self, serder, sigers): assert res.json == { "done": False, "error": None, - "metadata": {"sn": 3}, - "name": "group.EDWg3-rB5FTpcckaYdBcexGmbLIO6AvAwjaJTBlXUn_I", + "metadata": {"pre": "EDWg3-rB5FTpcckaYdBcexGmbLIO6AvAwjaJTBlXUn_I", "sn": 3}, + "name": "group.EPKCBT0rSgFKTDRjynYzOTsYWo7fDNElTxFbRZZW9f6R", "response": None, } diff --git a/tests/app/test_ipexing.py b/tests/app/test_ipexing.py index c45d6e84..436488c3 100644 --- a/tests/app/test_ipexing.py +++ b/tests/app/test_ipexing.py @@ -334,7 +334,7 @@ def test_ipex_grant(helpers, mockHelpingNowIso8601, seeder): assert res.json == {'done': False, 'error': None, 'metadata': {'said': 'ELkQART3yXFd8C6ImzGyqlDrgVUDtCfh1Goqr1PCbi9r'}, - 'name': 'exchange.EFnYGvF_ENKJ_4PGsWsvfd_R6m5cN-3KYsz_0mAuNpCm', + 'name': 'exchange.ELkQART3yXFd8C6ImzGyqlDrgVUDtCfh1Goqr1PCbi9r', 'response': None} assert len(agent.exchanges) == 1 assert len(agent.grants) == 1 @@ -1551,7 +1551,7 @@ def test_ipex_apply(helpers, mockHelpingNowIso8601): assert res.json == {'done': False, 'error': None, 'metadata': {'said': 'EPAThHL_ExMdhQoLTxsMWdsDo-aunDFZPkK_UKlCVe2d'}, - 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'name': 'exchange.EPAThHL_ExMdhQoLTxsMWdsDo-aunDFZPkK_UKlCVe2d', 'response': None} assert res.status_code == 200 @@ -1670,7 +1670,7 @@ def test_ipex_offer(helpers, mockHelpingNowIso8601): assert res.json == {'done': False, 'error': None, 'metadata': {'said': 'ECa9XU2648ryO8PXKEcWkS7V-hvpj86Nh3rjGv93g6jT'}, - 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'name': 'exchange.ECa9XU2648ryO8PXKEcWkS7V-hvpj86Nh3rjGv93g6jT', 'response': None} assert res.status_code == 200 @@ -1720,7 +1720,7 @@ def test_ipex_offer(helpers, mockHelpingNowIso8601): assert res.json == {'done': False, 'error': None, 'metadata': {'said': 'EM79tlKrG142-jcaglGnIXKRfLW_DKOK5pnTwN60yz5U'}, - 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'name': 'exchange.EM79tlKrG142-jcaglGnIXKRfLW_DKOK5pnTwN60yz5U', 'response': None} assert res.status_code == 200 @@ -1827,7 +1827,7 @@ def test_ipex_agree(helpers, mockHelpingNowIso8601): assert res.json == {'done': False, 'error': None, 'metadata': {'said': 'ENMBCgTGXxiMuTMcfGWp4uqnsiso1Jm3tAAn1x7ZPRox'}, - 'name': 'exchange.EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY', + 'name': 'exchange.ENMBCgTGXxiMuTMcfGWp4uqnsiso1Jm3tAAn1x7ZPRox', 'response': None} assert res.status_code == 200 diff --git a/tests/core/test_longrunning.py b/tests/core/test_longrunning.py index 267a6ed5..015ded2d 100644 --- a/tests/core/test_longrunning.py +++ b/tests/core/test_longrunning.py @@ -1,5 +1,6 @@ from keri.help import helping +import pytest from keria.app import aiding from keri.kering import ValidationError from keria.core import longrunning @@ -159,16 +160,118 @@ def test_operations(helpers): assert len(res.json) == 0 op = agent.monitor.status( - longrunning.Op(type='query', oid=recp, start=helping.nowIso8601(), metadata={'sn': '0'})) - assert op.name == f"query.{recp}" + longrunning.Op(type=longrunning.OpTypes.query, oid=f"{recp}.0", start=helping.nowIso8601(), metadata={'pre': recp, 'sn': '0'})) + assert op.name == f"query.{recp}.0" assert op.done is True op = agent.monitor.status( - longrunning.Op(type='query', oid=recp, start=helping.nowIso8601(), metadata={'sn': '4'})) - assert op.name == f"query.{recp}" + longrunning.Op(type=longrunning.OpTypes.query, oid=f"{recp}.4", start=helping.nowIso8601(), metadata={'pre': recp, 'sn': '4'})) + assert op.name == f"query.{recp}.4" assert op.done is False +def test_operation_bad_metadata(helpers): + with helpers.openKeria() as (agency, agent, app, client): + # Witness + witop = longrunning.Op(type=longrunning.OpTypes.witness, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + + with pytest.raises(ValidationError) as err: + witop.metadata = {"pre": "EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1"} + agent.monitor.status(witop) + assert str(err.value) == "invalid long running witness operation, metadata missing required fields ('pre', 'sn')" + + with pytest.raises(ValidationError) as err: + witop.metadata = {"sn": "0"} + agent.monitor.status(witop) + assert str(err.value) == "invalid long running witness operation, metadata missing required fields ('pre', 'sn')" + + # Oobi + oobiop = longrunning.Op(type=longrunning.OpTypes.oobi, oid="oobioid", + start=helping.nowIso8601(), metadata={}) + with pytest.raises(ValidationError) as err: + agent.monitor.status(oobiop) + assert str(err.value) == "invalid OOBI long running operation, missing oobi" + + # Delegation + delop = longrunning.Op(type=longrunning.OpTypes.delegation, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + with pytest.raises(ValidationError) as err: + agent.monitor.status(delop) + assert str(err.value) == "invalid long running delegation operation, metadata missing required field 'pre'" + + # Group + groupop = longrunning.Op(type=longrunning.OpTypes.group, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + + with pytest.raises(ValidationError) as err: + groupop.metadata = {"pre": "EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1"} + agent.monitor.status(groupop) + assert str(err.value) == "invalid long running group operation, metadata missing required fields ('pre', 'sn')" + + with pytest.raises(ValidationError) as err: + groupop.metadata = {"sn": "0"} + agent.monitor.status(groupop) + assert str(err.value) == "invalid long running group operation, metadata missing required fields ('pre', 'sn')" + + # Query + queryop = longrunning.Op(type=longrunning.OpTypes.query, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + with pytest.raises(ValidationError) as err: + agent.monitor.status(queryop) + assert str(err.value) == "invalid long running query operation, metadata missing required field 'pre'" + + # Registry + registryop = longrunning.Op(type=longrunning.OpTypes.registry, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + + with pytest.raises(ValidationError) as err: + registryop.metadata = {"pre": "EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1"} + agent.monitor.status(registryop) + assert str(err.value) == "invalid long running registry operation, metadata missing required fields ('pre', 'anchor')" + + with pytest.raises(ValidationError) as err: + registryop.metadata = {"anchor": + {"i": "EKQSWRXh_JHX61NdrL6wJ8ELMwG4zFY8y-sU1nymYzXZ", + "s": "1", + "d": "EHgwVwQT15OJvilVvW57HE4w0-GPs_Stj2OFoAHZSysY"}} + agent.monitor.status(registryop) + assert str(err.value) == "invalid long running registry operation, metadata missing required fields ('pre', 'anchor')" + + # Credential + credop = longrunning.Op(type=longrunning.OpTypes.credential, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + with pytest.raises(ValidationError) as err: + agent.monitor.status(credop) + assert str(err.value) == "invalid long running credential operation, metadata missing 'ced' field" + + # End role + endop = longrunning.Op(type=longrunning.OpTypes.endrole, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + + with pytest.raises(ValidationError) as err: + witop.metadata = {"cid": "EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", "role": "agent"} + agent.monitor.status(endop) + assert str(err.value) == "invalid long running endrole operation, metadata missing required fields ('cid', 'role', 'eid')" + + with pytest.raises(ValidationError) as err: + witop.metadata = {"cid": "EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", "eid": "EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9"} + agent.monitor.status(endop) + assert str(err.value) == "invalid long running endrole operation, metadata missing required fields ('cid', 'role', 'eid')" + + with pytest.raises(ValidationError) as err: + witop.metadata = {"role": "agent", "eid": "EI7AkI40M11MS7lkTCb10JC9-nDt-tXwQh44OHAFlv_9"} + agent.monitor.status(endop) + assert str(err.value) == "invalid long running endrole operation, metadata missing required fields ('cid', 'role', 'eid')" + + # Challenge + challengeop = longrunning.Op(type=longrunning.OpTypes.challenge, oid="EIsavDv6zpJDPauh24RSCx00jGc6VMe3l84Y8pPS8p-1", + start=helping.nowIso8601(), metadata={}) + with pytest.raises(ValidationError) as err: + agent.monitor.status(challengeop) + assert str(err.value) == "invalid long running challenge operation, metadata missing 'words' field" + + def test_error(helpers): with helpers.openKeria() as (agency, agent, app, client):