diff --git a/Roost-README.md b/Roost-README.md new file mode 100644 index 00000000..dd5bcc8d --- /dev/null +++ b/Roost-README.md @@ -0,0 +1,25 @@ + +# RoostGPT generated pytest code for API Testing + +RoostGPT generats code in `tests` folder within given project path. +Dependency file i.e. `requirements-roost.txt` is also created in the given project path + +Below are the sample steps to run the generated tests. Sample commands contains use of package manager i.e. `uv`. Alternatively python and pip can be used directly. +1. ( Optional ) Create virtual Env . +2. Install dependencies +``` +uv venv // Create virtual Env +uv pip install -r requirements-roost.txt // Install all dependencies + +``` + +Test configurations and test_data is loaded from config.yml. e.g. API HOST, auth, common path parameters of endpoint. +Either set defalt value in this config.yml file OR use ENV. e.g. export API_HOST="https://example.com/api/v2" + +Once configuration values are set, use below commands to run the tests. +``` +// Run generated tests +uv run pytest -m smoke // Run only smoke tests +uv run pytest -s tests/generated-test.py // Run specific test file +``` + \ No newline at end of file diff --git a/requirements-roost.txt b/requirements-roost.txt new file mode 100644 index 00000000..bdaa6d20 --- /dev/null +++ b/requirements-roost.txt @@ -0,0 +1,14 @@ + +connexion +Flask +flask_testing +jsonschema +pytest +python_dateutil +PyYAML +referencing +Requests +setuptools +six +urllib3 +xmltodict \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/api.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/api.json new file mode 100644 index 00000000..a8781928 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/api.json @@ -0,0 +1 @@ +{"openapi":"3.0.4","info":{"title":"Swagger Petstore - OpenAPI 3.0","description":"This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)","termsOfService":"https://swagger.io/terms/","contact":{"email":"apiteam@swagger.io"},"license":{"name":"Apache 2.0","url":"https://www.apache.org/licenses/LICENSE-2.0.html"},"version":"1.0.27"},"externalDocs":{"description":"Find out more about Swagger","url":"https://swagger.io"},"servers":[{"url":"/api/v3"}],"tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"https://swagger.io"}},{"name":"store","description":"Access to Petstore orders","externalDocs":{"description":"Find out more about our store","url":"https://swagger.io"}},{"name":"user","description":"Operations about user"}],"paths":{"/pet":{"put":{"tags":["pet"],"summary":"Update an existing pet.","description":"Update an existing pet by Id.","operationId":"updatePet","requestBody":{"description":"Update an existent pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"422":{"description":"Validation exception"},"default":{"description":"Unexpected error"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Add a new pet to the store.","description":"Add a new pet to the store.","operationId":"addPet","requestBody":{"description":"Create a new pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid input"},"422":{"description":"Validation exception"},"default":{"description":"Unexpected error"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByStatus":{"get":{"tags":["pet"],"summary":"Finds Pets by status.","description":"Multiple status values can be provided with comma separated strings.","operationId":"findPetsByStatus","parameters":[{"name":"status","in":"query","description":"Status values that need to be considered for filter","required":true,"explode":true,"schema":{"type":"string","default":"available","enum":["available","pending","sold"]}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid status value"},"default":{"description":"Unexpected error"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags.","description":"Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":true,"explode":true,"schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid tag value"},"default":{"description":"Unexpected error"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID.","description":"Returns a single pet.","operationId":"getPetById","parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"default":{"description":"Unexpected error"}},"security":[{"api_key":[]},{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data.","description":"Updates a pet resource based on the form data.","operationId":"updatePetWithForm","parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"name","in":"query","description":"Name of pet that needs to be updated","schema":{"type":"string"}},{"name":"status","in":"query","description":"Status of pet that needs to be updated","schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid input"},"default":{"description":"Unexpected error"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet.","description":"Delete a pet.","operationId":"deletePet","parameters":[{"name":"api_key","in":"header","description":"","required":false,"schema":{"type":"string"}},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Pet deleted"},"400":{"description":"Invalid pet value"},"default":{"description":"Unexpected error"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"Uploads an image.","description":"Upload image of the pet.","operationId":"uploadFile","parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"additionalMetadata","in":"query","description":"Additional Metadata","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}},"400":{"description":"No file uploaded"},"404":{"description":"Pet not found"},"default":{"description":"Unexpected error"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status.","description":"Returns a map of status codes to quantities.","operationId":"getInventory","responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}}},"default":{"description":"Unexpected error"}},"security":[{"api_key":[]}]}},"/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet.","description":"Place a new order in the store.","operationId":"placeOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Order"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"description":"Invalid input"},"422":{"description":"Validation exception"},"default":{"description":"Unexpected error"}}}},"/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID.","description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.","operationId":"getOrderById","parameters":[{"name":"orderId","in":"path","description":"ID of order that needs to be fetched","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"},"default":{"description":"Unexpected error"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by identifier.","description":"For valid response try integer IDs with value < 1000. Anything above 1000 or non-integers will generate API errors.","operationId":"deleteOrder","parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"order deleted"},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"},"default":{"description":"Unexpected error"}}}},"/user":{"post":{"tags":["user"],"summary":"Create user.","description":"This can only be done by the logged in user.","operationId":"createUser","requestBody":{"description":"Created user object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}}}},"default":{"description":"Unexpected error"}}}},"/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array.","description":"Creates list of users with given input array.","operationId":"createUsersWithListInput","requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}},"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}}}},"default":{"description":"Unexpected error"}}}},"/user/login":{"get":{"tags":["user"],"summary":"Logs user into the system.","description":"Log into the system.","operationId":"loginUser","parameters":[{"name":"username","in":"query","description":"The user name for login","required":false,"schema":{"type":"string"}},{"name":"password","in":"query","description":"The password for login in clear text","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","headers":{"X-Rate-Limit":{"description":"calls per hour allowed by the user","schema":{"type":"integer","format":"int32"}},"X-Expires-After":{"description":"date in UTC when token expires","schema":{"type":"string","format":"date-time"}}},"content":{"application/xml":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"string"}}}},"400":{"description":"Invalid username/password supplied"},"default":{"description":"Unexpected error"}}}},"/user/logout":{"get":{"tags":["user"],"summary":"Logs out current logged in user session.","description":"Log user out of the system.","operationId":"logoutUser","parameters":[],"responses":{"200":{"description":"successful operation"},"default":{"description":"Unexpected error"}}}},"/user/{username}":{"get":{"tags":["user"],"summary":"Get user by user name.","description":"Get user detail based on username.","operationId":"getUserByName","parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"},"default":{"description":"Unexpected error"}}},"put":{"tags":["user"],"summary":"Update user resource.","description":"This can only be done by the logged in user.","operationId":"updateUser","parameters":[{"name":"username","in":"path","description":"name that need to be deleted","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Update an existent user in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"200":{"description":"successful operation"},"400":{"description":"bad request"},"404":{"description":"user not found"},"default":{"description":"Unexpected error"}}},"delete":{"tags":["user"],"summary":"Delete user resource.","description":"This can only be done by the logged in user.","operationId":"deleteUser","parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User deleted"},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"},"default":{"description":"Unexpected error"}}}}},"components":{"schemas":{"Order":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"petId":{"type":"integer","format":"int64","example":198772},"quantity":{"type":"integer","format":"int32","example":7},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","example":"approved","enum":["placed","approved","delivered"]},"complete":{"type":"boolean"}},"xml":{"name":"order"}},"Category":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":1},"name":{"type":"string","example":"Dogs"}},"xml":{"name":"category"}},"User":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"username":{"type":"string","example":"theUser"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"James"},"email":{"type":"string","example":"john@email.com"},"password":{"type":"string","example":"12345"},"phone":{"type":"string","example":"12345"},"userStatus":{"type":"integer","description":"User Status","format":"int32","example":1}},"xml":{"name":"user"}},"Tag":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"tag"}},"Pet":{"required":["name","photoUrls"],"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"name":{"type":"string","example":"doggie"},"category":{"$ref":"#/components/schemas/Category"},"photoUrls":{"type":"array","xml":{"wrapped":true},"items":{"type":"string","xml":{"name":"photoUrl"}}},"tags":{"type":"array","xml":{"wrapped":true},"items":{"$ref":"#/components/schemas/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"pet"}},"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}},"xml":{"name":"##default"}}},"requestBodies":{"Pet":{"description":"Pet object that needs to be added to the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"UserArray":{"description":"List of user object","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}}},"securitySchemes":{"petstore_auth":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://petstore3.swagger.io/oauth/authorize","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"}}}},"api_key":{"type":"apiKey","name":"api_key","in":"header"}}}} \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/config.yml b/tests/SWAGGER_PETSTORE_OPENAPI_30/config.yml new file mode 100644 index 00000000..b481532c --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/config.yml @@ -0,0 +1,23 @@ + +# This config.yml contains user provided data for api testing. Allows to define values here or use ENV to load values. e.g. ENV[API_HOST] = "https://exampl2.com" +# api: +# host: "${API_HOST:-https://example.com/api/v2}" # includes base path +# auth: +# api_key: "${API_KEY:-}" +# api_key_header: "${KEYNAME:-DefaultValue}" # openapi.spec.security.KEY_NAME +# basic_auth: "${username:-}:${password:-}" +# test_data: +# id: "${TEST_ID:-282739-1238371-219393-2833}" # Any test data key value pair e.g. GET /api/v1/cart/:id +# context-id: "${TEST_context-id:-}" # GET /api/v1/{context-id}/summary + + + +api: + host: "${SWAGGER_PETSTORE_OPENAPI_30_API_HOST:-https://petstore3.swagger.io/api/v3}" +auth: + petstore_auth: "${PETSTORE_AUTH_OAUTH2_TOKEN:-}" + api_key: "${api_key:-}" +test_data: + petId: "${TEST_petId:-}" + orderId: "${TEST_orderId:-}" + username: "${TEST_username:-}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/conftest.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/conftest.py new file mode 100644 index 00000000..291b5881 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/conftest.py @@ -0,0 +1,91 @@ +import pytest +import requests +import yaml +import os +import re +from pathlib import Path +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry + +def _expand_env_in_string(value): + pattern = r'\${([^}:]+):-([^}]+)}' + matches = re.findall(pattern, value) + for var, default in matches: + value = value.replace(f'${{{var}:-{default}}}', os.getenv(var, default)) + return value + +def load_config(): + config_path = Path(__file__).parent / 'config.yml' + try: + with open(config_path, 'r') as file: + config = yaml.safe_load(file) + # Expand environment variables in the config + for section in config: + for key, value in config[section].items(): + config[section][key] = _expand_env_in_string(value) + return config + except FileNotFoundError: + raise Exception("Config file not found") + except yaml.YAMLError as e: + raise Exception(f"Error parsing config file: {e}") + +class APIClient: + def __init__(self, base_url, auth_token=None, api_key=None): + self.base_url = base_url.rstrip('/') + self.session = requests.Session() + self.session.headers.update({'Authorization': f'Bearer {auth_token}'}) + if api_key: + self.session.headers.update({'api_key': api_key}) + retries = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504]) + self.session.mount('https://', HTTPAdapter(max_retries=retries)) + + def make_request(self, endpoint, method='GET', headers=None, **kwargs): + url = f"{self.base_url}/{endpoint.lstrip('/')}" + response = self.session.request(method, url, headers=headers, timeout=10, **kwargs) + response.raise_for_status() + return response + + def get(self, endpoint, **kwargs): + return self.make_request(endpoint, 'GET', **kwargs) + + def post(self, endpoint, **kwargs): + return self.make_request(endpoint, 'POST', **kwargs) + + def put(self, endpoint, **kwargs): + return self.make_request(endpoint, 'PUT', **kwargs) + + def delete(self, endpoint, **kwargs): + return self.make_request(endpoint, 'DELETE', **kwargs) + + def patch(self, endpoint, **kwargs): + return self.make_request(endpoint, 'PATCH', **kwargs) + +@pytest.fixture(scope='session') +def config(): + return load_config() + +@pytest.fixture(scope='session') +def api_client(config): + api_config = config['api'] + auth_config = config['auth'] + return APIClient(base_url=api_config['host'], auth_token=auth_config['petstore_auth'], api_key=auth_config['api_key']) + +@pytest.fixture(scope='session') +def config_test_data(config): + return config['test_data'] + +@pytest.fixture(scope='session') +def host(config): + return config['api']['host'] + +@pytest.fixture(scope='session') +def auth(config): + return config['auth']['petstore_auth'] + +@pytest.fixture(scope='session') +def api_key(config): + return config['auth']['api_key'] + +@pytest.mark.smoke +def pytest_configure(config): + config.addinivalue_line("markers", "smoke: mark test as smoke test") diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/pet.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet.json new file mode 100644 index 00000000..dbff7b91 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet.json @@ -0,0 +1,57 @@ +[ + { + "name": "doggie", + "photoUrls": [ + "http://example.com/photo1" + ], + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "id": 10, + "name": "doggie", + "category": { + "id": 1, + "name": "Dogs" + }, + "photoUrls": [ + "http://example.com/photo1", + "http://example.com/photo2" + ], + "tags": [ + { + "id": 1, + "name": "tag1" + } + ], + "status": "available", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "photoUrls": [ + "http://example.com/photo1" + ], + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "name": "doggie", + "photoUrls": [ + "http://example.com/photo1" + ], + "status": "invalid_status", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "id": 9999, + "name": "unknown", + "photoUrls": [ + "http://example.com/photo1" + ], + "status": "available", + "statusCode": 422, + "scenario": "Client error responses: Unprocessable Entity" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_findByStatus.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_findByStatus.json new file mode 100644 index 00000000..c0c5aa83 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_findByStatus.json @@ -0,0 +1,31 @@ +[ + { + "status": "available", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "status": "pending", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "status": "sold", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "status": "invalidStatus", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "status": "notFound", + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_findByTags.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_findByTags.json new file mode 100644 index 00000000..5e9bfec4 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_findByTags.json @@ -0,0 +1,51 @@ +[ + { + "tags": [ + { + "id": 1, + "name": "Dogs" + } + ], + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "tags": [], + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "tags": [ + { + "id": 2, + "name": "Cats" + }, + { + "id": 3, + "name": "Birds" + } + ], + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "tags": [ + { + "id": "invalid", + "name": "InvalidTag" + } + ], + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "tags": [ + { + "id": 4, + "name": "Reptiles" + } + ], + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_petId.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_petId.json new file mode 100644 index 00000000..56848ac4 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_petId.json @@ -0,0 +1,32 @@ +[ + { + "api_key": "validApiKey123", + "petId": 101, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "api_key": "validApiKey456", + "petId": 202, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "api_key": "", + "petId": 303, + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "api_key": "invalidApiKey", + "petId": "invalidId", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "api_key": "validApiKey789", + "petId": 999999, + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_petId_uploadImage.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_petId_uploadImage.json new file mode 100644 index 00000000..32f18a11 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/pet_petId_uploadImage.json @@ -0,0 +1,28 @@ +[ + { + "petId": 12345, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "petId": 67890, + "additionalMetadata": "This is a test image", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "additionalMetadata": "Missing petId", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "petId": "invalidId", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "petId": 99999, + "statusCode": 404, + "scenario": "Client error responses: Not Found" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/store_inventory.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/store_inventory.json new file mode 100644 index 00000000..694702a1 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/store_inventory.json @@ -0,0 +1,22 @@ +[ + { + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "statusCode": "default", + "scenario": "Unknown response class" + }, + { + "statusCode": "default", + "scenario": "Unknown response class" + }, + { + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/store_order.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/store_order.json new file mode 100644 index 00000000..e1dd68b0 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/store_order.json @@ -0,0 +1,45 @@ +[ + { + "id": 10, + "petId": 198772, + "quantity": 7, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "id": 10, + "petId": 198772, + "quantity": 7, + "shipDate": "2023-11-01T10:00:00Z", + "status": "approved", + "complete": true, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "id": 10, + "petId": 198772, + "status": "approved", + "complete": true, + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "id": 10, + "petId": 198772, + "quantity": 7, + "status": "pending", + "complete": false, + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "id": 10, + "petId": 999999, + "quantity": 7, + "status": "placed", + "complete": true, + "statusCode": 422, + "scenario": "Client error responses: Unprocessable Entity" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/store_order_orderId.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/store_order_orderId.json new file mode 100644 index 00000000..cc4d5874 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/store_order_orderId.json @@ -0,0 +1,32 @@ +[ + { + "orderId": 123, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "orderId": 456, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "orderId": "abc", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "orderId": -1, + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "orderId": 999999, + "statusCode": 404, + "scenario": "Client error responses: Not Found" + }, + { + "orderId": 0, + "statusCode": 404, + "scenario": "Client error responses: Not Found" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_findByStatus_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_findByStatus_get.py new file mode 100644 index 00000000..3aac355c --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_findByStatus_get.py @@ -0,0 +1,106 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet/findByStatus_get for http method type GET +# RoostTestHash=785de05d92 +# +# + +# ********RoostGPT******** +""" +Pytest test suite for testing the /pet/findByStatus endpoint of the Petstore API. + +Instructions for running the tests: +1. Ensure you have pytest installed: `pip install pytest` +2. Place this test file, `conftest.py`, `validator.py`, and `config.yml` in the same directory. +3. Ensure the `pet_findByStatus.json` file is in the same directory as this test file. +4. Run the tests using the command: `pytest .py` + +This test suite covers: +- Valid scenarios with different pet statuses. +- Authentication scenarios (valid and invalid). +- Response validation using SwaggerSchemaValidator. +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Constants +TEST_DATA_FILENAME = "pet_findByStatus.json" +API_SPEC_PATH = "api.json" + +# Load test data +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME +with open(_endpoint_data_path, "r") as file: + _ENDPOINT_DATA = json.load(file) + +# Swagger validator +swagger_validator = SwaggerSchemaValidator(API_SPEC_PATH) + +# Parametrization +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"{case['scenario']}-{case['status']}" for case in _ENDPOINT_DATA] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_find_pets_by_status(api_client, scenario_case): + """ + Test /pet/findByStatus endpoint with various status values. + + This test checks: + - Successful retrieval of pets by status. + - Response schema validation. + """ + status = scenario_case['status'] + expected_status_code = scenario_case['statusCode'] + + # Prepare request + query_params = {'status': status} + + # Execute request + response = api_client.get('/pet/findByStatus', params=query_params) + + # Validate response status code + assert response.status_code == expected_status_code, f"Unexpected status code: {response.status_code}" + + # Validate response schema + validation_result = swagger_validator.validate_schema_by_response( + endpoint="/pet/findByStatus", + method="GET", + status_code=str(expected_status_code), + response=response + ) + assert validation_result["valid"], f"Schema validation failed: {validation_result.get('message')}" + +def test_find_pets_by_status_invalid_auth(api_client): + """ + Test /pet/findByStatus endpoint with invalid authentication. + + This test checks: + - Proper handling of invalid auth token. + """ + query_params = {'status': 'available'} + invalid_api_client = APIClient(base_url=api_client.base_url, auth_token="invalid_token") + + response = invalid_api_client.get('/pet/findByStatus', params=query_params) + + # Assuming 401 Unauthorized for invalid authentication + assert response.status_code == 401, "Expected 401 Unauthorized for invalid token" + +def test_find_pets_by_status_no_auth(api_client): + """ + Test /pet/findByStatus endpoint without authentication. + + This test checks: + - Proper handling of missing auth token. + """ + query_params = {'status': 'available'} + no_auth_client = APIClient(base_url=api_client.base_url) + + response = no_auth_client.get('/pet/findByStatus', params=query_params) + + # Assuming 401 Unauthorized for missing authentication + assert response.status_code == 401, "Expected 401 Unauthorized for missing token" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_findByTags_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_findByTags_get.py new file mode 100644 index 00000000..99dd5ff9 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_findByTags_get.py @@ -0,0 +1,110 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet/findByTags_get for http method type GET +# RoostTestHash=7439f031be +# +# + +# ********RoostGPT******** +""" +Test suite for the /pet/findByTags endpoint using pytest. + +To run the tests, use the command: +pytest test_find_by_tags.py + +Ensure conftest.py is available in the same directory or in the pytest path. +""" + +import pytest +from pathlib import Path +from validator import SwaggerSchemaValidator +import json + +# Load API specification +api_spec_path = Path(__file__).parent / 'api.json' +validator = SwaggerSchemaValidator(str(api_spec_path)) + +# Load test data from JSON file +TEST_DATA_FILENAME = 'pet_findByTags.json' +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME + +def _load_json_file(path): + with open(path, 'r') as f: + return json.load(f) + +_ENDPOINT_DATA = _load_json_file(_endpoint_data_path) + +def _param_ids(data): + return [case['scenario'] for case in data] + +@pytest.mark.parametrize("scenario_case", _ENDPOINT_DATA, ids=_param_ids(_ENDPOINT_DATA)) +def test_find_by_tags(api_client, scenario_case): + """ + Test the /pet/findByTags endpoint with various scenarios. + """ + # Prepare the request parameters + params = {} + if 'tags' in scenario_case: + params['tags'] = scenario_case['tags'] + + # Validate request before sending + request_validation = validator.validate_json(params, "findPetsByTagsRequest") + assert request_validation['valid'], f"Request validation failed: {request_validation.get('message')}" + + # Perform the API call + response = api_client.get('/pet/findByTags', params=params) + + # Validate response status code + expected_status_code = scenario_case.get('statusCode', 'default') + assert response.status_code == expected_status_code, f"Expected status code {expected_status_code}, got {response.status_code}" + + # Validate response schema + response_validation = validator.validate_schema_by_response('/pet/findByTags', 'GET', str(expected_status_code), response) + assert response_validation['valid'], f"Response validation failed: {response_validation.get('message')}" + +@pytest.mark.parametrize("scenario_case", _ENDPOINT_DATA, ids=_param_ids(_ENDPOINT_DATA)) +def test_find_by_tags_invalid_auth(api_client, scenario_case): + """ + Test the /pet/findByTags endpoint with invalid authentication. + """ + # Prepare the request parameters + params = {} + if 'tags' in scenario_case: + params['tags'] = scenario_case['tags'] + + # Perform the API call with invalid auth + api_client.session.headers.update({'Authorization': 'Bearer invalid_token'}) + response = api_client.get('/pet/findByTags', params=params) + + # Validate response status code for unauthorized access + assert response.status_code == 401, f"Expected status code 401 for unauthorized access, got {response.status_code}" + + # Validate response schema if applicable + if response.status_code == 401: + response_validation = validator.validate_schema_by_response('/pet/findByTags', 'GET', '401', response) + assert response_validation['valid'], f"Response validation failed: {response_validation.get('message')}" + +@pytest.mark.parametrize("scenario_case", _ENDPOINT_DATA, ids=_param_ids(_ENDPOINT_DATA)) +def test_find_by_tags_missing_auth(api_client, scenario_case): + """ + Test the /pet/findByTags endpoint with missing authentication. + """ + # Prepare the request parameters + params = {} + if 'tags' in scenario_case: + params['tags'] = scenario_case['tags'] + + # Perform the API call with missing auth + api_client.session.headers.pop('Authorization', None) + response = api_client.get('/pet/findByTags', params=params) + + # Validate response status code for unauthorized access + assert response.status_code == 401, f"Expected status code 401 for unauthorized access, got {response.status_code}" + + # Validate response schema if applicable + if response.status_code == 401: + response_validation = validator.validate_schema_by_response('/pet/findByTags', 'GET', '401', response) + assert response_validation['valid'], f"Response validation failed: {response_validation.get('message')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_delete.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_delete.py new file mode 100644 index 00000000..43c28311 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_delete.py @@ -0,0 +1,97 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet/{petId}_delete for http method type DELETE +# RoostTestHash=1909018834 +# +# + +# ********RoostGPT******** +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Load the OpenAPI specification +swagger_spec_path = 'api.json' +swagger_validator = SwaggerSchemaValidator(swagger_spec_path) + +# Load test data from the JSON file +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / 'pet_petId.json' +with open(_endpoint_data_path, 'r') as file: + _ENDPOINT_TEST_DATA = json.load(file) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_TEST_DATA +_PARAM_IDS = [str(case['petId']) for case in _ENDPOINT_TEST_DATA] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_pet(api_client, scenario_case): + """ + Test the DELETE /pet/{petId} endpoint. + """ + pet_id = scenario_case['petId'] + expected_status_code = scenario_case['statusCode'] + api_key = scenario_case.get('api_key', None) + + # Construct the endpoint + endpoint = f"/pet/{pet_id}" + + # Prepare headers if api_key is present + headers = {} + if api_key: + headers['api_key'] = api_key + + # Execute the DELETE request + try: + response = api_client.delete(endpoint, headers=headers) + response_status_code = response.status_code + except requests.exceptions.HTTPError as e: + response_status_code = e.response.status_code + + # Validate response status code + assert response_status_code == expected_status_code, f"Expected {expected_status_code}, got {response_status_code}" + + # Validate response schema + schema_validation_result = swagger_validator.validate_schema_by_response(endpoint, 'delete', str(expected_status_code), response) + assert schema_validation_result['valid'], f"Schema validation failed: {schema_validation_result.get('message', '')}" + +# Security schema tests +@pytest.mark.parametrize("api_key,expected_status_code", [ + ("invalidApiKey", 401), + (None, 401) +], ids=["invalid_key", "missing_key"]) +def test_delete_pet_security(api_client, api_key, expected_status_code): + """ + Test security schema for DELETE /pet/{petId} endpoint. + """ + pet_id = 101 # Example pet id for security tests + endpoint = f"/pet/{pet_id}" + + headers = {} + if api_key: + headers['api_key'] = api_key + + # Execute the DELETE request + try: + response = api_client.delete(endpoint, headers=headers) + response_status_code = response.status_code + except requests.exceptions.HTTPError as e: + response_status_code = e.response.status_code + + # Validate response status code + assert response_status_code == expected_status_code, f"Expected {expected_status_code}, got {response_status_code}" + + # Validate response schema if applicable + if response_status_code != 200: + schema_validation_result = swagger_validator.validate_schema_by_response(endpoint, 'delete', str(response_status_code), response) + assert schema_validation_result['valid'], f"Schema validation failed: {schema_validation_result.get('message', '')}" + +""" +Instructions for running the tests: +1. Ensure that the 'api.json' OpenAPI spec and 'pet_petId.json' test data files are in the same directory as this test file. +2. Run the tests using pytest: + pytest .py +""" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_get.py new file mode 100644 index 00000000..48e3a65f --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_get.py @@ -0,0 +1,122 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet/{petId}_get for http method type GET +# RoostTestHash=bc8f34e70c +# +# + +# ********RoostGPT******** +""" +Pytest test suite for RESTful API defined in OpenAPI spec. + +Instructions for setup and execution: +- Ensure `conftest.py`, `validator.py`, and `config.yml` are correctly configured. +- Place `pet_petId.json` in the same directory as this test file. +- Run tests using `pytest` command. + +Test cases are designed to validate: +- Endpoint functionality for `/pet/{petId}`. +- Security schema (api_key, petstore_auth). +- Response schema and status codes. +- Handling of valid, invalid, and edge cases. +""" + +import pytest +from pathlib import Path +import json +from validator import SwaggerSchemaValidator + +# Constants +API_SPEC_PATH = 'api.json' +TEST_DATA_FILENAME = 'pet_petId.json' + +# Load test data from JSON file +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME + +def _load_json_file(path): + with open(path, 'r') as f: + return json.load(f) + +_ENDPOINT_DATA = _load_json_file(_endpoint_data_path) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"{case['scenario']}" for case in _ENDPOINT_DATA] + +# Initialize SwaggerSchemaValidator +validator = SwaggerSchemaValidator(API_SPEC_PATH) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_pet_by_id(api_client, config_test_data, scenario_case): + """Test GET /pet/{petId} endpoint with various scenarios.""" + pet_id = scenario_case.get('petId', config_test_data['petId']) + expected_status_code = scenario_case['statusCode'] + + # Make GET request to the API + response = api_client.get(f"/pet/{pet_id}") + + # Validate response status code + assert response.status_code == expected_status_code, f"Unexpected status code: {response.status_code}" + + # Validate response schema + validation_result = validator.validate_schema_by_response( + endpoint="/pet/{petId}", + method="GET", + status_code=str(expected_status_code), + response=response + ) + assert validation_result['valid'], f"Schema validation failed: {validation_result.get('message')}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_pet_by_id_auth(api_client, scenario_case): + """Test GET /pet/{petId} endpoint with valid authentication.""" + pet_id = scenario_case.get('petId', 0) + expected_status_code = scenario_case['statusCode'] + + # Simulate valid API key and token authentication + response = api_client.get(f"/pet/{pet_id}") + + # Validate response status code + assert response.status_code == expected_status_code, f"Unexpected status code: {response.status_code}" + + # Validate response schema + validation_result = validator.validate_schema_by_response( + endpoint="/pet/{petId}", + method="GET", + status_code=str(expected_status_code), + response=response + ) + assert validation_result['valid'], f"Schema validation failed: {validation_result.get('message')}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_pet_by_id_invalid_auth(api_client, scenario_case): + """Test GET /pet/{petId} endpoint with invalid authentication.""" + pet_id = scenario_case.get('petId', 0) + + # Simulate invalid API key and token + api_client.session.headers.update({'Authorization': 'Bearer invalid_token'}) + api_client.session.headers.update({'api_key': 'invalid_key'}) + + # Make GET request to the API + response = api_client.get(f"/pet/{pet_id}") + + # Validate response status code + assert response.status_code == 401, "Expected 401 Unauthorized for invalid authentication" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_pet_by_id_no_auth(api_client, scenario_case): + """Test GET /pet/{petId} endpoint with missing authentication.""" + pet_id = scenario_case.get('petId', 0) + + # Remove auth headers + api_client.session.headers.pop('Authorization', None) + api_client.session.headers.pop('api_key', None) + + # Make GET request to the API + response = api_client.get(f"/pet/{pet_id}") + + # Validate response status code + assert response.status_code == 401, "Expected 401 Unauthorized for missing authentication" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_post.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_post.py new file mode 100644 index 00000000..b234cb5d --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_post.py @@ -0,0 +1,141 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet/{petId}_post for http method type POST +# RoostTestHash=30359d59f9 +# +# + +# ********RoostGPT******** +""" +Pytest Test Suite for Petstore API + +This module contains test cases for the Petstore API defined in the OpenAPI specification. +Ensure the conftest.py and validator.py are correctly configured and accessible. + +Instructions: +- Place this file alongside the conftest.py and validator.py files. +- Ensure the pet_petId.json test data file is in the same directory. +- Run the tests using pytest. + +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Constants +HERE = Path(__file__).resolve().parent +API_SPEC_PATH = HERE / 'api.json' +TEST_DATA_FILENAME = 'pet_petId.json' +ENDPOINT = '/pet/{petId}' + +# Load test data +def _load_json_file(file_path): + with open(file_path, 'r') as file: + return json.load(file) + +_ENDPOINT_DATA = _load_json_file(HERE / TEST_DATA_FILENAME) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"scenario={case['scenario']}" for case in _ENDPOINT_DATA] + +# Validator setup +swagger_validator = SwaggerSchemaValidator(API_SPEC_PATH) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_update_pet_with_form(api_client, config_test_data, scenario_case): + """ + Test the updatePetWithForm endpoint with various scenarios. + """ + pet_id = scenario_case.get("petId", config_test_data.get("petId")) + name = scenario_case.get("name") + status = scenario_case.get("status") + expected_status_code = scenario_case["statusCode"] + + # Request data + params = {} + if name: + params['name'] = name + if status: + params['status'] = status + + # Validate request schema + request_data = {"petId": pet_id, "name": name, "status": status} + request_data = {k: v for k, v in request_data.items() if v is not None} + validation_result = swagger_validator.validate_json(request_data, "Pet") + assert validation_result["valid"], f"Request schema validation failed: {validation_result}" + + # Make API request + response = api_client.post(ENDPOINT.format(petId=pet_id), params=params) + + # Assert response status code + assert response.status_code == expected_status_code, f"Unexpected status code: {response.status_code}" + + # Validate response schema + response_validation = swagger_validator.validate_schema_by_response( + endpoint=ENDPOINT, + method='POST', + status_code=str(expected_status_code), + response=response + ) + assert response_validation["valid"], f"Response schema validation failed: {response_validation}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_update_pet_with_form_invalid_auth(api_client, scenario_case): + """ + Test the updatePetWithForm endpoint with invalid authentication. + """ + pet_id = scenario_case.get("petId", 0) # Use invalid petId for this case + name = scenario_case.get("name") + status = scenario_case.get("status") + + # Use invalid auth token + api_client.session.headers.update({'Authorization': 'Bearer invalid_token'}) + + # Request data + params = {} + if name: + params['name'] = name + if status: + params['status'] = status + + # Make API request + response = api_client.post(ENDPOINT.format(petId=pet_id), params=params) + + # Assert response status code for unauthorized + assert response.status_code == 401, f"Expected status code 401, got: {response.status_code}" + + # Reset auth token + api_client.session.headers.update({'Authorization': f"Bearer {auth}"}) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_update_pet_with_form_no_auth(api_client, scenario_case): + """ + Test the updatePetWithForm endpoint with no authentication. + """ + pet_id = scenario_case.get("petId", 0) # Use invalid petId for this case + name = scenario_case.get("name") + status = scenario_case.get("status") + + # Remove auth header + api_client.session.headers.pop('Authorization', None) + + # Request data + params = {} + if name: + params['name'] = name + if status: + params['status'] = status + + # Make API request + response = api_client.post(ENDPOINT.format(petId=pet_id), params=params) + + # Assert response status code for unauthorized + assert response.status_code == 401, f"Expected status code 401, got: {response.status_code}" + + # Reset auth token + api_client.session.headers.update({'Authorization': f"Bearer {auth}"}) diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_uploadImage_post.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_uploadImage_post.py new file mode 100644 index 00000000..034fb9b2 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_petId_uploadImage_post.py @@ -0,0 +1,151 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet/{petId}/uploadImage_post for http method type POST +# RoostTestHash=b2ab25b329 +# +# + +# ********RoostGPT******** +""" +This module contains pytest tests for the API endpoint /pet/{petId}/uploadImage. +The tests are generated based on the API specification and test data provided in pet_petId_uploadImage.json. +Ensure that conftest.py and validator.py are in the same directory for fixtures and validation functions. + +Instructions for running the tests: +- Ensure pytest is installed: `pip install pytest` +- Run the tests using the command: `pytest .py` +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Constants +SPEC_PATH = 'api.json' +TEST_DATA_FILENAME = 'pet_petId_uploadImage.json' + +# Load the JSON test data +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME + +with open(_endpoint_data_path, 'r') as file: + ENDPOINT_TEST_DATA = json.load(file) + +# Reusable parametrize args +_PARAM_CASES = ENDPOINT_TEST_DATA +_PARAM_IDS = [f"scenario_{i}" for i in range(len(ENDPOINT_TEST_DATA))] + +# Initialize the Swagger schema validator +validator = SwaggerSchemaValidator(SPEC_PATH) + + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_upload_image(api_client, scenario_case): + """ + Test the /pet/{petId}/uploadImage endpoint for various scenarios. + + This test function validates the response for each scenario defined in the test data. + """ + petId = scenario_case.get("petId") + additionalMetadata = scenario_case.get("additionalMetadata", None) + expected_status_code = scenario_case.get("statusCode") + + # Construct the endpoint + endpoint = f"/pet/{petId}/uploadImage" + + # Request parameters + params = {} + if additionalMetadata: + params['additionalMetadata'] = additionalMetadata + + # Make the API request + response = api_client.post(endpoint, params=params) + + # Validate the response status code + assert response.status_code == expected_status_code, ( + f"Unexpected status code: {response.status_code}, expected: {expected_status_code}" + ) + + # Validate the response schema + validation_result = validator.validate_schema_by_response( + endpoint, 'post', str(response.status_code), response + ) + assert validation_result['valid'], ( + f"Response schema validation failed: {validation_result['message']}" + ) + + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_upload_image_invalid_auth(api_client, scenario_case): + """ + Test the /pet/{petId}/uploadImage endpoint with invalid authentication. + + This test function validates the response when an invalid token is used. + """ + petId = scenario_case.get("petId") + additionalMetadata = scenario_case.get("additionalMetadata", None) + + # Construct the endpoint + endpoint = f"/pet/{petId}/uploadImage" + + # Request parameters + params = {} + if additionalMetadata: + params['additionalMetadata'] = additionalMetadata + + # Override headers with invalid auth token + invalid_headers = {'Authorization': 'Bearer invalid_token'} + + # Make the API request with invalid auth + response = api_client.post(endpoint, params=params, headers=invalid_headers) + + # Validate the response status code + assert response.status_code == 401, ( + f"Unexpected status code: {response.status_code}, expected: 401" + ) + + # Validate the response schema + validation_result = validator.validate_schema_by_response( + endpoint, 'post', str(response.status_code), response + ) + assert validation_result['valid'], ( + f"Response schema validation failed: {validation_result['message']}" + ) + + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_upload_image_missing_auth(api_client, scenario_case): + """ + Test the /pet/{petId}/uploadImage endpoint with missing authentication. + + This test function validates the response when no authentication token is provided. + """ + petId = scenario_case.get("petId") + additionalMetadata = scenario_case.get("additionalMetadata", None) + + # Construct the endpoint + endpoint = f"/pet/{petId}/uploadImage" + + # Request parameters + params = {} + if additionalMetadata: + params['additionalMetadata'] = additionalMetadata + + # Make the API request without auth headers + response = api_client.post(endpoint, params=params, headers={}) + + # Validate the response status code + assert response.status_code == 401, ( + f"Unexpected status code: {response.status_code}, expected: 401" + ) + + # Validate the response schema + validation_result = validator.validate_schema_by_response( + endpoint, 'post', str(response.status_code), response + ) + assert validation_result['valid'], ( + f"Response schema validation failed: {validation_result['message']}" + ) diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_post.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_post.py new file mode 100644 index 00000000..53f9de40 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_post.py @@ -0,0 +1,103 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet_post for http method type POST +# RoostTestHash=68c053e0a8 +# +# + +# ********RoostGPT******** +""" +Instructions for running the tests: +- Ensure all required packages are installed. +- Place this test file in the same directory as `conftest.py`. +- Run the tests using the command: pytest .py +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Load the API spec for validation +swagger_validator = SwaggerSchemaValidator('api.json') + +_here = Path(__file__).resolve().parent +_ENDPOINT_DATA_PATH = _here / 'pet.json' + +def _load_json_file(file_path): + with open(file_path, 'r') as file: + return json.load(file) + +_ENDPOINT_DATA = _load_json_file(_ENDPOINT_DATA_PATH) + +@pytest.mark.parametrize("scenario_case", _ENDPOINT_DATA, ids=lambda case: case.get("scenario")) +def test_add_pet(api_client, config_test_data, scenario_case): + """Test the /pet endpoint with various scenarios.""" + # Load the necessary test data + test_data = config_test_data.copy() + test_data.update(scenario_case) + + # Prepare request payload + payload = { + "name": test_data.get("name"), + "photoUrls": test_data.get("photoUrls") + } + + # Add optional fields if present in the scenario case + if "category" in test_data: + payload["category"] = test_data["category"] + if "tags" in test_data: + payload["tags"] = test_data["tags"] + if "status" in test_data: + payload["status"] = test_data["status"] + + # Validate request payload before sending + validation_result = swagger_validator.validate_json(payload, "Pet") + assert validation_result["valid"], f"Request payload validation failed: {validation_result.get('message')}" + + # Make the API request + response = api_client.post('/pet', json=payload) + + # Validate response status code + expected_status_code = test_data.get("statusCode") + assert response.status_code == expected_status_code, f"Expected status code {expected_status_code}, got {response.status_code}" + + # Validate response schema + schema_validation_result = swagger_validator.validate_schema_by_response('/pet', 'POST', str(response.status_code), response) + assert schema_validation_result["valid"], f"Response schema validation failed: {schema_validation_result.get('message')}" + +@pytest.mark.parametrize("scenario_case", _ENDPOINT_DATA, ids=lambda case: case.get("scenario")) +def test_add_pet_invalid_auth(api_client, scenario_case): + """Test the /pet endpoint with invalid authentication.""" + # Load the necessary test data + test_data = scenario_case + + # Prepare request payload + payload = { + "name": test_data.get("name"), + "photoUrls": test_data.get("photoUrls") + } + + # Add optional fields if present in the scenario case + if "category" in test_data: + payload["category"] = test_data["category"] + if "tags" in test_data: + payload["tags"] = test_data["tags"] + if "status" in test_data: + payload["status"] = test_data["status"] + + # Validate request payload before sending + validation_result = swagger_validator.validate_json(payload, "Pet") + assert validation_result["valid"], f"Request payload validation failed: {validation_result.get('message')}" + + # Make the API request with invalid auth + response = api_client.session.post(f'{api_client.base_url}/pet', json=payload, headers={'Authorization': 'Bearer invalid_token'}) + + # Validate response status code + assert response.status_code == 401, f"Expected status code 401 for invalid auth, got {response.status_code}" + + # Validate response schema for error case + schema_validation_result = swagger_validator.validate_schema_by_response('/pet', 'POST', 'default', response) + assert schema_validation_result["valid"], f"Response schema validation failed: {schema_validation_result.get('message')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_put.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_put.py new file mode 100644 index 00000000..211d66e6 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_pet_put.py @@ -0,0 +1,75 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /pet_put for http method type PUT +# RoostTestHash=42edb35103 +# +# + +# ********RoostGPT******** +""" +This module contains pytest-based tests for the Petstore API. +Ensure you have the necessary environment variables set or use the defaults from the config.yml. + +To run the tests, use the command: +pytest .py + +Make sure the conftest.py and validator.py are in the same directory or accessible in the PYTHONPATH. +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Load the API specification for validation +SPEC_PATH = 'api.json' +schema_validator = SwaggerSchemaValidator(SPEC_PATH) + +# Load endpoint test data from pet.json +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / 'pet.json' +with open(_endpoint_data_path, 'r') as f: + _ENDPOINT_DATA = json.load(f) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [case['scenario'] for case in _ENDPOINT_DATA] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_update_pet(api_client, scenario_case, config_test_data): + """ + Test updating an existing pet. + """ + # Merge default test data with scenario specific data + test_data = {**config_test_data, **scenario_case} + + # Ensure required fields are present + if 'name' not in test_data or 'photoUrls' not in test_data: + pytest.skip("Skipping test due to missing required fields") + + # Prepare request data + request_data = { + "name": test_data.get("name"), + "photoUrls": test_data.get("photoUrls"), + "id": test_data.get("id"), + "category": test_data.get("category"), + "tags": test_data.get("tags"), + "status": test_data.get("status") + } + + # Validate request schema + schema_validator.validate_json(request_data, 'Pet') + + # Make the API call + response = api_client.put('/pet', json=request_data) + + # Validate response status code + assert response.status_code == test_data['statusCode'], f"Expected {test_data['statusCode']}, got {response.status_code}" + + # Validate response schema + validation_result = schema_validator.validate_schema_by_response( + '/pet', 'put', str(response.status_code), response + ) + assert validation_result['valid'], f"Response validation failed: {validation_result['message']}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_inventory_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_inventory_get.py new file mode 100644 index 00000000..3b069022 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_inventory_get.py @@ -0,0 +1,83 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /store/inventory_get for http method type GET +# RoostTestHash=d4ad50ae28 +# +# + +# ********RoostGPT******** +""" +Pytest test suite for API testing. + +Instructions for running the tests: +1. Ensure you have pytest installed. +2. Place the `store_inventory.json` file in the same directory as this test file. +3. Run the tests using the command: pytest .py +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Load endpoint test data +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / 'store_inventory.json' +with open(_endpoint_data_path) as f: + _ENDPOINT_DATA = json.load(f) + +# Load Swagger schema validator +swagger_validator = SwaggerSchemaValidator("api.json") + +# Parameterization setup +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"{case['scenario']}" for case in _ENDPOINT_DATA] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_inventory(api_client, scenario_case, host, auth, api_key): + """ + Test the /store/inventory endpoint with various scenarios. + """ + + endpoint = "/store/inventory" + expected_status_code = scenario_case['statusCode'] + + # Test with valid API key + response = api_client.get(endpoint) + assert response.status_code == expected_status_code, f"Unexpected status code: {response.status_code}" + + # Validate response schema + validation_result = swagger_validator.validate_schema_by_response(endpoint, 'GET', str(expected_status_code), response) + assert validation_result['valid'], f"Schema validation failed: {validation_result.get('message')}" + + # Test with invalid API key (hardcoded for testing purposes) + api_client.session.headers.update({'api_key': 'invalid_key'}) + response = api_client.get(endpoint) + + if expected_status_code != 200: + assert response.status_code != 200, "Expected failure with invalid API key" + + # Reset API key for further tests + api_client.session.headers.update({'api_key': api_key}) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_inventory_without_auth(api_client, scenario_case): + """ + Test the /store/inventory endpoint without authentication. + """ + + endpoint = "/store/inventory" + expected_status_code = scenario_case['statusCode'] + + # Remove auth headers + api_client.session.headers.pop('Authorization', None) + api_client.session.headers.pop('api_key', None) + + response = api_client.get(endpoint) + if expected_status_code != 200: + assert response.status_code != 200, "Expected failure without authentication" + + # Restore headers for further tests + api_client.session.headers.update({'Authorization': f'Bearer {auth}'}) diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_orderId_delete.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_orderId_delete.py new file mode 100644 index 00000000..83bbe907 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_orderId_delete.py @@ -0,0 +1,107 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /store/order/{orderId}_delete for http method type DELETE +# RoostTestHash=afdda1c8b4 +# +# + +# ********RoostGPT******** +""" +Pytest suite for testing the /store/order/{orderId} endpoint. +Utilizes fixtures and validators for comprehensive coverage. +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Initialize validator with OpenAPI spec +swagger_validator = SwaggerSchemaValidator('api.json') + +# Load test data from JSON file +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / 'store_order_orderId.json' +with open(_endpoint_data_path, 'r') as file: + _ENDPOINT_DATA = json.load(file) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"OrderID: {case['orderId']}, Status: {case['statusCode']}" for case in _ENDPOINT_DATA] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_order(api_client, config_test_data, scenario_case): + """ + Test /store/order/{orderId} endpoint for various scenarios. + Validates response status codes and schema. + """ + order_id = scenario_case['orderId'] + expected_status = scenario_case['statusCode'] + endpoint = f"store/order/{order_id}" + + # Ensure valid request schema + swagger_validator.validate_json({"orderId": order_id}, "OrderId") + + # Perform API call + try: + response = api_client.delete(endpoint) + except requests.exceptions.HTTPError as e: + response = e.response + + # Validate response status code + assert response.status_code == expected_status, f"Expected {expected_status}, got {response.status_code}" + + # Validate response schema + validation_results = swagger_validator.validate_schema_by_response("/store/order/{orderId}", "DELETE", str(response.status_code), response) + assert validation_results['valid'], f"Schema validation failed: {validation_results.get('message')}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_order_invalid_auth(api_client, scenario_case): + """ + Test /store/order/{orderId} endpoint with invalid authentication. + """ + order_id = scenario_case['orderId'] + endpoint = f"store/order/{order_id}" + + # Use invalid token + api_client.session.headers.update({'Authorization': 'Bearer invalid_token'}) + + # Perform API call + try: + response = api_client.delete(endpoint) + except requests.exceptions.HTTPError as e: + response = e.response + + # Validate response status code for unauthorized access + assert response.status_code == 401, f"Expected 401 Unauthorized, got {response.status_code}" + + # Reset headers + api_client.session.headers.update({'Authorization': f"Bearer {api_client.session.headers['Authorization']}"}) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_order_missing_auth(api_client, scenario_case): + """ + Test /store/order/{orderId} endpoint with missing authentication. + """ + order_id = scenario_case['orderId'] + endpoint = f"store/order/{order_id}" + + # Remove auth header + headers = api_client.session.headers.copy() + del api_client.session.headers['Authorization'] + + # Perform API call + try: + response = api_client.delete(endpoint) + except requests.exceptions.HTTPError as e: + response = e.response + + # Validate response status code for missing auth + assert response.status_code == 401, f"Expected 401 Unauthorized, got {response.status_code}" + + # Restore headers + api_client.session.headers.update(headers) + +# Note: Additional edge case tests (e.g., boundary testing) can be added as needed. diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_orderId_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_orderId_get.py new file mode 100644 index 00000000..671ec34a --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_orderId_get.py @@ -0,0 +1,117 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /store/order/{orderId}_get for http method type GET +# RoostTestHash=d35051238a +# +# + +# ********RoostGPT******** +""" +Pytest test suite for the API defined in the provided OpenAPI specification. + +Tests are generated based on the API spec and test data from a JSON file. +Ensure that the `config.yml` file is correctly set up with the necessary +host and authentication details before running the tests. +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Constants +TEST_DATA_FILENAME = "store_order_orderId.json" +API_SPEC_PATH = "api.json" +ENDPOINT = "/store/order/{orderId}" +HTTP_METHOD = "GET" + +# Load test data +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME + +def _load_json_file(filepath): + with open(filepath, 'r') as file: + return json.load(file) + +_ENDPOINT_DATA = _load_json_file(_endpoint_data_path) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"order_id_{case['orderId']}" for case in _ENDPOINT_DATA] + +@pytest.fixture(scope='module') +def swagger_validator(): + return SwaggerSchemaValidator(API_SPEC_PATH) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_order_by_id(api_client, swagger_validator, scenario_case): + """ + Test the endpoint /store/order/{orderId} with various scenarios. + """ + order_id = scenario_case["orderId"] + expected_status_code = scenario_case["statusCode"] + + # Construct endpoint with order_id + endpoint = ENDPOINT.format(orderId=order_id) + + # Make request + response = api_client.get(endpoint) + + # Assert status code + assert response.status_code == expected_status_code, f"Expected {expected_status_code}, got {response.status_code}" + + # Validate response schema + validation_result = swagger_validator.validate_schema_by_response(endpoint, HTTP_METHOD, str(expected_status_code), response) + assert validation_result["valid"], f"Schema validation failed: {validation_result.get('message')}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_order_by_id_invalid_auth(api_client, scenario_case): + """ + Test the endpoint /store/order/{orderId} with invalid authentication. + """ + order_id = scenario_case["orderId"] + endpoint = ENDPOINT.format(orderId=order_id) + + # Use invalid token + api_client.session.headers.update({'Authorization': 'Bearer invalid_token'}) + + # Make request + response = api_client.get(endpoint) + + # Assert 401 Unauthorized + assert response.status_code == 401, f"Expected 401, got {response.status_code}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_order_by_id_missing_auth(api_client, scenario_case): + """ + Test the endpoint /store/order/{orderId} with missing authentication. + """ + order_id = scenario_case["orderId"] + endpoint = ENDPOINT.format(orderId=order_id) + + # Remove auth headers + api_client.session.headers.pop('Authorization', None) + + # Make request + response = api_client.get(endpoint) + + # Assert 401 Unauthorized + assert response.status_code == 401, f"Expected 401, got {response.status_code}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_get_order_by_id_unexpected_error(api_client, swagger_validator, scenario_case): + """ + Test the endpoint /store/order/{orderId} for unexpected errors. + """ + order_id = scenario_case["orderId"] + endpoint = ENDPOINT.format(orderId=order_id) + + # Intentionally cause an internal server error by manipulating the request + response = api_client.get(endpoint, params={"causeError": "true"}) + + # Assert response is not 2xx + assert response.status_code != 200, f"Expected non-2xx, got {response.status_code}" + validation_result = swagger_validator.validate_schema_by_response(endpoint, HTTP_METHOD, 'default', response) + assert validation_result["valid"], f"Schema validation failed: {validation_result.get('message')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_post.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_post.py new file mode 100644 index 00000000..fda010a6 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_store_order_post.py @@ -0,0 +1,102 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /store/order_post for http method type POST +# RoostTestHash=285bf3afc6 +# +# + +# ********RoostGPT******** +""" +Test suite for the /store/order endpoint using pytest framework. + +Setup: +- Ensure 'conftest.py', 'validator.py', and 'config.yml' are correctly configured. +- Place 'store_order.json' in the same directory as this test file. + +Execution: +- Run the tests with pytest command in the terminal. +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Constants +API_SPEC_PATH = 'api.json' +TEST_DATA_FILENAME = 'store_order.json' +ENDPOINT = '/store/order' +METHOD = 'POST' + +# Load the API specification +swagger_validator = SwaggerSchemaValidator(API_SPEC_PATH) + +# Load test data +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME + +def _load_json_file(file_path): + with open(file_path, 'r') as file: + return json.load(file) + +_ENDPOINT_DATA = _load_json_file(_endpoint_data_path) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"scenario_{i+1}" for i in range(len(_ENDPOINT_DATA))] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_store_order(api_client, scenario_case, config_test_data): + """ + Test the /store/order endpoint for various scenarios. + """ + # Prepare request data + request_data = { + "id": scenario_case.get("id"), + "petId": scenario_case.get("petId") or config_test_data.get("petId"), + "quantity": scenario_case.get("quantity"), + } + if "shipDate" in scenario_case: + request_data["shipDate"] = scenario_case["shipDate"] + if "status" in scenario_case: + request_data["status"] = scenario_case["status"] + if "complete" in scenario_case: + request_data["complete"] = scenario_case["complete"] + + # Validate request schema + validation_result = swagger_validator.validate_json(request_data, "order") + assert validation_result["valid"], f"Request validation failed: {validation_result.get('message')}" + + # Make API request + response = api_client.post(ENDPOINT, json=request_data) + + # Validate response status code + assert response.status_code == scenario_case["statusCode"], f"Expected {scenario_case['statusCode']}, got {response.status_code}" + + # Validate response schema + schema_validation_result = swagger_validator.validate_schema_by_response(ENDPOINT, METHOD, str(response.status_code), response) + assert schema_validation_result["valid"], f"Response validation failed: {schema_validation_result.get('message')}" + +@pytest.mark.parametrize("auth_token", ["", "invalid_token"]) +def test_store_order_auth_failure(api_client, auth_token, config_test_data): + """ + Test the /store/order endpoint with invalid/missing authentication. + """ + # Prepare request data + request_data = { + "id": 10, + "petId": config_test_data["petId"], + "quantity": 7 + } + + # Make API request with invalid/missing token + response = api_client.post(ENDPOINT, json=request_data, headers={'Authorization': f'Bearer {auth_token}'}) + + # Validate response status code + assert response.status_code == 401, f"Expected 401, got {response.status_code}" + + # Validate response schema + schema_validation_result = swagger_validator.validate_schema_by_response(ENDPOINT, METHOD, 'default', response) + assert schema_validation_result["valid"], f"Response validation failed: {schema_validation_result.get('message')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_createWithList_post.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_createWithList_post.py new file mode 100644 index 00000000..bfc74b0c --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_createWithList_post.py @@ -0,0 +1,126 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /user/createWithList_post for http method type POST +# RoostTestHash=e746dbb737 +# +# + +# ********RoostGPT******** +""" +Pytest test suite for the API endpoint /user/createWithList using pytest framework. + +Instructions for running the tests: +- Ensure that pytest and requests are installed in your environment. +- Place this file in the same directory as the conftest.py and validator.py files. +- Ensure the user_createWithList.json file is present in the same directory. +- Run the tests using the command: pytest -v + +This test suite covers: +1. Endpoint testing for /user/createWithList using POST method. +2. Security schema testing with valid and invalid credentials. +3. Validation of request and response schemas against the OpenAPI specification. +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Constants and paths +API_SPEC_PATH = "api.json" +TEST_DATA_FILENAME = "user_createWithList.json" + +# Load test data from JSON file +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME +with open(_endpoint_data_path, "r") as f: + ENDPOINT_TEST_DATA = json.load(f) + +# Initialize schema validator +validator = SwaggerSchemaValidator(API_SPEC_PATH) + +# Reusable parametrize args +_PARAM_CASES = ENDPOINT_TEST_DATA +_PARAM_IDS = [f"scenario_{i}" for i in range(len(ENDPOINT_TEST_DATA))] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_create_user_with_list(api_client, host, auth, scenario_case): + """ + Test the /user/createWithList endpoint with various scenarios. + """ + endpoint = "/user/createWithList" + url = f"{host}{endpoint}" + headers = {'Authorization': f'Bearer {auth}', 'Content-Type': 'application/json'} + + # Extract data from scenario + request_data = { + "id": scenario_case.get("id"), + "username": scenario_case.get("username"), + "email": scenario_case.get("email"), + "userStatus": scenario_case.get("userStatus"), + } + if "firstName" in scenario_case: + request_data["firstName"] = scenario_case["firstName"] + if "lastName" in scenario_case: + request_data["lastName"] = scenario_case["lastName"] + if "password" in scenario_case: + request_data["password"] = scenario_case["password"] + if "phone" in scenario_case: + request_data["phone"] = scenario_case["phone"] + + # Validate request schema + validation_result = validator.validate_json([request_data], "user") + assert validation_result["valid"], f"Request validation failed: {validation_result.get('message')}" + + # Make API request + response = api_client.post(endpoint, headers=headers, json=[request_data]) + + # Validate response status code + expected_status_code = scenario_case["statusCode"] + assert response.status_code == expected_status_code, f"Expected {expected_status_code}, got {response.status_code}" + + # Validate response schema + response_validation = validator.validate_schema_by_response(endpoint, "POST", str(response.status_code), response) + assert response_validation["valid"], f"Response validation failed: {response_validation.get('message')}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_create_user_with_list_invalid_auth(api_client, host, scenario_case): + """ + Test the /user/createWithList endpoint with invalid authentication. + """ + endpoint = "/user/createWithList" + url = f"{host}{endpoint}" + headers = {'Authorization': 'Bearer INVALID_TOKEN', 'Content-Type': 'application/json'} + + # Extract data from scenario + request_data = { + "id": scenario_case.get("id"), + "username": scenario_case.get("username"), + "email": scenario_case.get("email"), + "userStatus": scenario_case.get("userStatus"), + } + if "firstName" in scenario_case: + request_data["firstName"] = scenario_case["firstName"] + if "lastName" in scenario_case: + request_data["lastName"] = scenario_case["lastName"] + if "password" in scenario_case: + request_data["password"] = scenario_case["password"] + if "phone" in scenario_case: + request_data["phone"] = scenario_case["phone"] + + # Validate request schema + validation_result = validator.validate_json([request_data], "user") + assert validation_result["valid"], f"Request validation failed: {validation_result.get('message')}" + + # Make API request + response = api_client.post(endpoint, headers=headers, json=[request_data]) + + # Validate response status code for unauthorized access + assert response.status_code == 401, f"Expected 401 Unauthorized, got {response.status_code}" + + # Validate response schema if applicable + if response.status_code in ["401", "403"]: + response_validation = validator.validate_schema_by_response(endpoint, "POST", str(response.status_code), response) + assert response_validation["valid"], f"Response validation failed: {response_validation.get('message')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_login_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_login_get.py new file mode 100644 index 00000000..de13bf65 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_login_get.py @@ -0,0 +1,133 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /user/login_get for http method type GET +# RoostTestHash=fbb4064353 +# +# + +# ********RoostGPT******** +""" +Pytest test suite for the '/user/login' endpoint using pytest framework. + +This suite tests various scenarios including successful login, invalid credentials, and error handling. +It uses fixtures from conftest.py to manage configuration and authentication tokens. + +Instructions: +- Ensure 'user_login.json' is in the same directory as this test file. +- Execute the tests using the following command: + pytest .py +""" + +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Define constants +API_SPEC_PATH = 'api.json' +TEST_DATA_FILENAME = 'user_login.json' + +# Load test data +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME +with open(_endpoint_data_path, 'r') as f: + _ENDPOINT_DATA = json.load(f) + +# Initialize validator +schema_validator = SwaggerSchemaValidator(API_SPEC_PATH) + +# Parametrize test cases +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [case.get('scenario', 'Unknown') for case in _ENDPOINT_DATA] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_user_login(api_client, config_test_data, scenario_case): + """ + Test '/user/login' endpoint with various scenarios. + + :param scenario_case: Test case data loaded from user_login.json + """ + # Prepare request data + username = scenario_case.get('username', config_test_data.get('username')) + password = scenario_case.get('password', config_test_data.get('password')) + expected_status_code = scenario_case['statusCode'] + + # Validate request schema + request_data = {"username": username, "password": password} + schema_validator.validate_json(request_data, "UserLoginRequest") + + # Make API request + response = api_client.post('/user/login', params=request_data) + + # Validate response schema + validation_result = schema_validator.validate_schema_by_response( + endpoint='/user/login', + method='POST', + status_code=str(expected_status_code), + response=response + ) + + assert response.status_code == expected_status_code, f"Expected {expected_status_code}, got {response.status_code}" + assert validation_result['valid'], f"Response schema validation failed: {validation_result.get('message')}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_user_login_invalid_auth(api_client, scenario_case): + """ + Test '/user/login' endpoint with invalid authentication scenarios. + + :param scenario_case: Test case data loaded from user_login.json + """ + # Prepare request data + username = scenario_case.get('username', 'invalidUser') + password = scenario_case.get('password', 'invalidPass') + expected_status_code = 400 # Assuming 400 for invalid credentials + + # Validate request schema + request_data = {"username": username, "password": password} + schema_validator.validate_json(request_data, "UserLoginRequest") + + # Make API request + response = api_client.post('/user/login', params=request_data) + + # Validate response schema + validation_result = schema_validator.validate_schema_by_response( + endpoint='/user/login', + method='POST', + status_code=str(expected_status_code), + response=response + ) + + assert response.status_code == expected_status_code, f"Expected {expected_status_code}, got {response.status_code}" + assert validation_result['valid'], f"Response schema validation failed: {validation_result.get('message')}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_user_login_missing_auth(api_client, scenario_case): + """ + Test '/user/login' endpoint with missing authentication. + + :param scenario_case: Test case data loaded from user_login.json + """ + # Prepare request data + username = scenario_case.get('username', '') + password = scenario_case.get('password', '') + expected_status_code = 400 # Assuming 400 for missing credentials + + # Validate request schema + request_data = {"username": username, "password": password} + schema_validator.validate_json(request_data, "UserLoginRequest") + + # Make API request + response = api_client.post('/user/login', params=request_data) + + # Validate response schema + validation_result = schema_validator.validate_schema_by_response( + endpoint='/user/login', + method='POST', + status_code=str(expected_status_code), + response=response + ) + + assert response.status_code == expected_status_code, f"Expected {expected_status_code}, got {response.status_code}" + assert validation_result['valid'], f"Response schema validation failed: {validation_result.get('message')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_logout_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_logout_get.py new file mode 100644 index 00000000..02f748a5 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_logout_get.py @@ -0,0 +1,83 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /user/logout_get for http method type GET +# RoostTestHash=1092b8f68f +# +# + +# ********RoostGPT******** +""" +Pytest test suite for API endpoint testing using pytest framework. + +Setup: +- Ensure you have pytest installed: `pip install pytest` +- Place this file in the same directory as `conftest.py`, `validator.py`, and `user_logout.json`. + +Execution: +- Run the tests using: `pytest .py` + +This script tests the `/user/logout` endpoint defined in the API specification. +""" + +import pytest +from validator import SwaggerSchemaValidator +from pathlib import Path +import json + +# Constants +API_SPEC_PATH = "api.json" +ENDPOINT = "/user/logout" +METHOD = "POST" +TEST_DATA_FILENAME = "user_logout.json" + +# Load test data from JSON file +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME +with open(_endpoint_data_path, 'r') as f: + _ENDPOINT_DATA = json.load(f) + +# Parametrize args +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [str(case['statusCode']) for case in _ENDPOINT_DATA] + +# Initialize SwaggerSchemaValidator +validator = SwaggerSchemaValidator(API_SPEC_PATH) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_user_logout(api_client, scenario_case, auth, api_key): + """ + Test the `/user/logout` endpoint with different scenarios. + + Parameters: + - scenario_case: Dict containing the status code and description for the test case. + - api_client: APIClient instance from the conftest.py fixture. + - auth: Authorization token from the conftest.py fixture. + - api_key: API key from the conftest.py fixture. + """ + status_code = scenario_case['statusCode'] + + # Prepare request + headers = { + 'Authorization': f'Bearer {auth}', + 'api_key': api_key + } + + # Perform request + try: + response = api_client.post(ENDPOINT, headers=headers) + response_status_code = response.status_code + except Exception as e: + if status_code == "invalid_code": + pytest.skip(f"Skipping test due to invalid status code: {e}") + else: + raise + + # Validate response + if isinstance(status_code, int): + assert response_status_code == status_code, f"Expected status code {status_code}, got {response_status_code}" + validation_result = validator.validate_schema_by_response(ENDPOINT, METHOD, str(response_status_code), response) + assert validation_result["valid"], f"Response schema validation failed: {validation_result.get('message')}" + else: + assert not (200 <= response_status_code < 300), "Unexpected success status code for invalid test case" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_post.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_post.py new file mode 100644 index 00000000..9c2509db --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_post.py @@ -0,0 +1,88 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /user_post for http method type POST +# RoostTestHash=701b5cbba5 +# +# + +# ********RoostGPT******** +""" +Pytest test suite for RESTful API based on OpenAPI specification. + +Instructions for setup and execution: +1. Ensure pytest and required dependencies are installed. +2. Place this script in the same directory as `conftest.py` and `user.json`. +3. Execute the test suite using the command: pytest .py + +This suite tests the '/user' endpoint for the following: +- Successful user creation. +- Various response status codes. +- Validation of request and response schemas. +""" + +import pytest +from pathlib import Path +import json +from validator import SwaggerSchemaValidator + +# Load API spec for validation +SPEC_PATH = Path(__file__).parent / "api.json" +swagger_validator = SwaggerSchemaValidator(str(SPEC_PATH)) + +# Load test data +HERE = Path(__file__).resolve().parent +ENDPOINT_TEST_DATA_PATH = HERE / "user.json" + +def _load_json_file(file_path): + with open(file_path, 'r') as file: + return json.load(file) + +ENDPOINT_TEST_DATA = _load_json_file(ENDPOINT_TEST_DATA_PATH) + +# Parametrize test cases +PARAM_CASES = ENDPOINT_TEST_DATA +PARAM_IDS = [str(data['id']) + ' ' + data['scenario'] for data in ENDPOINT_TEST_DATA] + +@pytest.mark.parametrize("scenario_case", PARAM_CASES, ids=PARAM_IDS) +def test_create_user(api_client, scenario_case, config_test_data): + """ + Test the '/user' endpoint for creating a user. + """ + + # Setup endpoint and payload + endpoint = "/user" + payload = { + "id": scenario_case["id"], + "username": scenario_case["username"] + } + + # Optional fields + if "firstName" in scenario_case: + payload["firstName"] = scenario_case["firstName"] + if "lastName" in scenario_case: + payload["lastName"] = scenario_case["lastName"] + if "email" in scenario_case: + payload["email"] = scenario_case["email"] + if "password" in scenario_case: + payload["password"] = scenario_case["password"] + if "phone" in scenario_case: + payload["phone"] = scenario_case["phone"] + if "userStatus" in scenario_case: + payload["userStatus"] = scenario_case["userStatus"] + + # Validate request payload against schema + schema_validation_result = swagger_validator.validate_json(payload, "user") + assert schema_validation_result["valid"], f"Request schema validation failed: {schema_validation_result.get('message')}" + + # Make API request + response = api_client.post(endpoint, json=payload) + + # Validate response status code + expected_status_code = scenario_case["statusCode"] + assert response.status_code == expected_status_code, f"Expected {expected_status_code}, got {response.status_code}" + + # Validate response schema + response_validation_result = swagger_validator.validate_schema_by_response(endpoint, "POST", response.status_code, response) + assert response_validation_result["valid"], f"Response schema validation failed: {response_validation_result.get('message')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_delete.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_delete.py new file mode 100644 index 00000000..ab56caa7 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_delete.py @@ -0,0 +1,141 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /user/{username}_delete for http method type DELETE +# RoostTestHash=7a2f597335 +# +# + +# ********RoostGPT******** +""" +Pytest suite for testing the API endpoint /user/{username}. +Tests cover all response codes and security scenarios. +""" + +import pytest +from pathlib import Path +import json +from validator import SwaggerSchemaValidator + +# Load API specification +API_SPEC_PATH = Path(__file__).parent / 'api.json' +validator = SwaggerSchemaValidator(API_SPEC_PATH) + +# Load endpoint test data +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / 'user_username.json' +with open(_endpoint_data_path) as f: + _ENDPOINT_TEST_DATA = json.load(f) + +# Reusable parametrize args +_PARAM_CASES = _ENDPOINT_TEST_DATA +_PARAM_IDS = [f"{case['scenario']}" for case in _ENDPOINT_TEST_DATA] + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_user(api_client, scenario_case, auth, api_key): + """ + Test DELETE /user/{username} endpoint for various scenarios. + """ + + username = scenario_case.get("username") + expected_status_code = scenario_case.get("statusCode") + + endpoint = f"user/{username}" + + # Validate request schema (if applicable) + # Assume no request body validation is needed for DELETE method in this case + + # Perform API request + response = api_client.delete(endpoint) + + # Validate response schema + validation_result = validator.validate_schema_by_response( + endpoint=f"/user/{{username}}", + method="DELETE", + status_code=str(expected_status_code), + response=response + ) + + assert validation_result['valid'], f"Schema validation failed: {validation_result.get('message', '')}" + + # Additional response checks + assert response.status_code == expected_status_code, ( + f"Expected status code {expected_status_code}, got {response.status_code}" + ) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_user_invalid_auth(api_client, scenario_case): + """ + Test DELETE /user/{username} with invalid authentication. + """ + + username = scenario_case.get("username") + endpoint = f"user/{username}" + + # Perform API request with invalid token + invalid_token = "invalid_token" + response = api_client.delete(endpoint, headers={"Authorization": f"Bearer {invalid_token}"}) + + # Validate response schema for unauthorized access + validation_result = validator.validate_schema_by_response( + endpoint=f"/user/{{username}}", + method="DELETE", + status_code="401", + response=response + ) + + assert validation_result['valid'], f"Schema validation failed: {validation_result.get('message', '')}" + assert response.status_code == 401, ( + f"Expected status code 401 for invalid auth, got {response.status_code}" + ) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_user_missing_auth(api_client, scenario_case): + """ + Test DELETE /user/{username} without authentication. + """ + + username = scenario_case.get("username") + endpoint = f"user/{username}" + + # Perform API request without any token + response = api_client.delete(endpoint, headers={"Authorization": ""}) + + # Validate response schema for unauthorized access + validation_result = validator.validate_schema_by_response( + endpoint=f"/user/{{username}}", + method="DELETE", + status_code="401", + response=response + ) + + assert validation_result['valid'], f"Schema validation failed: {validation_result.get('message', '')}" + assert response.status_code == 401, ( + f"Expected status code 401 for missing auth, got {response.status_code}" + ) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_delete_user_not_found(api_client, scenario_case, auth, api_key): + """ + Test DELETE /user/{username} when the user is not found. + """ + + username = scenario_case.get("username") + "_nonexistent" + endpoint = f"user/{username}" + + # Perform API request + response = api_client.delete(endpoint) + + # Validate response schema for user not found + validation_result = validator.validate_schema_by_response( + endpoint=f"/user/{{username}}", + method="DELETE", + status_code="404", + response=response + ) + + assert validation_result['valid'], f"Schema validation failed: {validation_result.get('message', '')}" + assert response.status_code == 404, ( + f"Expected status code 404 for user not found, got {response.status_code}" + ) diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_get.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_get.py new file mode 100644 index 00000000..bd76904c --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_get.py @@ -0,0 +1,80 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /user/{username}_get for http method type GET +# RoostTestHash=c0fb7cdf2c +# +# + +# ********RoostGPT******** +""" +Pytest suite for testing the /user/{username} endpoint of the API. + +This suite tests various scenarios including successful responses, +client errors, server errors, and unexpected errors. It leverages +the conftest.py and validator.py for configuration and validation. + +To run the tests, ensure you have pytest installed and execute: +pytest test_user_endpoint.py +""" + +import pytest +from pathlib import Path +import json +from validator import SwaggerSchemaValidator + +# Constants +API_SPEC_PATH = 'api.json' +ENDPOINT = '/user/{username}' +METHOD = 'GET' +TEST_DATA_FILENAME = 'user_username.json' + +# Initialize SwaggerSchemaValidator +validator = SwaggerSchemaValidator(API_SPEC_PATH) + +# Load test data from JSON file +_here = Path(__file__).resolve().parent +_endpoint_data_path = _here / TEST_DATA_FILENAME + +def _load_json_file(path): + with open(path, 'r') as file: + return json.load(file) + +_ENDPOINT_DATA = _load_json_file(_endpoint_data_path) + +# Parametrize test cases using JSON data +@pytest.mark.parametrize("scenario_case", _ENDPOINT_DATA, ids=lambda x: x['scenario']) +def test_user_endpoint(api_client, scenario_case, host, auth, api_key): + """ + Test the /user/{username} endpoint with various scenarios. + + This function covers successful responses, client errors, and unexpected errors. + """ + username = scenario_case.get('username') + expected_status_code = scenario_case.get('statusCode') + scenario = scenario_case.get('scenario') + + # Prepare the endpoint with given username + endpoint = ENDPOINT.format(username=username) + + # Make the API request + try: + response = api_client.get(endpoint) + response_status_code = response.status_code + except Exception as e: + pytest.skip(f"Skipping test due to error: {str(e)}") + + # Validate response status code + assert response_status_code == expected_status_code, f"Expected {expected_status_code}, got {response_status_code}" + + # Validate response against OpenAPI spec + validation_result = validator.validate_schema_by_response(endpoint, METHOD, str(expected_status_code), response) + + if expected_status_code == 200: + assert validation_result['valid'], f"Validation failed: {validation_result.get('message', '')}" + else: + if expected_status_code == 'default': + assert not (200 <= response_status_code < 300), "Unexpected 2xx response" + else: + assert validation_result['valid'], f"Validation failed: {validation_result.get('message', '')}" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_put.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_put.py new file mode 100644 index 00000000..a8ff557f --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/test_user_username_put.py @@ -0,0 +1,105 @@ +# ********RoostGPT******** + +# Test generated by RoostGPT for test pyapi-petstore-v3 using AI Type Open AI and AI Model gpt-4o +# +# Test file generated for /user/{username}_put for http method type PUT +# RoostTestHash=1f1b62cfe6 +# +# + +# ********RoostGPT******** +import pytest +import json +from pathlib import Path +from validator import SwaggerSchemaValidator + +# Constants and Paths +TEST_DATA_FILENAME = 'user_username.json' +API_SPEC_PATH = 'api.json' +_HERE = Path(__file__).resolve().parent +_ENDPOINT_DATA_PATH = _HERE / TEST_DATA_FILENAME +_ENDPOINT_DATA = json.loads(_ENDPOINT_DATA_PATH.read_text()) +_PARAM_CASES = _ENDPOINT_DATA +_PARAM_IDS = [f"Scenario: {case['scenario']}" for case in _ENDPOINT_DATA] + +# Initialize Swagger Schema Validator +swagger_validator = SwaggerSchemaValidator(API_SPEC_PATH) + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_update_user(api_client, config_test_data, scenario_case): + """ + Test update user endpoint with various scenarios. + """ + username = scenario_case.get('username', config_test_data['username']) + endpoint = f"/user/{username}" + headers = {'Content-Type': 'application/json'} + request_body = { + "id": scenario_case.get('id'), + "username": scenario_case.get('username'), + "firstName": scenario_case.get('firstName'), + "lastName": scenario_case.get('lastName'), + "email": scenario_case.get('email'), + "password": scenario_case.get('password'), + "phone": scenario_case.get('phone'), + "userStatus": scenario_case.get('userStatus') + } + + # Remove None values from request body + request_body = {k: v for k, v in request_body.items() if v is not None} + + # Validate request schema + request_validation = swagger_validator.validate_json(request_body, 'User') + assert request_validation['valid'], f"Request validation failed: {request_validation}" + + # Make API request + response = api_client.put(endpoint, headers=headers, json=request_body) + + # Validate response + expected_status_code = scenario_case['statusCode'] + assert response.status_code == expected_status_code, f"Expected {expected_status_code}, got {response.status_code}" + + response_validation = swagger_validator.validate_schema_by_response(endpoint, 'put', str(response.status_code), response) + assert response_validation['valid'], f"Response validation failed: {response_validation}" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_update_user_authentication(api_client, scenario_case): + """ + Test update user endpoint with authentication scenarios. + """ + username = scenario_case.get('username', 'default_user') + endpoint = f"/user/{username}" + headers = {'Content-Type': 'application/json'} + + # Test with missing auth + response = api_client.put(endpoint, headers=headers) + assert response.status_code == 401, "Expected 401 Unauthorized for missing auth" + + # Test with invalid auth + invalid_client = APIClient(base_url=api_client.base_url, auth_token="invalid_token") + response = invalid_client.put(endpoint, headers=headers) + assert response.status_code == 401, "Expected 401 Unauthorized for invalid auth" + +@pytest.mark.parametrize("scenario_case", _PARAM_CASES, ids=_PARAM_IDS) +def test_update_user_error_handling(api_client, scenario_case): + """ + Test error handling for update user endpoint. + """ + username = scenario_case.get('username', 'non_existent_user') + endpoint = f"/user/{username}" + headers = {'Content-Type': 'application/json'} + + # Test user not found + response = api_client.put(endpoint, headers=headers) + assert response.status_code == 404, "Expected 404 Not Found for non-existent user" + + # Test bad request + bad_request_body = {"invalid_field": "data"} + response = api_client.put(endpoint, headers=headers, json=bad_request_body) + assert response.status_code == 400, "Expected 400 Bad Request for invalid data" + +""" +Instructions: +- Ensure `conftest.py`, `validator.py`, and `config.yml` are properly set up in your testing environment. +- Place `api.json` in the same directory as this test file. +- Run tests using `pytest` command in your terminal. +""" diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/user.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/user.json new file mode 100644 index 00000000..11489110 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/user.json @@ -0,0 +1,55 @@ +[ + { + "id": 10, + "username": "theUser", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "id": 11, + "username": "anotherUser", + "firstName": "Alice", + "lastName": "Doe", + "email": "alice@example.com", + "password": "password123", + "phone": "67890", + "userStatus": 1, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "username": "missingIdUser", + "firstName": "Bob", + "lastName": "Smith", + "email": "bob@example.com", + "password": "pass456", + "phone": "54321", + "userStatus": 1, + "statusCode": "default", + "scenario": "Unknown response class" + }, + { + "id": 12, + "username": "invalidStatusUser", + "firstName": "Charlie", + "lastName": "Brown", + "email": "charlie@example.com", + "password": "pass789", + "phone": "98765", + "userStatus": "active", + "statusCode": "default", + "scenario": "Unknown response class" + }, + { + "id": 13, + "username": "nonExistingUser", + "firstName": "David", + "lastName": "Green", + "email": "david@example.com", + "password": "pass101", + "phone": "45678", + "userStatus": 1, + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/user_createWithList.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_createWithList.json new file mode 100644 index 00000000..841313fb --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_createWithList.json @@ -0,0 +1,46 @@ +[ + { + "id": 10, + "username": "theUser", + "email": "john@email.com", + "userStatus": 1, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "id": 11, + "username": "anotherUser", + "firstName": "Jane", + "lastName": "Doe", + "email": "jane.doe@email.com", + "password": "password123", + "phone": "9876543210", + "userStatus": 2, + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "username": "missingFieldsUser", + "email": "missingfields@email.com", + "statusCode": "default", + "scenario": "Unknown response class" + }, + { + "id": "invalidId", + "username": "invalidDataUser", + "firstName": "Invalid", + "email": "invalidemail.com", + "userStatus": "active", + "statusCode": "default", + "scenario": "Unknown response class" + }, + { + "id": 12, + "username": "", + "firstName": "EmptyUsername", + "email": "empty@username.com", + "userStatus": 1, + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/user_login.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_login.json new file mode 100644 index 00000000..5e5bd666 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_login.json @@ -0,0 +1,37 @@ +[ + { + "username": "validUser", + "password": "validPass123", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "username": "validUser", + "password": "validPass123", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "username": "validUser", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "username": "", + "password": "validPass123", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "username": "invalidUser!", + "password": "invalidPass!", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "username": "nonExistentUser", + "password": "somePass123", + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/user_logout.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_logout.json new file mode 100644 index 00000000..484eedb6 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_logout.json @@ -0,0 +1,22 @@ +[ + { + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "statusCode": "invalid_code", + "scenario": "Unknown response class" + }, + { + "statusCode": 404, + "scenario": "Unknown response class" + }, + { + "statusCode": 500, + "scenario": "Unknown response class" + }, + { + "statusCode": 400, + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/user_username.json b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_username.json new file mode 100644 index 00000000..3ef19d52 --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/user_username.json @@ -0,0 +1,32 @@ +[ + { + "username": "validUser123", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "username": "validUser456", + "statusCode": 200, + "scenario": "Successful responses: OK" + }, + { + "username": "", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "username": "invalidUser@#!", + "statusCode": 400, + "scenario": "Client error responses: Bad Request" + }, + { + "username": "nonExistentUser", + "statusCode": 404, + "scenario": "Client error responses: Not Found" + }, + { + "username": "unexpectedErrorUser", + "statusCode": "default", + "scenario": "Unknown response class" + } +] \ No newline at end of file diff --git a/tests/SWAGGER_PETSTORE_OPENAPI_30/validator.py b/tests/SWAGGER_PETSTORE_OPENAPI_30/validator.py new file mode 100644 index 00000000..7404300c --- /dev/null +++ b/tests/SWAGGER_PETSTORE_OPENAPI_30/validator.py @@ -0,0 +1,221 @@ + +import json +import yaml +from jsonschema import ( + Draft202012Validator, + Draft7Validator, + Draft4Validator, + ValidationError, +) +from referencing import Registry, Resource +from typing import Dict, Any +import requests + + +class SwaggerSchemaValidator: + """ + Validates JSON, XML, and text responses + """ + + def __init__(self, swagger_source: str): + self.spec = self._load_spec(swagger_source) + self.is_swagger2 = False + self.schemas = self._extract_schemas() + self.registry = Registry() + + for name, schema in self.schemas.items(): + pointer = ( + f"#/definitions/{name}" if self.is_swagger2 + else f"#/components/schemas/{name}" + ) + + wrapped = { + "$schema": "https://json-schema.org/draft/2020-12/schema", + **schema, + } + self.registry = self.registry.with_resource( + pointer, + Resource.from_contents(wrapped) + ) + + def _load_spec(self, source: str) -> Dict[str, Any]: + if source.startswith(("http://", "https://")): + resp = requests.get(source) + resp.raise_for_status() + text = resp.text + + try: + return yaml.safe_load(text) + except yaml.YAMLError: + try: + return json.loads(text) + except json.JSONDecodeError: + raise ValueError("URL does not contain valid YAML or JSON") + + with open(source, "r") as f: + text = f.read() + + if source.endswith((".yaml", ".yml")): + return yaml.safe_load(text) + if source.endswith(".json"): + return json.loads(text) + + raise ValueError("File must be YAML or JSON") + + def _extract_schemas(self): + if "components" in self.spec and "schemas" in self.spec["components"]: + self.is_swagger2 = False + return self.spec["components"]["schemas"] + + if "definitions" in self.spec: + self.is_swagger2 = True + return self.spec["definitions"] + + raise ValueError("No schemas found under components/schemas or definitions") + + def get_version(self): + return self.spec.get("openapi") or self.spec.get("swagger") or "" + + def select_validator(self): + v = self.get_version() + + if v.startswith("2."): + return Draft4Validator + if v.startswith("3.0"): + return Draft7Validator + if v.startswith("3.1"): + return Draft202012Validator + + return Draft202012Validator + + def resolve_ref(self, ref): + if ref.startswith("#/"): + parts = ref.lstrip("#/").split("/") + node = self.spec + for p in parts: + node = node[p] + return node + + raise ValueError(f"External refs not supported: {ref}") + + def deref(self, schema): + if isinstance(schema, dict): + if "$ref" in schema: + resolved = self.resolve_ref(schema["$ref"]) + return self.deref(resolved) + return {k: self.deref(v) for k, v in schema.items()} + + if isinstance(schema, list): + return [self.deref(v) for v in schema] + + return schema + + def detect_format(self, response): + ctype = response.headers.get("Content-Type", "").lower() + if "json" in ctype: + return "json" + if "xml" in ctype: + return "xml" + if "text" in ctype: + return "text" + return "binary" + + def parse_body(self, response, fmt): + if fmt == "json": + return json.loads(response.text) + + if fmt == "xml": + import xmltodict + return xmltodict.parse(response.text) + + if fmt == "text": + return response.text + + return response.content + + def extract_schema_for_media_type(self, response_block, content_type): + content = response_block.get("content", {}) + + if content_type in content: + return content[content_type].get("schema") + + if "json" in content_type: + for k, v in content.items(): + if k == "application/json" or k.endswith("+json"): + return v.get("schema") + + if "xml" in content_type: + for k, v in content.items(): + if "xml" in k: + return v.get("schema") + + if "text/plain" in content: + return content["text/plain"].get("schema") + + return None + + + def validate_json(self, data, schema_name): + if schema_name not in self.schemas: + raise ValueError(f"Schema '{schema_name}' not found") + + schema = self.deref(self.schemas[schema_name]) + validator_cls = self.select_validator() + validator = validator_cls(schema, registry=self.registry) + + try: + validator.validate(data) + return {"valid": True} + except ValidationError as e: + return { + "valid": False, + "message": e.message, + "path": list(e.path), + "schema_path": list(e.schema_path), + } + + def validate_schema_by_response(self, endpoint, method, status_code, response): + fmt = self.detect_format(response) + + paths = self.spec.get("paths", {}) + op = paths.get(endpoint, {}).get(method.lower()) + + if not op: + return {"valid": False, "message": f"Method {method} not found at path {endpoint}"} + + responses = op.get("responses", {}) + response_block = responses.get(status_code) + + if not response_block: + return {"valid": False, "message": f"No response block for {status_code}"} + + ctype = response.headers.get("Content-Type", "").split(";")[0].strip() + + if "content" in response_block: + schema = self.extract_schema_for_media_type(response_block, ctype) + else: + schema = response_block.get("schema") + + if schema is None: + return {"valid": True, "message": "No schema defined for this content type"} + + try: + data = self.parse_body(response, fmt) + except Exception as e: + return {"valid": False, "message": f"Body parsing failed: {e}"} + + schema = self.deref(schema) + + validator_cls = self.select_validator() + validator = validator_cls(schema, registry=self.registry) + + try: + validator.validate(data) + return {"valid": True} + except ValidationError as e: + return { + "valid": False, + "message": e.message, + "path": list(e.path), + "schema_path": list(e.schema_path), + }