Towards a1 1.0.0: rmr improvements
[ric-plt/a1.git] / tests / test_controller.py
index 5370207..2866dd8 100644 (file)
 # ==================================================================================
 import tempfile
 import os
+
 from rmr.rmr_mocks import rmr_mocks
 from a1 import app
-from a1 import exceptions
-from rmr import rmr
-import testing_helpers
 import pytest
 
+
+ADM_CTRL = "admission_control_policy"
+ADM_CTRL_POLICIES = "/a1-p/policytypes/20000/policies"
+ADM_CTRL_INSTANCE = ADM_CTRL_POLICIES + "/" + ADM_CTRL
+ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
+ADM_CTRL_TYPE = "/a1-p/policytypes/20000"
+TEST_TYPE = "/a1-p/policytypes/20001"
+
+
 # http://flask.pocoo.org/docs/1.0/testing/
 @pytest.fixture
 def client():
@@ -36,45 +43,46 @@ def client():
     os.unlink(app.app.config["DATABASE"])
 
 
-def _fake_dequeue(
-    monkeypatch,
-    msg_payload={"status": "SUCCESS", "foo": "bar"},
-    msg_type=20001,
-    msg_state=0,
-    jsonb=True,
-    unexpected_first=True,
-):
+def _fake_dequeue(_filter_type):
     """
-    generates a mock rmr message response (returns a function that does; uses closures to set params)
+    for monkeypatching a1rmnr.dequeue_all_messages with a good status
     """
-    new_messages = []
-    # stick a message we don't want at the front of the queue, then stick the message we want
-    if unexpected_first:
-        monkeypatch.setattr("rmr.rmr.rmr_torcv_msg", rmr_mocks.rcv_mock_generator(msg_payload, -1, msg_state, jsonb))
-        sbuf = rmr.rmr_alloc_msg(None, None)
-        sbuf = rmr.rmr_torcv_msg(None, sbuf, None)
-        summary = rmr.message_summary(sbuf)
-        new_messages.append(summary)
+    fake_msg = {}
+    pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "OK"}'
+    fake_msg["payload"] = pay
+    new_messages = [fake_msg]
+    return new_messages
 
-    monkeypatch.setattr("rmr.rmr.rmr_torcv_msg", rmr_mocks.rcv_mock_generator(msg_payload, msg_type, msg_state, jsonb))
-    sbuf = rmr.rmr_alloc_msg(None, None)
-    sbuf = rmr.rmr_torcv_msg(None, sbuf, None)
-    summary = rmr.message_summary(sbuf)
-    new_messages.append(summary)
 
-    def f():
-        return new_messages
-
-    return f
+def _fake_dequeue_none(_filter_type):
+    """
+    for monkeypatching a1rmnr.dequeue_all_messages with no waiting messages
+    """
+    return []
 
 
-# Actual Tests
+def _fake_dequeue_deleted(_filter_type):
+    """
+    for monkeypatching a1rmnr.dequeue_all_messages with a DELETED status
+    """
+    fake_msg = {}
+    pay = b'{"policy_type_id": 20000, "policy_instance_id": "admission_control_policy", "handler_id": "test_receiver", "status": "DELETED"}'
+    fake_msg["payload"] = pay
+    new_messages = [fake_msg]
+    return new_messages
 
 
 def _test_put_patch(monkeypatch):
-    testing_helpers.patch_all(monkeypatch)
+    rmr_mocks.patch_rmr(monkeypatch)
     monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(0))  # good sends for this whole batch
 
+    # we need this because free expects a real sbuf
+    # TODO: move this into rmr_mocks
+    def noop(_sbuf):
+        pass
+
+    monkeypatch.setattr("rmr.rmr.rmr_free_msg", noop)
+
     # we need to repatch alloc (already patched in patch_rmr) to fix the transactionid, alloc is called in send and recieve
     def fake_alloc(_unused, _alsounused):
         sbuf = rmr_mocks.Rmr_mbuf_t()
