Skip to content

Commit

Permalink
Merge pull request #52 from alexgolec/more-tests
Browse files Browse the repository at this point in the history
More tests
  • Loading branch information
alexgolec authored May 12, 2024
2 parents c20c42f + 87d860e commit 7b75203
Show file tree
Hide file tree
Showing 10 changed files with 2,097 additions and 4 deletions.
2 changes: 1 addition & 1 deletion schwab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#from . import contrib
from . import debug
from . import orders
#from . import streaming
from . import streaming
from . import utils

from .version import version as __version__
Expand Down
3 changes: 2 additions & 1 deletion schwab/debug.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import atexit
import httpx
import json
import logging
import sys
import schwab
Expand Down Expand Up @@ -52,7 +53,7 @@ def register_redactions_from_response(resp):
if resp.status_code == httpx.codes.OK:
try:
register_redactions(resp.json())
except __json_errors:
except json.decoder.JSONDecodeError:
pass


Expand Down
4 changes: 2 additions & 2 deletions schwab/orders/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class OptionSymbol:
in front because strike is less than 1000)
* ``SPXW 240420C05040000``: SPX Weekly Apr 20, 2024 5040 Call (note the
one zero in front because strike is greater than 1000)
Note while each of the individual parts is validated by itself, the
option symbol itself may not represent a traded option:
Expand Down Expand Up @@ -93,7 +93,7 @@ def __init__(self, underlying_symbol, expiration_date, contract_type,
if (strike is None or not isinstance(strike_price_as_string, str)
or strike <= 0):
raise ValueError(
'Strike price must be a string representing a positive ' +
'strike price must be a string representing a positive ' +
'float')

# Remove extraneous zeroes at the end
Expand Down
159 changes: 159 additions & 0 deletions tests/debug_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import atexit
import io
import json
import logging
import schwab
import unittest

from schwab.client import Client
from .utils import MockResponse, no_duplicates
from unittest.mock import Mock, patch


class RedactorTest(unittest.TestCase):

def setUp(self):
self.redactor = schwab.debug.LogRedactor()

@no_duplicates
def test_no_redactions(self):
self.assertEqual('test message', self.redactor.redact('test message'))

@no_duplicates
def test_simple_redaction(self):
self.redactor.register('secret', 'SECRET')

self.assertEqual(
'<REDACTED SECRET> message',
self.redactor.redact('secret message'))

@no_duplicates
def test_multiple_registrations_same_string(self):
self.redactor.register('secret', 'SECRET')
self.redactor.register('secret', 'SECRET')

self.assertEqual(
'<REDACTED SECRET> message',
self.redactor.redact('secret message'))

@no_duplicates
def test_multiple_registrations_same_string_different_label(self):
self.redactor.register('secret-A', 'SECRET')
self.redactor.register('secret-B', 'SECRET')

self.assertEqual(
'<REDACTED SECRET-1> message <REDACTED SECRET-2>',
self.redactor.redact('secret-A message secret-B'))


class RegisterRedactionsTest(unittest.TestCase):

def setUp(self):
self.captured = io.StringIO()
self.logger = logging.getLogger('test')
self.dump_logs = schwab.debug._enable_bug_report_logging(
output=self.captured, loggers=[self.logger])
schwab.LOG_REDACTOR = schwab.debug.LogRedactor()

@no_duplicates
def test_empty_string(self):
schwab.debug.register_redactions('')

@no_duplicates
def test_empty_dict(self):
schwab.debug.register_redactions({})

@no_duplicates
def test_empty_list(self):
schwab.debug.register_redactions([])

@no_duplicates
def test_dict(self):
schwab.debug.register_redactions(
{'BadNumber': '100001'},
bad_patterns=['bad'])
schwab.debug.register_redactions(
{'OtherBadNumber': '200002'},
bad_patterns=['bad'])

self.logger.info('Bad Number: 100001')
self.logger.info('Other Bad Number: 200002')

self.dump_logs()
self.assertRegex(
self.captured.getvalue(),
r'\[.*\] Bad Number: <REDACTED BadNumber>\n' +
r'\[.*\] Other Bad Number: <REDACTED OtherBadNumber>\n')

@no_duplicates
def test_list_of_dict(self):
schwab.debug.register_redactions(
[{'GoodNumber': '900009'},
{'BadNumber': '100001'},
{'OtherBadNumber': '200002'}],
bad_patterns=['bad'])

self.logger.info('Good Number: 900009')
self.logger.info('Bad Number: 100001')
self.logger.info('Other Bad Number: 200002')

self.dump_logs()
self.assertRegex(
self.captured.getvalue(),
r'\[.*\] Good Number: 900009\n' +
r'\[.*\] Bad Number: <REDACTED 1-BadNumber>\n' +
r'\[.*\] Other Bad Number: <REDACTED 2-OtherBadNumber>\n')

@no_duplicates
def test_whitelist(self):
schwab.debug.register_redactions(
[{'GoodNumber': '900009'},
{'BadNumber': '100001'},
{'OtherBadNumber': '200002'}],
bad_patterns=['bad'],
whitelisted=['otherbadnumber'])

self.logger.info('Good Number: 900009')
self.logger.info('Bad Number: 100001')
self.logger.info('Other Bad Number: 200002')

self.dump_logs()
self.assertRegex(
self.captured.getvalue(),
r'\[.*\] Good Number: 900009\n' +
r'\[.*\] Bad Number: <REDACTED 1-BadNumber>\n' +
r'\[.*\] Other Bad Number: 200002\n')

@no_duplicates
@patch('schwab.debug.register_redactions', new_callable=Mock)
def test_register_from_request_success(self, register_redactions):
resp = MockResponse({'success': 1}, 200)
schwab.debug.register_redactions_from_response(resp)
register_redactions.assert_called_with({'success': 1})

@no_duplicates
@patch('schwab.debug.register_redactions', new_callable=Mock)
def test_register_from_request_not_okay(self, register_redactions):
resp = MockResponse({'success': 1}, 403)
schwab.debug.register_redactions_from_response(resp)
register_redactions.assert_not_called()

@no_duplicates
@patch('schwab.debug.register_redactions', new_callable=Mock)
def test_register_unparseable_json(self, register_redactions):
class MR(MockResponse):
def json(self):
raise json.decoder.JSONDecodeError('e243rschwabgew', '', 0)

resp = MR({'success': 1}, 200)
schwab.debug.register_redactions_from_response(resp)
register_redactions.assert_not_called()

class EnableDebugLoggingTest(unittest.TestCase):

@patch('logging.Logger.addHandler')
def test_enable_doesnt_throw_exceptions(self, _):
try:
schwab.debug.enable_bug_report_logging()
except AttributeError:
self.fail("debug.enable_bug_report_logging() raised AttributeError unexpectedly")
Empty file added tests/orders/__init__.py
Empty file.
31 changes: 31 additions & 0 deletions tests/orders/common_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from ..utils import has_diff, no_duplicates
from schwab.orders.common import *
from schwab.orders.generic import OrderBuilder

import unittest

class MultiOrderTest(unittest.TestCase):

@no_duplicates
def test_oco(self):
self.assertFalse(has_diff({
'orderStrategyType': 'OCO',
'childOrderStrategies': [
{'session': 'NORMAL'},
{'duration': 'DAY'},
]
}, one_cancels_other(
OrderBuilder().set_session(Session.NORMAL),
OrderBuilder().set_duration(Duration.DAY)).build()))

@no_duplicates
def test_trigger(self):
self.assertFalse(has_diff({
'orderStrategyType': 'TRIGGER',
'session': 'NORMAL',
'childOrderStrategies': [
{'duration': 'DAY'},
]
}, first_triggers_second(
OrderBuilder().set_session(Session.NORMAL),
OrderBuilder().set_duration(Duration.DAY)).build()))
Loading

0 comments on commit 7b75203

Please sign in to comment.