Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 76bdccc4

Přidáno uživatelem Jakub Šilhavý před více než 2 roky(ů)

re #9421 Added unit tests for resend_cached_payload() and send_data(); moved api_client.py into usb_detector package

Zobrazit rozdíly:

client/src/api_client.py
1
import json
2
import requests
3
import logging
4
from time import sleep
5
from diskcache import Deque
6
from requests import HTTPError, ConnectionError
7
from requests.exceptions import InvalidSchema
8

  
9
_uri = None
10
_cache = None
11
_config = None
12

  
13

  
14
def api_client_set_config(config):
15
    global _config, _cache, _uri
16
    _config = config
17
    _cache = _init_cache()
18
    _uri = config.server_url + ":" + config.server_port + config.server_endpoint
19

  
20

  
21
def _init_cache():
22
    return Deque(directory=_config.cache_dir)
23

  
24

  
25
def send_data(payload: dict):
26
    if _uri is None:
27
        logging.warning(f"sending payload = {payload} failed because uri is set to None")
28
        _cache_failed_payload(payload)
29
        return
30
    try:
31
        logging.info(f"sending payload = {payload} to {_uri}")
32
        response = requests.post(url=_uri, data=json.dumps(payload))
33
        logging.info(f"response text: {response.text}")
34
    except (ConnectionError, InvalidSchema):
35
        logging.warning(f"sending payload = {payload} to {_uri} failed")
36
        _cache_failed_payload(payload)
37
    except HTTPError as error:
38
        logging.error(f"HTTP Error ({_uri}) payload = {payload}, {error}")
39
        _cache_failed_payload(payload)
40

  
41

  
42
def _cache_failed_payload(payload: dict):
43
    if len(_cache) >= _config.cache_max_entries:
44
        oldest_payload = _cache.pop()
45
        logging.warning(f"cache is full - discarding payload = {oldest_payload}")
46

  
47
    logging.info(f"adding payload = {payload} into cache")
48
    _cache.append(payload)
49

  
50

  
51
def _resend_cached_payloads():
52
    retries = min(_config.max_retries, len(_cache))
53
    logging.info(f"emptying the cache ({retries} records)")
54
    for _ in range(0, retries):
55
        payload = _cache.pop()
56
        send_data(payload)
57

  
58

  
59
def api_client_run():
60
    while True:
61
        _resend_cached_payloads()
62
        sleep(_config.cache_retry_period_seconds)
client/src/main.py
9 9
from config_manager import Config
10 10
from usb_detector.detector import register_listener, usb_detector_run, usb_detector_set_config
11 11
from usb_detector.event_listener import usb_connected_callback, usb_disconnected_callback
12
from client.src.api_client import api_client_run, api_client_set_config
12
from usb_detector.api_client import api_client_run, api_client_set_config
13 13

  
14 14

  
15 15
def init_logging(app_config: Config):
client/src/usb_detector/api_client.py
1
import json
2
import requests
3
import logging
4
from time import sleep
5
from diskcache import Deque
6
from requests import HTTPError, ConnectionError
7
from requests.exceptions import InvalidSchema
8

  
9
_uri = None
10
_cache = None
11
_config = None
12

  
13

  
14
def api_client_set_config(config):
15
    global _config, _cache, _uri
16
    _config = config
17
    _cache = _init_cache()
18
    _uri = config.server_url + ":" + config.server_port + config.server_endpoint
19

  
20

  
21
def _init_cache():
22
    return Deque(directory=_config.cache_dir)
23

  
24

  
25
def send_data(payload: dict):
26
    if _uri is None:
27
        logging.warning(f"sending payload = {payload} failed because uri is set to None")
28
        _cache_failed_payload(payload)
29
        return
30
    try:
31
        logging.info(f"sending payload = {payload} to {_uri}")
32
        response = requests.post(url=_uri, data=json.dumps(payload))
33
        logging.info(f"response text: {response.text}")
34
    except (ConnectionError, InvalidSchema):
35
        logging.warning(f"sending payload = {payload} to {_uri} failed")
36
        _cache_failed_payload(payload)
37
    except HTTPError as error:
38
        logging.error(f"HTTP Error ({_uri}) payload = {payload}, {error}")
39
        _cache_failed_payload(payload)
40

  
41

  
42
def _cache_failed_payload(payload: dict):
43
    if len(_cache) >= _config.cache_max_entries:
44
        oldest_payload = _cache.pop()
45
        logging.warning(f"cache is full - discarding payload = {oldest_payload}")
46

  
47
    logging.info(f"adding payload = {payload} into cache")
48
    _cache.append(payload)
49

  
50

  
51
def _resend_cached_payloads():
52
    retries = min(_config.max_retries, len(_cache))
53
    logging.info(f"emptying the cache ({retries} records)")
54
    for _ in range(0, retries):
55
        payload = _cache.pop()
56
        send_data(payload)
57

  
58

  
59
def api_client_run():
60
    while True:
61
        _resend_cached_payloads()
62
        sleep(_config.cache_retry_period_seconds)
client/src/usb_detector/event_listener.py
3 3
import getpass
4 4
from datetime import datetime
5 5

  
6
from ..api_client import send_data
6
from .api_client import send_data
7 7

  
8 8

  
9 9
def _get_metadata() -> dict:
client/tests/api_client/test_api_client_set_config.py
1
import client.src.api_client
1
from client.src.usb_detector import api_client
2 2

  
3 3
from unittest import mock
4 4

  
5 5

  
6
@mock.patch('client.src.api_client._init_cache')
6
@mock.patch('client.src.usb_detector.api_client._init_cache')
7 7
def test_api_client_set_config_1(_init_cache_mock):
8 8
    class Config:
9 9
        def __init__(self):
......
12 12
            self.server_endpoint = "/api/v1/usb-logs"
13 13

  
14 14
    config = Config()
15
    client.src.api_client.api_client_set_config(config)
15
    api_client.api_client_set_config(config)
16 16

  
17
    assert client.src.api_client._config is config
18
    assert client.src.api_client._uri == "127.0.0.1:54444/api/v1/usb-logs"
17
    assert api_client._config is config
18
    assert api_client._uri == "127.0.0.1:54444/api/v1/usb-logs"
client/tests/api_client/test_cache_failed_payload.py
1
import client.src.api_client as api_client
1
from client.src.usb_detector import api_client
2 2

  
3 3

  
4 4
class CacheMock:
client/tests/api_client/test_resend_cached_payloads.py
1
from client.src.usb_detector import api_client
2

  
3
from unittest import mock
4

  
5

  
6
class CacheMock:
7

  
8
    def __init__(self):
9
        self._data = []
10

  
11
    def pop(self):
12
        return self._data.pop()
13

  
14
    def append(self, payload):
15
        self._data.append(payload)
16

  
17
    def __len__(self):
18
        return len(self._data)
19

  
20

  
21
class ConfigMock:
22

  
23
    def __init__(self):
24
        self.cache_max_entries = 5
25
        self.max_retries = 3
26

  
27

  
28
payload_mock = {
29
    "device": {
30
        "vendor_id": 1,
31
        "product_id": 2
32
    }
33
}
34

  
35

  
36
@mock.patch('client.src.usb_detector.api_client.send_data')
37
def test_resend_cached_payloads_1(send_data_mock):
38
    cache = CacheMock()
39
    config = ConfigMock()
40

  
41
    api_client._cache = cache
42
    api_client._config = config
43

  
44
    for _ in range(0, config.cache_max_entries + 1):
45
        api_client._cache_failed_payload(payload_mock)
46

  
47
    api_client._resend_cached_payloads()
48
    send_data_mock.assert_called()
49
    assert len(api_client._cache) == 2
50

  
51
    api_client._resend_cached_payloads()
