63831b0d81ce606e84e2744f8814da4bceddee51
[ric-plt/a1.git] / tests / test_controller.py
1 # ==================================================================================
2 #       Copyright (c) 2019 Nokia
3 #       Copyright (c) 2018-2019 AT&T Intellectual Property.
4 #
5 #   Licensed under the Apache License, Version 2.0 (the "License");
6 #   you may not use this file except in compliance with the License.
7 #   You may obtain a copy of the License at
8 #
9 #          http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #   Unless required by applicable law or agreed to in writing, software
12 #   distributed under the License is distributed on an "AS IS" BASIS,
13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 #   See the License for the specific language governing permissions and
15 #   limitations under the License.
16 # ==================================================================================
17
18 import time
19 from rmr.rmr_mocks import rmr_mocks
20 from a1 import a1rmr
21
22
23 ADM_CTRL = "admission_control_policy"
24 ADM_CTRL_POLICIES = "/a1-p/policytypes/20000/policies"
25 ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL
26 ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
27 ADM_CTRL_TYPE = "/a1-p/policytypes/20000"
28 TEST_TYPE = "/a1-p/policytypes/20001"
29
30
31 def _fake_dequeue():
32     """for monkeypatching with a good status"""
33     pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "OK"}'
34     fake_msg = {"payload": pay}
35     return [fake_msg]
36
37
38 def _fake_dequeue_none():
39     """for monkeypatching with no waiting messages"""
40     return []
41
42
43 def _fake_dequeue_deleted():
44     """for monkeypatching  with a DELETED status"""
45     new_msgs = []
46
47     # insert some that don't exist to make sure nothing blows up
48     pay = b'{"policy_type_id": 20666, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "DELETED"}'
49     fake_msg = {"payload": pay}
50     new_msgs.append(fake_msg)
51
52     pay = b'{"policy_type_id": 20000, "policy_instance_id": "darkness", "handler_id": "test_receiver", "status": "DELETED"}'
53     fake_msg = {"payload": pay}
54     new_msgs.append(fake_msg)
55
56     # insert a bad one with a malformed body to make sure we keep going
57     fake_msg = {"payload": "asdf"}
58     new_msgs.append(fake_msg)
59
60     pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "DELETED"}'
61     fake_msg = {"payload": pay}
62     new_msgs.append(fake_msg)
63
64     return new_msgs
65
66
67 def _test_put_patch(monkeypatch):
68     rmr_mocks.patch_rmr(monkeypatch)
69     # assert that rmr bad states don't cause problems
70     monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
71
72     # we need this because free expects a real sbuf
73     # TODO: move this into rmr_mocks
74     def noop(_sbuf):
75         pass
76
77     monkeypatch.setattr("rmr.rmr.rmr_free_msg", noop)
78
79     # we need to repatch alloc (already patched in patch_rmr) to fix the transactionid, alloc is called in send and recieve
80     def fake_alloc(_unused, _alsounused):
81         sbuf = rmr_mocks.Rmr_mbuf_t()
82         sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
83         return sbuf
84
85     # we also need to repatch set, since in the send function, we alloc, then set a new transid
86     def fake_set_transactionid(sbuf):
87         sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
88
89     # Note, we could have just patched summary, but this patches at a "lower level" so is a better test
90     monkeypatch.setattr("rmr.rmr.rmr_alloc_msg", fake_alloc)
91     monkeypatch.setattr("rmr.rmr.generate_and_set_transaction_id", fake_set_transactionid)
92
93
94 # Module level Hack
95
96
97 RMR_THREAD = None
98
99
100 def setup_module():
101     """module level setup"""
102     global RMR_THREAD
103
104     def noop():
105         pass
106
107     # launch the thread with a fake init func and a patched rcv func; we will "repatch" later
108     RMR_THREAD = a1rmr.start_rmr_thread(init_func_override=noop, rcv_func_override=_fake_dequeue_none)
109
110
111 # Actual Tests
112
113
114 def test_workflow_nothing_there_yet(client):
115     """ test policy put good"""
116     # no type there yet
117     res = client.get(ADM_CTRL_TYPE)
118     assert res.status_code == 404
119
120     # no types at all
121     res = client.get("/a1-p/policytypes")
122     assert res.status_code == 200
123     assert res.json == []
124
125     # instance 404 because type not there yet
126     res = client.get(ADM_CTRL_POLICIES)
127     assert res.status_code == 404
128
129
130 def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
131     """
132     test a full A1 workflow
133     """
134     # put the type
135     res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
136     assert res.status_code == 201
137
138     # cant replace types
139     res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
140     assert res.status_code == 400
141
142     # type there now
143     res = client.get(ADM_CTRL_TYPE)
144     assert res.status_code == 200
145     assert res.json == adm_type_good
146     res = client.get("/a1-p/policytypes")
147     assert res.status_code == 200
148     assert res.json == [20000]
149
150     # instance 200 but empty list
151     res = client.get(ADM_CTRL_POLICIES)
152     assert res.status_code == 200
153     assert res.json == []
154
155     # no instance there yet
156     res = client.get(ADM_CTRL_INSTANCE)
157     assert res.status_code == 404
158     res = client.get(ADM_CTRL_INSTANCE_STATUS)
159     assert res.status_code == 404
160
161     # create a good instance
162     _test_put_patch(monkeypatch)
163     res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
164     assert res.status_code == 202
165
166     # replace is allowed on instances
167     res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
168     assert res.status_code == 202
169
170     # instance 200 and in list
171     res = client.get(ADM_CTRL_POLICIES)
172     assert res.status_code == 200
173     assert res.json == [ADM_CTRL]
174
175     def get_instance_good(expected):
176         # get the instance
177         res = client.get(ADM_CTRL_INSTANCE)
178         assert res.status_code == 200
179         assert res.json == adm_instance_good
180
181         # get the instance status
182         res = client.get(ADM_CTRL_INSTANCE_STATUS)
183         assert res.status_code == 200
184         assert res.get_data(as_text=True) == expected
185
186     # try a status get but we didn't get any ACKs yet to test NOT IN EFFECT
187     time.sleep(1)  # wait for the rmr thread
188     get_instance_good("NOT IN EFFECT")
189
190     # now pretend we did get a good ACK
191     RMR_THREAD._rcv_func = _fake_dequeue
192     time.sleep(1)  # wait for the rmr thread
193     get_instance_good("IN EFFECT")
194
195     # cant delete type until there are no instances
196     res = client.delete(ADM_CTRL_TYPE)
197     assert res.status_code == 400
198
199     # delete it
200     res = client.delete(ADM_CTRL_INSTANCE)
201     assert res.status_code == 202
202     res = client.delete(ADM_CTRL_INSTANCE)  # should be able to do multiple deletes
203     assert res.status_code == 202
204
205     # status after a delete, but there are no messages yet, should still return
206     time.sleep(1)  # wait for the rmr thread
207     get_instance_good("IN EFFECT")
208
209     # now pretend we deleted successfully
210     RMR_THREAD._rcv_func = _fake_dequeue_deleted
211     time.sleep(1)  # wait for the rmr thread
212     # list still 200 but no instance
213     res = client.get(ADM_CTRL_POLICIES)
214     assert res.status_code == 200
215     assert res.json == []
216     res = client.get(ADM_CTRL_INSTANCE_STATUS)  # cant get status
217     assert res.status_code == 404
218     res = client.get(ADM_CTRL_INSTANCE)  # cant get instance
219     assert res.status_code == 404
220
221     # delete the type
222     res = client.delete(ADM_CTRL_TYPE)
223     assert res.status_code == 204
224
225     # cant touch this
226     res = client.get(ADM_CTRL_TYPE)
227     assert res.status_code == 404
228     res = client.delete(ADM_CTRL_TYPE)
229     assert res.status_code == 404
230
231
232 def test_bad_instances(client, monkeypatch, adm_type_good):
233     """
234     test various failure modes
235     """
236     # put the type (needed for some of the tests below)
237     rmr_mocks.patch_rmr(monkeypatch)
238     res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
239     assert res.status_code == 201
240
241     # bad body
242     res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
243     assert res.status_code == 400
244
245     # bad media type
246     res = client.put(ADM_CTRL_INSTANCE, data="notajson")
247     assert res.status_code == 415
248
249     # delete a non existent instance
250     res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
251     assert res.status_code == 404
252
253     # get a non existent instance
254     RMR_THREAD._rcv_func = _fake_dequeue
255     time.sleep(1)
256     res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
257     assert res.status_code == 404
258
259     # delete the type (as cleanup)
260     res = client.delete(ADM_CTRL_TYPE)
261     assert res.status_code == 204
262
263
264 def test_illegal_types(client, monkeypatch, adm_type_good):
265     """
266     Test illegal types
267     """
268     res = client.put("/a1-p/policytypes/19999", json=adm_type_good)
269     assert res.status_code == 400
270     res = client.put("/a1-p/policytypes/21024", json=adm_type_good)
271     assert res.status_code == 400
272
273
274 def test_healthcheck(client):
275     """
276     test healthcheck
277     """
278     res = client.get("/a1-p/healthcheck")
279     assert res.status_code == 200
280
281
282 def teardown_module():
283     """module teardown"""
284     RMR_THREAD.stop()