def test_regex(): schema = S.String(regex=r'\d+') assert normalize_schema(schema, '3000') == '3000' with pytest.raises(E.RegexMismatch) as ei: normalize_schema(schema, 'foo') assert ei.value.value == 'foo' assert ei.value.regex == r'\d+'
def test_disallow_unknown_in_normalize_schema(): with pytest.raises(E.UnknownFields) as ei: normalize_schema(S.Dict(schema=id_int), { 'id': 3, 'foo': 'bar' }, allow_unknown=False)
def test_allow_unknown_in_anyof_schema(): schema = S.Dict( allow_unknown=True, anyof=[S.SubSchema(x=S.String()), S.SubSchema(y=S.String())]) val = {'x': 'foo', 'extra': 'bar'} normalize_schema(schema, val, allow_unknown=False) == val
def test_invalid_ingredients_field_condition(): """ A variety of bad field conditions. """ examples = [ ( { # A condition without a predicate "value": "moo", "aggregation": "sum", "condition": { "field": "cow" }, }, E.ExpectedOneField, ), ( { # A condition with two operators "value": "moo", "aggregation": "sum", "condition": { "field": "cow", "in": 1, "gt": 2 }, }, E.DisallowedField, ), ] for ingr, expected_exception in examples: v = {"a": {"field": deepcopy(ingr)}} with pytest.raises(expected_exception): normalize_schema(shelf_schema, v, allow_unknown=False)
def test_rename_with_both_attributes_present(): schema = S.Dict(schema={ 'foo': { 'rename': 'moo', 'coerce': str }, }, allow_unknown=True) val = {'foo': 1, 'moo': 2} assert normalize_schema(schema, val) == {'moo': '1'} # Yes, we can swap attributes schema = S.Dict(schema={ 'foo': { 'rename': 'moo', 'coerce': str }, 'moo': { 'rename': 'foo', 'coerce': str }, }, allow_unknown=True) val = {'foo': 1, 'moo': 2} assert normalize_schema(schema, val) == {'moo': '1', 'foo': '2'}
def test_dimension(): v = {"a": {"kind": "Dimension", "field": "foo", "icon": "squee"}} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "a": { "field": { "aggregation": "none", "value": "foo" }, "kind": "dimension", "icon": "squee", } } v = {"a": {"kind": "Dimension", "field": "foo + moo", "icon": "squee"}} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "a": { "field": { "operators": [{ "operator": "+", "field": { "value": "moo" } }], "aggregation": "none", "value": "foo", }, "kind": "dimension", "icon": "squee", } }
def test_invalid_conditions(): conditions = [ { "field": "foo", "gt": [22] }, { "field": "foo", "gt": { "a": 2 } }, { "field": "foo", "lte": { "a": 2 } }, { "field": "foo", "notin": {} }, ] shelf = {"a": {"kind": "metric", "field": {"value": "a"}}} for cond in conditions: v = deepcopy(shelf) v["a"]["field"]["condition"] = cond with pytest.raises(Exception): normalize_schema(shelf_schema, v, allow_unknown=False)
def test_field_default(): defaults = [24, True, 11.21243, "heythere"] for d in defaults: v = { "foo": { "kind": "metric", "field": { "value": "foo", "default": d } } } x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "foo": { "field": { "_coalesce_to_value": d, "aggregation": "sum", "value": "foo", }, "kind": "metric", } } # Bad data type for default v = {"foo": {"kind": "metric", "field": {"value": "foo", "default": {}}}} with pytest.raises(E.NoneMatched): normalize_schema(shelf_schema, v, allow_unknown=False)
def test_coerce_raises(): """If a coerce raises, it is wrapped in a CoerceUnexpectedError.""" schema = S.Dict( schema={'key': S.String(required=False, coerce=lambda x: 1 / 0)}) with pytest.raises(E.CoerceUnexpectedError) as ei: normalize_schema(schema, {'key': 'hello'}) assert ei.value.value == 'hello' assert type(ei.value.exception) == ZeroDivisionError
def test_excludes_single(): schema = S.Dict(schema={'x': S.String(excludes='other')}) with pytest.raises(E.DisallowedField) as ei: normalize_schema(schema, { 'x': 'foo', 'other': 'bar' }, allow_unknown=True)
def test_list_schema(): schema = S.List(schema=S.Integer()) val = [1, 2, 3] assert normalize_schema(schema, val) == val with pytest.raises(E.BadType) as ei: normalize_schema(schema, [1, 'two', object()]) assert ei.value.value == 'two' assert ei.value.stack == (1, )
def test_ingredient(): v = {"a": {"kind": "metric", "field": "foo"}} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "a": { "field": { "aggregation": "sum", "value": "foo" }, "kind": "metric" } } v = {"a": {"kind": "metric", "field": "max(foo)"}} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "a": { "field": { "aggregation": "max", "value": "foo" }, "kind": "metric" } } v = { "a": { "kind": "metric", "field": { "value": "foo", "condition": { "field": "moo", "gt": "cow" } }, } } x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "a": { "field": { "value": "foo", "aggregation": "sum", "condition": { "field": { "aggregation": "none", "value": "moo" }, "gt": "cow", "_op": "__gt__", "_op_value": "cow", }, }, "kind": "metric", } }
def test_validator_raises(): """If a validator raises, it is wrapped in a ValidatorUnexpectedError.""" schema = S.Dict( schema={ 'key': S.String(required=False, validator=lambda f, v, e: 1 / 0) }) with pytest.raises(E.ValidatorUnexpectedError) as ei: normalize_schema(schema, {'key': 'hello'}) assert ei.value.field == 'key' assert ei.value.value == 'hello' assert type(ei.value.exception) == ZeroDivisionError
def test_default_setter_raises(): """If a default_setter raises, it is wrapped in a DefaultSetterUnexpectedError.""" schema = S.Dict( schema={ 'key': S.String(required=False, default_setter=lambda x: 1 / 0) }) with pytest.raises(E.DefaultSetterUnexpectedError) as ei: normalize_schema(schema, {}) assert ei.value.key == 'key' assert ei.value.value == {} assert type(ei.value.exception) == ZeroDivisionError
def test_field_parsing(): """Measure and Metric are synonyms All kinds are normalized, so casing of kind doesn't matter """ v = {"foo": {"kind": "Metric", "field": {"value": "foo"}}, "_version": "1"} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "foo": { "field": { "aggregation": "sum", "value": "foo" }, "kind": "metric" } } v = {"foo": {"kind": "METRIC", "field": "foo"}, "_version": "1"} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "foo": { "field": { "aggregation": "sum", "value": "foo" }, "kind": "metric" } } v = {"foo": {"kind": "MEASURE", "field": "max(a)"}, "_version": "1"} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "foo": { "field": { "aggregation": "max", "value": "a" }, "kind": "metric" } } v = {"foo": {"kind": "meaSURE", "field": "max(a)"}, "_version": "1"} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "foo": { "field": { "aggregation": "max", "value": "a" }, "kind": "metric" } }
def test_rename_with_default(): schema = S.Dict(schema={ 'foo': { 'rename': 'moo', 'type': 'boolean', 'default': True }, }) val = {} assert normalize_schema(schema, val) == {'moo': True} val = {'foo': False} assert normalize_schema(schema, val) == {'moo': False}
def test_nullable_with_anyof(): """This is the second reason that sureberus exists.""" anyof = { 'nullable': True, 'anyof': [S.Integer(), S.String()], } assert normalize_schema(anyof, None) == None
def test_condition_ref(): shelf = { "a": { "kind": "metric", "field": { "value": "a", "condition": "@foo" } }, "foo": { "field": "b" }, } x = normalize_schema(shelf_schema, shelf, allow_unknown=False) assert x == { "a": { "field": { "aggregation": "sum", "value": "a" }, "kind": "metric" }, "foo": { "field": { "aggregation": "sum", "value": "b" }, "kind": "metric" }, }
def test_ingredient_names(self): config = { "metrics": ["foo"], "dimensions": ["bar"], "filters": ["baz"] } assert normalize_schema(recipe_schema, config) == config
def test_filter_objects(self): """Recipes can have in-line filters, since it's common for those to be specific to a particular Recipe. """ config = { "metrics": ["foo"], "dimensions": ["bar"], "filters": [{ "field": "xyzzy", "gt": 3 }], } assert normalize_schema(recipe_schema, config) == { "metrics": ["foo"], "dimensions": ["bar"], "filters": [{ "field": { "aggregation": "none", "value": "xyzzy" }, "gt": 3, "_op": "__gt__", "_op_value": 3, }], }
def test_valid_ingredients_field(): """ A variety of good fields. """ examples = [ ({ "value": "foo" }, { "aggregation": "sum", "value": "foo" }), ( { "value": "foo", "aggregation": "sum" }, { "aggregation": "sum", "value": "foo" }, ), ] for fld, expected in examples: v = {"a": {"field": deepcopy(fld)}} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x["a"]["field"] == expected
def test_invalid_ingredients(): examples = [ ({ "kind": "asa", "field": "moo" }, E.DisallowedValue), ({ "kind": "Sque", "field": "moo" }, E.DisallowedValue), ] for ingr, expected_exception in examples: v = {"a": deepcopy(ingr)} with pytest.raises(expected_exception): normalize_schema(shelf_schema, v, allow_unknown=False)
def parse_unvalidated_condition(cond, selectable): if cond is None: return try: cond = normalize_schema(condition_schema, cond, allow_unknown=False) except E.SureError as e: raise BadIngredient(str(e)) return parse_validated_condition(cond, selectable)
def test_rename(): schema = S.Dict(schema={ 'foo': { 'rename': 'moo' }, }) val = {'foo': 2} assert normalize_schema(schema, val) == {'moo': 2}
def ingredient_from_unvalidated_dict(unvalidated_ingr, selectable): try: ingr_dict = normalize_schema(ingredient_schema, unvalidated_ingr, allow_unknown=True) except E.SureError as e: raise BadIngredient(str(e)) return create_ingredient_from_config(ingr_dict, selectable)
def test_field_as(): v = {"foo": {"kind": "metric", "field": {"value": "foo", "as": "integer"}}} x = normalize_schema(shelf_schema, v, allow_unknown=False) assert x == { "foo": { "field": { "_cast_to_datatype": "integer", "aggregation": "sum", "value": "foo", }, "kind": "metric", } } # Bad data type to cast to v = {"foo": {"kind": "metric", "field": {"value": "foo", "as": "squee"}}} with pytest.raises(E.DisallowedValue): normalize_schema(shelf_schema, v, allow_unknown=False)
def test_rename_with_coerce(): schema = S.Dict(schema={ 'foo': { 'rename': 'moo', 'coerce': str }, }) val = {'foo': 2} assert normalize_schema(schema, val) == {'moo': '2'}
def test_coerce(): def _to_list(item): if isinstance(item, list): return item else: return [item] schema = {'coerce': _to_list} assert normalize_schema(schema, 33) == [33]
def test_rename_with_maxlength(): schema = S.Dict(schema={ 'foo': { 'rename': 'moo', 'maxlength': 3 }, }) val = {'foo': 'fooob'} with pytest.raises(E.MaxLengthExceeded): assert normalize_schema(schema, val)
def parse_unvalidated_field(unvalidated_fld, selectable, aggregated=True): kind = "Metric" if aggregated else "Dimension" ingr = {"field": unvalidated_fld, "kind": kind} try: ingr_dict = normalize_schema(ingredient_schema, ingr, allow_unknown=True) except E.SureError as e: raise BadIngredient(str(e)) return parse_validated_field(ingr_dict["field"], selectable)