cti-python-stix2/stix2/test/v21/test_location.py

376 lines
11 KiB
Python
Raw Normal View History

2017-10-23 14:06:29 +02:00
import datetime as dt
import re
import pytest
import pytz
import stix2
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
import stix2.exceptions
2017-10-23 14:06:29 +02:00
from .constants import LOCATION_ID
EXPECTED_LOCATION_1 = """{
"type": "location",
"spec_version": "2.1",
2017-10-23 14:06:29 +02:00
"id": "location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64",
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 48.8566,
"longitude": 2.3522
}"""
EXPECTED_LOCATION_1_REPR = "Location(" + " ".join(
"""
2017-10-23 14:06:29 +02:00
type='location',
spec_version='2.1',
2017-10-23 14:06:29 +02:00
id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64',
created='2016-04-06T20:03:00.000Z',
modified='2016-04-06T20:03:00.000Z',
latitude=48.8566,
longitude=2.3522""".split(),
) + ")"
2017-10-23 14:06:29 +02:00
EXPECTED_LOCATION_2 = """{
"type": "location",
"spec_version": "2.1",
2017-10-23 14:06:29 +02:00
"id": "location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64",
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"region": "north-america"
}
"""
EXPECTED_LOCATION_2_REPR = "Location(" + " ".join(
"""
2017-10-23 14:06:29 +02:00
type='location',
spec_version='2.1',
2017-10-23 14:06:29 +02:00
id='location--a6e9345f-5a15-4c29-8bb3-7dcc5d168d64',
created='2016-04-06T20:03:00.000Z',
modified='2016-04-06T20:03:00.000Z',
region='north-america'""".split(),
) + ")"
2017-10-23 14:06:29 +02:00
def test_location_with_some_required_properties():
now = dt.datetime(2016, 4, 6, 20, 3, 0, tzinfo=pytz.utc)
loc = stix2.v21.Location(
2017-10-23 14:06:29 +02:00
id=LOCATION_ID,
created=now,
modified=now,
latitude=48.8566,
longitude=2.3522,
2017-10-23 14:06:29 +02:00
)
assert str(loc) == EXPECTED_LOCATION_1
rep = re.sub(r"(\[|=| )u('|\"|\\\'|\\\")", r"\g<1>\g<2>", repr(loc))
assert rep == EXPECTED_LOCATION_1_REPR
@pytest.mark.parametrize(
"data", [
EXPECTED_LOCATION_2,
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"region": "north-america",
},
],
)
2017-10-23 14:06:29 +02:00
def test_parse_location(data):
location = stix2.parse(data, version="2.1")
2017-10-23 14:06:29 +02:00
assert location.type == 'location'
assert location.spec_version == '2.1'
2017-10-23 14:06:29 +02:00
assert location.id == LOCATION_ID
assert location.created == dt.datetime(2016, 4, 6, 20, 3, 0, tzinfo=pytz.utc)
assert location.modified == dt.datetime(2016, 4, 6, 20, 3, 0, tzinfo=pytz.utc)
assert location.region == 'north-america'
rep = re.sub(r"(\[|=| )u('|\"|\\\'|\\\")", r"\g<1>\g<2>", repr(location))
assert rep == EXPECTED_LOCATION_2_REPR
2018-07-25 18:43:57 +02:00
@pytest.mark.parametrize(
"data", [
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-07-25 18:43:57 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 90.01,
"longitude": 0.0,
},
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-07-25 18:43:57 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": -90.1,
"longitude": 0.0,
},
],
)
def test_location_bad_latitude(data):
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
2018-07-25 18:43:57 +02:00
stix2.parse(data)
2018-10-15 20:48:52 +02:00
assert "Invalid value for Location 'latitude'" in str(excinfo.value)
2018-07-25 18:43:57 +02:00
@pytest.mark.parametrize(
"data", [
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-07-25 18:43:57 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 80,
"longitude": 180.1,
},
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-07-25 18:43:57 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 80,
"longitude": -180.1,
},
],
)
def test_location_bad_longitude(data):
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
2018-07-25 18:43:57 +02:00
stix2.parse(data)
2018-10-15 20:48:52 +02:00
assert "Invalid value for Location 'longitude'" in str(excinfo.value)
2018-07-25 18:43:57 +02:00
@pytest.mark.parametrize(
"data", [
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-07-25 18:43:57 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"longitude": 175.7,
"precision": 20,
},
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-07-25 18:43:57 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 80,
"precision": 20,
},
],
)
def test_location_properties_missing_when_precision_is_present(data):
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
stix2.parse(data)
assert any(x in str(excinfo.value) for x in ("(latitude, precision)", "(longitude, precision)"))
@pytest.mark.parametrize(
"data", [
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-07-25 18:43:57 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 18.468842,
"longitude": -66.120711,
"precision": -100.0,
},
],
)
def test_location_negative_precision(data):
Improved the exception class hierarchy: - Removed all plain python base classes (e.g. ValueError, TypeError) - Renamed InvalidPropertyConfigurationError -> PropertyPresenceError, since incorrect values could be considered a property config error, and I really just wanted this class to apply to presence (co-)constraint violations. - Added ObjectConfigurationError as a superclass of InvalidValueError, PropertyPresenceError, and any other exception that could be raised during _STIXBase object init, which is when the spec compliance checks happen. This class is intended to represent general spec violations. - Did some class reordering in exceptions.py, so all the ObjectConfigurationError subclasses were together. Changed how property "cleaning" errors were handled: - Previous docs said they should all be ValueErrors, but that would require extra exception check-and-replace complexity in the property implementations, so that requirement is removed. Doc is changed to just say that cleaning problems should cause exceptions to be raised. _STIXBase._check_property() now handles most exception types, not just ValueError. - Decided to try chaining the original clean error to the InvalidValueError, in case the extra diagnostics would be helpful in the future. This is done via 'six' adapter function and only works on python3. - A small amount of testing was removed, since it was looking at custom exception properties which became unavailable once the exception was replaced with InvalidValueError. Did another pass through unit tests to fix breakage caused by the changed exception class hierarchy. Removed unnecessary observable extension handling code from parse_observable(), since it was all duplicated in ExtensionsProperty. The redundant code in parse_observable() had different exception behavior than ExtensionsProperty, which makes the API inconsistent and unit tests more complicated. (Problems in ExtensionsProperty get replaced with InvalidValueError, but extensions problems handled directly in parse_observable() don't get the same replacement, and so the exception type is different.) Redid the workbench monkeypatching. The old way was impossible to make work, and had caused ugly ripple effect hackage in other parts of the codebase. Now, it replaces the global object maps with factory functions which behave the same way when called, as real classes. Had to fix up a few unit tests to get them all passing with this monkeypatching in place. Also remove all the xfail markings in the workbench test suite, since all tests now pass. Since workbench monkeypatching isn't currently affecting any unit tests, tox.ini was simplified to remove the special-casing for running the workbench tests. Removed the v20 workbench test suite, since the workbench currently only works with the latest stix object version.
2019-07-19 20:50:11 +02:00
with pytest.raises(stix2.exceptions.InvalidValueError) as excinfo:
2018-07-25 18:43:57 +02:00
stix2.parse(data)
2018-10-15 20:48:52 +02:00
assert "Invalid value for Location 'precision'" in str(excinfo.value)
@pytest.mark.parametrize(
"data,msg", [
(
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-10-15 20:48:52 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 18.468842,
"precision": 5.0,
},
2018-10-17 13:56:10 +02:00
"(longitude, precision) are not met.",
2018-10-15 20:48:52 +02:00
),
(
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-10-15 20:48:52 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"longitude": 160.7,
"precision": 5.0,
},
2018-10-17 13:56:10 +02:00
"(latitude, precision) are not met.",
2018-10-15 20:48:52 +02:00
),
],
)
2018-10-17 13:56:10 +02:00
def test_location_latitude_dependency_missing(data, msg):
2018-10-15 20:48:52 +02:00
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
stix2.parse(data)
assert msg in str(excinfo.value)
@pytest.mark.parametrize(
"data,msg", [
(
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-10-15 20:48:52 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"latitude": 18.468842,
},
2018-10-17 13:56:10 +02:00
"(longitude, latitude) are not met.",
2018-10-15 20:48:52 +02:00
),
(
{
"type": "location",
"spec_version": "2.1",
2019-01-23 16:56:20 +01:00
"id": LOCATION_ID,
2018-10-15 20:48:52 +02:00
"created": "2016-04-06T20:03:00.000Z",
"modified": "2016-04-06T20:03:00.000Z",
"longitude": 160.7,
},
2018-10-17 13:56:10 +02:00
"(latitude, longitude) are not met.",
2018-10-15 20:48:52 +02:00
),
],
)
2018-10-17 13:56:10 +02:00
def test_location_lat_or_lon_dependency_missing(data, msg):
2018-10-15 20:48:52 +02:00
with pytest.raises(stix2.exceptions.DependentPropertiesError) as excinfo:
stix2.parse(data)
assert msg in str(excinfo.value)
def test_location_complex_presence_constraint():
with pytest.raises(stix2.exceptions.PropertyPresenceError):
stix2.parse({
"type": "location",
"spec_version": "2.1",
"id": LOCATION_ID,
})
def test_google_map_url_long_lat_provided():
expected_url = "https://www.google.com/maps/search/?api=1&query=41.862401%2C-87.616001"
loc = stix2.v21.Location(
latitude=41.862401,
longitude=-87.616001,
)
loc_url = loc.to_maps_url()
assert loc_url == expected_url
def test_google_map_url_multiple_props_no_long_lat_provided():
expected_url = "https://www.google.com/maps/search/?api=1&query=1410+Museum+Campus+Drive%2C+Chicago%2C+IL+60605%2CUnited+States+of+America%2CNorth+America"
now = dt.datetime(2019, 2, 7, 12, 34, 56, tzinfo=pytz.utc)
loc = stix2.v21.Location(
type="location",
id=LOCATION_ID,
created=now,
modified=now,
region="North America",
country="United States of America",
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
)
loc_url = loc.to_maps_url()
assert loc_url == expected_url
def test_google_map_url_multiple_props_and_long_lat_provided():
expected_url = "https://www.google.com/maps/search/?api=1&query=41.862401%2C-87.616001"
loc = stix2.v21.Location(
region="North America",
country="United States of America",
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
latitude=41.862401,
longitude=-87.616001,
)
loc_url = loc.to_maps_url()
assert loc_url == expected_url
def test_map_url_invalid_map_engine_provided():
loc = stix2.v21.Location(
latitude=41.862401,
longitude=-87.616001,
)
with pytest.raises(ValueError) as excinfo:
loc.to_maps_url("Fake Maps")
assert "is not a valid or currently-supported map engine" in str(excinfo.value)
def test_bing_map_url_long_lat_provided():
expected_url = "https://bing.com/maps/default.aspx?where1=41.862401%2C-87.616001&lvl=16"
loc = stix2.v21.Location(
latitude=41.862401,
longitude=-87.616001,
)
loc_url = loc.to_maps_url("Bing Maps")
assert loc_url == expected_url
def test_bing_map_url_multiple_props_no_long_lat_provided():
expected_url = "https://bing.com/maps/default.aspx?where1=1410+Museum+Campus+Drive%2C+Chicago%2C+IL+60605%2CUnited+States+of+America%2CNorth+America&lvl=16"
loc = stix2.v21.Location(
region="North America",
country="United States of America",
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
)
loc_url = loc.to_maps_url("Bing Maps")
assert loc_url == expected_url
def test_bing_map_url_multiple_props_and_long_lat_provided():
expected_url = "https://bing.com/maps/default.aspx?where1=41.862401%2C-87.616001&lvl=16"
loc = stix2.v21.Location(
region="North America",
country="United States of America",
street_address="1410 Museum Campus Drive, Chicago, IL 60605",
latitude=41.862401,
longitude=-87.616001,
)
loc_url = loc.to_maps_url("Bing Maps")
assert loc_url == expected_url