-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pytest-lsp: Document generic RPC server testing
- Loading branch information
Showing
7 changed files
with
156 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ User Guide | |
guide/client-capabilities | ||
guide/fixtures | ||
guide/troubleshooting | ||
guide/testing-json-rpc-servers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
Testing JSON-RPC Servers | ||
======================== | ||
|
||
While ``pytest-lsp`` is primarily focused on writing tests for LSP servers it is possible to reuse some of the machinery to test other JSON-RPC servers. | ||
|
||
A Simple JSON-RPC Server | ||
------------------------ | ||
|
||
As an example we'll reuse some of the `pygls`_ internals to write a simple JSON-RPC server that implements the following protocol. | ||
|
||
- client to server request ``math/add``, returns the sum of two numbers ``a`` and ``b`` | ||
- client to server request ``math/sub``, returns the difference of two numbers ``a`` and ``b`` | ||
- server to client notification ``log/message``, allows the server to send debug messages to the client. | ||
|
||
.. note:: | ||
|
||
The details of the implementation below don't really matter as we just need *something* to help us illustrate how to use ``pytest-lsp`` in this way. | ||
|
||
Remember you can write your servers in whatever language/framework you prefer! | ||
|
||
.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/generic-rpc/server.py | ||
:language: python | ||
|
||
Constructing a Client | ||
--------------------- | ||
|
||
While ``pytest-lsp`` can manage the connection between client and server, it needs to be given a client that understands the protocol that the server implements. | ||
This is done with a factory function | ||
|
||
.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/generic-rpc/t_server.py | ||
:language: python | ||
:start-at: def client_factory(): | ||
:end-at: return client | ||
|
||
The Client Fixture | ||
------------------ | ||
|
||
Once you have your factory function defined you can pass it to the :class:`~pytest_lsp.ClientServerConfig` when defining your client fixture | ||
|
||
.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/generic-rpc/t_server.py | ||
:language: python | ||
:start-at: @pytest_lsp.fixture( | ||
:end-at: # Teardown code | ||
|
||
Writing Test Cases | ||
------------------ | ||
|
||
With the client fixuture defined, test cases are written almost identically as they would be for your LSP servers. | ||
The only difference is that the generic :meth:`~pygls:pygls.protocol.JsonRPCProtocol.send_request_async` and :meth:`~pygls:pygls.protocol.JsonRPCProtocol.notify` methods are used to communicate with the server. | ||
|
||
.. literalinclude:: ../../../lib/pytest-lsp/tests/examples/generic-rpc/t_server.py | ||
:language: python | ||
:start-at: @pytest.mark.asyncio | ||
|
||
However, it is also possible to extend the base :class:`~pygls:pygls.client.JsonRPCClient` to provide a higher level interface to your server. | ||
See the `SubprocessSphinxClient`_ from the `esbonio`_ project for such an example. | ||
|
||
.. _esbonio: https://github.com/swyddfa/esbonio | ||
.. _pygls: https://github.com/openlawlibrary/pygls | ||
.. _SubprocessSphinxClient: https://github.com/swyddfa/esbonio/blob/develop/lib/esbonio/esbonio/server/features/sphinx_manager/client_subprocess.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from pygls.protocol import JsonRPCProtocol, default_converter | ||
from pygls.server import Server | ||
|
||
server = Server(protocol_cls=JsonRPCProtocol, converter_factory=default_converter) | ||
|
||
|
||
@server.lsp.fm.feature("math/add") | ||
def addition(ls: Server, params): | ||
a = params.a | ||
b = params.b | ||
|
||
ls.lsp.notify("log/message", dict(message=f"{a=}")) | ||
ls.lsp.notify("log/message", dict(message=f"{b=}")) | ||
|
||
return dict(total=a + b) | ||
|
||
|
||
@server.lsp.fm.feature("math/sub") | ||
def subtraction(ls: Server, params): | ||
a = params.a | ||
b = params.b | ||
|
||
ls.lsp.notify("log/message", dict(message=f"{a=}")) | ||
ls.lsp.notify("log/message", dict(message=f"{b=}")) | ||
|
||
return dict(total=b - a) | ||
|
||
|
||
if __name__ == "__main__": | ||
server.start_io() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import logging | ||
import sys | ||
|
||
import pytest | ||
import pytest_lsp | ||
from pygls.client import JsonRPCClient | ||
from pytest_lsp import ClientServerConfig | ||
|
||
|
||
def client_factory(): | ||
client = JsonRPCClient() | ||
|
||
@client.feature("log/message") | ||
def _on_message(params): | ||
logging.info("LOG: %s", params.message) | ||
|
||
return client | ||
|
||
|
||
@pytest_lsp.fixture( | ||
config=ClientServerConfig( | ||
client_factory=client_factory, server_command=[sys.executable, "server.py"] | ||
), | ||
) | ||
async def client(rpc_client: JsonRPCClient): | ||
# Setup code here (if any) | ||
|
||
yield | ||
|
||
# Teardown code here (if any) | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_add(client: JsonRPCClient): | ||
"""Ensure that the server implements addition correctly.""" | ||
|
||
result = await client.protocol.send_request_async( | ||
"math/add", params={"a": 1, "b": 2} | ||
) | ||
assert result.total == 3 | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_sub(client: JsonRPCClient): | ||
"""Ensure that the server implements addition correctly.""" | ||
|
||
result = await client.protocol.send_request_async( | ||
"math/sub", params={"a": 1, "b": 2} | ||
) | ||
assert result.total == -1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters