Skip to content

Commit

Permalink
json payload: keys/values without quotes & prerequest message cleanup…
Browse files Browse the repository at this point in the history
… & use pytest to test dothtp request files
  • Loading branch information
cedric05 committed Nov 8, 2024
1 parent 7cbd652 commit 699404f
Show file tree
Hide file tree
Showing 15 changed files with 210 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
run: |
# current dothttp prints to terminal, which is not good for pytest.
# although, for test cases, we should ask those who prints to return
pytest -s --html=report.html --self-contained-html
pytest
- name: Upload test report
uses: actions/upload-artifact@v4
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
# unit tests need python-magic
# package in pypi woudn't need python-magic
# for integration tests
pytest -s
pytest
- name: Build Distribution
run: |
# install prerequisites
Expand Down
2 changes: 1 addition & 1 deletion dothttp/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.0.43a24"
__version__ = "0.0.43a25"
4 changes: 2 additions & 2 deletions dothttp/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ class PayloadDataNotValidException(PayloadNotValidException):
pass


@exception_wrapper("python/js test script compilation failed `{payload}`")
@exception_wrapper("PreRequest Function:`{function}` failed with error: `{payload}`")
class PreRequestScriptException(DotHttpException):
pass


@exception_wrapper("python/js test script compilation failed `{payload}`")
@exception_wrapper("Python test function: `{function}` execution failed `{payload}`")
class ScriptException(DotHttpException):
pass

Expand Down
8 changes: 4 additions & 4 deletions dothttp/http.tx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ PAYLOAD:
;

TRIPLE_OR_DOUBLE_STRING:
triple = TRIPLE_QUOTE_STRING | str = STRING
triple = TRIPLE_QUOTE_STRING | str = STRING | str = ID
;


Expand Down Expand Up @@ -208,7 +208,7 @@ Array:
;

Value:
strs += TRIPLE_OR_DOUBLE_STRING | var=VarString | flt=Float | int=Int | bl=Bool | object=Object | array=Array | null="null" | expr=Expression
flt=Float | int=Int | bl=Bool | null="null" |strs += TRIPLE_OR_DOUBLE_STRING | var=VarString | object=Object | array=Array | expr=Expression
;

Expression:
Expand Down Expand Up @@ -249,8 +249,8 @@ Object:
;

Member:
(key+=TRIPLE_OR_DOUBLE_STRING ':' value=Value) |
(var=VarString ':' value=Value)
(key+=TRIPLE_OR_DOUBLE_STRING (':'|'=') value=Value) |
(var=VarString (':'|'=') value=Value)
;
Comment:
UnixComment | CommentLine | CommentBlock
Expand Down
22 changes: 14 additions & 8 deletions dothttp/script/js3py.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,13 @@ def init_request_script(self):
def pre_request_script(self):
try:
self._pre_request_script()
except PreRequestScriptException:
raise
except Exception as exc:
request_logger.error("unknown exception happened", exc_info=True)
raise PreRequestScriptException(payload=str(exc))
raise PreRequestScriptException(payload=str(exc), function="''")

def execute_test_script(self, resp):
def execute_test_script(self, resp) -> ScriptResult:
if not self.client.request.test_script:
return ScriptResult(stdout="", error="", properties={}, tests=[])
try:
Expand Down Expand Up @@ -235,7 +237,7 @@ def _execute_test_script(self, resp) -> None:
js_template.replace("JS_CODE_REPLACE", self.client.request.test_script)
)
except JsException as exc:
raise ScriptException(payload=str(exc))
raise ScriptException(payload=str(exc), function="''")
content_type = resp.headers.get("content-type", "text/plain")
# in some cases mimetype can have charset
# like text/plain; charset=utf-8
Expand Down Expand Up @@ -279,7 +281,7 @@ def __init__(self, httpdef: HttpDef, prop: PropertyProvider) -> None:
)
exec(byte_code, script_gloabal, self.local)
except Exception as exc:
raise ScriptException(payload=str(exc))
raise ScriptException(payload=str(exc), function="test_script.py")

def _init_request_script(self) -> None:
# just for variables initialization
Expand All @@ -288,9 +290,13 @@ def _init_request_script(self) -> None:
func()

def _pre_request_script(self) -> None:
for key, func in self.local.items():
if (key.startswith("pre")) and isinstance(func, types.FunctionType):
func()
for key, pre_request_func in self.local.items():
if (key.startswith("pre")) and isinstance(pre_request_func, types.FunctionType):
try:
pre_request_func()
except Exception as exc:
request_logger.error(f"pre request script failed {exc}")
raise PreRequestScriptException(function=key, payload=str(exc))

def _execute_test_script(self, resp: Response) -> ScriptResult:
script_result = ScriptResult(stdout="", error="", properties={}, tests=[])
Expand All @@ -306,7 +312,7 @@ def _execute_test_script(self, resp: Response) -> ScriptResult:
test_result.result = func()
test_result.success = True
except BaseException as exc:
test_result.error = str(exc)
test_result.error = f"\n\nTest function with name `{key}` failed with error `{exc}`\n\n"
test_result.success = False
request_logger.error(f"test execution failed {exc}")
script_result.tests.append(test_result)
Expand Down
Empty file added dothttp_test/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions dothttp_test/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pytest
from .conftest import *

if __name__ == "__main__":
# run pytest with the current module
# this will not work because of arguments
# use `pytest dothttp_test --directory test/extensions/commands/names.http --prefix test -v -s --html=report.html`
pytest.main(["dothttp_test", "-v", "-s", "--html=report.html"])
26 changes: 26 additions & 0 deletions dothttp_test/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
def pytest_addoption(parser):
parser.addoption(
"--prefix", action="store", type=str, help="prefix for httpdef name", default="*"
)
parser.addoption(
"--directory",
action="store",
nargs="+",
help="list of directories to search for httpdef files",
default=["."],
)
parser.addoption("--property-file", help="property file")
parser.addoption(
"--no-cookie",
help="cookie storage is disabled",
action="store_const",
const=True,
)
parser.addoption(
"--env",
help="environment to select in property file. properties will be enabled on FIFO",
nargs="+",
default=["*"],
)
parser.addoption("--property", help="list of property's",
nargs="+", default=[])
125 changes: 125 additions & 0 deletions dothttp_test/dothttp_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import os
from collections import namedtuple
import logging

from dothttp.parse import (
Config,
)
from dothttp.parse.request_base import (
RequestCompiler,
dothttp_model,
)
from dotextensions.server.handlers.basic_handlers import RunHttpFileHandler


LOGGER = logging.getLogger(__name__)


HttpDefTest = namedtuple(
"HttpDef", ["display", "file", "name", "config", "failed"])


def pytest_generate_tests(metafunc):
prefix = metafunc.config.getoption("prefix")
directory = metafunc.config.getoption("directory")
# iterate through all sub directories in the directory
# and get all httpdef files
# and pass it to the test function
tests_to_run = []

for dir in directory:
if not os.path.exists(dir):
LOGGER.error(f"{dir} does not exists")
continue
if os.path.isfile(dir):
tests_to_run += extrct_tests_to_run(metafunc,
prefix, dir)
else:
for root, _dirs, files in os.walk(dir):
for filename in files:
tests_to_run += extrct_tests_to_run(
metafunc, prefix, os.path.join(root, filename))
metafunc.parametrize("httpdeftest", tests_to_run, ids=lambda x: x.display)


