#
# 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.assertEqual(p.exitcode, 2)
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"})