@@ -90,115 +98,143 @@ def _test_put_patch(monkeypatch):
     monkeypatch.setattr("rmr.rmr.generate_and_set_transaction_id", fake_set_transactionid)
 
 
-def test_xapp_put_good(client, monkeypatch):
+# Actual Tests
+
+
+def test_xapp_put_good(client, monkeypatch, adm_type_good, adm_instance_good):
     """ test policy put good"""
-    _test_put_patch(monkeypatch)
-    monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch))
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 200
-    assert res.json == {"status": "SUCCESS", "foo": "bar"}
 
+    # no type there yet
+    res = client.get(ADM_CTRL_TYPE)
+    assert res.status_code == 404
 
-def test_xapp_put_bad(client, monkeypatch):
-    """Test policy put fails"""
-    _test_put_patch(monkeypatch)
-    # return from policy handler has a status indicating FAIL
-    monkeypatch.setattr(
-        "a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_payload={"status": "FAIL", "foo": "bar"})
-    )
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 502
-    assert res.json["reason"] == "BAD STATUS"
-    assert res.json["return_payload"] == {"status": "FAIL", "foo": "bar"}
-
-    # return from policy handler has no status field
-    monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_payload={"foo": "bar"}))
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 502
-    assert res.json["reason"] == "NO STATUS"
-    assert res.json["return_payload"] == {"foo": "bar"}
-
-    # return from policy handler not a json
-    monkeypatch.setattr(
-        "a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_payload="booger", jsonb=False)
-    )
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 502
-    assert res.json["reason"] == "NOT JSON"
-    assert res.json["return_payload"] == "booger"
-
-    # bad type
-    monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_type=666))
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 504
-    assert res.data == b"\"A1 was expecting an ACK back but it didn't receive one or didn't recieve the expected ACK\"\n"
-
-    # bad state
-    monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch, msg_state=666))
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 504
-    assert res.data == b"\"A1 was expecting an ACK back but it didn't receive one or didn't recieve the expected ACK\"\n"
-
-
-def test_xapp_put_bad_send(client, monkeypatch):
-    """
-    Test bad send failures
-    """
-    testing_helpers.patch_all(monkeypatch)
+    # no types at all
+    res = client.get("/a1-p/policytypes")
+    assert res.status_code == 200
+    assert res.json == []
 
-    monkeypatch.setattr("a1.a1rmr._dequeue_all_waiting_messages", _fake_dequeue(monkeypatch))
-    res = client.put("/ric/policies/admission_control_policy", json={"not": "expected"})
-    assert res.status_code == 400
+    # instance 404 because type not there yet
+    monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue_none)
+    res = client.get(ADM_CTRL_POLICIES)
+    assert res.status_code == 404
 
-    monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 504
-    assert res.data == b'"A1 was unable to send a needed message to a downstream subscriber"\n'
+    # put the type
+    res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
+    assert res.status_code == 201
 
-    monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(5))
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 504
-    assert res.data == b'"A1 was unable to send a needed message to a downstream subscriber"\n'
+    # type there now
+    res = client.get(ADM_CTRL_TYPE)
+    assert res.status_code == 200
+    assert res.json == adm_type_good
+    res = client.get("/a1-p/policytypes")
+    assert res.status_code == 200
+    assert res.json == [20000]
 
+    # instance 200 but empty list
+    res = client.get(ADM_CTRL_POLICIES)
+    assert res.status_code == 200
+    assert res.json == []
 
-def test_bad_requests(client, monkeypatch):
-    """Test bad requests"""
-    testing_helpers.patch_all(monkeypatch)
+    # no instance there yet
+    res = client.get(ADM_CTRL_INSTANCE)
+    assert res.status_code == 404
+    res = client.get(ADM_CTRL_INSTANCE_STATUS)
+    assert res.status_code == 404
+
+    # create a good instance
+    _test_put_patch(monkeypatch)
+    res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
+    assert res.status_code == 202
 