def extrct_tests_to_run(metafunc, prefix, filename):
tests_to_run = []
if filename.endswith(".http"):
with open(os.path.join(filename)) as f:
httpdef = f.read()
try:
model = dothttp_model.model_from_str(httpdef)
for http in model.allhttps:
if prefix is not None:
if prefix == "*":
tests_to_run.append(
HttpDefTest(
file=filename,
name=http.namewrap.name,
display=f"{filename} - name={http.namewrap.name}",
config=metafunc.config,
failed=False
)
)
elif http.namewrap and http.namewrap.name.startswith(prefix):
tests_to_run.append(
HttpDefTest(
file=filename,
name=http.namewrap.name,
display=f"{filename} - name={http.namewrap.name}",
config=metafunc.config,
failed=False
)
)
except:
tests_to_run.append(
HttpDefTest(
file=filename,
name="entire_file",
display=f"{filename} - name='entire_file'",
config=metafunc.config,
failed=True
)
)
LOGGER.error(f"error in parsing {filename}")
return tests_to_run


def test_httpdef(httpdeftest: HttpDefTest):
if httpdeftest.failed:
assert False, "failed to parse"
# read below arguments from command line
config = Config(
file=httpdeftest.file,
target=httpdeftest.name,
property_file=httpdeftest.config.getoption("property_file"),
env=httpdeftest.config.getoption("env"),
properties=httpdeftest.config.getoption("property"),
debug=True,
info=True,
curl=False,
no_cookie=False,
format=False,
)
comp = RequestCompiler(config)

realized_http_def_content = RunHttpFileHandler.get_http_from_req(
comp.httpdef)

logging.warning(
f"realized_http_def_content {realized_http_def_content}",
)

resp = comp.get_response()

logging.warning(f"resp={resp}")

script_result = comp.script_execution.execute_test_script(resp)

for test in script_result.tests:
# at present only one failure is reported in the test.
# if there are multiple failures, only the first one is reported
logging.warning(
f"script_result: test={test}, test_success: {test.success}, error:{test.error}",
)
assert test.success, test.error
4 changes: 3 additions & 1 deletion examples/example.http
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ POST https://httpbin.org/post
"lastName": {{lastName='Doe'}},
"age": {{age=25}},
"fullName": "{{firstName}} {{lastName}}",
"ageInDays": (25*365),
ageInDays: (25*365),
// sample without no quotes
description: sampleOneWithNoQuotesInValue
}
> {%

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dothttp-req"
version = "0.0.43a24"
version = "0.0.43a25"
description = "Dothttp is Simple http client for testing and development"
authors = ["Prasanth <[email protected]>"]
license = "MIT"
Expand All @@ -10,7 +10,7 @@ classifiers = [
"Operating System :: OS Independent",
]
exclude = ["test", "test.core", "test.extensions", "benchmarks"]
packages = [{'include'='dotextensions'}, {'include'='dothttp'}, {'include'='dothttp_req'}]
packages = [{'include'='dotextensions'}, {'include'='dothttp'}, {'include'='dothttp_req'}, {'include'='dothttp_test'}]
repository = "https://github.com/cedric05/dothttp"

[tool.poetry.urls]
Expand Down
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
addopts = --ignore=dothttp_test -s
9 changes: 7 additions & 2 deletions test/core/payload/jsonpayload2.http
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ json({
"null": null,
"bool": false,
"bool2": true,
"float":1.121212,
"float2":1
"float" : 1.121212,
"float2":1,
"number":0,
testWithNoQuotes: true,
testNoQuotesAnd_Number1: true,
testWithQuotesAndEqual = true,

})
16 changes: 14 additions & 2 deletions test/core/test_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,20 @@ def test_json_payload2(self):
)
self.assertEqual("POST", req.method, "incorrect method")
self.assertEqual(
b'{"string": "simple", "list": ["dothttp", "azure"], "null": null, "bool": false, "bool2": true, "float": 1.121212, "float2": 1}',
req.body,
{
"string": "simple",
"list": ["dothttp", "azure"],
"null": None,
"bool": False,
"bool2": True,
"float": 1.121212,
"float2": 1,
"number": 0,
"testWithNoQuotes": True,
"testNoQuotesAnd_Number1": True,
"testWithQuotesAndEqual": True
},
json.loads(req.body),
"incorrect method",
)

Expand Down

0 comments on commit 699404f

Please sign in to comment.