1# Copyright 2018 Palantir Technologies, Inc.
2# pylint: disable=redefined-outer-name
3from io import BytesIO
4import datetime
5import os
6import sys
7import mock
8import pytest
9
10from pyls_jsonrpc.streams import JsonRpcStreamReader, JsonRpcStreamWriter
11
12
13@pytest.fixture()
14def rfile():
15    return BytesIO()
16
17
18@pytest.fixture()
19def wfile():
20    return BytesIO()
21
22
23@pytest.fixture()
24def reader(rfile):
25    return JsonRpcStreamReader(rfile)
26
27
28@pytest.fixture()
29def writer(wfile):
30    return JsonRpcStreamWriter(wfile, sort_keys=True)
31
32
33def test_reader(rfile, reader):
34    rfile.write(
35        b'Content-Length: 49\r\n'
36        b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n'
37        b'\r\n'
38        b'{"id": "hello", "method": "method", "params": {}}'
39    )
40    rfile.seek(0)
41
42    consumer = mock.Mock()
43    reader.listen(consumer)
44
45    consumer.assert_called_once_with({
46        'id': 'hello',
47        'method': 'method',
48        'params': {}
49    })
50
51
52def test_reader_bad_message(rfile, reader):
53    rfile.write(b'Hello world')
54    rfile.seek(0)
55
56    # Ensure the listener doesn't throw
57    consumer = mock.Mock()
58    reader.listen(consumer)
59    consumer.assert_not_called()
60
61
62def test_reader_bad_json(rfile, reader):
63    rfile.write(
64        b'Content-Length: 8\r\n'
65        b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n'
66        b'\r\n'
67        b'{hello}}'
68    )
69    rfile.seek(0)
70
71    # Ensure the listener doesn't throw
72    consumer = mock.Mock()
73    reader.listen(consumer)
74    consumer.assert_not_called()
75
76
77def test_writer(wfile, writer):
78    writer.write({
79        'id': 'hello',
80        'method': 'method',
81        'params': {}
82    })
83
84    if os.name == 'nt':
85        assert wfile.getvalue() == (
86            b'Content-Length: 49\r\n'
87            b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n'
88            b'\r\n'
89            b'{"id": "hello", "method": "method", "params": {}}'
90        )
91    else:
92        assert wfile.getvalue() == (
93            b'Content-Length: 44\r\n'
94            b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n'
95            b'\r\n'
96            b'{"id":"hello","method":"method","params":{}}'
97        )
98
99
100class JsonDatetime(datetime.datetime):
101    """Monkey path json datetime."""
102    def __json__(self):
103        if sys.version_info.major == 3:
104            dif = int(self.timestamp())
105        else:
106            dif = int((self - datetime.datetime(1970, 1, 1)).total_seconds())
107        return '{0}'.format(dif)
108
109
110def test_writer_bad_message(wfile, writer):
111    # A datetime isn't serializable(or poorly serializable),
112    # ensure the write method doesn't throw, but the result could be empty
113    # or the correct datetime
114    datetime.datetime = JsonDatetime
115    writer.write(datetime.datetime(
116        year=2019,
117        month=1,
118        day=1,
119        hour=1,
120        minute=1,
121        second=1,
122    ))
123
124    assert wfile.getvalue() in [
125        b'',
126        b'Content-Length: 10\r\n'
127        b'Content-Type: application/vscode-jsonrpc; charset=utf8\r\n'
128        b'\r\n'
129        b'1546304461'
130    ]
131