diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fe88bd7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,13 @@ +# AGENTS.md + +## Pull Requests + +PRs MUST be opened against **customerio/cdp-analytics-python** (branch: `main`). + +Never open PRs against segmentio/analytics-python — this repo is a fork and we do not contribute upstream. + +When creating a PR, always use: `gh pr create --repo customerio/cdp-analytics-python --base main` + +## Tests + +Run tests with: `make test` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/customerio/analytics/request.py b/customerio/analytics/request.py index 5b0eb99..8573fe8 100644 --- a/customerio/analytics/request.py +++ b/customerio/analytics/request.py @@ -39,14 +39,13 @@ def post(write_key, host=None, gzip=False, timeout=15, proxies=None, **kwargs): "data": data, "auth": auth, "headers": headers, - "timeout": 15, + "timeout": timeout, } - if proxies: + if proxies is not None: kwargs['proxies'] = proxies - res = _session.post(url, data=data, auth=auth, - headers=headers, timeout=timeout) + res = _session.post(url, **kwargs) if res.status_code == 200: log.debug('data uploaded successfully') diff --git a/customerio/analytics/test/client.py b/customerio/analytics/test/client.py index 5fc5835..4c10be3 100644 --- a/customerio/analytics/test/client.py +++ b/customerio/analytics/test/client.py @@ -318,13 +318,14 @@ def test_gzip(self): def test_user_defined_upload_size(self): client = Client('testsecret', on_error=self.fail, - upload_size=10, upload_interval=3) + upload_size=10, upload_interval=0.3) def mock_post_fn(*args, **kwargs): self.assertEqual(len(kwargs['batch']), 10) # the post function should be called 2 times, with a batch size of 10 - # each time. + # each time. 0.3s upload_interval ensures both batches are sent + # during 1 second sleep. with mock.patch('customerio.analytics.consumer.post', side_effect=mock_post_fn) \ as mock_post: for _ in range(20): @@ -332,6 +333,17 @@ def mock_post_fn(*args, **kwargs): time.sleep(1) self.assertEqual(mock_post.call_count, 2) + def test_user_defined_upload_interval(self): + upload_interval = 0.3 + client = Client('testsecret', on_error=self.fail, + upload_size=100, upload_interval=upload_interval) + + with mock.patch('customerio.analytics.consumer.post') as mock_post: + for _ in range(3): + client.identify('userId', {'trait': 'value'}) + time.sleep(upload_interval * 1.1) + self.assertEqual(mock_post.call_count, 3) + def test_user_defined_timeout(self): client = Client('testsecret', timeout=10) for consumer in client.consumers: @@ -343,6 +355,16 @@ def test_default_timeout_15(self): self.assertEqual(consumer.timeout, 15) def test_proxies(self): - client = Client('testsecret', proxies='203.243.63.16:80') - success, msg = client.identify('userId', {'trait': 'value'}) - self.assertTrue(success) + proxies = { + 'https': 'http://proxy.example.com:8080', + 'http': 'http://proxy.example.com:8080', + } + client = Client('testsecret', sync_mode=True, proxies=proxies) + mock_response = mock.Mock() + mock_response.status_code = 200 + with mock.patch('customerio.analytics.request._session.post', + return_value=mock_response) as mock_post: + client.identify('userId', {'trait': 'value'}) + mock_post.assert_called_once() + _, call_kwargs = mock_post.call_args + self.assertEqual(call_kwargs['proxies'], proxies) diff --git a/customerio/analytics/test/consumer.py b/customerio/analytics/test/consumer.py index d10ded2..d6c5f5f 100644 --- a/customerio/analytics/test/consumer.py +++ b/customerio/analytics/test/consumer.py @@ -198,12 +198,10 @@ def mock_post_fn(_, data, **kwargs): q.join() self.assertEqual(mock_post.call_count, 2) - @classmethod - def test_proxies(cls): - consumer = Consumer(None, 'testsecret', proxies='203.243.63.16:80') - track = { - 'type': 'track', - 'event': 'python event', - 'userId': 'userId' + def test_proxies(self): + proxies = { + 'http': 'http://192.0.2.1:80', + 'https': 'http://192.0.2.1:80', } - consumer.request([track]) + consumer = Consumer(None, 'testsecret', proxies=proxies) + self.assertEqual(consumer.proxies, proxies) diff --git a/customerio/analytics/test/request.py b/customerio/analytics/test/request.py index 003caa1..ecc2509 100644 --- a/customerio/analytics/test/request.py +++ b/customerio/analytics/test/request.py @@ -1,5 +1,6 @@ from datetime import datetime, date import unittest +import unittest.mock as mock import json import requests @@ -52,11 +53,34 @@ def test_should_timeout(self): 'type': 'track' }], timeout=0.0001) - def test_proxies(self): - res = post('testsecret', batch=[{ - 'userId': 'userId', - 'event': 'python event', - 'type': 'track', - 'proxies': '203.243.63.16:80' - }]) - self.assertEqual(res.status_code, 200) + def test_proxies_passed_to_session(self): + proxies = { + 'https': 'http://proxy.example.com:8080', + 'http': 'http://proxy.example.com:8080', + } + mock_response = mock.Mock() + mock_response.status_code = 200 + with mock.patch('customerio.analytics.request._session.post', + return_value=mock_response) as mock_post: + post('testsecret', proxies=proxies, batch=[{ + 'userId': 'userId', + 'event': 'python event', + 'type': 'track', + }]) + mock_post.assert_called_once() + _, call_kwargs = mock_post.call_args + self.assertEqual(call_kwargs['proxies'], proxies) + + def test_no_proxies_by_default(self): + mock_response = mock.Mock() + mock_response.status_code = 200 + with mock.patch('customerio.analytics.request._session.post', + return_value=mock_response) as mock_post: + post('testsecret', batch=[{ + 'userId': 'userId', + 'event': 'python event', + 'type': 'track', + }]) + mock_post.assert_called_once() + _, call_kwargs = mock_post.call_args + self.assertNotIn('proxies', call_kwargs)