# # Test for API infrastructure # import os import multiprocessing as mp import sys import tempfile import unittest import osbuild from osbuild.util import jsoncomm class APITester(osbuild.api.BaseAPI): """Records the number of messages and if it got cleaned up""" def __init__(self, sockaddr): super().__init__(sockaddr) self.clean = False self.messages = 0 endpoint = "test-api" def _message(self, msg, _fds, sock): self.messages += 1 if msg["method"] == "echo": msg["method"] = "reply" sock.send(msg) def _cleanup(self): self.clean = True class TestAPI(unittest.TestCase): """Check API infrastructure""" def setUp(self): self.tmp = tempfile.TemporaryDirectory() def tearDown(self): self.tmp.cleanup() def test_basic(self): # Basic API communication and cleanup checks socket = os.path.join(self.tmp.name, "socket") api = APITester(socket) with api: with jsoncomm.Socket.new_client(socket) as client: req = {'method': 'echo', 'data': 'Hello'} client.send(req) msg, _, _ = client.recv() self.assertEqual(msg["method"], "reply") self.assertEqual(req["data"], msg["data"]) self.assertEqual(api.clean, True) self.assertEqual(api.messages, 1) # Assert proper cleanup self.assertIsNone(api.thread) self.assertIsNone(api.event_loop) def test_reentrancy_guard(self): socket = os.path.join(self.tmp.name, "socket") api = APITester(socket) with api: with self.assertRaises(AssertionError): with api: pass def test_get_arguments(self): tmpdir = self.tmp.name path = os.path.join(tmpdir, "osbuild-api") args = {"options": {"answer": 42}} monitor = osbuild.monitor.BaseMonitor(sys.stderr.fileno()) with osbuild.api.API(args, monitor, socket_address=path) as _: data = osbuild.api.arguments(path=path) self.assertEqual(data, args) def test_exception(self): # Check that 'api.exception' correctly sets 'API.exception' tmpdir = self.tmp.name path = os.path.join(tmpdir, "osbuild-api") args = {} monitor = osbuild.monitor.BaseMonitor(sys.stderr.fileno()) def exception(path): with osbuild.api.exception_handler(path): raise ValueError("osbuild test exception") assert False, "api.exception should exit process" api = osbuild.api.API(args, monitor, socket_address=path) with api: p = mp.Process(target=exception, args=(path, )) p.start() p.join() self.assertIsNotNone(api.error, "Error not set") self.assertIn("type", api.error, "Error has no 'type' set") self.assertEqual("exception", api.error["type"], "Not an exception") e = api.error["data"] for field in ("type", "value", "traceback"): self.assertIn(field, e, f"Exception needs '{field}'") self.assertEqual(e["value"], "osbuild test exception") self.assertEqual(e["type"], "ValueError") self.assertIn("exception", e["traceback"]) def test_metadata(self): # Check that `api.metadata` leads to `API.metadata` being # set correctly tmpdir = self.tmp.name path = os.path.join(tmpdir, "osbuild-api") args = {} monitor = osbuild.monitor.BaseMonitor(sys.stderr.fileno()) def metadata(path): data = {"meta": "42"} osbuild.api.metadata(data, path=path) return 0 api = osbuild.api.API(args, monitor, socket_address=path) with api: p = mp.Process(target=metadata, args=(path, )) p.start() p.join() self.assertEqual(p.exitcode, 0) metadata = api.metadata # pylint: disable=no-member assert metadata self.assertEqual(metadata, {"meta": "42"})