File size: 4,327 Bytes
d1ceb73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import json
import os
import pathlib
import shutil
from pathlib import Path
from typing import Text

from jupyter_server.serverapp import ServerApp
from pytest import fixture
from tornado.httpserver import HTTPRequest
from tornado.httputil import HTTPServerRequest
from tornado.queues import Queue
from tornado.web import Application

# local imports
from jupyter_lsp import LanguageServerManager
from jupyter_lsp.constants import APP_CONFIG_D_SECTIONS
from jupyter_lsp.handlers import LanguageServersHandler, LanguageServerWebSocketHandler

# these should always be available in a test environment
KNOWN_SERVERS = [
    "bash-language-server",
    "dockerfile-language-server-nodejs",
    "typescript-language-server",
    "pylsp",
    "unified-language-server",
    "sql-language-server",
    "vscode-css-languageserver-bin",
    "vscode-html-languageserver-bin",
    "vscode-json-languageserver-bin",
    "yaml-language-server",
]

CMD_BASED_SERVERS = {
    "Rscript": ["r-languageserver"],
    "texlab": ["texlab"],
    "jedi-language-server": ["jedi-language-server"],
    "julia": ["julia-language-server"],
}

KNOWN_SERVERS += sum(
    [langs for cmd, langs in CMD_BASED_SERVERS.items() if shutil.which(cmd)], []
)

KNOWN_UNKNOWN_SERVERS = ["foo-language-server"]


def extra_node_roots():
    root = Path(os.environ.get("JLSP_TEST_ROOT") or Path.cwd())
    return dict(extra_node_roots=[str(root)] if root else [])


@fixture
def manager() -> LanguageServerManager:
    return LanguageServerManager(**extra_node_roots())


@fixture
def echo_spec():
    return {"argv": ["echo", "no server here"], "languages": ["klingon"], "version": 2}


@fixture
def echo_conf_json(echo_spec) -> str:
    return json.dumps(
        {"LanguageServerManager": {"language_servers": {"_echo_": echo_spec}}},
        indent=2,
        sort_keys=True,
    )


@fixture(params=sorted(APP_CONFIG_D_SECTIONS))
def app_config_d(request, tmp_path, monkeypatch) -> pathlib.Path:
    conf_d = tmp_path / f"jupyter{request.param}config.d"
    conf_d.mkdir()
    monkeypatch.setenv("JUPYTER_CONFIG_PATH", f"{tmp_path}")
    return conf_d


@fixture(params=sorted(KNOWN_SERVERS))
def known_server(request):
    return request.param


@fixture(params=sorted(KNOWN_UNKNOWN_SERVERS))
def known_unknown_server(request):
    return request.param


@fixture
def handlers(manager):
    ws_handler = MockWebsocketHandler()
    ws_handler.initialize(manager)
    handler = MockHandler()
    handler.initialize(manager)
    return handler, ws_handler


@fixture
def jsonrpc_init_msg():
    return json.dumps(
        {
            "id": 0,
            "jsonrpc": "2.0",
            "method": "initialize",
            "params": {
                "capabilities": {
                    # see: https://github.com/julia-vscode/LanguageServer.jl/issues/1008
                    # LanguageServer.jl assumes that it is not missing
                    "workspace": {"didChangeConfiguration": {}},
                    # LanguageServer.jl assumes that it is not missing
                    "textDocument": {},
                },
                "initializationOptions": None,
                "processId": None,
                "rootUri": pathlib.Path(__file__).parent.as_uri(),
                "workspaceFolders": None,
            },
        }
    )


@fixture
def app():
    return MockServerApp()


# mocks
class MockWebsocketHandler(LanguageServerWebSocketHandler):
    _messages_wrote = None  # type: Queue
    _ping_sent = None  # type: bool

    def __init__(self):
        self.request = HTTPServerRequest()
        self.application = Application()

    def initialize(self, manager):
        super().initialize(manager)
        self._messages_wrote = Queue()
        self._ping_sent = False

    def write_message(self, message: Text) -> None:  # type: ignore
        self.log.warning("write_message %s", message)
        self._messages_wrote.put_nowait(message)

    def send_ping(self):
        self._ping_sent = True


class MockHandler(LanguageServersHandler):
    _payload = None
    _jupyter_current_user = "foo"  # type:ignore[assignment]

    def __init__(self):
        self.request = HTTPRequest("GET")
        self.application = Application()

    def finish(self, payload):
        self._payload = payload


class MockServerApp(ServerApp):
    pass