|
15 | 15 | """Tests to assure the business snapshot end-point.""" |
16 | 16 | from datetime import datetime |
17 | 17 |
|
| 18 | +import pytest |
| 19 | + |
18 | 20 | from colin_api.exceptions import BusinessNotFoundException, PartiesNotFoundException |
19 | 21 | from colin_api.models import Business, Party, ShareObject |
| 22 | +from colin_api.models.shares import Share, ShareClass |
20 | 23 | from tests.unit import LEAR_ADDRESS, build_business, build_director, bypass_auth |
21 | 24 |
|
22 | 25 |
|
23 | 26 | SNAPSHOT_URL = '/api/v1/businesses/BC0870226/snapshot' |
24 | 27 |
|
25 | 28 |
|
| 29 | +def build_share_structure_variant(class_name, currency='CAD', other_currency=None, series_name=None): |
| 30 | + """Return a current ShareObject with the subject class name / currency.""" |
| 31 | + share_class = ShareClass() |
| 32 | + share_class.share_id = 0 |
| 33 | + share_class.share_name = class_name |
| 34 | + share_class.currency_type = currency |
| 35 | + share_class.other_currency = other_currency |
| 36 | + share_class.has_max_shares = 'N' # COLIN semantics: 'N' means has a maximum |
| 37 | + share_class.has_par_value = 'Y' |
| 38 | + share_class.has_special_rights = 'Y' |
| 39 | + share_class.par_value_amt = 1.5 |
| 40 | + share_class.max_number_shares = 10000 |
| 41 | + share_class.series = [] |
| 42 | + if series_name: |
| 43 | + series = Share() |
| 44 | + series.share_id = 1 |
| 45 | + series.share_name = series_name |
| 46 | + series.has_max_shares = 'Y' |
| 47 | + series.has_special_rights = 'N' |
| 48 | + series.max_number_shares = None |
| 49 | + share_class.series = [series] |
| 50 | + share_struct = ShareObject() |
| 51 | + share_struct.end_event_id = None |
| 52 | + share_struct.share_classes = [share_class] |
| 53 | + return share_struct |
| 54 | + |
| 55 | + |
26 | 56 | def test_get_snapshot(client, mocker, authorized, mock_db, mock_lookups): # pylint: disable=unused-argument |
27 | 57 | """Assert the full LEAR-normalized snapshot is returned.""" |
28 | 58 | rv = client.get(SNAPSHOT_URL) |
@@ -64,18 +94,18 @@ def test_get_snapshot(client, mocker, authorized, mock_db, mock_lookups): # pyl |
64 | 94 | }, |
65 | 95 | 'shareClasses': [{ |
66 | 96 | 'id': 0, |
67 | | - 'name': 'CLASS A', |
| 97 | + 'name': 'CLASS A Shares', |
68 | 98 | 'priority': 0, |
69 | 99 | 'hasMaximumShares': True, |
70 | 100 | 'maxNumberOfShares': 10000, |
71 | 101 | 'hasParValue': True, |
72 | 102 | 'parValue': 1.5, |
73 | | - 'currency': 'OTH', |
| 103 | + 'currency': 'OTHER', |
74 | 104 | 'currencyAdditional': 'BITCOIN', |
75 | 105 | 'hasRightsOrRestrictions': True, |
76 | 106 | 'series': [{ |
77 | 107 | 'id': 1, |
78 | | - 'name': 'SERIES 1', |
| 108 | + 'name': 'SERIES 1 Shares', |
79 | 109 | 'priority': 1, |
80 | 110 | 'hasMaximumShares': False, |
81 | 111 | 'maxNumberOfShares': None, |
@@ -152,6 +182,46 @@ def test_get_snapshot_without_share_structure(client, mocker, authorized, mock_d |
152 | 182 | assert rv.json['shareClasses'] == [] |
153 | 183 |
|
154 | 184 |
|
| 185 | +@pytest.mark.parametrize('colin_name, expected_name', [ |
| 186 | + ('CLASS A COMMON SHARES', 'CLASS A COMMON Shares'), # all-caps legacy suffix replaced |
| 187 | + ('preferred shares', 'preferred Shares'), # lowercase suffix replaced (tombstone migration parity) |
| 188 | + ('Class A Shares', 'Class A Shares'), # already normalized - unchanged |
| 189 | + ('CLASS B', 'CLASS B Shares'), # no suffix - appended |
| 190 | +]) |
| 191 | +def test_get_snapshot_normalizes_share_names(client, mocker, authorized, mock_db, mock_lookups, |
| 192 | + colin_name, expected_name): # pylint: disable=unused-argument |
| 193 | + """Assert class and series names get the ' Shares' suffix legal-api requires.""" |
| 194 | + mocker.patch.object(ShareObject, 'get_all', return_value=[ |
| 195 | + build_share_structure_variant(colin_name, series_name='SERIES 1')]) |
| 196 | + |
| 197 | + rv = client.get(SNAPSHOT_URL) |
| 198 | + |
| 199 | + assert rv.status_code == 200 |
| 200 | + assert rv.json['shareClasses'][0]['name'] == expected_name |
| 201 | + assert rv.json['shareClasses'][0]['series'][0]['name'] == 'SERIES 1 Shares' |
| 202 | + |
| 203 | + |
| 204 | +@pytest.mark.parametrize('currency, other_currency, expected, expected_additional', [ |
| 205 | + ('CAD', None, 'CAD', None), # real code passes through |
| 206 | + ('OTH', 'CAD', 'CAD', None), # tombstone migration parity fold |
| 207 | + ('OTH', ' usd ', 'USD', None), # any valid ISO 4217 code is promoted |
| 208 | + ('OTH', 'BITCOIN', 'OTHER', 'BITCOIN'), # non-ISO free text stays flagged |
| 209 | + ('OTH', None, 'OTHER', None), |
| 210 | +]) |
| 211 | +def test_get_snapshot_normalizes_currency(client, mocker, authorized, mock_db, mock_lookups, |
| 212 | + currency, other_currency, expected, |
| 213 | + expected_additional): # pylint: disable=unused-argument |
| 214 | + """Assert COLIN's OTH currency is folded to an ISO code or OTHER.""" |
| 215 | + mocker.patch.object(ShareObject, 'get_all', return_value=[ |
| 216 | + build_share_structure_variant('Class A Shares', currency=currency, other_currency=other_currency)]) |
| 217 | + |
| 218 | + rv = client.get(SNAPSHOT_URL) |
| 219 | + |
| 220 | + assert rv.status_code == 200 |
| 221 | + assert rv.json['shareClasses'][0]['currency'] == expected |
| 222 | + assert rv.json['shareClasses'][0]['currencyAdditional'] == expected_additional |
| 223 | + |
| 224 | + |
155 | 225 | def test_get_snapshot_null_good_standing(client, mocker, authorized, mock_db, |
156 | 226 | mock_lookups): # pylint: disable=unused-argument |
157 | 227 | """Assert COLIN's tri-state good standing passes through as null when unknown.""" |
|
0 commit comments