import inspect import json import os import random import shutil import string import time from lib import BaseTest from testout import TestOut try: import requests except ImportError: requests = None # States for returned tasks from the API TASK_SUCCEEDED = 2 TASK_FAILED = 3 class APITest(BaseTest): """ BaseTest + testing aptly API """ aptly_server = None aptly_out = None debugOutput = True base_url = "127.0.0.1:8765" configOverride = { "FileSystemPublishEndpoints": { "apiandserve": { "rootDir": f"{os.environ['HOME']}/{BaseTest.aptlyDir}/apiandserve", "linkMethod": "symlink" } }, "enableSwaggerEndpoint": True } def fixture_available(self): return super(APITest, self).fixture_available() and requests is not None def prepare(self): if APITest.aptly_server is None: super(APITest, self).prepare() APITest.aptly_out = TestOut() configPath = os.path.join(os.environ["HOME"], self.aptlyConfigFile) APITest.aptly_server = self._start_process(f"aptly api serve -no-lock -config={configPath} -listen={self.base_url}", stdout=APITest.aptly_out, stderr=APITest.aptly_out) time.sleep(1) else: APITest.aptly_out.clear() if os.path.exists(os.path.join(os.environ["HOME"], self.aptlyDir, "upload")): shutil.rmtree(os.path.join(os.environ["HOME"], self.aptlyDir, "upload")) self.prepare_fixture() def debug_output(self): return APITest.aptly_out.get_contents() def run(self): pass def get(self, uri, *args, **kwargs): return requests.get("http://%s%s" % (self.base_url, uri), *args, **kwargs) def post(self, uri, *args, **kwargs): if "json" in kwargs: kwargs["data"] = json.dumps(kwargs.pop("json")) if "headers" not in kwargs: kwargs["headers"] = {} kwargs["headers"]["Content-Type"] = "application/json" return requests.post("http://%s%s" % (self.base_url, uri), *args, **kwargs) def _ensure_async(self, kwargs): # Make sure we run this as an async task params = kwargs.get('params', {}) params.setdefault('_async', True) kwargs['params'] = params def post_task(self, uri, *args, **kwargs): self._ensure_async(kwargs) resp = self.post(uri, *args, **kwargs) if resp.status_code != 202: raise Exception("async api error: " + resp.text) _id = resp.json()['ID'] resp = self.get("/api/tasks/" + str(_id) + "/wait") self.check_equal(resp.status_code, 200) return self.get("/api/tasks/" + str(_id)) def check_task(self, task): self.check_equal(task.status_code, 200) if task.json()['State'] != TASK_SUCCEEDED: resp2 = self.get("/api/tasks/" + str(task.json()['ID']) + "/output") raise Exception(f"task failed: {resp2.text}") def check_task_fail(self, task, expected_output=None): self.check_equal(task.status_code, 200) if task.json()['State'] == TASK_SUCCEEDED: raise Exception("task expected to fail") if expected_output: resp = self.get("/api/tasks/" + str(task.json()['ID']) + "/output") self.check_equal(resp.json(), expected_output) def put(self, uri, *args, **kwargs): if "json" in kwargs: kwargs["data"] = json.dumps(kwargs.pop("json")) if "headers" not in kwargs: kwargs["headers"] = {} kwargs["headers"]["Content-Type"] = "application/json" return requests.put("http://%s%s" % (self.base_url, uri), *args, **kwargs) def put_task(self, uri, *args, **kwargs): self._ensure_async(kwargs) resp = self.put(uri, *args, **kwargs) if resp.status_code != 202: return resp _id = resp.json()['ID'] resp = self.get("/api/tasks/" + str(_id) + "/wait") self.check_equal(resp.status_code, 200) return self.get("/api/tasks/" + str(_id)) def delete(self, uri, *args, **kwargs): if "json" in kwargs: kwargs["data"] = json.dumps(kwargs.pop("json")) if "headers" not in kwargs: kwargs["headers"] = {} kwargs["headers"]["Content-Type"] = "application/json" return requests.delete("http://%s%s" % (self.base_url, uri), *args, **kwargs) def delete_task(self, uri, *args, **kwargs): self._ensure_async(kwargs) resp = self.delete(uri, *args, **kwargs) if resp.status_code != 202: return resp _id = resp.json()['ID'] resp = self.get("/api/tasks/" + str(_id) + "/wait") self.check_equal(resp.status_code, 200) return self.get("/api/tasks/" + str(_id)) def upload(self, uri, *filenames, **kwargs): upload_name = kwargs.pop("upload_name", None) directory = kwargs.pop("directory", "files") assert kwargs == {} files = {} for filename in filenames: fp = open(os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), directory, filename), "rb") if upload_name is not None: upload_filename = upload_name else: upload_filename = filename files[upload_filename] = (upload_filename, fp) return self.post(uri, files=files) @classmethod def shutdown_class(cls): if cls.aptly_server is not None: cls.aptly_server.terminate() cls.aptly_server.wait() cls.aptly_server = None if APITest.aptly_out is not None: APITest.aptly_out.close() def random_name(self): return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(15))