From 8c21e55de5015572859c95bc8d2efccdcb7fe673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Otto?= Date: Wed, 29 May 2024 22:14:17 +0200 Subject: [PATCH] Add request.execute() function --- README.md | 19 ++++----------- fences/open_api/generate.py | 43 +++++++++++++++++++++++++++------- requirements-dev.txt | 1 + test/open_api/test_generate.py | 5 ++-- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index fc23369..ee6bfa3 100644 --- a/README.md +++ b/README.md @@ -353,8 +353,7 @@ description = { graph = parse_open_api(description) for i in graph.generate_paths(): - test_case = graph.execute(i.path) - request = test_case.to_request() + request = graph.execute(i.path) request.dump() ``` @@ -403,13 +402,11 @@ PATCH /videos/0 PATCH /videos/0 BODY: true PATCH /videos/0 -PATCH /videos/0 -PATCH /videos/0 ``` -Please note, that Fences does not include a client to execute these tests. -You can do this on your own, for example using the `requests` library: +You can execute the generated tests using the `request.execute()` method. +Please note, that you need to install the `requests` library for this. ```python import requests @@ -418,15 +415,9 @@ import requests graph = parse_open_api(description) for i in graph.generate_paths(): - test_case = graph.execute(i.path) - request = test_case.to_request() + request = graph.execute(i.path) request.dump() - requests.request( - url='http://localhost:8080' + test_case.path, - method=test_case.operation.method, - data=request.body, - headers=dict(request.headers), - ) + request.execute('http://localhost:8080') ``` diff --git a/fences/open_api/generate.py b/fences/open_api/generate.py index 528e30d..41d7c5f 100644 --- a/fences/open_api/generate.py +++ b/fences/open_api/generate.py @@ -17,13 +17,13 @@ @dataclass class Request: + operation: Operation path: str - method: str headers: ListOfPairs body: Optional[str] def dump(self, body_max_chars=80): - print(f"{self.method.upper()} {self.path}") + print(f"{self.operation.method.upper()} {self.path}") if self.body: if len(self.body) > body_max_chars: b = self.body[:body_max_chars] + '...' @@ -31,6 +31,17 @@ def dump(self, body_max_chars=80): b = self.body print(f" BODY: {b}") + def execute(self, host: str): + import requests # optional dependency + if host.endswith('/'): + host = host[:-1] + requests.request( + url=host + self.path, + method=self.operation.method, + data=self.body, + headers=dict(self.headers) + ) + @dataclass class TestCase: @@ -56,12 +67,21 @@ def to_request(self) -> Request: body = json.dumps(self.body) return Request( path=path, - method=self.operation.method, headers=headers, body=body, + operation=self.operation ) +class ExtractRequestLeaf(Leaf): + + def apply(self, data: TestCase) -> any: + return data.to_request() + + def description(self) -> str: + return "Convert to request" + + class StartTestCase(Decision): def __init__(self, path: str, operation: Operation) -> None: super().__init__(operation.operation_id, True) @@ -71,6 +91,9 @@ def __init__(self, path: str, operation: Operation) -> None: def apply(self, data: any) -> any: return TestCase(self.path, self.operation) + def description(self) -> str: + return "Start Test Case" + class InsertParamLeaf(Leaf): def __init__(self, is_valid: bool, parameter: Parameter, value: any) -> None: @@ -152,13 +175,13 @@ def add(self, schema: any, components: Dict[str, any]) -> Samples: def parse(open_api: any) -> Node: openapi: OpenApi = OpenApi.from_dict(open_api) - super_root = NoOpDecision() sample_cache = SampleCache() # Create graph + root = NoOpDecision() for path in openapi.paths: for operation in path.operations: - root = StartTestCase(path.path, operation) + op_root = StartTestCase(path.path, operation) for param in operation.parameters: param_root = NoOpDecision(f"{operation.operation_id}/{param.name}") samples = sample_cache.add(param.schema, openapi.components) @@ -168,7 +191,7 @@ def parse(open_api: any) -> Node: param_root.add_transition(InsertParamLeaf(False, param, sample)) if param.position != ParameterPosition.PATH: param_root.add_transition(NoOpLeaf(is_valid=not param.required)) - root.add_transition(param_root) + op_root.add_transition(param_root) if operation.request_body: body_root = NoOpDecision('BODY') bodies = sample_cache.add(operation.request_body.schema, openapi.components) @@ -177,6 +200,8 @@ def parse(open_api: any) -> Node: for body in bodies.invalid: body_root.add_transition(InsertBodyLeaf(False, body)) body_root.add_transition(NoOpLeaf(is_valid=not operation.request_body.required)) - root.add_transition(body_root) - super_root.add_transition(root) - return super_root + op_root.add_transition(body_root) + op_root.add_transition(ExtractRequestLeaf()) + root.add_transition(op_root) + + return root diff --git a/requirements-dev.txt b/requirements-dev.txt index c0be21f..095a51f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,3 +7,4 @@ coverage>=6.3 PyYAML>=5.3 json-schema-tool==0.1.0 hypothesis-jsonschema==0.23.0 +requests>=2 diff --git a/test/open_api/test_generate.py b/test/open_api/test_generate.py index 608abb2..fe7fd99 100644 --- a/test/open_api/test_generate.py +++ b/test/open_api/test_generate.py @@ -1,4 +1,4 @@ -from fences.open_api.generate import parse, TestCase +from fences.open_api.generate import parse, Request from fences.core.render import render import unittest @@ -16,8 +16,7 @@ def check(self, schema, debug=False): render(graph).write_svg('graph.svg') for i in graph.generate_paths(): - test_case: TestCase = graph.execute(i.path) - request = test_case.to_request() + request: Request = graph.execute(i.path) if debug: request.dump()