diff --git a/pyproject.toml b/pyproject.toml index c78509078..22cf16eea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,6 +115,7 @@ test = [ "coverage>=5.3.1", "pyte>=0.8.0", "virtualenv>=20.16.2", + "requests", ] dev = [ "xonsh[test,doc]", diff --git a/tests/test_xonfig.py b/tests/test_xonfig.py index be451a068..145363c64 100644 --- a/tests/test_xonfig.py +++ b/tests/test_xonfig.py @@ -10,12 +10,14 @@ import io import re import pytest # noqa F401 +import requests +from xonsh.webconfig import file_writes from xonsh.webconfig import main as web_main from xonsh.xonfig import xonfig_main -def test_xonfg_help(capsys, xession): +def test_xonfig_help(capsys, xession): """verify can invoke it, and usage knows about all the options""" with pytest.raises(SystemExit): xonfig_main(["-h"]) @@ -34,62 +36,101 @@ def test_xonfg_help(capsys, xession): } +class MockRequest: + """Mock socket.socket for testing request handler""" + + def __init__(self, path: str): + self._path = path + self.data = b"" + self.content = [] + self.method = None + self.handler = None + + def getsockname(self): + return ("sockname",) + + def _request(self): + web_main.XonshConfigHTTPRequestHandler(self, (0, 0), None) + return self + + def get(self): + self.method = "GET" + self._request() + return self.data.decode() + + def post(self, **data): + self.method = "POST" + if data: + req = requests.Request( + method=self.method, url=f"http://localhost{self._path}", data=data + ).prepare() + self.content = [f"{k}: {v}".encode() for k, v in req.headers.items()] + [ + b"", # empty line to show end of headers + ( + req.body.encode() + if isinstance(req.body, str) + else b"" if req.body is None else req.body + ), + ] + self._request() + return self.data.decode() + + def makefile(self, *args, **kwargs): + if args[0] == "rb": + body = f"{self.method} {self._path} HTTP/1.0".encode() + if self.content: + body = b"\n".join([body, *self.content]) + return io.BytesIO(body) + elif args[0] == "wb": + return io.BytesIO(b"") + else: + raise ValueError("Unknown file type to make", args, kwargs) + + def sendall(self, data): + self.data = data + + @pytest.fixture def request_factory(): - class MockSocket: - def getsockname(self): - return ("sockname",) - - def sendall(self, data): - self.data = data - - class MockRequest: - _sock = MockSocket() - - def __init__(self, path: str, method: str): - self._path = path - self.data = b"" - self.method = method.upper() - - def makefile(self, *args, **kwargs): - if args[0] == "rb": - return io.BytesIO(f"{self.method} {self._path} HTTP/1.0".encode()) - elif args[0] == "wb": - return io.BytesIO(b"") - else: - raise ValueError("Unknown file type to make", args, kwargs) - - def sendall(self, data): - self.data = data - - return MockRequest - - -@pytest.fixture -def get_req(request_factory): from urllib import parse - def factory(path, data: "dict[str, str]|None" = None): - if data: - path = path + "?" + parse.urlencode(data) - request = request_factory(path, "get") - handle = web_main.XonshConfigHTTPRequestHandler(request, (0, 0), None) - return request, handle, request.data.decode() + def factory(path, **params): + if params: + path = path + "?" + parse.urlencode(params) + return MockRequest(path) return factory +@pytest.fixture +def rc_file(tmp_path, monkeypatch): + file = tmp_path / "xonshrc" + monkeypatch.setattr(file_writes, "RC_FILE", str(file)) + return file + + class TestXonfigWeb: - def test_colors_get(self, get_req): - _, _, resp = get_req("/") + def test_colors_get(self, request_factory): + resp = request_factory("/").get() assert "Colors" in resp - def test_xontribs_get(self, get_req): - _, _, resp = get_req("/xontribs") + def test_colors_post(self, request_factory, rc_file): + resp = request_factory("/", selected="default").post() + assert "$XONSH_COLOR_STYLE = 'default'" in rc_file.read_text() + assert "302" in resp # redirect + + def test_xontribs_get(self, request_factory): + resp = request_factory("/xontribs").get() assert "Xontribs" in resp - def test_prompts_get(self, get_req): - _, _, resp = get_req("/prompts") + def test_xontribs_post(self, request_factory, rc_file, mocker): + mocker.patch("xonsh.xontribs.xontribs_load", return_value=(None, None, None)) + resp = request_factory("/xontribs").post(xontrib1="") + assert "xontrib load xontrib1" in rc_file.read_text() + assert "302" in resp + + def test_prompts_get(self, request_factory): + resp = request_factory("/prompts").get() assert "Prompts" in resp diff --git a/xonsh/webconfig/file_writes.py b/xonsh/webconfig/file_writes.py index 3e7c08b2a..9e0bf85b7 100644 --- a/xonsh/webconfig/file_writes.py +++ b/xonsh/webconfig/file_writes.py @@ -44,13 +44,18 @@ def config_to_xonsh( return re.sub(r"\\r", "", "\n".join(lines)) +RC_FILE = "~/.xonshrc" + + def insert_into_xonshrc( config: dict, - xonshrc="~/.xonshrc", + xonshrc=None, prefix="# XONSH WEBCONFIG START", suffix="# XONSH WEBCONFIG END", ): """Places a config dict into the xonshrc.""" + if xonshrc is None: + xonshrc = RC_FILE # get current contents fname = os.path.expanduser(xonshrc) if os.path.isfile(fname):