summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/tests/test_tools.py
blob: eaae3e2959e0c7374b3c38c8f9b3113c9dad0248 (plain)
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# -*- coding: utf-8 -*-
from __future__ import absolute_import

import unittest

import cherrypy
from cherrypy.lib.sessions import RamSession

try:
    from mock import patch
except ImportError:
    from unittest.mock import patch

from ..controllers import APIRouter, BaseController, Proxy, RESTController, Router
from ..controllers._version import APIVersion
from ..services.exception import handle_rados_error
from ..tests import ControllerTestCase
from ..tools import dict_contains_path, dict_get, json_str_to_object, \
    merge_list_of_dicts_by_key, partial_dict


# pylint: disable=W0613
@Router('/foo', secure=False)
class FooResource(RESTController):
    elems = []

    def list(self):
        return FooResource.elems

    def create(self, a):
        FooResource.elems.append({'a': a})
        return {'a': a}

    def get(self, key):
        return {'detail': (key, [])}

    def delete(self, key):
        del FooResource.elems[int(key)]

    def bulk_delete(self):
        FooResource.elems = []

    def set(self, key, newdata):
        FooResource.elems[int(key)] = {'newdata': newdata}
        return dict(key=key, newdata=newdata)


@Router('/foo/:key/:method', secure=False)
class FooResourceDetail(RESTController):
    def list(self, key, method):
        return {'detail': (key, [method])}


@APIRouter('/rgw/proxy', secure=False)
class GenerateControllerRoutesController(BaseController):
    @Proxy()
    def __call__(self, path, **params):
        pass


@APIRouter('/fooargs', secure=False)
class FooArgs(RESTController):
    def set(self, code, name=None, opt1=None, opt2=None):
        return {'code': code, 'name': name, 'opt1': opt1, 'opt2': opt2}

    @handle_rados_error('foo')
    def create(self, my_arg_name):
        return my_arg_name

    def list(self):
        raise cherrypy.NotFound()


class Root(object):
    foo_resource = FooResource()
    fooargs = FooArgs()


class RESTControllerTest(ControllerTestCase):

    @classmethod
    def setup_server(cls):
        cls.setup_controllers(
            [FooResource, FooResourceDetail, FooArgs, GenerateControllerRoutesController])

    def test_empty(self):
        self._delete("/foo")
        self.assertStatus(204)
        self._get("/foo")
        self.assertStatus('200 OK')
        self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type())
        self.assertBody('[]')

    def test_fill(self):
        sess_mock = RamSession()
        with patch('cherrypy.session', sess_mock, create=True):
            data = {'a': 'b'}
            for _ in range(5):
                self._post("/foo", data)
                self.assertJsonBody(data)
                self.assertStatus(201)
                self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type())

            self._get("/foo")
            self.assertStatus('200 OK')
            self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type())
            self.assertJsonBody([data] * 5)

            self._put('/foo/0', {'newdata': 'newdata'})
            self.assertStatus('200 OK')
            self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type())
            self.assertJsonBody({'newdata': 'newdata', 'key': '0'})

    def test_not_implemented(self):
        self._put("/foo")
        self.assertStatus(404)
        body = self.json_body()
        self.assertIsInstance(body, dict)
        assert body['detail'] == "The path '/foo' was not found."
        assert '404' in body['status']

    def test_args_from_json(self):
        self._put("/api/fooargs/hello", {'name': 'world'})
        self.assertJsonBody({'code': 'hello', 'name': 'world', 'opt1': None, 'opt2': None})

        self._put("/api/fooargs/hello", {'name': 'world', 'opt1': 'opt1'})
        self.assertJsonBody({'code': 'hello', 'name': 'world', 'opt1': 'opt1', 'opt2': None})

        self._put("/api/fooargs/hello", {'name': 'world', 'opt2': 'opt2'})
        self.assertJsonBody({'code': 'hello', 'name': 'world', 'opt1': None, 'opt2': 'opt2'})

    def test_detail_route(self):
        self._get('/foo/default')
        self.assertJsonBody({'detail': ['default', []]})

        self._get('/foo/default/default')
        self.assertJsonBody({'detail': ['default', ['default']]})

        self._get('/foo/1/detail')
        self.assertJsonBody({'detail': ['1', ['detail']]})

        self._post('/foo/1/detail', 'post-data')
        self.assertStatus(404)

    def test_generate_controller_routes(self):
        # We just need to add this controller in setup_server():
        # noinspection PyStatementEffect
        # pylint: disable=pointless-statement
        GenerateControllerRoutesController


