107 lines
4.5 KiB
Python
107 lines
4.5 KiB
Python
|
|
"""Regression tests for the Add Space surrounding-quote strip.
|
||
|
|
|
||
|
|
When users use macOS Finder's "Copy as Pathname" (Cmd+Option+C) the path
|
||
|
|
arrives wrapped in single quotes by default — e.g. `'/Users/x/Documents/foo'`.
|
||
|
|
Other shells and OS file managers do similar things with double quotes.
|
||
|
|
The Add Space input would reject these as "not a directory" because the
|
||
|
|
literal quote characters became part of the path.
|
||
|
|
|
||
|
|
This file pins the behaviour:
|
||
|
|
- Surrounding paired quotes (single or double) are stripped before validation.
|
||
|
|
- Only the OUTERMOST pair is removed — internal quotes survive.
|
||
|
|
- Mismatched / unpaired quotes are preserved (path may legitimately contain one).
|
||
|
|
- Whitespace outside the quotes is also handled.
|
||
|
|
"""
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from api.workspace import _strip_surrounding_quotes
|
||
|
|
|
||
|
|
|
||
|
|
class TestStripSurroundingQuotes:
|
||
|
|
def test_unwrapped_path_unchanged(self):
|
||
|
|
assert _strip_surrounding_quotes("/Users/x/Documents/foo") == "/Users/x/Documents/foo"
|
||
|
|
|
||
|
|
def test_single_quotes_stripped(self):
|
||
|
|
# macOS Finder default
|
||
|
|
assert _strip_surrounding_quotes("'/Users/x/Documents/foo'") == "/Users/x/Documents/foo"
|
||
|
|
|
||
|
|
def test_double_quotes_stripped(self):
|
||
|
|
assert _strip_surrounding_quotes('"/Users/x/Documents/foo"') == "/Users/x/Documents/foo"
|
||
|
|
|
||
|
|
def test_outer_whitespace_stripped_first(self):
|
||
|
|
# User pastes with trailing whitespace, then the quotes are visible
|
||
|
|
assert (
|
||
|
|
_strip_surrounding_quotes(" '/Users/x/Documents/foo' ")
|
||
|
|
== "/Users/x/Documents/foo"
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_only_outermost_pair_removed(self):
|
||
|
|
# Paths can legitimately contain quote characters mid-string
|
||
|
|
assert (
|
||
|
|
_strip_surrounding_quotes("'/Users/x/it's-mine/foo'")
|
||
|
|
== "/Users/x/it's-mine/foo"
|
||
|
|
)
|
||
|
|
|
||
|
|
def test_unpaired_leading_quote_preserved(self):
|
||
|
|
# Lone quote that doesn't have a partner — assume it's part of the path
|
||
|
|
assert _strip_surrounding_quotes("'/Users/x/foo") == "'/Users/x/foo"
|
||
|
|
|
||
|
|
def test_unpaired_trailing_quote_preserved(self):
|
||
|
|
assert _strip_surrounding_quotes("/Users/x/foo'") == "/Users/x/foo'"
|
||
|
|
|
||
|
|
def test_mismatched_quote_pair_preserved(self):
|
||
|
|
# ' on one side, " on the other — not a paired quote, leave alone
|
||
|
|
assert _strip_surrounding_quotes("'/Users/x/foo\"") == "'/Users/x/foo\""
|
||
|
|
|
||
|
|
def test_empty_string(self):
|
||
|
|
assert _strip_surrounding_quotes("") == ""
|
||
|
|
|
||
|
|
def test_just_a_pair_of_quotes(self):
|
||
|
|
# Edge case: someone pastes only the quotes — strip to empty
|
||
|
|
assert _strip_surrounding_quotes("''") == ""
|
||
|
|
assert _strip_surrounding_quotes('""') == ""
|
||
|
|
|
||
|
|
def test_non_quote_paired_chars_preserved(self):
|
||
|
|
# Don't strip arbitrary matching first-and-last chars
|
||
|
|
assert _strip_surrounding_quotes("/foo/") == "/foo/"
|
||
|
|
assert _strip_surrounding_quotes("aaa") == "aaa"
|
||
|
|
|
||
|
|
|
||
|
|
class TestWorkspaceAddRouteStripsQuotes:
|
||
|
|
"""End-to-end: when a quoted path is POSTed to /api/workspaces/add, the
|
||
|
|
server should accept it as if the quotes weren't there.
|
||
|
|
|
||
|
|
This is a tiny smoke test using the validate_workspace_to_add helper
|
||
|
|
directly (the route handler also calls _strip_surrounding_quotes via
|
||
|
|
the import in api/routes.py — verified by the unit tests above).
|
||
|
|
"""
|
||
|
|
|
||
|
|
def test_validate_unwraps_quoted_path_for_existing_dir(self, tmp_path):
|
||
|
|
from api.workspace import validate_workspace_to_add
|
||
|
|
|
||
|
|
d = tmp_path / "my workspace with spaces"
|
||
|
|
d.mkdir()
|
||
|
|
# Quoted form — what Finder pastes
|
||
|
|
quoted = f"'{d}'"
|
||
|
|
p = validate_workspace_to_add(quoted)
|
||
|
|
assert str(p) == str(d.resolve())
|
||
|
|
|
||
|
|
def test_validate_unwraps_double_quoted_path(self, tmp_path):
|
||
|
|
from api.workspace import validate_workspace_to_add
|
||
|
|
|
||
|
|
d = tmp_path / "my-workspace"
|
||
|
|
d.mkdir()
|
||
|
|
quoted = f'"{d}"'
|
||
|
|
p = validate_workspace_to_add(quoted)
|
||
|
|
assert str(p) == str(d.resolve())
|
||
|
|
|
||
|
|
def test_validate_quote_only_resolves_to_empty_after_strip(self):
|
||
|
|
"""`''` strips to `""`; the empty-string check belongs at the route handler
|
||
|
|
layer (which returns "path is required"), not the validator. validate_workspace_to_add
|
||
|
|
on `""` resolves to the process CWD, which may or may not be a directory —
|
||
|
|
not the validator's responsibility. This test pins that the strip happens
|
||
|
|
and the validator is then handed the empty form, not anything corrupted.
|
||
|
|
"""
|
||
|
|
# Direct strip check — confirms the layer responsible for the strip works.
|
||
|
|
assert _strip_surrounding_quotes("''") == ""
|