52
    assert len(api_client._cache) == 0
53

  
54
    api_client._resend_cached_payloads()
55
    assert len(api_client._cache) == 0
56

  
57

  
58
@mock.patch('client.src.usb_detector.api_client.send_data')
59
def test_resend_cached_payloads_2(send_data_mock):
60
    cache = CacheMock()
61
    config = ConfigMock()
62

  
63
    api_client._cache = cache
64
    api_client._config = config
65

  
66
    api_client._resend_cached_payloads()
67
    send_data_mock.assert_not_called()
68
    assert len(api_client._cache) == 0
69

  
70

  
71
@mock.patch('client.src.usb_detector.api_client.send_data')
72
def test_resend_cached_payloads_3(send_data_mock):
73
    cache = CacheMock()
74
    config = ConfigMock()
75

  
76
    api_client._cache = cache
77
    api_client._config = config
78

  
79
    for _ in range(0, 2):
80
        api_client._cache_failed_payload(payload_mock)
81

  
82
    assert len(api_client._cache) == 2
83
    api_client._resend_cached_payloads()
84
    send_data_mock.assert_called()
85
    assert len(api_client._cache) == 0
86

  
client/tests/api_client/test_send_data.py
1
from client.src.usb_detector import api_client
2

  
3
import requests
4
from unittest import mock
5

  
6

  
7
@mock.patch('client.src.usb_detector.api_client._cache_failed_payload')
8
def test_send_data_1(_cache_failed_payload_mock):
9
    payload_mock = {
10
        "device": {
11
            "vendor_id": 1,
12
            "product_id": 2
13
        }
14
    }
15

  
16
    api_client._uri = None
17
    api_client.send_data(payload_mock)
18

  
19
    args = _cache_failed_payload_mock.call_args.args
20
    _cache_failed_payload_mock.assert_called()
21
    assert args[0] == payload_mock
22

  
23

  
24
@mock.patch('client.src.usb_detector.api_client._cache_failed_payload')
25
def test_send_data_2(_cache_failed_payload_mock):
26
    payload_mock = {
27
        "device": {
28
            "vendor_id": 1,
29
            "product_id": 2
30
        }
31
    }
32

  
33
    api_client._uri = "127.0.0.1:54444/api/v1/usb-logs"
34
    api_client.send_data(payload_mock)
35

  
36
    args = _cache_failed_payload_mock.call_args.args
37
    _cache_failed_payload_mock.assert_called()
38
    assert args[0] == payload_mock
39

  
40

  
41
@mock.patch('client.src.usb_detector.api_client.requests.post')
42
@mock.patch('client.src.usb_detector.api_client._cache_failed_payload')
43
def test_send_data_3(_cache_failed_payload_mock, post_mock):
44
    payload_mock = {
45
        "device": {
46
            "vendor_id": 1,
47
            "product_id": 2
48
        }
49
    }
50

  
51
    api_client._uri = "127.0.0.1:54444/api/v1/usb-logs"
52
    api_client.send_data(payload_mock)
53

  
54
    _cache_failed_payload_mock.assert_not_called()
55

  
56

  
57
@mock.patch('client.src.usb_detector.api_client.requests.post')
58
@mock.patch('client.src.usb_detector.api_client._cache_failed_payload')
59
def test_send_data_4(_cache_failed_payload_mock, post_mock):
60
    payload_mock = {
61
        "device": {
62
            "vendor_id": 1,
63
            "product_id": 2
64
        }
65
    }
66
    post_mock.side_effect = requests.exceptions.HTTPError()
67

  
68
    api_client._uri = "127.0.0.1:54444/api/v1/usb-logs"
69
    api_client.send_data(payload_mock)
70

  
71
    args = _cache_failed_payload_mock.call_args.args
72
    _cache_failed_payload_mock.assert_called()
73
    assert args[0] == payload_mock

Také k dispozici: Unified diff