# # Copyright (C) 2026 Red Hat, Inc. # SPDX-License-Identifier: GPL-3.0-or-later import argparse import asyncio import ssl from pathlib import Path from typing import Any, AsyncGenerator import pytest import pytest_asyncio from cockpit.bridge import Bridge from .mocktransport import MockTransport MOCK_DATA = Path(__file__).parent.parent / 'data' @pytest_asyncio.fixture async def no_init_transport() -> AsyncGenerator[MockTransport, None]: bridge = Bridge(argparse.Namespace(privileged=False, beipack=False)) transport = MockTransport(bridge) try: yield transport finally: await transport.stop() @pytest.fixture def transport(no_init_transport: MockTransport) -> MockTransport: no_init_transport.init() return no_init_transport async def assert_http_response(transport: MockTransport, ch: str, *, tls: bool = False) -> None: await transport.assert_msg('', command='response', channel=ch, status=200, reason='OK') channel, data = await transport.next_frame() assert channel == ch if tls: assert data == b'Hello TLS\r\n' else: assert data == b'Hello\r\n' await transport.assert_msg('', command='done', channel=ch) await transport.assert_msg('', command='close', channel=ch) @pytest_asyncio.fixture(params=['tcp', 'unix']) async def http_server(request: pytest.FixtureRequest, tmp_path: Path) -> AsyncGenerator[dict[str, Any], None]: async def serve(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None: while True: line = await reader.readline() if not line.strip(): break writer.write(b'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello\r\n') await writer.drain() writer.close() if request.param == 'tcp': server = await asyncio.start_server(serve, '127.0.0.1', 0) port = server.sockets[0].getsockname()[1] open_args = {'port': port} else: sock = str(tmp_path / 'http.sock') server = await asyncio.start_unix_server(serve, sock) open_args = {'unix': sock} yield open_args server.close() @pytest_asyncio.fixture async def tls_server() -> AsyncGenerator[int, None]: async def serve(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None: while True: line = await reader.readline() if not line.strip(): break writer.write(b'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello TLS\r\n') await writer.drain() writer.close() server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) server_ctx.load_cert_chain(MOCK_DATA / 'mock-server.crt', MOCK_DATA / 'mock-server.key') server = await asyncio.start_server(serve, '127.0.0.1', 0, ssl=server_ctx) yield server.sockets[0].getsockname()[1] server.close() @pytest.mark.asyncio async def test_open_error(transport: MockTransport) -> None: await transport.check_open('http-stream2', problem='protocol-error', reply_keys={'message': "attribute 'method' required"}) await transport.check_open('http-stream2', method='POST', path='/tmp/', problem='protocol-error', reply_keys={'message': 'no "port" or "unix" option for channel'}) await transport.check_open('http-stream2', method='POST', path='/tmp/', port="foo", problem='protocol-error', reply_keys={'message': "attribute 'port': must have type int"}) await transport.check_open('http-stream2', method='GET', path='/test', unix='/tmp/sock', tls={}, problem='protocol-error', reply_keys={'message': 'TLS on Unix socket is not supported'}) @pytest.mark.asyncio async def test_http_request(transport: MockTransport, http_server: dict[str, Any]) -> None: ch = await transport.check_open('http-stream2', method='GET', path='/test', **http_server) transport.send_done(ch) await assert_http_response(transport, ch, tls=False) @pytest.mark.asyncio async def test_tls_request(transport: MockTransport, tls_server: int) -> None: ch = await transport.check_open('http-stream2', method='GET', path='/test', port=tls_server, tls={'authority': {'file': str(MOCK_DATA / 'mock-server.crt')}}) transport.send_done(ch) await assert_http_response(transport, ch, tls=True) @pytest.mark.asyncio async def test_tls_request_no_validate(transport: MockTransport, tls_server: int) -> None: ch = await transport.check_open('http-stream2', method='GET', path='/test', port=tls_server, tls={'validate': False}) transport.send_done(ch) await assert_http_response(transport, ch, tls=True) @pytest.mark.asyncio async def test_tls_no_validate(transport: MockTransport, tls_server: int) -> None: ch = await transport.check_open('http-stream2', method='GET', path='/test', port=tls_server, tls={'validate': False}) transport.send_done(ch) await assert_http_response(transport, ch, tls=True)