aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/tests/test_remote_paths.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-cli/tests/test_remote_paths.py')
-rw-r--r--packages/meshbay-cli/tests/test_remote_paths.py77
1 files changed, 77 insertions, 0 deletions
diff --git a/packages/meshbay-cli/tests/test_remote_paths.py b/packages/meshbay-cli/tests/test_remote_paths.py
new file mode 100644
index 0000000..5192e7b
--- /dev/null
+++ b/packages/meshbay-cli/tests/test_remote_paths.py
@@ -0,0 +1,77 @@
+"""Remote paths and group handles — `docs/MESHBAY_DESIGN.md` §8.4, `group-link.js`."""
+
+import pytest
+from meshbay_cli.client.paths import encode_name, handle, parse, resolve_group
+from meshbay_cli.errors import NOT_FOUND, USAGE, CliError
+
+GROUPS = [
+ {"id": "1", "name": "Family", "owner_username": "alice"},
+ {"id": "2", "name": "Family", "owner_username": "bob"},
+ {"id": "3", "name": "Some Saga/Box @Set", "owner_username": "alice"},
+ {"id": "4", "name": "me@home", "owner_username": "carol"},
+ {"id": "5", "name": "Work", "owner_username": "Dave"},
+]
+
+
+def test_nothing_or_a_slash_is_every_group():
+ assert parse("") is None
+ assert parse("/") is None
+
+
+def test_a_typed_path_decodes_only_the_slash_and_the_percent():
+ p = parse("Some Saga%2FBox @Set@alice/share/100% done.txt")
+ assert p.group == "Some Saga/Box @Set@alice"
+ assert p.parts == ["share", "100% done.txt"]
+ assert parse("G/a%252Fb").parts == ["a%2Fb"]
+
+
+def test_a_link_copied_from_the_application_is_decoded_in_full():
+ p = parse("https://hub.example/#/Some%20Saga%2FBox%20@Set@alice/share/b%C3%A9b%C3%A9.jpg")
+ assert p.from_link
+ assert p.group == "Some Saga/Box @Set@alice"
+ assert p.parts == ["share", "bébé.jpg"]
+
+
+def test_dot_segments_are_refused():
+ with pytest.raises(CliError) as e:
+ parse("Family/share/../etc")
+ assert e.value.code == USAGE
+
+
+def test_names_are_nfc():
+ decomposed = "été"
+ assert parse(f"G/{decomposed}").parts == ["été"]
+
+
+def test_the_owner_is_after_the_last_at():
+ assert resolve_group(GROUPS, "Family@bob")["id"] == "2"
+ assert resolve_group(GROUPS, "Some Saga/Box @Set@alice")["id"] == "3"
+ assert resolve_group(GROUPS, "me@home@carol")["id"] == "4"
+
+
+def test_a_name_with_an_at_of_its_own_still_resolves_without_owner():
+ assert resolve_group(GROUPS, "me@home")["id"] == "4"
+
+
+def test_names_and_owners_compare_without_case():
+ assert resolve_group(GROUPS, "some saga/box @set")["id"] == "3"
+ assert resolve_group(GROUPS, "work@dave")["id"] == "5"
+
+
+def test_two_groups_of_one_name_ask_for_the_owner():
+ with pytest.raises(CliError) as e:
+ resolve_group(GROUPS, "Family")
+ assert e.value.code == USAGE
+ assert "Family@alice" in e.value.message and "Family@bob" in e.value.message
+
+
+def test_an_unknown_group_is_not_found():
+ with pytest.raises(CliError) as e:
+ resolve_group(GROUPS, "Nope")
+ assert e.value.code == NOT_FOUND
+
+
+def test_a_handle_can_be_typed_back():
+ for g in GROUPS:
+ assert resolve_group(GROUPS, parse(handle(g)).group)["id"] == g["id"]
+ assert encode_name("100%/x") == "100%25%2Fx"