diff --git a/stix2/test/test_properties.py b/stix2/test/test_properties.py new file mode 100644 index 0000000..a129b12 --- /dev/null +++ b/stix2/test/test_properties.py @@ -0,0 +1,341 @@ +import datetime as dt + +import pytest +import pytz + +import stix2 +from stix2.exceptions import ExtraPropertiesError +from stix2.properties import ( + BinaryProperty, BooleanProperty, EmbeddedObjectProperty, EnumProperty, + FloatProperty, HexProperty, IntegerProperty, ListProperty, Property, + StringProperty, TimestampProperty, TypeProperty, +) + + +def test_property(): + p = Property() + + assert p.required is False + assert p.clean('foo') == 'foo' + assert p.clean(3) == 3 + + +def test_basic_clean(): + class Prop(Property): + + def clean(self, value): + if value == 42: + return value + else: + raise ValueError("Must be 42") + + p = Prop() + + assert p.clean(42) == 42 + with pytest.raises(ValueError): + p.clean(41) + + +def test_property_default(): + class Prop(Property): + + def default(self): + return 77 + + p = Prop() + + assert p.default() == 77 + + +def test_fixed_property(): + p = Property(fixed="2.0") + + assert p.clean("2.0") + with pytest.raises(ValueError): + assert p.clean("x") is False + with pytest.raises(ValueError): + assert p.clean(2.0) is False + + assert p.default() == "2.0" + assert p.clean(p.default()) + + +def test_list_property(): + p = ListProperty(StringProperty) + + assert p.clean(['abc', 'xyz']) + with pytest.raises(ValueError): + p.clean([]) + + +def test_list_property_property_type_custom(): + class TestObj(stix2.base._STIXBase): + _type = "test" + _properties = { + "foo": StringProperty(), + } + p = ListProperty(EmbeddedObjectProperty(type=TestObj)) + + objs_custom = [ + TestObj(foo="abc", bar=123, allow_custom=True), + TestObj(foo="xyz"), + ] + + assert p.clean(objs_custom) + + dicts_custom = [ + {"foo": "abc", "bar": 123}, + {"foo": "xyz"}, + ] + + # no opportunity to set allow_custom=True when using dicts + with pytest.raises(ExtraPropertiesError): + p.clean(dicts_custom) + + +def test_list_property_object_type(): + class TestObj(stix2.base._STIXBase): + _type = "test" + _properties = { + "foo": StringProperty(), + } + p = ListProperty(TestObj) + + objs = [TestObj(foo="abc"), TestObj(foo="xyz")] + assert p.clean(objs) + + dicts = [{"foo": "abc"}, {"foo": "xyz"}] + assert p.clean(dicts) + + +def test_list_property_object_type_custom(): + class TestObj(stix2.base._STIXBase): + _type = "test" + _properties = { + "foo": StringProperty(), + } + p = ListProperty(TestObj) + + objs_custom = [ + TestObj(foo="abc", bar=123, allow_custom=True), + TestObj(foo="xyz"), + ] + + assert p.clean(objs_custom) + + dicts_custom = [ + {"foo": "abc", "bar": 123}, + {"foo": "xyz"}, + ] + + # no opportunity to set allow_custom=True when using dicts + with pytest.raises(ExtraPropertiesError): + p.clean(dicts_custom) + + +def test_list_property_bad_element_type(): + with pytest.raises(TypeError): + ListProperty(1) + + +def test_list_property_bad_value_type(): + class TestObj(stix2.base._STIXBase): + _type = "test" + _properties = { + "foo": StringProperty(), + } + + list_prop = ListProperty(TestObj) + with pytest.raises(ValueError): + list_prop.clean([1]) + + +def test_string_property(): + prop = StringProperty() + + assert prop.clean('foobar') + assert prop.clean(1) + assert prop.clean([1, 2, 3]) + + +def test_type_property(): + prop = TypeProperty('my-type') + + assert prop.clean('my-type') + with pytest.raises(ValueError): + prop.clean('not-my-type') + assert prop.clean(prop.default()) + + +@pytest.mark.parametrize( + "value", [ + 2, + -1, + 3.14, + False, + ], +) +def test_integer_property_valid(value): + int_prop = IntegerProperty() + assert int_prop.clean(value) is not None + + +@pytest.mark.parametrize( + "value", [ + -1, + -100, + -50 * 6, + ], +) +def test_integer_property_invalid_min_with_constraints(value): + int_prop = IntegerProperty(min=0, max=180) + with pytest.raises(ValueError) as excinfo: + int_prop.clean(value) + assert "minimum value is" in str(excinfo.value) + + +@pytest.mark.parametrize( + "value", [ + 181, + 200, + 50 * 6, + ], +) +def test_integer_property_invalid_max_with_constraints(value): + int_prop = IntegerProperty(min=0, max=180) + with pytest.raises(ValueError) as excinfo: + int_prop.clean(value) + assert "maximum value is" in str(excinfo.value) + + +@pytest.mark.parametrize( + "value", [ + "something", + StringProperty(), + ], +) +def test_integer_property_invalid(value): + int_prop = IntegerProperty() + with pytest.raises(ValueError): + int_prop.clean(value) + + +@pytest.mark.parametrize( + "value", [ + 2, + -1, + 3.14, + False, + ], +) +def test_float_property_valid(value): + int_prop = FloatProperty() + assert int_prop.clean(value) is not None + + +@pytest.mark.parametrize( + "value", [ + "something", + StringProperty(), + ], +) +def test_float_property_invalid(value): + int_prop = FloatProperty() + with pytest.raises(ValueError): + int_prop.clean(value) + + +@pytest.mark.parametrize( + "value", [ + True, + False, + 'True', + 'False', + 'true', + 'false', + 'TRUE', + 'FALSE', + 'T', + 'F', + 't', + 'f', + 1, + 0, + ], +) +def test_boolean_property_valid(value): + bool_prop = BooleanProperty() + + assert bool_prop.clean(value) is not None + + +@pytest.mark.parametrize( + "value", [ + 'abc', + ['false'], + {'true': 'true'}, + 2, + -1, + ], +) +def test_boolean_property_invalid(value): + bool_prop = BooleanProperty() + with pytest.raises(ValueError): + bool_prop.clean(value) + + +@pytest.mark.parametrize( + "value", [ + '2017-01-01T12:34:56Z', + ], +) +def test_timestamp_property_valid(value): + ts_prop = TimestampProperty() + assert ts_prop.clean(value) == dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc) + + +def test_timestamp_property_invalid(): + ts_prop = TimestampProperty() + with pytest.raises(TypeError): + ts_prop.clean(1) + with pytest.raises(ValueError): + ts_prop.clean("someday sometime") + + +def test_binary_property(): + bin_prop = BinaryProperty() + + assert bin_prop.clean("TG9yZW0gSXBzdW0=") + with pytest.raises(ValueError): + bin_prop.clean("foobar") + + +def test_hex_property(): + hex_prop = HexProperty() + + assert hex_prop.clean("4c6f72656d20497073756d") + with pytest.raises(ValueError): + hex_prop.clean("foobar") + + +@pytest.mark.parametrize( + "value", [ + ['a', 'b', 'c'], + ('a', 'b', 'c'), + 'b', + ], +) +def test_enum_property_valid(value): + enum_prop = EnumProperty(value) + assert enum_prop.clean('b') + + +def test_enum_property_clean(): + enum_prop = EnumProperty(['1']) + assert enum_prop.clean(1) == '1' + + +def test_enum_property_invalid(): + enum_prop = EnumProperty(['a', 'b', 'c']) + with pytest.raises(ValueError): + enum_prop.clean('z') diff --git a/stix2/test/v20/test_properties.py b/stix2/test/v20/test_properties.py index 6810966..b03879c 100644 --- a/stix2/test/v20/test_properties.py +++ b/stix2/test/v20/test_properties.py @@ -6,175 +6,16 @@ import stix2 import stix2.base from stix2.exceptions import ( AtLeastOnePropertyError, CustomContentError, DictionaryKeyError, - ExtraPropertiesError, ) from stix2.properties import ( - BinaryProperty, BooleanProperty, DictionaryProperty, - EmbeddedObjectProperty, EnumProperty, ExtensionsProperty, FloatProperty, - HashesProperty, HexProperty, IDProperty, IntegerProperty, ListProperty, - Property, ReferenceProperty, STIXObjectProperty, StringProperty, - TimestampProperty, TypeProperty, + DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty, + HashesProperty, IDProperty, ListProperty, ReferenceProperty, + STIXObjectProperty, ) from stix2.v20.common import MarkingProperty from . import constants - -def test_property(): - p = Property() - - assert p.required is False - assert p.clean('foo') == 'foo' - assert p.clean(3) == 3 - - -def test_basic_clean(): - class Prop(Property): - - def clean(self, value): - if value == 42: - return value - else: - raise ValueError("Must be 42") - - p = Prop() - - assert p.clean(42) == 42 - with pytest.raises(ValueError): - p.clean(41) - - -def test_property_default(): - class Prop(Property): - - def default(self): - return 77 - - p = Prop() - - assert p.default() == 77 - - -def test_fixed_property(): - p = Property(fixed="2.0") - - assert p.clean("2.0") - with pytest.raises(ValueError): - assert p.clean("x") is False - with pytest.raises(ValueError): - assert p.clean(2.0) is False - - assert p.default() == "2.0" - assert p.clean(p.default()) - - -def test_list_property_property_type(): - p = ListProperty(StringProperty) - - assert p.clean(['abc', 'xyz']) - with pytest.raises(ValueError): - p.clean([]) - - -def test_list_property_property_type_custom(): - class TestObj(stix2.base._STIXBase): - _type = "test" - _properties = { - "foo": StringProperty(), - } - p = ListProperty(EmbeddedObjectProperty(type=TestObj)) - - objs_custom = [ - TestObj(foo="abc", bar=123, allow_custom=True), - TestObj(foo="xyz"), - ] - - assert p.clean(objs_custom) - - dicts_custom = [ - {"foo": "abc", "bar": 123}, - {"foo": "xyz"}, - ] - - # no opportunity to set allow_custom=True when using dicts - with pytest.raises(ExtraPropertiesError): - p.clean(dicts_custom) - - -def test_list_property_object_type(): - class TestObj(stix2.base._STIXBase): - _type = "test" - _properties = { - "foo": StringProperty(), - } - p = ListProperty(TestObj) - - objs = [TestObj(foo="abc"), TestObj(foo="xyz")] - assert p.clean(objs) - - dicts = [{"foo": "abc"}, {"foo": "xyz"}] - assert p.clean(dicts) - - -def test_list_property_object_type_custom(): - class TestObj(stix2.base._STIXBase): - _type = "test" - _properties = { - "foo": StringProperty(), - } - p = ListProperty(TestObj) - - objs_custom = [ - TestObj(foo="abc", bar=123, allow_custom=True), - TestObj(foo="xyz"), - ] - - assert p.clean(objs_custom) - - dicts_custom = [ - {"foo": "abc", "bar": 123}, - {"foo": "xyz"}, - ] - - # no opportunity to set allow_custom=True when using dicts - with pytest.raises(ExtraPropertiesError): - p.clean(dicts_custom) - - -def test_list_property_bad_element_type(): - with pytest.raises(TypeError): - ListProperty(1) - - -def test_list_property_bad_value_type(): - class TestObj(stix2.base._STIXBase): - _type = "test" - _properties = { - "foo": StringProperty(), - } - - list_prop = ListProperty(TestObj) - with pytest.raises(ValueError): - list_prop.clean([1]) - - -def test_string_property(): - prop = StringProperty() - - assert prop.clean('foobar') - assert prop.clean(1) - assert prop.clean([1, 2, 3]) - - -def test_type_property(): - prop = TypeProperty('my-type') - - assert prop.clean('my-type') - with pytest.raises(ValueError): - prop.clean('not-my-type') - assert prop.clean(prop.default()) - - ID_PROP = IDProperty('my-type', spec_version="2.0") MY_ID = 'my-type--232c9d3f-49fc-4440-bb01-607f638778e7' @@ -242,123 +83,6 @@ def test_id_property_default(): assert ID_PROP.clean(default) == default -@pytest.mark.parametrize( - "value", [ - 2, - -1, - 3.14, - False, - ], -) -def test_integer_property_valid(value): - int_prop = IntegerProperty() - assert int_prop.clean(value) is not None - - -@pytest.mark.parametrize( - "value", [ - -1, - -100, - -5 * 6, - ], -) -def test_integer_property_invalid_min_with_constraints(value): - int_prop = IntegerProperty(min=0, max=180) - with pytest.raises(ValueError) as excinfo: - int_prop.clean(value) - assert "minimum value is" in str(excinfo.value) - - -@pytest.mark.parametrize( - "value", [ - 181, - 200, - 50 * 6, - ], -) -def test_integer_property_invalid_max_with_constraints(value): - int_prop = IntegerProperty(min=0, max=180) - with pytest.raises(ValueError) as excinfo: - int_prop.clean(value) - assert "maximum value is" in str(excinfo.value) - - -@pytest.mark.parametrize( - "value", [ - "something", - StringProperty(), - ], -) -def test_integer_property_invalid(value): - int_prop = IntegerProperty() - with pytest.raises(ValueError): - int_prop.clean(value) - - -@pytest.mark.parametrize( - "value", [ - 2, - -1, - 3.14, - False, - ], -) -def test_float_property_valid(value): - int_prop = FloatProperty() - assert int_prop.clean(value) is not None - - -@pytest.mark.parametrize( - "value", [ - "something", - StringProperty(), - ], -) -def test_float_property_invalid(value): - int_prop = FloatProperty() - with pytest.raises(ValueError): - int_prop.clean(value) - - -@pytest.mark.parametrize( - "value", [ - True, - False, - 'True', - 'False', - 'true', - 'false', - 'TRUE', - 'FALSE', - 'T', - 'F', - 't', - 'f', - 1, - 0, - ], -) -def test_boolean_property_valid(value): - bool_prop = BooleanProperty() - - assert bool_prop.clean(value) is not None - - -@pytest.mark.parametrize( - "value", [ - 'abc', - ['false'], - {'true': 'true'}, - 2, - -1, - ], -) -def test_boolean_property_invalid(value): - bool_prop = BooleanProperty() - with pytest.raises(ValueError): - bool_prop.clean(value) - - def test_reference_property(): ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.0") @@ -381,40 +105,6 @@ def test_reference_property_specific_type(): "my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf" -@pytest.mark.parametrize( - "value", [ - '2017-01-01T12:34:56Z', - ], -) -def test_timestamp_property_valid(value): - ts_prop = TimestampProperty() - assert ts_prop.clean(value) == constants.FAKE_TIME - - -def test_timestamp_property_invalid(): - ts_prop = TimestampProperty() - with pytest.raises(TypeError): - ts_prop.clean(1) - with pytest.raises(ValueError): - ts_prop.clean("someday sometime") - - -def test_binary_property(): - bin_prop = BinaryProperty() - - assert bin_prop.clean("TG9yZW0gSXBzdW0=") - with pytest.raises(ValueError): - bin_prop.clean("foobar") - - -def test_hex_property(): - hex_prop = HexProperty() - - assert hex_prop.clean("4c6f72656d20497073756d") - with pytest.raises(ValueError): - hex_prop.clean("foobar") - - @pytest.mark.parametrize( "d", [ {'description': 'something'}, @@ -522,24 +212,6 @@ def test_embedded_property(): emb_prop.clean("string") -@pytest.mark.parametrize( - "value", [ - ['a', 'b', 'c'], - ('a', 'b', 'c'), - 'b', - ], -) -def test_enum_property_valid(value): - enum_prop = EnumProperty(value) - assert enum_prop.clean('b') - - -def test_enum_property_invalid(): - enum_prop = EnumProperty(['a', 'b', 'c']) - with pytest.raises(ValueError): - enum_prop.clean('z') - - def test_extension_property_valid(): ext_prop = ExtensionsProperty(spec_version="2.0", enclosing_type='file') assert ext_prop({ diff --git a/stix2/test/v21/test_properties.py b/stix2/test/v21/test_properties.py index 84e87c4..36ff858 100644 --- a/stix2/test/v21/test_properties.py +++ b/stix2/test/v21/test_properties.py @@ -5,73 +5,15 @@ from stix2.exceptions import ( AtLeastOnePropertyError, CustomContentError, DictionaryKeyError, ) from stix2.properties import ( - BinaryProperty, BooleanProperty, DictionaryProperty, - EmbeddedObjectProperty, EnumProperty, ExtensionsProperty, FloatProperty, - HashesProperty, HexProperty, IDProperty, IntegerProperty, ListProperty, - Property, ReferenceProperty, StringProperty, TimestampProperty, - TypeProperty, + DictionaryProperty, EmbeddedObjectProperty, ExtensionsProperty, + HashesProperty, IDProperty, ListProperty, ReferenceProperty, + StringProperty, TypeProperty, ) from stix2.v21.common import MarkingProperty from . import constants -def test_property(): - p = Property() - - assert p.required is False - assert p.clean('foo') == 'foo' - assert p.clean(3) == 3 - - -def test_basic_clean(): - class Prop(Property): - - def clean(self, value): - if value == 42: - return value - else: - raise ValueError("Must be 42") - - p = Prop() - - assert p.clean(42) == 42 - with pytest.raises(ValueError): - p.clean(41) - - -def test_property_default(): - class Prop(Property): - - def default(self): - return 77 - - p = Prop() - - assert p.default() == 77 - - -def test_fixed_property(): - p = Property(fixed="2.0") - - assert p.clean("2.0") - with pytest.raises(ValueError): - assert p.clean("x") is False - with pytest.raises(ValueError): - assert p.clean(2.0) is False - - assert p.default() == "2.0" - assert p.clean(p.default()) - - -def test_list_property(): - p = ListProperty(StringProperty) - - assert p.clean(['abc', 'xyz']) - with pytest.raises(ValueError): - p.clean([]) - - def test_dictionary_property(): p = DictionaryProperty(StringProperty) @@ -161,123 +103,6 @@ def test_id_property_default(): assert ID_PROP.clean(default) == default -@pytest.mark.parametrize( - "value", [ - 2, - -1, - 3.14, - False, - ], -) -def test_integer_property_valid(value): - int_prop = IntegerProperty() - assert int_prop.clean(value) is not None - - -@pytest.mark.parametrize( - "value", [ - -1, - -100, - -50 * 6, - ], -) -def test_integer_property_invalid_min_with_constraints(value): - int_prop = IntegerProperty(min=0, max=180) - with pytest.raises(ValueError) as excinfo: - int_prop.clean(value) - assert "minimum value is" in str(excinfo.value) - - -@pytest.mark.parametrize( - "value", [ - 181, - 200, - 50 * 6, - ], -) -def test_integer_property_invalid_max_with_constraints(value): - int_prop = IntegerProperty(min=0, max=180) - with pytest.raises(ValueError) as excinfo: - int_prop.clean(value) - assert "maximum value is" in str(excinfo.value) - - -@pytest.mark.parametrize( - "value", [ - "something", - StringProperty(), - ], -) -def test_integer_property_invalid(value): - int_prop = IntegerProperty() - with pytest.raises(ValueError): - int_prop.clean(value) - - -@pytest.mark.parametrize( - "value", [ - 2, - -1, - 3.14, - False, - ], -) -def test_float_property_valid(value): - int_prop = FloatProperty() - assert int_prop.clean(value) is not None - - -@pytest.mark.parametrize( - "value", [ - "something", - StringProperty(), - ], -) -def test_float_property_invalid(value): - int_prop = FloatProperty() - with pytest.raises(ValueError): - int_prop.clean(value) - - -@pytest.mark.parametrize( - "value", [ - True, - False, - 'True', - 'False', - 'true', - 'false', - 'TRUE', - 'FALSE', - 'T', - 'F', - 't', - 'f', - 1, - 0, - ], -) -def test_boolean_property_valid(value): - bool_prop = BooleanProperty() - - assert bool_prop.clean(value) is not None - - -@pytest.mark.parametrize( - "value", [ - 'abc', - ['false'], - {'true': 'true'}, - 2, - -1, - ], -) -def test_boolean_property_invalid(value): - bool_prop = BooleanProperty() - with pytest.raises(ValueError): - bool_prop.clean(value) - - def test_reference_property(): ref_prop = ReferenceProperty(valid_types="my-type", spec_version="2.1") @@ -300,40 +125,6 @@ def test_reference_property_specific_type(): "my-type--8a8e8758-f92c-4058-ba38-f061cd42a0cf" -@pytest.mark.parametrize( - "value", [ - '2017-01-01T12:34:56Z', - ], -) -def test_timestamp_property_valid(value): - ts_prop = TimestampProperty() - assert ts_prop.clean(value) == constants.FAKE_TIME - - -def test_timestamp_property_invalid(): - ts_prop = TimestampProperty() - with pytest.raises(TypeError): - ts_prop.clean(1) - with pytest.raises(ValueError): - ts_prop.clean("someday sometime") - - -def test_binary_property(): - bin_prop = BinaryProperty() - - assert bin_prop.clean("TG9yZW0gSXBzdW0=") - with pytest.raises(ValueError): - bin_prop.clean("foobar") - - -def test_hex_property(): - hex_prop = HexProperty() - - assert hex_prop.clean("4c6f72656d20497073756d") - with pytest.raises(ValueError): - hex_prop.clean("foobar") - - @pytest.mark.parametrize( "d", [ {'description': 'something'}, @@ -452,29 +243,6 @@ def test_embedded_property(): emb_prop.clean("string") -@pytest.mark.parametrize( - "value", [ - ['a', 'b', 'c'], - ('a', 'b', 'c'), - 'b', - ], -) -def test_enum_property_valid(value): - enum_prop = EnumProperty(value) - assert enum_prop.clean('b') - - -def test_enum_property_clean(): - enum_prop = EnumProperty(['1']) - assert enum_prop.clean(1) == '1' - - -def test_enum_property_invalid(): - enum_prop = EnumProperty(['a', 'b', 'c']) - with pytest.raises(ValueError): - enum_prop.clean('z') - - def test_extension_property_valid(): ext_prop = ExtensionsProperty(spec_version='2.1', enclosing_type='file') assert ext_prop({