-
Notifications
You must be signed in to change notification settings - Fork 41
Add initial unit tests to the proxy #216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
|
||
import json | ||
import unittest | ||
from contextlib import contextmanager | ||
from io import BytesIO | ||
from unittest.mock import Mock, patch | ||
|
||
from proxy.server import Handler | ||
|
||
class UnittestHandler(Handler): | ||
"""A testable version of Handler that doesn't auto-handle requests""" | ||
|
||
def __init__(self): | ||
# Skip the parent __init__ to avoid automatic handling | ||
# Instead, set up the minimal attributes needed for testing | ||
self.path = "" | ||
self.send_response = Mock() | ||
self.send_header = Mock() | ||
self.end_headers = Mock() | ||
self.wfile = BytesIO() | ||
self.rfile = BytesIO() | ||
self.headers = {} | ||
self.client_address = ('127.0.0.1', 12345) | ||
self.server = Mock() | ||
self.request_version = 'HTTP/1.1' | ||
self.command = 'GET' | ||
|
||
|
||
def common_patches(func): | ||
"""Decorator to apply common patches to test methods""" | ||
@patch('proxy.server.api_base_url', '') | ||
@patch('proxy.server.proxystats', { | ||
'gets': 0, 'posts': 0, 'errors': 0, 'timeout': 0, | ||
'uri': {}, 'start': 1000, 'clear': 0 | ||
}) | ||
@patch('proxy.server.proxystats_lock') | ||
def wrapper(*args, **kwargs): | ||
return func(*args, **kwargs) | ||
return wrapper | ||
|
||
|
||
@contextmanager | ||
def standard_test_patches(): | ||
"""Context manager for standard test patches""" | ||
with patch('proxy.server.proxystats_lock'), \ | ||
patch('proxy.server.proxystats', { | ||
'gets': 0, 'posts': 0, 'errors': 0, 'timeout': 0, | ||
'uri': {}, 'start': 1000, 'clear': 0 | ||
}), \ | ||
patch('proxy.server.api_base_url', ''): | ||
yield | ||
|
||
class BaseDoGetTest(unittest.TestCase): | ||
"""Base test class with common setup and helper methods""" | ||
|
||
def setUp(self): | ||
"""Common setup for all test cases""" | ||
# Use our testable handler | ||
self.handler = UnittestHandler() | ||
|
||
# Mock wfile.write for easier testing | ||
self.handler.wfile = Mock() | ||
self.handler.wfile.write = Mock() | ||
|
||
def get_written_json(self): | ||
"""Helper to extract and parse JSON from written response""" | ||
written_data = self.handler.wfile.write.call_args[0][0] | ||
return json.loads(written_data.decode('utf8')) | ||
|
||
def get_written_text(self): | ||
"""Helper to extract text from written response""" | ||
written_data = self.handler.wfile.write.call_args[0][0] | ||
return written_data.decode('utf8') | ||
|
||
def assert_json_response(self, expected_key, expected_value): | ||
"""Helper to assert JSON response contains expected key-value""" | ||
result = self.get_written_json() | ||
self.assertIn(expected_key, result) | ||
self.assertEqual(result[expected_key], expected_value) | ||
|
||
class TestDoGetStatsEndpoints(BaseDoGetTest): | ||
"""Test cases for stats-related endpoints""" | ||
|
||
def test_stats_endpoint(self): | ||
"""Test /stats endpoint - using context manager approach""" | ||
with standard_test_patches(), \ | ||
patch('proxy.server.safe_pw_call') as mock_safe_call, \ | ||
patch('proxy.server.resource') as mock_resource, \ | ||
patch('proxy.server.time') as mock_time, \ | ||
patch('proxy.server.pw') as mock_pw, \ | ||
patch('proxy.server.health_check_enabled', False): | ||
|
||
self.handler.path = "/stats" | ||
mock_time.time.return_value = 2000 | ||
mock_resource.getrusage.return_value = Mock(ru_maxrss=1024) | ||
mock_safe_call.return_value = "Test Site" | ||
mock_pw.cloudmode = False | ||
mock_pw.fleetapi = False | ||
|
||
self.handler.do_GET() | ||
|
||
result = self.get_written_json() | ||
self.assertEqual(result["ts"], 2000) | ||
self.assertEqual(result["mem"], 1024) | ||
|
||
def test_stats_clear_endpoint(self): | ||
"""Test /stats/clear endpoint - using context manager with custom proxystats""" | ||
with patch('proxy.server.proxystats_lock'), \ | ||
patch('proxy.server.proxystats', {'gets': 10, 'errors': 2, 'uri': {'/test': 5}, 'clear': 0}) as mock_stats, \ | ||
patch('proxy.server.api_base_url', ''), \ | ||
patch('proxy.server.time') as mock_time: | ||
jasonacox marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
self.handler.path = "/stats/clear" | ||
mock_time.time.return_value = 3000 | ||
|
||
self.handler.do_GET() | ||
|
||
# Check that stats were cleared | ||
self.assertEqual(mock_stats["gets"], 1) | ||
self.assertEqual(mock_stats["errors"], 0) | ||
self.assertEqual(mock_stats["uri"], {'/stats/clear': 1}) | ||
self.assertEqual(mock_stats["clear"], 3000) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
[pytest] | ||
# Basic configuration | ||
addopts = --cov=pypowerwall --cov-branch --cov-report=term-missing --cov-report=xml --cov-report=html | ||
testpaths = pypowerwall/tests | ||
testpaths = pypowerwall/tests proxy/tests | ||
python_files = test_*.py | ||
python_classes = Test* | ||
python_functions = test_* |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.