1import datetime as dt 2 3import pytest 4import pytz 5 6import stix2 7from stix2.exceptions import ExtraPropertiesError, STIXError 8from stix2.properties import ( 9 BinaryProperty, BooleanProperty, EmbeddedObjectProperty, EnumProperty, 10 FloatProperty, HexProperty, IntegerProperty, ListProperty, Property, 11 StringProperty, TimestampProperty, TypeProperty, 12) 13 14 15def test_property(): 16 p = Property() 17 18 assert p.required is False 19 assert p.clean('foo') == 'foo' 20 assert p.clean(3) == 3 21 22 23def test_basic_clean(): 24 class Prop(Property): 25 26 def clean(self, value): 27 if value == 42: 28 return value 29 else: 30 raise ValueError("Must be 42") 31 32 p = Prop() 33 34 assert p.clean(42) == 42 35 with pytest.raises(ValueError): 36 p.clean(41) 37 38 39def test_property_default(): 40 class Prop(Property): 41 42 def default(self): 43 return 77 44 45 p = Prop() 46 47 assert p.default() == 77 48 49 50def test_property_fixed(): 51 p = Property(fixed="2.0") 52 53 assert p.clean("2.0") 54 with pytest.raises(ValueError): 55 assert p.clean("x") is False 56 with pytest.raises(ValueError): 57 assert p.clean(2.0) is False 58 59 assert p.default() == "2.0" 60 assert p.clean(p.default()) 61 62 63def test_property_fixed_and_required(): 64 with pytest.raises(STIXError): 65 Property(default=lambda: 3, required=True) 66 67 68def test_list_property(): 69 p = ListProperty(StringProperty) 70 71 assert p.clean(['abc', 'xyz']) 72 with pytest.raises(ValueError): 73 p.clean([]) 74 75 76def test_list_property_property_type_custom(): 77 class TestObj(stix2.base._STIXBase): 78 _type = "test" 79 _properties = { 80 "foo": StringProperty(), 81 } 82 p = ListProperty(EmbeddedObjectProperty(type=TestObj)) 83 84 objs_custom = [ 85 TestObj(foo="abc", bar=123, allow_custom=True), 86 TestObj(foo="xyz"), 87 ] 88 89 assert p.clean(objs_custom) 90 91 dicts_custom = [ 92 {"foo": "abc", "bar": 123}, 93 {"foo": "xyz"}, 94 ] 95 96 # no opportunity to set allow_custom=True when using dicts 97 with pytest.raises(ExtraPropertiesError): 98 p.clean(dicts_custom) 99 100 101def test_list_property_object_type(): 102 class TestObj(stix2.base._STIXBase): 103 _type = "test" 104 _properties = { 105 "foo": StringProperty(), 106 } 107 p = ListProperty(TestObj) 108 109 objs = [TestObj(foo="abc"), TestObj(foo="xyz")] 110 assert p.clean(objs) 111 112 dicts = [{"foo": "abc"}, {"foo": "xyz"}] 113 assert p.clean(dicts) 114 115 116def test_list_property_object_type_custom(): 117 class TestObj(stix2.base._STIXBase): 118 _type = "test" 119 _properties = { 120 "foo": StringProperty(), 121 } 122 p = ListProperty(TestObj) 123 124 objs_custom = [ 125 TestObj(foo="abc", bar=123, allow_custom=True), 126 TestObj(foo="xyz"), 127 ] 128 129 assert p.clean(objs_custom) 130 131 dicts_custom = [ 132 {"foo": "abc", "bar": 123}, 133 {"foo": "xyz"}, 134 ] 135 136 # no opportunity to set allow_custom=True when using dicts 137 with pytest.raises(ExtraPropertiesError): 138 p.clean(dicts_custom) 139 140 141def test_list_property_bad_element_type(): 142 with pytest.raises(TypeError): 143 ListProperty(1) 144 145 146def test_list_property_bad_value_type(): 147 class TestObj(stix2.base._STIXBase): 148 _type = "test" 149 _properties = { 150 "foo": StringProperty(), 151 } 152 153 list_prop = ListProperty(TestObj) 154 with pytest.raises(ValueError): 155 list_prop.clean([1]) 156 157 158def test_string_property(): 159 prop = StringProperty() 160 161 assert prop.clean('foobar') 162 assert prop.clean(1) 163 assert prop.clean([1, 2, 3]) 164 165 166def test_type_property(): 167 prop = TypeProperty('my-type') 168 169 assert prop.clean('my-type') 170 with pytest.raises(ValueError): 171 prop.clean('not-my-type') 172 assert prop.clean(prop.default()) 173 174 175@pytest.mark.parametrize( 176 "value", [ 177 2, 178 -1, 179 3.14, 180 False, 181 ], 182) 183def test_integer_property_valid(value): 184 int_prop = IntegerProperty() 185 assert int_prop.clean(value) is not None 186 187 188@pytest.mark.parametrize( 189 "value", [ 190 -1, 191 -100, 192 -50 * 6, 193 ], 194) 195def test_integer_property_invalid_min_with_constraints(value): 196 int_prop = IntegerProperty(min=0, max=180) 197 with pytest.raises(ValueError) as excinfo: 198 int_prop.clean(value) 199 assert "minimum value is" in str(excinfo.value) 200 201 202@pytest.mark.parametrize( 203 "value", [ 204 181, 205 200, 206 50 * 6, 207 ], 208) 209def test_integer_property_invalid_max_with_constraints(value): 210 int_prop = IntegerProperty(min=0, max=180) 211 with pytest.raises(ValueError) as excinfo: 212 int_prop.clean(value) 213 assert "maximum value is" in str(excinfo.value) 214 215 216@pytest.mark.parametrize( 217 "value", [ 218 "something", 219 StringProperty(), 220 ], 221) 222def test_integer_property_invalid(value): 223 int_prop = IntegerProperty() 224 with pytest.raises(ValueError): 225 int_prop.clean(value) 226 227 228@pytest.mark.parametrize( 229 "value", [ 230 2, 231 -1, 232 3.14, 233 False, 234 ], 235) 236def test_float_property_valid(value): 237 int_prop = FloatProperty() 238 assert int_prop.clean(value) is not None 239 240 241@pytest.mark.parametrize( 242 "value", [ 243 "something", 244 StringProperty(), 245 ], 246) 247def test_float_property_invalid(value): 248 int_prop = FloatProperty() 249 with pytest.raises(ValueError): 250 int_prop.clean(value) 251 252 253@pytest.mark.parametrize( 254 "value", [ 255 True, 256 False, 257 'True', 258 'False', 259 'true', 260 'false', 261 'TRUE', 262 'FALSE', 263 'T', 264 'F', 265 't', 266 'f', 267 1, 268 0, 269 ], 270) 271def test_boolean_property_valid(value): 272 bool_prop = BooleanProperty() 273 274 assert bool_prop.clean(value) is not None 275 276 277@pytest.mark.parametrize( 278 "value", [ 279 'abc', 280 ['false'], 281 {'true': 'true'}, 282 2, 283 -1, 284 ], 285) 286def test_boolean_property_invalid(value): 287 bool_prop = BooleanProperty() 288 with pytest.raises(ValueError): 289 bool_prop.clean(value) 290 291 292@pytest.mark.parametrize( 293 "value", [ 294 '2017-01-01T12:34:56Z', 295 ], 296) 297def test_timestamp_property_valid(value): 298 ts_prop = TimestampProperty() 299 assert ts_prop.clean(value) == dt.datetime(2017, 1, 1, 12, 34, 56, tzinfo=pytz.utc) 300 301 302def test_timestamp_property_invalid(): 303 ts_prop = TimestampProperty() 304 with pytest.raises(TypeError): 305 ts_prop.clean(1) 306 with pytest.raises(ValueError): 307 ts_prop.clean("someday sometime") 308 309 310def test_binary_property(): 311 bin_prop = BinaryProperty() 312 313 assert bin_prop.clean("TG9yZW0gSXBzdW0=") 314 with pytest.raises(ValueError): 315 bin_prop.clean("foobar") 316 317 318def test_hex_property(): 319 hex_prop = HexProperty() 320 321 assert hex_prop.clean("4c6f72656d20497073756d") 322 with pytest.raises(ValueError): 323 hex_prop.clean("foobar") 324 325 326@pytest.mark.parametrize( 327 "value", [ 328 ['a', 'b', 'c'], 329 ('a', 'b', 'c'), 330 'b', 331 ], 332) 333def test_enum_property_valid(value): 334 enum_prop = EnumProperty(value) 335 assert enum_prop.clean('b') 336 337 338def test_enum_property_clean(): 339 enum_prop = EnumProperty(['1']) 340 assert enum_prop.clean(1) == '1' 341 342 343def test_enum_property_invalid(): 344 enum_prop = EnumProperty(['a', 'b', 'c']) 345 with pytest.raises(ValueError): 346 enum_prop.clean('z') 347