From b50f1967c6edb76a5d69f0e92c69f7a1efc16734 Mon Sep 17 00:00:00 2001 From: Lars Karlitski Date: Thu, 30 May 2019 02:14:26 +0200 Subject: [PATCH] Add API integration test Test the HTTP API directly from outside the VM by forwarding /run/weldr/api.socket to a TCP port on the host. This is only a start to test that the API always returns JSON (see previous commit). --- test/check-api | 69 ++++++++++++++++++++++++++++++++++++++++++++ test/composertest.py | 5 ++-- test/run | 1 + 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100755 test/check-api diff --git a/test/check-api b/test/check-api new file mode 100755 index 00000000..92a228fb --- /dev/null +++ b/test/check-api @@ -0,0 +1,69 @@ +#!/usr/bin/python3 + +import composertest +import requests +import subprocess + + +class TestApi(composertest.ComposerTestCase): + """Test Composer HTTP API""" + + def setUp(self): + super().setUp() + + # Forward /run/weldr/api.socket to a port on the host + # Set ExitOnForwardFailure so that ssh blocks until the forward is set + # up before going to the background (-f), which it closes stdout. We + # wait for that by calling read() on it. + self.composer_port = self.network._lock(8080) + forwarder_command = [*self.ssh_command, "-fNT", + "-o", "ExitOnForwardFailure=yes", + "-L", f"localhost:{self.composer_port}:/run/weldr/api.socket"] + self.forwarder_proc = subprocess.Popen(forwarder_command, stdout=subprocess.PIPE) + self.forwarder_proc.stdout.read() + + def tearDown(self): + self.forwarder_proc.terminate() + try: + self.forwarder_proc.wait(timeout=1) + except TimeoutError: + self.forwarder_proc.kill() + super().tearDown() + + def request(self, method, path, check=True): + self.assertEqual(path[0], "/") + r = requests.request(method, f"http://localhost:{self.composer_port}{path}", timeout=30) + if check: + r.raise_for_status() + return r + + def test_basic(self): + """Basic checks for the API""" + + # + # API status without depsolve errors + # + status = self.request("GET", "/api/status").json() + self.assertEqual(status.keys(), { "build", "api", "db_version", "schema_version", "db_supported", "backend", "msgs" }) + self.assertEqual(status["msgs"], []) + + # + # HTTP errors should return json responses + # + r = self.request("GET", "/marmalade", check=False) + self.assertEqual(r.status_code, 404) + self.assertEqual(r.json(), { + "status": False, + "errors": [{ "id": "HTTPError", "code": 404, "msg": "Not Found" }] + }) + + r = self.request("POST", "/api/status", check=False) + self.assertEqual(r.status_code, 405) + self.assertEqual(r.json(), { + "status": False, + "errors": [{ "id": "HTTPError", "code": 405, "msg": "Method Not Allowed" }] + }) + + +if __name__ == '__main__': + composertest.main() diff --git a/test/composertest.py b/test/composertest.py index d01c69ee..6258da30 100644 --- a/test/composertest.py +++ b/test/composertest.py @@ -28,8 +28,8 @@ class ComposerTestCase(unittest.TestCase): sit = False def setUp(self): - network = testvm.VirtNetwork(0) - self.machine = testvm.VirtMachine(self.image, networking=network.host(), memory_mb=2048) + self.network = testvm.VirtNetwork(0) + self.machine = testvm.VirtMachine(self.image, networking=self.network.host(), memory_mb=2048) print(f"Starting virtual machine '{self.image}'") self.machine.start() @@ -58,7 +58,6 @@ class ComposerTestCase(unittest.TestCase): # Peek into internal data structure, because there's no way to get the # TestResult at this point. `errors` is a list of tuples (method, error) errors = filter(None, [ e[1] for e in self._outcome.errors ]) - if errors and self.sit: for e in errors: print_exception(*e) diff --git a/test/run b/test/run index 4be74f4d..4c0556b7 100755 --- a/test/run +++ b/test/run @@ -14,4 +14,5 @@ if [ -n "$TEST_SCENARIO" ]; then fi else test/check-cli TestImages + test/check-api fi