diff --git a/tests/pylorax/test_server.py b/tests/pylorax/test_server.py index ceeb89b6..943dfab7 100644 --- a/tests/pylorax/test_server.py +++ b/tests/pylorax/test_server.py @@ -15,14 +15,17 @@ # along with this program. If not, see . # import os +from glob import glob import shutil import tempfile +import time from threading import Lock import unittest from flask import json import pytoml as toml -from pylorax.api.config import configure, make_yum_dirs +from pylorax.api.config import configure, make_yum_dirs, make_queue_dirs +from pylorax.api.queue import start_queue_monitor from pylorax.api.recipes import open_or_create_repo, commit_recipe_directory from pylorax.api.server import server, GitLock, YumLock from pylorax.api.yumbase import get_base_object @@ -38,6 +41,11 @@ class ServerTestCase(unittest.TestCase): server.config["GITLOCK"] = GitLock(repo=repo, lock=Lock(), dir=repo_dir) server.config["COMPOSER_CFG"] = configure(root_dir=repo_dir, test_config=True) + os.makedirs(joinpaths(server.config["COMPOSER_CFG"].get("composer", "share_dir"), "composer")) + errors = make_queue_dirs(server.config["COMPOSER_CFG"], 0) + if errors: + raise RuntimeError("\n".join(errors)) + make_yum_dirs(server.config["COMPOSER_CFG"]) yb = get_base_object(server.config["COMPOSER_CFG"]) server.config["YUMLOCK"] = YumLock(yb=yb, lock=Lock()) @@ -47,9 +55,16 @@ class ServerTestCase(unittest.TestCase): self.examples_path = "./tests/pylorax/recipes/" + # Copy the shared files over to the directory tree we are using + share_path = "./share/composer/" + for f in glob(joinpaths(share_path, "*")): + shutil.copy(f, joinpaths(server.config["COMPOSER_CFG"].get("composer", "share_dir"), "composer")) + # Import the example recipes commit_recipe_directory(server.config["GITLOCK"].repo, "master", self.examples_path) + start_queue_monitor(server.config["COMPOSER_CFG"], 0, 0) + @classmethod def tearDownClass(self): shutil.rmtree(server.config["REPO_DIR"]) @@ -502,3 +517,204 @@ class ServerTestCase(unittest.TestCase): resp = self.server.get("/api/docs/modules.html") doc_str = open(os.path.abspath(joinpaths(os.path.dirname(__file__), "../../docs/html/modules.html"))).read() self.assertEqual(doc_str, resp.data) + + def wait_for_status(self, uuid, wait_status): + """Helper function that waits for a status + + :param uuid: UUID of the build to check + :type uuid: str + :param wait_status: List of statuses to exit on + :type wait_status: list of str + :returns: True if status was found, False if it timed out + :rtype: bool + + This will time out after 60 seconds + """ + start = time.time() + while True: + resp = self.server.get("/api/v0/compose/info/%s" % uuid) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + queue_status = data.get("queue_status") + if queue_status in wait_status: + return True + if time.time() > start + 60: + return False + time.sleep(5) + + def test_compose_01_types(self): + """Test the /api/v0/compose/types route""" + resp = self.server.get("/api/v0/compose/types") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual({"name": "tar", "enabled": True} in data["types"], True) + + def test_compose_02_create_failed(self): + """Test the /api/v0/compose routes with a failed test compose""" + test_compose = {"recipe_name": "glusterfs", + "compose_type": "tar", + "branch": "master"} + + resp = self.server.post("/api/v0/compose?test=1", + data=json.dumps(test_compose), + content_type="application/json") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["status"], True, "Failed to start test compose: %s" % data) + + build_id = data["build_id"] + + # Is it in the queue list (either new or run is fine, based on timing) + resp = self.server.get("/api/v0/compose/queue") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [e["id"] for e in data["new"] + data["run"]] + self.assertEqual(build_id in ids, True, "Failed to add build to the queue") + + # Wait for it to start + self.assertEqual(self.wait_for_status(build_id, ["RUNNING"]), True, "Failed to start test compose") + + # Wait for it to finish + self.assertEqual(self.wait_for_status(build_id, ["FAILED"]), True, "Failed to finish test compose") + + resp = self.server.get("/api/v0/compose/info/%s" % build_id) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["queue_status"], "FAILED", "Build not in FAILED state") + + # Test the /api/v0/compose/failed route + resp = self.server.get("/api/v0/compose/failed") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [e["id"] for e in data["failed"]] + self.assertEqual(build_id in ids, True, "Failed build not listed by /compose/failed") + + # Test the /api/v0/compose/finished route + resp = self.server.get("/api/v0/compose/finished") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["finished"], [], "Finished build not listed by /compose/finished") + + # Test the /api/v0/compose/status/ route + resp = self.server.get("/api/v0/compose/status/%s" % build_id) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [(e["id"], e["queue_status"]) for e in data["uuids"]] + self.assertEqual((build_id, "FAILED") in ids, True, "Failed build not listed by /compose/status") + + # Test the /api/v0/compose/cancel/ route + resp = self.server.post("/api/v0/compose?test=1", + data=json.dumps(test_compose), + content_type="application/json") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["status"], True, "Failed to start test compose: %s" % data) + + cancel_id = data["build_id"] + + # Wait for it to start + self.assertEqual(self.wait_for_status(cancel_id, ["RUNNING"]), True, "Failed to start test compose") + + # Cancel the build + resp = self.server.delete("/api/v0/compose/cancel/%s" % cancel_id) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["status"], True, "Failed to cancel test compose: %s" % data) + + # Delete the failed build + # Test the /api/v0/compose/delete/ route + resp = self.server.delete("/api/v0/compose/delete/%s" % build_id) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [(e["uuid"], e["status"]) for e in data["uuids"]] + self.assertEqual((build_id, True) in ids, True, "Failed to delete test compose: %s" % data) + + # Make sure the failed list is empty + resp = self.server.get("/api/v0/compose/failed") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["failed"], [], "Failed to delete the failed build: %s" % data) + + def test_compose_03_create_finished(self): + """Test the /api/v0/compose routes with a finished test compose""" + test_compose = {"recipe_name": "glusterfs", + "compose_type": "tar", + "branch": "master"} + + resp = self.server.post("/api/v0/compose?test=2", + data=json.dumps(test_compose), + content_type="application/json") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["status"], True, "Failed to start test compose: %s" % data) + + build_id = data["build_id"] + + # Is it in the queue list (either new or run is fine, based on timing) + resp = self.server.get("/api/v0/compose/queue") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [e["id"] for e in data["new"] + data["run"]] + self.assertEqual(build_id in ids, True, "Failed to add build to the queue") + + # Wait for it to start + self.assertEqual(self.wait_for_status(build_id, ["RUNNING"]), True, "Failed to start test compose") + + # Wait for it to finish + self.assertEqual(self.wait_for_status(build_id, ["FINISHED"]), True, "Failed to finish test compose") + + resp = self.server.get("/api/v0/compose/info/%s" % build_id) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["queue_status"], "FINISHED", "Build not in FINISHED state") + + # Test the /api/v0/compose/finished route + resp = self.server.get("/api/v0/compose/finished") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [e["id"] for e in data["finished"]] + self.assertEqual(build_id in ids, True, "Finished build not listed by /compose/finished") + + # Test the /api/v0/compose/failed route + resp = self.server.get("/api/v0/compose/failed") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["failed"], [], "Failed build not listed by /compose/failed") + + # Test the /api/v0/compose/status/ route + resp = self.server.get("/api/v0/compose/status/%s" % build_id) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [(e["id"], e["queue_status"]) for e in data["uuids"]] + self.assertEqual((build_id, "FINISHED") in ids, True, "Finished build not listed by /compose/status") + + # Test the /api/v0/compose/metadata/ route + resp = self.server.get("/api/v0/compose/metadata/%s" % build_id) + self.assertEqual(resp.status_code, 200) + self.assertEqual(len(resp.data) > 1024, True) + + # Test the /api/v0/compose/results/ route + resp = self.server.get("/api/v0/compose/results/%s" % build_id) + self.assertEqual(resp.status_code, 200) + self.assertEqual(len(resp.data) > 1024, True) + + # Test the /api/v0/compose/image/ route + resp = self.server.get("/api/v0/compose/image/%s" % build_id) + self.assertEqual(resp.status_code, 200) + self.assertEqual(len(resp.data) > 0, True) + self.assertEqual(resp.data, "TEST IMAGE") + + # Delete the finished build + # Test the /api/v0/compose/delete/ route + resp = self.server.delete("/api/v0/compose/delete/%s" % build_id) + data = json.loads(resp.data) + self.assertNotEqual(data, None) + ids = [(e["uuid"], e["status"]) for e in data["uuids"]] + self.assertEqual((build_id, True) in ids, True, "Failed to delete test compose: %s" % data) + + # Make sure the finished list is empty + resp = self.server.get("/api/v0/compose/finished") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + self.assertEqual(data["finished"], [], "Failed to delete the failed build: %s" % data) +>>>>>>> Add tests for /compose API