-    # test a 404
-    res = client.put("/ric/policies/noexist", json=testing_helpers.good_payload())
+    # instance 200 and in list
+    res = client.get(ADM_CTRL_POLICIES)
+    assert res.status_code == 200
+    assert res.json == [ADM_CTRL]
+
+    def get_instance_good(expected):
+        # get the instance
+        res = client.get(ADM_CTRL_INSTANCE)
+        assert res.status_code == 200
+        assert res.json == adm_instance_good
+
+        # get the instance status
+        res = client.get(ADM_CTRL_INSTANCE_STATUS)
+        assert res.status_code == 200
+        assert res.get_data(as_text=True) == expected
+
+    # try a status get but pretend we didn't get any ACKs yet to test NOT IN EFFECT
+    monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue_none)
+    get_instance_good("NOT IN EFFECT")
+
+    # now pretend we did get a good ACK
+    monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue)
+    get_instance_good("IN EFFECT")
+
+    # delete it
+    res = client.delete(ADM_CTRL_INSTANCE)
+    assert res.status_code == 202
+    res = client.delete(ADM_CTRL_INSTANCE)  # should be able to do multiple deletes
+    assert res.status_code == 202
+
+    # status after a delete, but there are no messages yet, should still return
+    monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue)
+    get_instance_good("IN EFFECT")
+
+    # now pretend we deleted successfully
+    monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue_deleted)
+    res = client.get(ADM_CTRL_INSTANCE_STATUS)  # cant get status
+    assert res.status_code == 404
+    res = client.get(ADM_CTRL_INSTANCE)  # cant get instance
     assert res.status_code == 404
+    # list still 200 but no instance
+    res = client.get(ADM_CTRL_POLICIES)
+    assert res.status_code == 200
+    assert res.json == []
 
-    # bad media type
-    res = client.put("/ric/policies/admission_control_policy", data="notajson")
-    assert res.status_code == 415
 
-    # test a PUT body against a poliucy not expecting one
-    res = client.put("/ric/policies/test_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 400
-    assert res.data == b'"BODY SUPPLIED BUT POLICY HAS NO EXPECTED BODY"\n'
+def test_xapp_put_good_bad_rmr(client, monkeypatch, adm_instance_good):
+    """
+    assert that rmr bad states don't cause problems
+    """
+    _test_put_patch(monkeypatch)
+    monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
+    res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
+    assert res.status_code == 202
 
+    monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(5))
+    res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
+    assert res.status_code == 202
 
-def test_missing_manifest(client, monkeypatch):
+
+def test_bad_instances(client, monkeypatch, adm_type_good):
     """
-    test that we get a 500 with an approrpiate message on a missing manifest
+    Test bad send failures
     """
+    rmr_mocks.patch_rmr(monkeypatch)
 
-    def f():
-        raise exceptions.MissingManifest()
+    # TODO: reenable this after delete!
+    # put the type
+    # res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
+    # assert res.status_code == 201
 
-    monkeypatch.setattr("a1.utils.get_ric_manifest", f)
+    # illegal type range
+    res = client.put("/a1-p/policytypes/19999", json=adm_type_good)
+    assert res.status_code == 400
+    res = client.put("/a1-p/policytypes/21024", json=adm_type_good)
+    assert res.status_code == 400
+
+    # bad body
+    res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
+    assert res.status_code == 400
 
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 500
-    assert res.data == b'"A1 was unable to find the required RIC manifest. report this!"\n'
+    # bad media type
+    res = client.put(ADM_CTRL_INSTANCE, data="notajson")
+    assert res.status_code == 415
 
 
-def test_missing_rmr(client, monkeypatch):
+def test_healthcheck(client):
     """
-    test that we get a 500 with an approrpiate message on a missing rmr rmr_string
+    test healthcheck
     """
-    testing_helpers.patch_all(monkeypatch, nonexisting_rmr=True)
-    res = client.put("/ric/policies/admission_control_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 500
-    assert res.data == b'"A1 does not have a mapping for the desired rmr string. report this!"\n'
+    res = client.get("/a1-p/healthcheck")
+    assert res.status_code == 200