f4e0ae2340c53e4dfa5e0b37aab483756e85a7cd
[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 import tempfile
18 import os
19
20 from rmr.rmr_mocks import rmr_mocks
21 from a1 import app
22 import pytest
23
24
25 ADM_CTRL = "admission_control_policy"
26 ADM_CTRL_POLICIES = "/a1-p/policytypes/20000/policies"
27 ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL
28 ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
29 ADM_CTRL_TYPE = "/a1-p/policytypes/20000"
30 TEST_TYPE = "/a1-p/policytypes/20001"
31
32
33 # http://flask.pocoo.org/docs/1.0/testing/
34 @pytest.fixture
35 def client():
36     db_fd, app.app.config["DATABASE"] = tempfile.mkstemp()
37     app.app.config["TESTING"] = True
38     cl = app.app.test_client()
39
40     yield cl
41
42     os.close(db_fd)
43     os.unlink(app.app.config["DATABASE"])
44
45
46 def _fake_dequeue(_filter_type):
47     """
48     for monkeypatching a1rmnr.dequeue_all_messages with a good status
49     """
50     fake_msg = {}
51     pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "OK"}'
52     fake_msg["payload"] = pay
53     new_messages = [fake_msg]
54     return new_messages
55
56
57 def _fake_dequeue_none(_filter_type):
58     """
59     for monkeypatching a1rmnr.dequeue_all_messages with no waiting messages
60     """
61     return []
62
63
64 def _fake_dequeue_deleted(_filter_type):
65     """
66     for monkeypatching a1rmnr.dequeue_all_messages with a DELETED status
67     """
68     new_msgs = []
69
70     # insert some that don't exist to make sure nothing blows up
71     pay = b'{"policy_type_id": 20666, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "DELETED"}'
72     fake_msg = {"payload": pay}
73     new_msgs.append(fake_msg)
74
75     pay = b'{"policy_type_id": 20000, "policy_instance_id": "darkness", "handler_id": "test_receiver", "status": "DELETED"}'
76     fake_msg = {"payload": pay}
77     new_msgs.append(fake_msg)
78
79     pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "DELETED"}'
80     fake_msg = {"payload": pay}
81     new_msgs.append(fake_msg)
82
83     return new_msgs
84
85
86 def _test_put_patch(monkeypatch):
87     rmr_mocks.patch_rmr(monkeypatch)
88     monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(0))  # good sends for this whole batch
89
90     # we need this because free expects a real sbuf
91     # TODO: move this into rmr_mocks
92     def noop(_sbuf):
93         pass
94
95     monkeypatch.setattr("rmr.rmr.rmr_free_msg", noop)
96
97     # we need to repatch alloc (already patched in patch_rmr) to fix the transactionid, alloc is called in send and recieve
98     def fake_alloc(_unused, _alsounused):
99         sbuf = rmr_mocks.Rmr_mbuf_t()
100         sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
101         return sbuf
102
103     # we also need to repatch set, since in the send function, we alloc, then set a new transid
104     def fake_set_transactionid(sbuf):
105         sbuf.contents.xaction = b"d49b53e478b711e9a1130242ac110002"
106
107     # Note, we could have just patched summary, but this patches at a "lower level" so is a better test
108     monkeypatch.setattr("rmr.rmr.rmr_alloc_msg", fake_alloc)
109     monkeypatch.setattr("rmr.rmr.generate_and_set_transaction_id", fake_set_transactionid)
110
111
112 # Actual Tests
113
114
115 def test_workflow_nothing_there_yet(client, monkeypatch, adm_type_good, adm_instance_good):
116     """ test policy put good"""
117
118     # no type there yet
119     res = client.get(ADM_CTRL_TYPE)
120     assert res.status_code == 404
121
122     # no types at all
123     res = client.get("/a1-p/policytypes")
124     assert res.status_code == 200
125     assert res.json == []
126
127     # instance 404 because type not there yet
128     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue_none)
129     res = client.get(ADM_CTRL_POLICIES)
130     assert res.status_code == 404
131
132
133 def test_workflow(client, monkeypatch, adm_type_good, adm_instance_good):
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     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue_none)
152     res = client.get(ADM_CTRL_POLICIES)
153     assert res.status_code == 200
154     assert res.json == []
155
156     # no instance there yet
157     res = client.get(ADM_CTRL_INSTANCE)
158     assert res.status_code == 404
159     res = client.get(ADM_CTRL_INSTANCE_STATUS)
160     assert res.status_code == 404
161
162     # create a good instance
163     _test_put_patch(monkeypatch)
164     # assert that rmr bad states don't cause problems
165     monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
166     res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
167     assert res.status_code == 202
168
169     # replace is allowed on instances
170     res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
171     assert res.status_code == 202
172
173     # instance 200 and in list
174     res = client.get(ADM_CTRL_POLICIES)
175     assert res.status_code == 200
176     assert res.json == [ADM_CTRL]
177
178     def get_instance_good(expected):
179         # get the instance
180         res = client.get(ADM_CTRL_INSTANCE)
181         assert res.status_code == 200
182         assert res.json == adm_instance_good
183
184         # get the instance status
185         res = client.get(ADM_CTRL_INSTANCE_STATUS)
186         assert res.status_code == 200
187         assert res.get_data(as_text=True) == expected
188
189     # try a status get but pretend we didn't get any ACKs yet to test NOT IN EFFECT
190     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue_none)
191     get_instance_good("NOT IN EFFECT")
192
193     # now pretend we did get a good ACK
194     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue)
195     get_instance_good("IN EFFECT")
196
197     # cant delete type until there are no instances
198     res = client.delete(ADM_CTRL_TYPE)
199     assert res.status_code == 400
200
201     # delete it
202     res = client.delete(ADM_CTRL_INSTANCE)
203     assert res.status_code == 202
204     res = client.delete(ADM_CTRL_INSTANCE)  # should be able to do multiple deletes
205     assert res.status_code == 202
206
207     # status after a delete, but there are no messages yet, should still return
208     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue)
209     get_instance_good("IN EFFECT")
210
211     # now pretend we deleted successfully
212     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue_deleted)
213     res = client.get(ADM_CTRL_INSTANCE_STATUS)  # cant get status
214     assert res.status_code == 404
215     res = client.get(ADM_CTRL_INSTANCE)  # cant get instance
216     assert res.status_code == 404
217
218     # list still 200 but no instance
219     res = client.get(ADM_CTRL_POLICIES)
220     assert res.status_code == 200
221     assert res.json == []
222
223     # delete the type
224     res = client.delete(ADM_CTRL_TYPE)
225     assert res.status_code == 204
226
227     # cant touch this
228     res = client.get(ADM_CTRL_TYPE)
229     assert res.status_code == 404
230     res = client.delete(ADM_CTRL_TYPE)
231     assert res.status_code == 404
232
233
234 def test_bad_instances(client, monkeypatch, adm_type_good):
235     """
236     test various failure modes
237     """
238     # put the type (needed for some of the tests below)
239     rmr_mocks.patch_rmr(monkeypatch)
240     res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
241     assert res.status_code == 201
242
243     # bad body
244     res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
245     assert res.status_code == 400
246
247     # bad media type
248     res = client.put(ADM_CTRL_INSTANCE, data="notajson")
249     assert res.status_code == 415
250
251     # delete a non existent instance
252     res = client.delete(ADM_CTRL_INSTANCE + "DARKNESS")
253     assert res.status_code == 404
254
255     # get a non existent instance
256     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue)
257     res = client.get(ADM_CTRL_INSTANCE + "DARKNESS")
258     assert res.status_code == 404
259
260     # delete the type (as cleanup)
261     res = client.delete(ADM_CTRL_TYPE)
262     assert res.status_code == 204
263
264
265 def test_illegal_types(client, monkeypatch, adm_type_good):
266     """
267     Test illegal types
268     """
269     res = client.put("/a1-p/policytypes/19999", json=adm_type_good)
270     assert res.status_code == 400
271     res = client.put("/a1-p/policytypes/21024", json=adm_type_good)
272     assert res.status_code == 400
273
274
275 def test_healthcheck(client):
276     """
277     test healthcheck
278     """
279     res = client.get("/a1-p/healthcheck")
280     assert res.status_code == 200