mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-11-04 09:57:37 +03:00 
			
		
		
		
	Add tests
This commit is contained in:
		
							parent
							
								
									b3c62f5fd7
								
							
						
					
					
						commit
						e6c9181fc5
					
				| 
						 | 
				
			
			@ -7,11 +7,11 @@ monitors:
 | 
			
		|||
  example:
 | 
			
		||||
    method: "GET"
 | 
			
		||||
    url: "http://example.com"
 | 
			
		||||
    timeout: 5
 | 
			
		||||
    check_every: 5
 | 
			
		||||
    expected_codes: [200]
 | 
			
		||||
 | 
			
		||||
  httpbin:
 | 
			
		||||
    method: "GET"
 | 
			
		||||
    url: "http://httpbin.org/get"
 | 
			
		||||
    timeout: 5
 | 
			
		||||
    check_every: 5
 | 
			
		||||
    expected_codes: [200]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,27 +16,32 @@ class Dispatcher:
 | 
			
		|||
 | 
			
		||||
    def __init__(self, monitors: List[Monitor]) -> None:
 | 
			
		||||
        self._monitors = monitors
 | 
			
		||||
        self._monitor_tasks: List[asyncio.Task] = []  # type: ignore
 | 
			
		||||
        self._monitor_tasks: List[asyncio.Task] = []
 | 
			
		||||
        self._stopping = False
 | 
			
		||||
 | 
			
		||||
    def run(self) -> None:
 | 
			
		||||
        asyncio.run(self._do_work())
 | 
			
		||||
        asyncio.run(self.start())
 | 
			
		||||
 | 
			
		||||
    async def _do_work(self) -> None:
 | 
			
		||||
    async def start(self) -> None:
 | 
			
		||||
        logger.info('Dispatcher is starting up')
 | 
			
		||||
 | 
			
		||||
        for monitor in self._monitors:
 | 
			
		||||
            self._monitor_tasks.append(asyncio.create_task(self._run_monitor(monitor)))
 | 
			
		||||
            logger.info('Monitoring task has been started %s', monitor.full_name)
 | 
			
		||||
            self._monitor_tasks.append(
 | 
			
		||||
                asyncio.create_task(self._run_monitor(monitor)),
 | 
			
		||||
            )
 | 
			
		||||
            logger.info(
 | 
			
		||||
                'Monitoring task has been started %s',
 | 
			
		||||
                monitor.full_name,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        asyncio.get_event_loop().add_signal_handler(signal.SIGTERM, self._stop)
 | 
			
		||||
        asyncio.get_event_loop().add_signal_handler(signal.SIGINT, self._stop)
 | 
			
		||||
        asyncio.get_event_loop().add_signal_handler(signal.SIGTERM, self.stop)
 | 
			
		||||
        asyncio.get_event_loop().add_signal_handler(signal.SIGINT, self.stop)
 | 
			
		||||
 | 
			
		||||
        await asyncio.gather(*self._monitor_tasks, return_exceptions=True)
 | 
			
		||||
 | 
			
		||||
        self._stop()
 | 
			
		||||
        self.stop()
 | 
			
		||||
 | 
			
		||||
    def _stop(self) -> None:
 | 
			
		||||
    def stop(self) -> None:
 | 
			
		||||
        if self._stopping:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
"""Http client module."""
 | 
			
		||||
 | 
			
		||||
from aiohttp import ClientSession, ClientResponse
 | 
			
		||||
from aiohttp import ClientSession, ClientTimeout, ClientResponse
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HttpClient:
 | 
			
		||||
 | 
			
		||||
    async def request(self, method: str, url: str) -> ClientResponse:
 | 
			
		||||
        async with ClientSession() as session:
 | 
			
		||||
    async def request(self, method: str, url: str, timeout: int) -> ClientResponse:
 | 
			
		||||
        async with ClientSession(timeout=ClientTimeout(timeout)) as session:
 | 
			
		||||
            async with session.request(method, url) as response:
 | 
			
		||||
                return response
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
"""Monitors module."""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import time
 | 
			
		||||
from typing import Dict, Any
 | 
			
		||||
 | 
			
		||||
from .http import HttpClient
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +31,7 @@ class HttpMonitor(Monitor):
 | 
			
		|||
        self._client = http_client
 | 
			
		||||
        self._method = options.pop('method')
 | 
			
		||||
        self._url = options.pop('url')
 | 
			
		||||
        self._expected_codes = options.pop('expected_codes')
 | 
			
		||||
        self._timeout = options.pop('timeout')
 | 
			
		||||
        super().__init__(check_every=options.pop('check_every'))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
| 
						 | 
				
			
			@ -38,5 +39,20 @@ class HttpMonitor(Monitor):
 | 
			
		|||
        return '{0}.{1}(url="{2}")'.format(__name__, self.__class__.__name__, self._url)
 | 
			
		||||
 | 
			
		||||
    async def check(self) -> None:
 | 
			
		||||
        response = await self._client.request(method=self._method, url=self._url)
 | 
			
		||||
        self.logger.info('Return response code: %s', response.status)
 | 
			
		||||
        time_start = time.time()
 | 
			
		||||
 | 
			
		||||
        response = await self._client.request(
 | 
			
		||||
            method=self._method,
 | 
			
		||||
            url=self._url,
 | 
			
		||||
            timeout=self._timeout,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        time_end = time.time()
 | 
			
		||||
        time_took = time_end - time_start
 | 
			
		||||
 | 
			
		||||
        self.logger.info(
 | 
			
		||||
            'Response code: %s, content length: %s, request took: %s seconds',
 | 
			
		||||
            response.status,
 | 
			
		||||
            response.content_length,
 | 
			
		||||
            round(time_took, 3)
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
"""Tests module."""
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
import dataclasses
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
from .containers import ApplicationContainer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG = {
 | 
			
		||||
    'log': {
 | 
			
		||||
        'level': 'INFO',
 | 
			
		||||
        'formant': '[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s',
 | 
			
		||||
    },
 | 
			
		||||
    'monitors': {
 | 
			
		||||
        'example': {
 | 
			
		||||
            'method': 'GET',
 | 
			
		||||
            'url': 'http://fake-example.com',
 | 
			
		||||
            'timeout': 1,
 | 
			
		||||
            'check_every': 1,
 | 
			
		||||
        },
 | 
			
		||||
        'httpbin': {
 | 
			
		||||
            'method': 'GET',
 | 
			
		||||
            'url': 'http://fake-httpbin.org/get',
 | 
			
		||||
            'timeout': 1,
 | 
			
		||||
            'check_every': 1,
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclasses.dataclass
 | 
			
		||||
class RequestStub:
 | 
			
		||||
    status: int
 | 
			
		||||
    content_length: int
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def container():
 | 
			
		||||
    container = ApplicationContainer()
 | 
			
		||||
    container.config.from_dict(CONFIG)
 | 
			
		||||
    return container
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.asyncio
 | 
			
		||||
async def test_example_monitor(container, caplog):
 | 
			
		||||
    caplog.set_level('INFO')
 | 
			
		||||
 | 
			
		||||
    http_client_mock = mock.AsyncMock()
 | 
			
		||||
    http_client_mock.request.return_value = RequestStub(
 | 
			
		||||
        status=200,
 | 
			
		||||
        content_length=635,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    with container.http_client.override(http_client_mock):
 | 
			
		||||
        example_monitor = container.example_monitor()
 | 
			
		||||
        await example_monitor.check()
 | 
			
		||||
 | 
			
		||||
    assert 'http://fake-example.com' in caplog.text
 | 
			
		||||
    assert 'Response code: 200' in caplog.text
 | 
			
		||||
    assert 'content length: 635' in caplog.text
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.asyncio
 | 
			
		||||
async def test_httpbin_monitor(container, caplog):
 | 
			
		||||
    caplog.set_level('INFO')
 | 
			
		||||
 | 
			
		||||
    http_client_mock = mock.AsyncMock()
 | 
			
		||||
    http_client_mock.request.return_value = RequestStub(
 | 
			
		||||
        status=200,
 | 
			
		||||
        content_length=482,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    with container.http_client.override(http_client_mock):
 | 
			
		||||
        httpbin_monitor = container.httpbin_monitor()
 | 
			
		||||
        await httpbin_monitor.check()
 | 
			
		||||
 | 
			
		||||
    assert 'http://fake-httpbin.org/get' in caplog.text
 | 
			
		||||
    assert 'Response code: 200' in caplog.text
 | 
			
		||||
    assert 'content length: 482' in caplog.text
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.asyncio
 | 
			
		||||
async def test_dispatcher(container, caplog, event_loop):
 | 
			
		||||
    caplog.set_level('INFO')
 | 
			
		||||
 | 
			
		||||
    example_monitor_mock = mock.AsyncMock()
 | 
			
		||||
    httpbin_monitor_mock = mock.AsyncMock()
 | 
			
		||||
 | 
			
		||||
    with container.example_monitor.override(example_monitor_mock), \
 | 
			
		||||
             container.httpbin_monitor.override(httpbin_monitor_mock):
 | 
			
		||||
 | 
			
		||||
        dispatcher = container.dispatcher()
 | 
			
		||||
        event_loop.create_task(dispatcher.start())
 | 
			
		||||
        await asyncio.sleep(0.1)
 | 
			
		||||
        dispatcher.stop()
 | 
			
		||||
 | 
			
		||||
    assert example_monitor_mock.check.called
 | 
			
		||||
    assert httpbin_monitor_mock.check.called
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,6 @@
 | 
			
		|||
dependency-injector
 | 
			
		||||
aiohttp
 | 
			
		||||
pyyaml
 | 
			
		||||
pytest
 | 
			
		||||
pytest-asyncio
 | 
			
		||||
pytest-cov
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user