diff --git a/ramlfications/errors.py b/ramlfications/errors.py index f4d97c8..fe9c119 100644 --- a/ramlfications/errors.py +++ b/ramlfications/errors.py @@ -31,6 +31,10 @@ class BaseRAMLParserError(BaseRAMLError): pass +class InvalidRAMLStructureError(BaseRAMLParserError): + """Disallowed structure was found in YAML.""" + + class InvalidRootNodeError(BaseRAMLParserError): pass diff --git a/ramlfications/models/base.py b/ramlfications/models/base.py index 90268e3..be6aae6 100644 --- a/ramlfications/models/base.py +++ b/ramlfications/models/base.py @@ -112,8 +112,11 @@ class BaseNamedParameter(object): in RAML file, defaults to ``name``. :param list enum: Array of valid parameter values, or ``None``. \ Only applies when primative ``type`` is ``string``. - :param example: Example value for property, or ``None``. Type of \ - ``example`` will match that of ``type``. + :param example: Example value for property, or ``None``. \ + For RAML 0.8, the type of ``example`` will match that of ``type``. \ + For RAML 1.0, ``example`` will be an ``Example`` object with a \ + ``value`` attribute whose type matches that of ``type``. + :param examples: List of ``Example`` objects (RAML 1.0 only). :param int max_length: Parameter value's maximum number of \ characters, or ``None``. Only applies when primative ``type`` \ is ``string``. @@ -144,7 +147,6 @@ class BaseNamedParameter(object): validator=string_type_parameter) pattern = attr.ib(repr=False, default=None, validator=string_type_parameter) - repeat = attr.ib(repr=False, default=False) @attr.s @@ -183,3 +185,15 @@ def description(self): if self.desc: return BaseContent(self.desc) return None + + +@attr.s +class BaseParameterRaml08(object): + """TODO: writeme""" + repeat = attr.ib(repr=False) + + +@attr.s +class BaseParameterRaml10(object): + """TODO: writeme""" + examples = attr.ib(repr=False) diff --git a/ramlfications/models/examples.py b/ramlfications/models/examples.py new file mode 100644 index 0000000..f39199d --- /dev/null +++ b/ramlfications/models/examples.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function + +import attr + + +@attr.s +class Example(object): + """ + Single example. + + Used starting with RAML 1.0. + + """ + value = attr.ib(repr=False) + name = attr.ib(default=None) + description = attr.ib(default=None, repr=False) + display_name = attr.ib(default=None, repr=False) + + # Not sure when validation of examples should get done; leave for now. + strict = attr.ib(default=True, repr=False) + + # TODO: this will need to support annotations. diff --git a/ramlfications/parser/parameters.py b/ramlfications/parser/parameters.py index 3c1c4eb..2bdc3f5 100644 --- a/ramlfications/parser/parameters.py +++ b/ramlfications/parser/parameters.py @@ -3,9 +3,13 @@ from __future__ import absolute_import, division, print_function +import attr from six import iteritems, itervalues, string_types from ramlfications.config import MEDIA_TYPES +from ramlfications.models.base import ( + BaseParameterRaml08, BaseParameterRaml10 +) from ramlfications.models.parameters import ( Body, Header, Response, URIParameter ) @@ -15,11 +19,21 @@ map_object, resolve_scalar_data, add_missing_uri_data ) from ramlfications.utils.parser import get_data_type_obj_by_name +from ramlfications.utils.examples import parse_examples class BaseParameterParser(object): + + # We keep a cache of concrete classes created when we mix in the + # RAML-version-specific specialization below so general equality + # support provided by the attr package remains useful. + # + # Creating a new class every time through breaks that. + # + _classes = {} + def create_base_param_obj(self, attribute_data, param_obj, - config, errors, **kw): + config, errors, root, **kw): """ Helper function to create a child of a :py:class:`.parameters.BaseParameter` object @@ -31,7 +45,6 @@ def create_base_param_obj(self, attribute_data, param_obj, required = _get(value, "required", default=True) else: required = _get(value, "required", default=False) - root = kw.get('root') data_type_name = _get(value, "type") data_type = get_data_type_obj_by_name(data_type_name, root) kwargs = dict( @@ -47,7 +60,6 @@ def create_base_param_obj(self, attribute_data, param_obj, enum=_get(value, "enum"), example=_get(value, "example"), required=required, - repeat=_get(value, "repeat", False), pattern=_get(value, "pattern"), type=_get(value, "type", "string"), config=config, @@ -58,7 +70,52 @@ def create_base_param_obj(self, attribute_data, param_obj, if param_obj is not Body: kwargs["desc"] = _get(value, "description") - item = param_obj(**kwargs) + # Getting the RAML version we're working with is a little + # tricky since we may have a root node, which always allows + # us to get it, but sometimes we don't (while processing + # parameters directly associated with the RAML root). + # + if root is None: + raml_version = self.kwargs['data']._raml_version + else: + raml_version = root.raml_version + + if raml_version == "0.8": + kwargs["repeat"] = _get(value, "repeat", False) + + if raml_version == "0.8" and isinstance(value, list): + # This is a sneaky union; need to handle this differently. + # Applies only to RAML 0.8; see: + # + # https://github.com/raml-org/raml-spec/blob/master/versions/ + # raml-08/raml-08.md#named-parameters-with-multiple-types + # + # TODO: Complete once union types are implemented. + pass + else: + kwargs.update(parse_examples(raml_version, value)) + + # build object class based off of raml version + ParamObj = param_obj + mixin = BaseParameterRaml10 + suffix = "10" + if raml_version == "0.8": + mixin = BaseParameterRaml08 + suffix = "08" + + key = param_obj, mixin + + if key in self._classes: + ParamObj = self._classes[key] + else: + @attr.s + class ParamObj(param_obj, mixin): + pass + + ParamObj.__name__ = param_obj.__name__ + suffix + self._classes[key] = ParamObj + + item = ParamObj(**kwargs) objects.append(item) return objects or None @@ -220,7 +277,8 @@ def parse_response_headers(self): header_objects = self.create_base_param_obj(headers, Header, self.root.config, self.root.errors, - method=self.method) + method=self.method, + root=self.root) return header_objects or None def parse_response_body(self): diff --git a/ramlfications/utils/examples.py b/ramlfications/utils/examples.py new file mode 100644 index 0000000..61b85aa --- /dev/null +++ b/ramlfications/utils/examples.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function + +from six import iteritems + +from ramlfications.errors import InvalidRAMLStructureError +from ramlfications.models.base import BaseContent +from ramlfications.models.examples import Example +from ramlfications.utils.nodelist import NodeList + + +def parse_examples(raml_version, data): + # + # This is the only place that knows when to supply examples and when + # not, and that checks when both are supplied. + # + # This is also responsible for generating structured Example objects + # when appropriate (RAML >= 1.0). + # + data = data or {} + kw = {} + example = data.get("example") + + if raml_version == "0.8": + if "examples" in data: + del data["examples"] + # TODO: Emit a lint warning if there's an examples node and + # we're processing RAML 0.8: authors may be expecting it to + # be honored. + kw["example"] = example + + if raml_version != "0.8": + if "example" in data: + kw["example"] = parse_example(None, example) + + kw["examples"] = None + if "examples" in data: + if "example" in data: + # TODO: Emit a lint warning during validation. + raise InvalidRAMLStructureError( + "example and examples cannot co-exist") + examples = data["examples"] + # Must be a map: + if not isinstance(examples, dict): + # Need to decide what exception to make this. + raise InvalidRAMLStructureError("examples must be a map node") + kw["examples"] = NodeList([parse_example(nm, val) + for nm, val in iteritems(examples)]) + + return kw + + +def parse_example(name, node): + data = dict(name=name, value=node) + if name: + data["display_name"] = name + if isinstance(node, dict) and "value" in node: + data["value"] = node["value"] + data["description"] = BaseContent(node.get("description", "")) + data["display_name"] = node.get("displayName", name) + data["strict"] = node.get("strict", True) + + return Example(**data) diff --git a/ramlfications/utils/types.py b/ramlfications/utils/types.py index a217cab..ddf55d4 100644 --- a/ramlfications/utils/types.py +++ b/ramlfications/utils/types.py @@ -8,6 +8,7 @@ from ramlfications.errors import UnknownDataTypeError from ramlfications.models import RAML_DATA_TYPES, STANDARD_RAML_TYPES from .common import merge_dicts +from .examples import parse_examples from .parser import convert_camel_case @@ -63,4 +64,7 @@ def parse_type(name, raw, root): data.pop("properties") except KeyError: pass + + data.update(parse_examples(root.raml_version, data)) + return data_type_cls(**data) diff --git a/tests/data/raml_08/examples.raml b/tests/data/raml_08/examples.raml new file mode 100644 index 0000000..ce963b0 --- /dev/null +++ b/tests/data/raml_08/examples.raml @@ -0,0 +1,77 @@ +#%RAML 0.8 + +title: Example +baseUri: https://example.test/{param}/ + +baseUriParameters: + param: + type: string + description: account name + example: splat + +mediaType: application/json +protocols: [ HTTPS ] + +/with_example_and_examples: + post: + queryParameters: + cached: + type: boolean + example: false + description: Allow cached data? + body: + application/json: + formParameters: + key: + type: string + example: This is the example. + + # examples is ignored; may produce an lint warning later. + examples: + simple: "abc" + fancy: "two words" + excessive: + displayName: "Serious Overkill" + description: | + There's way too much text here. + value: "This is a long example." + strict: false + + # This really isn't supported yet. + multityped: + - type: string + description: Long explanation. + example: Pile o text. + - type: file + description: File upload. + +/with_uri_param/{id}: + uriParameters: + id: + example: s234gs9 + +/with_example_structured: + post: + body: + application/json: + formParameters: + key: + type: object + example: + value: This whole map is a value. + +/with_example_unstructured: + post: + body: + application/json: + formParameters: + key: + type: string + example: This is a value. + +/with_header: + displayName: Silly, silly example. + headers: + x-extra-fluff: + type: boolean + example: true diff --git a/tests/data/raml_10/broken-example-with-examples.raml b/tests/data/raml_10/broken-example-with-examples.raml new file mode 100644 index 0000000..662d0a7 --- /dev/null +++ b/tests/data/raml_10/broken-example-with-examples.raml @@ -0,0 +1,18 @@ +#%RAML 1.0 + +title: Example +baseUri: https://example.test/{param}/ + +baseUriParameters: + param: + type: string + description: account name + example: splat + examples: + first: some text + second: + value: more text + strict: false + +mediaType: application/json +protocols: [ HTTPS ] diff --git a/tests/data/raml_10/broken-non-map-examples.raml b/tests/data/raml_10/broken-non-map-examples.raml new file mode 100644 index 0000000..f3acc7b --- /dev/null +++ b/tests/data/raml_10/broken-non-map-examples.raml @@ -0,0 +1,16 @@ +#%RAML 1.0 + +title: Example +baseUri: https://example.test/{param}/ + +baseUriParameters: + param: + type: string + description: account name + examples: + - some text + - value: more text + strict: false + +mediaType: application/json +protocols: [ HTTPS ] diff --git a/tests/data/raml_10/embedded-json-schema.json b/tests/data/raml_10/embedded-json-schema.json new file mode 100644 index 0000000..3b232f0 --- /dev/null +++ b/tests/data/raml_10/embedded-json-schema.json @@ -0,0 +1,13 @@ +{"type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "description": "Age in years", + "type": "integer", + "minimum": 0 + } + }, + "required": ["firstName", "lastName"] +} diff --git a/tests/data/raml_10/examples.raml b/tests/data/raml_10/examples.raml new file mode 100644 index 0000000..e49aaa0 --- /dev/null +++ b/tests/data/raml_10/examples.raml @@ -0,0 +1,151 @@ +#%RAML 1.0 + +title: Example +baseUri: https://example.test/{param}/ + +baseUriParameters: + param: + type: string + description: account name + example: splat + +mediaType: application/json +protocols: [ HTTPS ] + +types: + + with_multiple_examples: + type: string + examples: + simple: "abc" + fancy: "two words" + excessive: + displayName: "Serious Overkill" + description: | + There's way too much text here. + value: + error: | + This type is defined to be a string, so this should not be a map. + strict: false + + with_example_structured: + type: object + properties: + key: + type: string + example: + description: Typical value. + value: This is a value. + example: + description: This is a typical structure. + displayName: Typical Example + strict: true + value: + key: Yo. + + with_example_unstructured: + type: object + properties: + key: + type: string + example: This is a value. + example: + key: "Example value." + + with_example_unstructured_as_json: + type: object + properties: + key: + type: string + example: This is a value. + example: "{\"key\": \"Example value.\"}" + +# json_schema_example_structured_as_json: +# type: !include embedded-json-schema.json +# example: +# description: This is a typical structure. +# displayName: Typical Example +# strict: true +# value: | +# {"name": "Monty Python", "age": 42} + +/with_multiple_examples: + post: + queryParameters: + cached: + type: boolean + example: false + description: Allow cached data? + body: + application/json: + type: + type: string + properties: + key: + type: string + examples: + simple: "abc" + fancy: "two words" + excessive: + displayName: "Serious Overkill" + description: | + There's way too much text here. + value: "This is a long example." + strict: false + +/with_uri_param/{id}: + uriParameters: + id: + example: s234gs9 + +/with_example_structured: + post: + body: + application/json: + type: + type: object + properties: + key: + type: object + example: + value: This whole map is a value. + +/with_example_unstructured: + post: + body: + application/json: + type: + type: object + properties: + key: + type: string + example: This is a value. + +/with_header: + displayName: Silly, silly example. + headers: + x-extra-fluff: + type: boolean + example: true + x-structured: + example: + displayName: just a parameter + value: just a string + x-multiple: + type: string + examples: + simple: "42" + typical: + description: This is what we expect. + displayName: Typical Value + value: "typical" + strict: true + special: + description: No one expects the ... + displayName: Surprise Visit + value: Spanish Inqusition! + broken: + description: Send this for a 500 + displayName: "DON'T DO THIS" + strict: False + value: breakfast diff --git a/tests/data/v020examples/data_types_1_0.raml b/tests/data/v020examples/data_types_1_0.raml new file mode 100644 index 0000000..33a18a2 --- /dev/null +++ b/tests/data/v020examples/data_types_1_0.raml @@ -0,0 +1,134 @@ +#%RAML 1.0 + +title: Example +baseUri: https://example.test/{param}/ + +baseUriParameters: + param: + type: string + description: account name + example: splat + +mediaType: application/json +protocols: [ HTTPS ] + +types: + + with_multiple_examples: + type: string + examples: + simple: "abc" + fancy: "two words" + excessive: + displayName: "Serious Overkill" + description: | + There's way too much text here. + value: + error: This should not be a map. + strict: false + + with_example_structured: + type: object + properties: + key: + type: string + example: + description: Typical value. + value: This is a value. + example: + description: This is a typical structure. + displayName: Typical Example + strict: true + value: + key: Yo. + + with_example_unstructured: + type: object + properties: + key: + type: string + example: This is a value. + example: + key: "Example value." + + +/with_multiple_examples: + post: + queryParameters: + cached: + type: boolean + example: false + description: Allow cached data? + body: + application/json: + type: + type: string + properties: + key: + type: string + examples: + simple: "abc" + fancy: "two words" + excessive: + displayName: "Serious Overkill" + description: | + There's way too much text here. + value: "This is a long example." + strict: false + +/with_uri_param/{id}: + uriParameters: + id: + example: s234gs9 + +/with_example_structured: + post: + body: + application/json: + type: + type: object + properties: + key: + type: object + example: + value: This whole map is a value. + +/with_example_unstructured: + post: + body: + application/json: + type: + type: object + properties: + key: + type: string + example: This is a value. + +/with_header: + displayName: Silly, silly example. + headers: + x-extra-fluff: + type: boolean + example: true + x-structured: + example: + displayName: just a parameter + value: just a string + x-multiple: + type: string + examples: + simple: "42" + typical: + description: This is what we expect. + displayName: Typical Value + value: "typical" + strict: true + special: + description: No one expects the ... + displayName: Surprise Visit + value: Spanish Inqusition! + broken: + description: Send this for a 500 + displayName: "DON'T DO THIS" + strict: False + value: breakfast diff --git a/tests/integration/data_types/test_data_type_parameters.py b/tests/integration/data_types/test_data_type_parameters.py index 33b2295..2f1883f 100644 --- a/tests/integration/data_types/test_data_type_parameters.py +++ b/tests/integration/data_types/test_data_type_parameters.py @@ -37,8 +37,9 @@ def test_api_query_params(api): not_set = [ 'default', 'desc', 'description', 'enum', 'errors', 'example', 'max_length', 'maximum', 'min_length', 'minimum', 'pattern', - 'repeat' ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(id_, not_set) assert id_.data_type.name == 'EmployeeId' assert id_.data_type.type == 'string' @@ -57,8 +58,9 @@ def test_api_uri_params(api): not_set = [ 'default', 'desc', 'description', 'enum', 'errors', 'example', 'max_length', 'maximum', 'min_length', 'minimum', 'pattern', - 'repeat' ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(id_, not_set) assert id_.data_type.name == 'EmployeeId' assert id_.data_type.type == 'string' @@ -78,8 +80,9 @@ def test_api_response_headers(api): not_set = [ 'default', 'desc', 'description', 'enum', 'errors', 'example', 'max_length', 'maximum', 'min_length', 'minimum', 'pattern', - 'repeat' ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(id_, not_set) assert id_.data_type.name == 'EmployeeId' assert id_.data_type.type == 'string' diff --git a/tests/integration/data_types/test_data_type_resource_types.py b/tests/integration/data_types/test_data_type_resource_types.py index b67307d..eb86b45 100644 --- a/tests/integration/data_types/test_data_type_resource_types.py +++ b/tests/integration/data_types/test_data_type_resource_types.py @@ -74,8 +74,10 @@ def test_resource_query_params(api): not_set = [ 'default', 'desc', 'description', 'enum', 'errors', 'example', 'max_length', 'maximum', 'min_length', 'minimum', 'pattern', - 'repeat', 'required' + 'required' ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(q_param, not_set) not_set = [ @@ -103,8 +105,9 @@ def test_resource_uri_params(api): not_set = [ 'default', 'desc', 'description', 'enum', 'errors', 'example', 'max_length', 'maximum', 'min_length', 'minimum', 'pattern', - 'repeat' ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(uri_param, not_set) not_set = [ diff --git a/tests/integration/data_types/test_examples_08.py b/tests/integration/data_types/test_examples_08.py new file mode 100644 index 0000000..9043c39 --- /dev/null +++ b/tests/integration/data_types/test_examples_08.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function +""" +Tests for handling of data type examples in RAML 0.8. + +""" + +import os +import pytest + +from ramlfications.parser import parse_raml +from ramlfications.config import setup_config +from ramlfications.utils import load_file + +from tests.base import RAML_08 + + +@pytest.fixture(scope="session") +def api(): + ramlfile = os.path.join(RAML_08, "examples.raml") + loaded_raml = load_file(ramlfile) + conffile = os.path.join(RAML_08, "test_config.ini") + config = setup_config(conffile) + return parse_raml(loaded_raml, config) + + +def get_form_param(api, resource, name="key"): + res, = api.resources.filter_by(name=resource) + fp, = res.body[0].form_params.filter_by(name=name) + return fp + + +def test_examples_ignored(api): + fp = get_form_param(api, "/with_example_and_examples") + assert fp.example == "This is the example." + assert not hasattr(fp, "examples") + + +def test_simple_example_structured(api): + # + # If an example is presented as a YAML map with a value key, that's + # just part of the value, since RAML 0.8 doesn't have any notion of + # annotations or other facets of an example other than the value. + # + fp = get_form_param(api, "/with_example_structured") + assert fp.example == {"value": "This whole map is a value."} + assert not hasattr(fp, "examples") + + +def test_simple_example_unstructured(api): + fp = get_form_param(api, "/with_example_unstructured") + assert fp.example == "This is a value." + assert not hasattr(fp, "examples") + + +def test_header_with_example(api): + h = api.resources.filter_by(name="/with_header")[0].headers[0] + assert h.example is True + assert not hasattr(h, "examples") + + +def test_base_uri_param(api): + p = api.base_uri_params[0] + assert p.example == "splat" + assert not hasattr(p, "examples") + + +def test_query_param(api): + res = api.resources.filter_by(name="/with_example_and_examples")[0] + p = res.query_params.filter_by(name="cached")[0] + assert p.example is False + assert not hasattr(p, "examples") + + +def test_uri_param(api): + res = api.resources.filter_by(name="/with_uri_param/{id}")[0] + p = res.uri_params[0] + assert p.example == "s234gs9" + assert not hasattr(p, "examples") diff --git a/tests/integration/data_types/test_examples_10.py b/tests/integration/data_types/test_examples_10.py new file mode 100644 index 0000000..5916a91 --- /dev/null +++ b/tests/integration/data_types/test_examples_10.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function +""" +Tests for handling of data type examples in RAML 0.8. + +""" + +import os +import pytest + +from ramlfications import errors +from ramlfications.parser import parse_raml +from ramlfications.config import setup_config +from ramlfications.utils import load_file + +from tests.base import RAML_10 + + +@pytest.fixture(scope="session") +def api(): + ramlfile = os.path.join(RAML_10, "examples.raml") + loaded_raml = load_file(ramlfile) + conffile = os.path.join(RAML_10, "test-config.ini") + config = setup_config(conffile) + return parse_raml(loaded_raml, config) + + +def get_resource_body_type(api, resource): + res, = api.resources.filter_by(name=resource) + return res.body[0].type + + +def get_named_type(api, name): + t, = api.types.filter_by(name=name) + return t + + +def test_simple_example_structured(api): + t = get_named_type(api, "with_example_structured") + assert t.examples is None + + ex = t.example + assert ex.description.raw == "This is a typical structure." + assert ex.display_name == "Typical Example" + assert ex.strict is True + assert ex.value == {"key": "Yo."} + + +def test_simple_example_unstructured(api): + t = get_named_type(api, "with_example_unstructured") + assert t.examples is None + + ex = t.example + assert ex.description is None + assert ex.display_name is None + assert ex.strict is True + assert ex.value == {"key": "Example value."} + + +def test_multiple_examples(api): + t = get_named_type(api, "with_multiple_examples") + assert t.example is None + assert len(t.examples) == 3 + + ex, = t.examples.filter_by(name="simple") + assert ex.description is None + assert ex.display_name == "simple" + assert ex.strict is True + assert ex.value == "abc" + + ex, = t.examples.filter_by(name="fancy") + assert ex.description is None + assert ex.display_name == "fancy" + assert ex.strict is True + assert ex.value == "two words" + + ex, = t.examples.filter_by(name="excessive") + assert ex.description.raw == "There's way too much text here.\n" + assert ex.display_name == "Serious Overkill" + assert ex.strict is False + assert ex.value == { + "error": ("This type is defined to be a string," + " so this should not be a map.\n"), + } + + +def test_header_with_example(api): + h = api.resources.filter_by(name="/with_header")[0].headers[0] + assert h.name == "x-extra-fluff" + assert h.example.value is True + assert h.example.description is None + assert h.example.display_name is None + assert h.example.strict is True + + +def test_header_with_mutiple_examples(api): + headers = api.resources.filter_by(name="/with_header")[0].headers + h = headers.filter_by(name="x-multiple")[0] + assert h.name == "x-multiple" + assert h.example is None + assert len(h.examples) == 4 + + ex, = h.examples.filter_by(name="simple") + assert ex.description is None + assert ex.display_name == "simple" + assert ex.strict is True + assert ex.value == "42" + + ex, = h.examples.filter_by(name="typical") + assert ex.description.raw == "This is what we expect." + assert ex.display_name == "Typical Value" + assert ex.strict is True + assert ex.value == "typical" + + ex, = h.examples.filter_by(name="special") + assert ex.description.raw == "No one expects the ..." + assert ex.display_name == "Surprise Visit" + assert ex.strict is True + assert ex.value == "Spanish Inqusition!" + + ex, = h.examples.filter_by(name="broken") + assert ex.description.raw == "Send this for a 500" + assert ex.display_name == "DON'T DO THIS" + assert ex.strict is False + assert ex.value == "breakfast" + + +def test_disallows_example_with_examples(): + ramlfile = os.path.join(RAML_10, "broken-example-with-examples.raml") + loaded_raml = load_file(ramlfile) + conffile = os.path.join(RAML_10, "test-config.ini") + config = setup_config(conffile) + with pytest.raises(errors.InvalidRAMLStructureError) as e: + parse_raml(loaded_raml, config) + assert str(e.value) == "example and examples cannot co-exist" + + +def test_disallows_non_map_examples(): + ramlfile = os.path.join(RAML_10, "broken-non-map-examples.raml") + loaded_raml = load_file(ramlfile) + conffile = os.path.join(RAML_10, "test-config.ini") + config = setup_config(conffile) + with pytest.raises(errors.InvalidRAMLStructureError) as e: + parse_raml(loaded_raml, config) + assert str(e.value) == "examples must be a map node" diff --git a/tests/integration/raml_examples/typesystem/test_complex.py b/tests/integration/raml_examples/typesystem/test_complex.py index c937805..6fc0778 100644 --- a/tests/integration/raml_examples/typesystem/test_complex.py +++ b/tests/integration/raml_examples/typesystem/test_complex.py @@ -333,8 +333,9 @@ def test_resource(api): 'data_type', 'default', 'desc', 'description', 'enum', 'errors', 'example', 'max_length', 'maximum', 'min_length', 'minimum', 'pattern', - 'repeat', ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(u_param, not_set) _set = ["config", "raw"] for s in _set: diff --git a/tests/integration/test_root_node.py b/tests/integration/test_root_node.py index 11e9370..b45cd79 100644 --- a/tests/integration/test_root_node.py +++ b/tests/integration/test_root_node.py @@ -52,8 +52,10 @@ def test_root_node(api): assert b.required not_set = [ "default", "enum", "max_length", "maximum", "min_length", - "minimum", "pattern", "repeat" + "minimum", "pattern" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(b, not_set) u = api.uri_params[0] @@ -65,8 +67,10 @@ def test_root_node(api): assert u.required not_set = [ "default", "enum", "max_length", "maximum", "min_length", - "minimum", "pattern", "repeat" + "minimum", "pattern" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(b, not_set) d = api.documentation[0] diff --git a/tests/integration/test_security_schemes.py b/tests/integration/test_security_schemes.py index bf79469..4ce5482 100644 --- a/tests/integration/test_security_schemes.py +++ b/tests/integration/test_security_schemes.py @@ -116,8 +116,10 @@ def test_oauth_2_0_scheme(api): not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "default", "repeat", "pattern", "method", "required" + "default", "pattern", "method", "required" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(auth, not_set) foo = s.headers[1] @@ -128,8 +130,10 @@ def test_oauth_2_0_scheme(api): not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "default", "repeat", "pattern", "method", "required" + "default", "pattern", "method", "required" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(foo, not_set) resp401 = s.responses[0] @@ -183,8 +187,10 @@ def test_oauth_1_0_scheme(api): not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "default", "repeat", "pattern", "method", "required" + "default", "pattern", "method", "required" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(h, not_set) resp200 = s.responses[0] @@ -202,8 +208,10 @@ def test_oauth_1_0_scheme(api): assert rh.description.raw == desc not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "default", "repeat", "pattern", "method", "required" + "default", "pattern", "method", "required" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(rh, not_set) st = s.settings @@ -241,8 +249,10 @@ def test_basic(api): not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "default", "repeat", "pattern", "method", "required" + "default", "pattern", "method", "required" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(h, not_set) @@ -273,8 +283,10 @@ def test_digest(api): not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "default", "repeat", "pattern", "method", "required" + "default", "pattern", "method", "required" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(h, not_set) @@ -307,8 +319,10 @@ def test_custom(api): assert q.type == "string" not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "default", "repeat", "pattern", "required", "enum" + "default", "pattern", "required", "enum" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(q, not_set) u = s.uri_params[0] @@ -320,8 +334,10 @@ def test_custom(api): assert u.required not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "repeat", "pattern", "enum" + "pattern", "enum" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(u, not_set) f = s.form_params[0] @@ -331,8 +347,10 @@ def test_custom(api): assert f.type == "string" not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "repeat", "pattern", "enum", "required", "default" + "pattern", "enum", "required", "default" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(f, not_set) b = s.body[0] @@ -348,8 +366,10 @@ def test_custom(api): assert f.type == "string" not_set = [ "example", "min_length", "max_length", "minimum", "maximum", - "repeat", "pattern", "enum", "required", "default" + "pattern", "enum", "required", "default" ] + if api.raml_version == "0.8": + not_set.append("repeat") assert_not_set(f, not_set) st = s.settings