class RequestLoggingToolTest(ControllerTestCase):

    _request_logging = True

    @classmethod
    def setup_server(cls):
        cls.setup_controllers([FooResource])

    def test_is_logged(self):
        with patch('logging.Logger.debug') as mock_logger_debug:
            self._put('/foo/0', {'newdata': 'xyz'})
            self.assertStatus(200)
            call_args_list = mock_logger_debug.call_args_list
            _, host, _, method, user, path = call_args_list[0][0]
            self.assertEqual(host, '127.0.0.1')
            self.assertEqual(method, 'PUT')
            self.assertIsNone(user)
            self.assertEqual(path, '/foo/0')


class TestFunctions(unittest.TestCase):

    def test_dict_contains_path(self):
        x = {'a': {'b': {'c': 'foo'}}}
        self.assertTrue(dict_contains_path(x, ['a', 'b', 'c']))
        self.assertTrue(dict_contains_path(x, ['a', 'b', 'c']))
        self.assertTrue(dict_contains_path(x, ['a']))
        self.assertFalse(dict_contains_path(x, ['a', 'c']))
        self.assertTrue(dict_contains_path(x, []))

    def test_json_str_to_object(self):
        expected_result = {'a': 1, 'b': 'bbb'}
        self.assertEqual(expected_result, json_str_to_object('{"a": 1, "b": "bbb"}'))
        self.assertEqual(expected_result, json_str_to_object(b'{"a": 1, "b": "bbb"}'))
        self.assertEqual('', json_str_to_object(''))
        self.assertRaises(TypeError, json_str_to_object, None)

    def test_partial_dict(self):
        expected_result = {'a': 1, 'c': 3}
        self.assertEqual(expected_result, partial_dict({'a': 1, 'b': 2, 'c': 3}, ['a', 'c']))
        self.assertEqual({}, partial_dict({'a': 1, 'b': 2, 'c': 3}, []))
        self.assertEqual({}, partial_dict({}, []))
        self.assertRaises(KeyError, partial_dict, {'a': 1, 'b': 2, 'c': 3}, ['d'])
        self.assertRaises(TypeError, partial_dict, None, ['a'])
        self.assertRaises(TypeError, partial_dict, {'a': 1, 'b': 2, 'c': 3}, None)

    def test_dict_get(self):
        self.assertFalse(dict_get({'foo': {'bar': False}}, 'foo.bar'))
        self.assertIsNone(dict_get({'foo': {'bar': False}}, 'foo.bar.baz'))
        self.assertEqual(dict_get({'foo': {'bar': False}, 'baz': 'xyz'}, 'baz'), 'xyz')

    def test_merge_list_of_dicts_by_key(self):
        expected_result = [{'a': 1, 'b': 2, 'c': 3}, {'a': 4, 'b': 5, 'c': 6}]
        self.assertEqual(expected_result, merge_list_of_dicts_by_key(
            [{'a': 1, 'b': 2}, {'a': 4, 'b': 5}], [{'a': 1, 'c': 3}, {'a': 4, 'c': 6}], 'a'))

        expected_result = [{'a': 1, 'b': 2}, {'a': 4, 'b': 5, 'c': 6}]
        self.assertEqual(expected_result, merge_list_of_dicts_by_key(
            [{'a': 1, 'b': 2}, {'a': 4, 'b': 5}], [{}, {'a': 4, 'c': 6}], 'a'))
        self.assertRaises(TypeError, merge_list_of_dicts_by_key, None)