1import os
2
3import salt.config
4from tests.support.mock import patch
5from tests.support.runtests import RUNTIME_VARS
6
7try:
8    import cherrypy
9
10    HAS_CHERRYPY = True
11except ImportError:
12    HAS_CHERRYPY = False
13
14
15if HAS_CHERRYPY:
16    from tests.support.cptestcase import BaseCherryPyTestCase
17    from salt.netapi.rest_cherrypy import app
18else:
19    from tests.support.unit import TestCase, skipIf
20
21    @skipIf(HAS_CHERRYPY is False, "The CherryPy python package needs to be installed")
22    class BaseCherryPyTestCase(TestCase):
23        pass
24
25    class BaseToolsTest(BaseCherryPyTestCase):
26        pass
27
28
29class BaseRestCherryPyTest(BaseCherryPyTestCase):
30    """
31    A base TestCase subclass for the rest_cherrypy module
32
33    This mocks all interactions with Salt-core and sets up a dummy
34    (unsubscribed) CherryPy web server.
35    """
36
37    def __get_opts__(self):
38        return None
39
40    @classmethod
41    def setUpClass(cls):
42        master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master")
43        cls.config = salt.config.client_config(master_conf)
44        cls.base_opts = {}
45        cls.base_opts.update(cls.config)
46
47    @classmethod
48    def tearDownClass(cls):
49        del cls.config
50        del cls.base_opts
51
52    def setUp(self):
53        # Make a local reference to the CherryPy app so we can mock attributes.
54        self.app = app
55        self.addCleanup(delattr, self, "app")
56
57        master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master")
58        client_config = salt.config.client_config(master_conf)
59        base_opts = {}
60        base_opts.update(client_config)
61
62        base_opts.update(
63            self.__get_opts__()
64            or {
65                "external_auth": {
66                    "auto": {"saltdev": ["@wheel", "@runner", ".*"]},
67                    "pam": {"saltdev": ["@wheel", "@runner", ".*"]},
68                },
69                "rest_cherrypy": {"port": 8000, "debug": True},
70            }
71        )
72
73        root, apiopts, conf = app.get_app(base_opts)
74        cherrypy.tree.mount(root, "/", conf)
75        cherrypy.server.unsubscribe()
76        cherrypy.engine.start()
77
78        # Make sure cherrypy does not memleak on its bus since it keeps
79        # adding handlers without cleaning the old ones each time we setup
80        # a new application
81        for value in cherrypy.engine.listeners.values():
82            value.clear()
83        cherrypy.engine._priorities.clear()
84
85        self.addCleanup(cherrypy.engine.exit)
86
87
88class Root:
89    """
90    The simplest CherryPy app needed to test individual tools
91    """
92
93    exposed = True
94
95    _cp_config = {}
96
97    def GET(self):
98        return {"return": ["Hello world."]}
99
100    def POST(self, *args, **kwargs):
101        return {"return": [{"args": args}, {"kwargs": kwargs}]}
102
103
104if HAS_CHERRYPY:
105
106    class BaseToolsTest(BaseCherryPyTestCase):  # pylint: disable=E0102
107        """
108        A base class so tests can selectively turn individual tools on for testing
109        """
110
111        def __get_conf__(self):
112            return {
113                "/": {"request.dispatch": cherrypy.dispatch.MethodDispatcher()},
114            }
115
116        def __get_cp_config__(self):
117            return {}
118
119        def setUp(self):
120            root = Root()
121            patcher = patch.object(root, "_cp_config", self.__get_cp_config__())
122            patcher.start()
123            self.addCleanup(patcher.stop)
124
125            # Make sure cherrypy does not memleak on its bus since it keeps
126            # adding handlers without cleaning the old ones each time we setup
127            # a new application
128            for value in cherrypy.engine.listeners.values():
129                value.clear()
130            cherrypy.engine._priorities.clear()
131
132            app = cherrypy.tree.mount(root, "/", self.__get_conf__())
133            cherrypy.server.unsubscribe()
134            cherrypy.engine.start()
135            self.addCleanup(cherrypy.engine.exit)
136