diff options
Diffstat (limited to 'packages/meshbay-common/tests/test_version_negotiation.py')
| -rw-r--r-- | packages/meshbay-common/tests/test_version_negotiation.py | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/packages/meshbay-common/tests/test_version_negotiation.py b/packages/meshbay-common/tests/test_version_negotiation.py new file mode 100644 index 0000000..50bdf54 --- /dev/null +++ b/packages/meshbay-common/tests/test_version_negotiation.py @@ -0,0 +1,78 @@ +""" +The version range, checked rather than merely written. + +Before MNP 1.0 every message carried a `v` that no one read, so a mismatch +surfaced as a *missing field*: an 0.x client reading a 1.0 handshake ack finds no +`enabled_apps` and applies its documented fallback — show every app — which is a +wrong answer rather than an error. The three failure modes 1.0's flag day was +called for all present that way, which is why negotiation ships in the same +deployment rather than after it (phase 15.6, decision D2). +""" + +import pytest +from meshbay_common import MNP_VERSION +from meshbay_common.handshake import ( + MNP_MIN_SUPPORTED, + HandshakeError, + check_version, + parse_version, +) + + +def test_this_build_accepts_itself(): + check_version(MNP_VERSION, MNP_MIN_SUPPORTED) + + +def test_a_peer_that_declares_no_minimum_is_read_as_speaking_only_its_own(): + """ + Which is the right reading of every 0.x peer: none of them declared a range, + because none of them checked one. + """ + check_version(MNP_VERSION) + + +def test_an_older_peer_is_refused_with_a_code(): + with pytest.raises(HandshakeError) as caught: + check_version("0.15") + assert caught.value.code == "version_too_old" + # The text is for a human and may be reworded; the client matches the code. + assert "0.15" in str(caught.value) + + +def test_a_peer_requiring_more_than_we_speak_is_refused_with_a_code(): + ours = parse_version(MNP_VERSION) + future = f"{ours[0] + 1}.0" + with pytest.raises(HandshakeError) as caught: + check_version(future, future) + assert caught.value.code == "version_too_new" + + +def test_a_newer_peer_that_still_accepts_us_is_allowed(): + """ + The point of a range rather than an equality: a 1.4 node that still speaks to + 1.0 clients must not refuse one. + """ + ours = parse_version(MNP_VERSION) + check_version(f"{ours[0]}.{ours[1] + 4}", MNP_MIN_SUPPORTED) + + +@pytest.mark.parametrize("bad", ["", "one.two", "1", None, "1.0.0", "v1.0"]) +def test_an_unreadable_version_is_refused_not_guessed(bad): + with pytest.raises(HandshakeError) as caught: + check_version(bad) + assert caught.value.code == "version_unreadable" + + +def test_versions_order_numerically_not_lexically(): + """`"0.9" < "0.15"` as strings, and the opposite as versions.""" + assert parse_version("0.9") < parse_version("0.15") < parse_version("1.0") + + +def test_mnp_1_0_is_a_major_bump(): + """ + Recorded as a test because the number is the only thing that says "this one is + different". 1.0 seals the index and the ack under the group key: no 0.x peer + can open either, and there is nothing to be compatible with. + """ + assert parse_version(MNP_VERSION) >= (1, 0) + assert parse_version(MNP_MIN_SUPPORTED) >= (1, 0) |