Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions passpod/handshake.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ def __init__(

object.__setattr__(self, "_data", data)
self._set_history()
if self._data.get("messages"):
self.validate()
self.validate()

def __repr__(self):
return (
Expand All @@ -68,8 +67,7 @@ def from_mapping(cls, mapping):
instance = cls.__new__(cls)
object.__setattr__(instance, "_data", deepcopy(dict(mapping)))
instance._set_history()
if instance._data.get("messages"):
instance.validate()
instance.validate()
return instance

def _set_history(self):
Expand Down Expand Up @@ -134,9 +132,6 @@ def to_mapping(self):
return deepcopy(self._data)

def validate(self):
if not self._data.get("messages"):
return {"valid": True, "errors": []}

result = validateHandshake(self.to_mapping())
if not result.get("valid"):
raise PasspodValidationError("validateHandshake", result.get("errors", []))
Expand Down
36 changes: 36 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,42 @@ def test_validate_json_semantic_failure_preserves_validator_errors(self):
self.assertIn("PARENT_REQUIRED", [error["code"] for error in payload["errors"]])
assert_no_traceback(self, result)

def test_validate_rejects_empty_handshake(self):
with tempfile.TemporaryDirectory() as directory:
path = write_json(
directory,
"empty-handshake.json",
{"handshakeIdentity": "hs-empty-001", "messages": []},
)

result = run_cli("validate", str(path), "--json")

payload = json.loads(result.stderr)
self.assertEqual(1, result.returncode)
self.assertEqual("", result.stdout)
self.assertFalse(payload["valid"])
self.assertEqual("handshake", payload["artifact_type"])
self.assertIn("SCHEMA_INVALID", [error["code"] for error in payload["errors"]])
assert_no_traceback(self, result)

def test_validate_rejects_missing_messages(self):
with tempfile.TemporaryDirectory() as directory:
path = write_json(
directory,
"missing-messages.json",
{"handshakeIdentity": "hs-missing-001"},
)

result = run_cli("validate", str(path), "--json")

payload = json.loads(result.stderr)
self.assertEqual(2, result.returncode)
self.assertEqual("", result.stdout)
self.assertFalse(payload["valid"])
self.assertIsNone(payload["artifact_type"])
self.assertEqual("ARTIFACT_TYPE_UNKNOWN", payload["errors"][0]["code"])
assert_no_traceback(self, result)


class InputFailureTests(unittest.TestCase):
def test_detection_and_input_failures_are_exit_code_2(self):
Expand Down
19 changes: 13 additions & 6 deletions tests/test_sdk_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,20 @@ def msg(self, identity, message_type, parent=None, handshake_identity="hs-1"):
data["parentReference"] = parent
return Message.from_mapping(data)

def test_creation_with_handshake_identity(self):
handshake = Handshake("hs-1")
def test_empty_or_missing_messages_are_rejected(self):
cases = (
lambda: Handshake("hs-1"),
lambda: Handshake.from_mapping({"handshakeIdentity": "hs-1", "messages": []}),
lambda: Handshake.from_mapping({"handshakeIdentity": "hs-1"}),
)

self.assertEqual("hs-1", handshake.handshake_identity)
self.assertEqual("not_started", handshake.lifecycle)
self.assertFalse(handshake.is_closed)
self.assertEqual(0, len(handshake.history))
for construct in cases:
with self.subTest(construct=construct):
with self.assertRaises(PasspodValidationError) as raised:
construct()

self.assertEqual("validateHandshake", raised.exception.operation)
self.assertIn("SCHEMA_INVALID", error_codes(raised.exception))

def test_creation_from_minimal_valid_propose(self):
handshake = Handshake("hs-1", messages=[self.msg("msg-1", "PROPOSE")])
Expand Down
5 changes: 0 additions & 5 deletions tests/test_sdk_fixture_roundtrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,6 @@ def test_reprs_are_readable_and_bounded(self):
self.assertNotIn("largePayload", repr(message))

def test_handshake_read_only_conveniences(self):
empty = Handshake("hs-empty-001")
self.assertEqual(0, empty.message_count)
self.assertIsNone(empty.last_message)
self.assertIsNone(empty.get_message("missing"))

handshake = Handshake.from_mapping(load_json(ROOT / "examples" / "valid" / "complete-handshake.json"))
last = handshake.last_message

Expand Down
10 changes: 9 additions & 1 deletion tests/test_semantic_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ def assert_has_code(self, result, code):
self.assertFalse(result["valid"])
self.assertIn(code, error_codes(result))

def test_empty_or_missing_messages(self):
for handshake in (
{"handshakeIdentity": "hs-direct-001", "messages": []},
{"handshakeIdentity": "hs-direct-001"},
):
with self.subTest(handshake=handshake):
result = semantic_validator.validateHandshake(handshake)
self.assert_has_code(result, semantic_validator.SCHEMA_INVALID)

def test_duplicate_message_identity(self):
result = semantic_validator.validateHandshake(
self.handshake(
Expand Down Expand Up @@ -218,4 +227,3 @@ def test_profile_attempt_to_redefine_canonical_message_type(self):

if __name__ == "__main__":
unittest.main()

Loading