From 6c17ea3adaa848eca76071a12579de1619bff646 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Sat, 26 Mar 2022 11:09:32 +0800 Subject: [PATCH] change: remove har2case, move all features to go version --- .github/workflows/smoketest.yml | 2 - .github/workflows/unittest.yml | 1 - docs/CHANGELOG.md | 3 +- hrp/convert.go | 2 - hrp/internal/builtin/function.go | 1 + hrp/internal/har2case/core.go | 22 +- hrp/internal/har2case/core_test.go | 4 +- httprunner/cli.py | 17 +- httprunner/ext/har2case/__init__.py | 70 ----- httprunner/ext/har2case/core.py | 385 -------------------------- httprunner/ext/har2case/core_test.py | 180 ------------ httprunner/ext/har2case/utils.py | 130 --------- httprunner/ext/har2case/utils_test.py | 59 ---- httprunner/runner.py | 2 +- poetry.lock | 6 +- pyproject.toml | 3 +- 16 files changed, 22 insertions(+), 865 deletions(-) delete mode 100644 httprunner/ext/har2case/__init__.py delete mode 100644 httprunner/ext/har2case/core.py delete mode 100644 httprunner/ext/har2case/core_test.py delete mode 100644 httprunner/ext/har2case/utils.py delete mode 100644 httprunner/ext/har2case/utils_test.py diff --git a/.github/workflows/smoketest.yml b/.github/workflows/smoketest.yml index 28196ba4..a11870b6 100644 --- a/.github/workflows/smoketest.yml +++ b/.github/workflows/smoketest.yml @@ -36,10 +36,8 @@ jobs: - name: Test commands run: | poetry run hrun -V - poetry run har2case -h poetry run httprunner run -h poetry run httprunner startproject -h - poetry run httprunner har2case -h - name: Run smoketest - postman echo run: | poetry run hrun examples/postman_echo/request_methods diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index b87734fd..fb6e3b5d 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -33,7 +33,6 @@ jobs: poetry run httprunner poetry run hmake poetry run hrun - poetry run har2case poetry run coverage run --source=httprunner -m pytest httprunner - name: coverage report run: | diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5aca4971..37ac2752 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,7 +12,8 @@ **python version** -- change: remove locust, you should run load tests with go version +- change: remove har2case, move all features to go version, replace with `hrp run` +- change: remove locust, you should run load tests with go version, replace with `hrp boom` - change: remove fastapi and uvicorn dependencies - fix: ignore exceptions when reporting GA events - fix: remove misuse of NoReturn in Python typing diff --git a/hrp/convert.go b/hrp/convert.go index af0048b5..68297e04 100644 --- a/hrp/convert.go +++ b/hrp/convert.go @@ -140,8 +140,6 @@ func (tc *TCase) ToTestCase() (*TestCase, error) { return testCase, nil } -var ErrUnsupportedFileExt = fmt.Errorf("unsupported testcase file extension") - // APIPath implements IAPI interface. type APIPath string diff --git a/hrp/internal/builtin/function.go b/hrp/internal/builtin/function.go index ec8abf04..e271b447 100644 --- a/hrp/internal/builtin/function.go +++ b/hrp/internal/builtin/function.go @@ -231,6 +231,7 @@ func Interface2Float64(i interface{}) (float64, error) { var ErrUnsupportedFileExt = fmt.Errorf("unsupported file extension") +// LoadFile loads file content with file extension and assigns to structObj func LoadFile(path string, structObj interface{}) (err error) { log.Info().Str("path", path).Msg("load file") file, err := readFile(path) diff --git a/hrp/internal/har2case/core.go b/hrp/internal/har2case/core.go index 2848fdba..10d75f0a 100644 --- a/hrp/internal/har2case/core.go +++ b/hrp/internal/har2case/core.go @@ -29,17 +29,17 @@ func NewHAR(path string) *har { } type har struct { - path string - filterStr string - excludeStr string - profileJSON map[string]interface{} - outputDir string + path string + filterStr string + excludeStr string + profile map[string]interface{} + outputDir string } func (h *har) SetProfile(path string) { log.Info().Str("path", path).Msg("set profile") - h.profileJSON = make(map[string]interface{}) - err := builtin.LoadFile(path, h.profileJSON) + h.profile = make(map[string]interface{}) + err := builtin.LoadFile(path, h.profile) if err != nil { log.Warn().Str("path", path). Msg("invalid profile format, ignore!") @@ -145,7 +145,7 @@ func (h *har) prepareTestStep(entry *Entry) (*hrp.TStep, error) { Request: &hrp.Request{}, Validators: make([]interface{}, 0), }, - profileJSON: h.profileJSON, + profile: h.profile, } if err := step.makeRequestMethod(entry); err != nil { return nil, err @@ -173,7 +173,7 @@ func (h *har) prepareTestStep(entry *Entry) (*hrp.TStep, error) { type tStep struct { hrp.TStep - profileJSON map[string]interface{} + profile map[string]interface{} } func (s *tStep) makeRequestMethod(entry *Entry) error { @@ -201,7 +201,7 @@ func (s *tStep) makeRequestParams(entry *Entry) error { func (s *tStep) makeRequestCookies(entry *Entry) error { s.Request.Cookies = make(map[string]string) - cookies, ok := s.profileJSON["cookies"] + cookies, ok := s.profile["cookies"] if ok { // use cookies from profile cookies, ok := cookies.(map[string]interface{}) @@ -224,7 +224,7 @@ func (s *tStep) makeRequestCookies(entry *Entry) error { func (s *tStep) makeRequestHeaders(entry *Entry) error { s.Request.Headers = make(map[string]string) - headers, ok := s.profileJSON["headers"] + headers, ok := s.profile["headers"] if ok { // use headers from profile cookies, ok := headers.(map[string]interface{}) diff --git a/hrp/internal/har2case/core_test.go b/hrp/internal/har2case/core_test.go index d9953a98..93fb5ef6 100644 --- a/hrp/internal/har2case/core_test.go +++ b/hrp/internal/har2case/core_test.go @@ -58,12 +58,12 @@ func TestLoadHARWithProfile(t *testing.T) { if !assert.Equal(t, map[string]interface{}{"Content-Type": "application/x-www-form-urlencoded"}, - har.profileJSON["headers"]) { + har.profile["headers"]) { t.Fail() } if !assert.Equal(t, map[string]interface{}{"UserName": "debugtalk"}, - har.profileJSON["cookies"]) { + har.profile["cookies"]) { t.Fail() } } diff --git a/httprunner/cli.py b/httprunner/cli.py index 9041258d..033312c3 100644 --- a/httprunner/cli.py +++ b/httprunner/cli.py @@ -8,10 +8,9 @@ from loguru import logger from httprunner import __description__, __version__ from httprunner.compat import ensure_cli_args -from httprunner.ext.har2case import init_har2case_parser, main_har2case from httprunner.make import init_make_parser, main_make from httprunner.scaffold import init_parser_scaffold, main_scaffold -from httprunner.utils import init_sentry_sdk, ga_client +from httprunner.utils import ga_client, init_sentry_sdk init_sentry_sdk() @@ -67,7 +66,6 @@ def main(): subparsers = parser.add_subparsers(help="sub-command help") sub_parser_run = init_parser_run(subparsers) sub_parser_scaffold = init_parser_scaffold(subparsers) - sub_parser_har2case = init_har2case_parser(subparsers) sub_parser_make = init_make_parser(subparsers) if len(sys.argv) == 1: @@ -85,9 +83,6 @@ def main(): elif sys.argv[1] == "startproject": # httprunner startproject sub_parser_scaffold.print_help() - elif sys.argv[1] == "har2case": - # httprunner har2case - sub_parser_har2case.print_help() elif sys.argv[1] == "run": # httprunner run pytest.main(["-h"]) @@ -116,8 +111,6 @@ def main(): sys.exit(main_run(extra_args)) elif sys.argv[1] == "startproject": main_scaffold(args) - elif sys.argv[1] == "har2case": - main_har2case(args) elif sys.argv[1] == "make": main_make(args.testcase_path) @@ -150,13 +143,5 @@ def main_make_alias(): main() -def main_har2case_alias(): - """ command alias - har2case = httprunner har2case - """ - sys.argv.insert(1, "har2case") - main() - - if __name__ == "__main__": main() diff --git a/httprunner/ext/har2case/__init__.py b/httprunner/ext/har2case/__init__.py deleted file mode 100644 index 29920118..00000000 --- a/httprunner/ext/har2case/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -""" Convert HAR (HTTP Archive) to YAML/JSON testcase for HttpRunner. - -Usage: - # convert to JSON format testcase - $ hrun har2case demo.har - - # convert to YAML format testcase - $ hrun har2case demo.har -2y - -""" - -from httprunner.ext.har2case.core import HarParser -from httprunner.utils import ga_client - - -def init_har2case_parser(subparsers): - """ HAR converter: parse command line options and run commands. - """ - parser = subparsers.add_parser( - "har2case", - help="Convert HAR(HTTP Archive) to YAML/JSON testcases for HttpRunner.", - ) - parser.add_argument("har_source_file", nargs="?", help="Specify HAR source file") - parser.add_argument( - "-2y", - "--to-yml", - "--to-yaml", - dest="to_yaml", - action="store_true", - help="Convert to YAML format, if not specified, convert to pytest format by default.", - ) - parser.add_argument( - "-2j", - "--to-json", - dest="to_json", - action="store_true", - help="Convert to JSON format, if not specified, convert to pytest format by default.", - ) - parser.add_argument( - "--filter", - help="Specify filter keyword, only url include filter string will be converted.", - ) - parser.add_argument( - "--exclude", - help="Specify exclude keyword, url that includes exclude string will be ignored, " - "multiple keywords can be joined with '|'", - ) - parser.add_argument( - "--profile", - dest="profile", - help="Specify yaml file to overwrite headers and cookies in HAR.", - ) - - return parser - - -def main_har2case(args): - har_source_file = args.har_source_file - - if args.to_yaml: - output_file_type = "YAML" - elif args.to_json: - output_file_type = "JSON" - else: - output_file_type = "pytest" - - ga_client.track_event("ConvertTests", f"har2case {output_file_type}") - HarParser(har_source_file, args.filter, args.exclude, args.profile).gen_testcase(output_file_type) - - return 0 diff --git a/httprunner/ext/har2case/core.py b/httprunner/ext/har2case/core.py deleted file mode 100644 index efff6a4c..00000000 --- a/httprunner/ext/har2case/core.py +++ /dev/null @@ -1,385 +0,0 @@ -import base64 -import json -import os -import sys -import urllib.parse as urlparse -from typing import Text - -from httprunner.compat import ensure_path_sep -from loguru import logger -from sentry_sdk import capture_exception - -from httprunner.ext.har2case import utils -from httprunner.make import make_testcase, format_pytest_with_black -from httprunner.loader import load_test_file - -try: - from json.decoder import JSONDecodeError -except ImportError: - JSONDecodeError = ValueError - - -def ensure_file_path(path: Text) -> Text: - - if not path or not path.endswith(".har"): - logger.error("HAR file not specified.") - sys.exit(1) - - path = ensure_path_sep(path) - if not os.path.isfile(path): - logger.error(f"HAR file not exists: {path}") - sys.exit(1) - - if not os.path.isabs(path): - path = os.path.join(os.getcwd(), path) - - return path - - -class HarParser(object): - def __init__(self, har_file_path, filter_str=None, exclude_str=None, profile=None): - self.har_file_path = ensure_file_path(har_file_path) - self.filter_str = filter_str - self.exclude_str = exclude_str or "" - self.profile = profile and load_test_file(profile) - - def __make_request_url(self, teststep_dict, entry_json): - """ parse HAR entry request url and queryString, and make teststep url and params - - Args: - entry_json (dict): - { - "request": { - "url": "https://httprunner.top/home?v=1&w=2", - "queryString": [ - {"name": "v", "value": "1"}, - {"name": "w", "value": "2"} - ], - }, - "response": {} - } - - Returns: - { - "name: "/home", - "request": { - url: "https://httprunner.top/home", - params: {"v": "1", "w": "2"} - } - } - - """ - request_params = utils.convert_list_to_dict( - entry_json["request"].get("queryString", []) - ) - - url = entry_json["request"].get("url") - if not url: - logger.exception("url missed in request.") - sys.exit(1) - - parsed_object = urlparse.urlparse(url) - if request_params: - parsed_object = parsed_object._replace(query="") - teststep_dict["request"]["url"] = parsed_object.geturl() - teststep_dict["request"]["params"] = request_params - else: - teststep_dict["request"]["url"] = url - - teststep_dict["name"] = parsed_object.path - - def __make_request_method(self, teststep_dict, entry_json): - """ parse HAR entry request method, and make teststep method. - """ - method = entry_json["request"].get("method") - if not method: - logger.exception("method missed in request.") - sys.exit(1) - - teststep_dict["request"]["method"] = method - - def __make_request_cookies(self, teststep_dict, entry_json): - if self.profile and self.profile.get("cookies"): - teststep_dict["request"]["cookies"] = self.profile.get("cookies") - else: - cookies = {} - for cookie in entry_json["request"].get("cookies", []): - cookies[cookie["name"]] = cookie["value"] - - if cookies: - teststep_dict["request"]["cookies"] = cookies - - def __make_request_headers(self, teststep_dict, entry_json): - """ parse HAR entry request headers, and make teststep headers. - header in IGNORE_REQUEST_HEADERS will be ignored. - - Args: - entry_json (dict): - { - "request": { - "headers": [ - {"name": "Host", "value": "httprunner.top"}, - {"name": "Content-Type", "value": "application/json"}, - {"name": "User-Agent", "value": "iOS/10.3"} - ], - }, - "response": {} - } - - Returns: - { - "request": { - headers: {"Content-Type": "application/json"} - } - - """ - if self.profile and self.profile.get("headers"): - teststep_dict["request"]["headers"] = self.profile.get("headers") - else: - teststep_headers = {} - for header in entry_json["request"].get("headers", []): - if header["name"] == "cookie" or header["name"].startswith(":"): - continue - - teststep_headers[header["name"]] = header["value"] - - if teststep_headers: - teststep_dict["request"]["headers"] = teststep_headers - - def _make_request_data(self, teststep_dict, entry_json): - """ parse HAR entry request data, and make teststep request data - - Args: - entry_json (dict): - { - "request": { - "method": "POST", - "postData": { - "mimeType": "application/x-www-form-urlencoded; charset=utf-8", - "params": [ - {"name": "a", "value": 1}, - {"name": "b", "value": "2"} - } - }, - }, - "response": {...} - } - - - Returns: - { - "request": { - "method": "POST", - "data": {"v": "1", "w": "2"} - } - } - - """ - method = entry_json["request"].get("method") - if method in ["POST", "PUT", "PATCH"]: - postData = entry_json["request"].get("postData", {}) - mimeType = postData.get("mimeType") - - # Note that text and params fields are mutually exclusive. - if "text" in postData: - post_data = postData.get("text") - else: - params = postData.get("params", []) - post_data = utils.convert_list_to_dict(params) - - request_data_key = "data" - if not mimeType: - pass - elif mimeType.startswith("application/json"): - try: - post_data = json.loads(post_data) - request_data_key = "json" - except JSONDecodeError: - pass - elif mimeType.startswith("application/x-www-form-urlencoded"): - post_data = utils.convert_x_www_form_urlencoded_to_dict(post_data) - else: - # TODO: make compatible with more mimeType - pass - - teststep_dict["request"][request_data_key] = post_data - - def _make_validate(self, teststep_dict, entry_json): - """ parse HAR entry response and make teststep validate. - - Args: - entry_json (dict): - { - "request": {}, - "response": { - "status": 200, - "headers": [ - { - "name": "Content-Type", - "value": "application/json; charset=utf-8" - }, - ], - "content": { - "size": 71, - "mimeType": "application/json; charset=utf-8", - "text": "eyJJc1N1Y2Nlc3MiOnRydWUsIkNvZGUiOjIwMCwiTWVzc2FnZSI6bnVsbCwiVmFsdWUiOnsiQmxuUmVzdWx0Ijp0cnVlfX0=", - "encoding": "base64" - } - } - } - - Returns: - { - "validate": [ - {"eq": ["status_code", 200]} - ] - } - - """ - teststep_dict["validate"].append( - {"eq": ["status_code", entry_json["response"].get("status")]} - ) - - resp_content_dict = entry_json["response"].get("content") - - headers_mapping = utils.convert_list_to_dict( - entry_json["response"].get("headers", []) - ) - if "Content-Type" in headers_mapping: - teststep_dict["validate"].append( - {"eq": ["headers.Content-Type", headers_mapping["Content-Type"]]} - ) - - text = resp_content_dict.get("text") - if not text: - return - - mime_type = resp_content_dict.get("mimeType") - if mime_type and mime_type.startswith("application/json"): - - encoding = resp_content_dict.get("encoding") - if encoding and encoding == "base64": - content = base64.b64decode(text) - try: - content = content.decode("utf-8") - except UnicodeDecodeError: - logger.warning(f"failed to decode base64 content with utf-8 !") - return - else: - content = text - - try: - resp_content_json = json.loads(content) - except JSONDecodeError: - logger.warning(f"response content can not be loaded as json: {content}") - return - - if not isinstance(resp_content_json, dict): - # e.g. ['a', 'b'] - return - - for key, value in resp_content_json.items(): - if isinstance(value, (dict, list)): - continue - - teststep_dict["validate"].append({"eq": ["body.{}".format(key), value]}) - - def _prepare_teststep(self, entry_json): - """ extract info from entry dict and make teststep - - Args: - entry_json (dict): - { - "request": { - "method": "POST", - "url": "https://httprunner.top/api/v1/Account/Login", - "headers": [], - "queryString": [], - "postData": {}, - }, - "response": { - "status": 200, - "headers": [], - "content": {} - } - } - - """ - teststep_dict = {"name": "", "request": {}, "validate": []} - - self.__make_request_url(teststep_dict, entry_json) - self.__make_request_method(teststep_dict, entry_json) - self.__make_request_cookies(teststep_dict, entry_json) - self.__make_request_headers(teststep_dict, entry_json) - self._make_request_data(teststep_dict, entry_json) - self._make_validate(teststep_dict, entry_json) - - return teststep_dict - - def _prepare_config(self): - """ prepare config block. - """ - return {"name": "testcase description", "variables": {}, "verify": False} - - def _prepare_teststeps(self): - """ make teststep list. - teststeps list are parsed from HAR log entries list. - - """ - - def is_exclude(url, exclude_str): - exclude_str_list = exclude_str.split("|") - for exclude_str in exclude_str_list: - if exclude_str and exclude_str in url: - return True - - return False - - teststeps = [] - log_entries = utils.load_har_log_entries(self.har_file_path) - for entry_json in log_entries: - url = entry_json["request"].get("url") - if self.filter_str and self.filter_str not in url: - continue - - if is_exclude(url, self.exclude_str): - continue - - teststeps.append(self._prepare_teststep(entry_json)) - - return teststeps - - def _make_testcase(self): - """ Extract info from HAR file and prepare for testcase - """ - logger.info("Extract info from HAR file and prepare for testcase.") - - config = self._prepare_config() - teststeps = self._prepare_teststeps() - - testcase = {"config": config, "teststeps": teststeps} - return testcase - - def gen_testcase(self, file_type="pytest"): - logger.info(f"Start to generate testcase from {self.har_file_path}") - harfile = os.path.splitext(self.har_file_path)[0] - - try: - testcase = self._make_testcase() - except Exception as ex: - capture_exception(ex) - raise - - if file_type == "JSON": - output_testcase_file = f"{harfile}.json" - utils.dump_json(testcase, output_testcase_file) - elif file_type == "YAML": - output_testcase_file = f"{harfile}.yml" - utils.dump_yaml(testcase, output_testcase_file) - else: - # default to generate pytest file - testcase["config"]["path"] = self.har_file_path - output_testcase_file = make_testcase(testcase) - format_pytest_with_black(output_testcase_file) - - logger.info(f"generated testcase: {output_testcase_file}") diff --git a/httprunner/ext/har2case/core_test.py b/httprunner/ext/har2case/core_test.py deleted file mode 100644 index 0c39df1c..00000000 --- a/httprunner/ext/har2case/core_test.py +++ /dev/null @@ -1,180 +0,0 @@ -import os - -from httprunner.ext.har2case.core import HarParser -from httprunner.ext.har2case.utils import load_har_log_entries -from httprunner.ext.har2case.utils_test import TestHar2CaseUtils - - -class TestHar(TestHar2CaseUtils): - def setUp(self): - self.data_dir = os.path.join(os.getcwd(), "examples", "data", "har2case") - self.har_path = os.path.join(self.data_dir, "demo.har") - self.har_parser = HarParser(self.har_path) - self.profile_path = os.path.join(self.data_dir, "profile.yml") - - def test_prepare_teststep(self): - log_entries = load_har_log_entries(self.har_path) - teststep_dict = self.har_parser._prepare_teststep(log_entries[0]) - self.assertIn("name", teststep_dict) - self.assertIn("request", teststep_dict) - self.assertIn("validate", teststep_dict) - - validators_mapping = { - validator["eq"][0]: validator["eq"][1] - for validator in teststep_dict["validate"] - } - self.assertEqual(validators_mapping["status_code"], 200) - self.assertEqual(validators_mapping["body.IsSuccess"], True) - self.assertEqual(validators_mapping["body.Code"], 200) - self.assertEqual(validators_mapping["body.Message"], None) - - def test_prepare_teststeps(self): - teststeps = self.har_parser._prepare_teststeps() - self.assertIsInstance(teststeps, list) - self.assertIn("name", teststeps[0]) - self.assertIn("request", teststeps[0]) - self.assertIn("validate", teststeps[0]) - - def test_gen_testcase_yaml(self): - yaml_file = os.path.join(self.data_dir, "demo.yml") - - self.har_parser.gen_testcase(file_type="YAML") - self.assertTrue(os.path.isfile(yaml_file)) - os.remove(yaml_file) - - def test_gen_testcase_json(self): - json_file = os.path.join(self.data_dir, "demo.json") - - self.har_parser.gen_testcase(file_type="JSON") - self.assertTrue(os.path.isfile(json_file)) - os.remove(json_file) - - def test_profile(self): - har_parser = HarParser(self.har_path, profile=self.profile_path) - teststeps = har_parser._prepare_teststeps() - self.assertEqual( - teststeps[0]["request"]["headers"], - {"Content-Type": "application/x-www-form-urlencoded"}, - ) - self.assertEqual( - teststeps[0]["request"]["cookies"], - {"CASTGC": "TGT"}, - ) - - def test_filter(self): - filter_str = "httprunner" - har_parser = HarParser(self.har_path, filter_str) - teststeps = har_parser._prepare_teststeps() - self.assertEqual( - teststeps[0]["request"]["url"], - "https://httprunner.top/api/v1/Account/Login", - ) - - filter_str = "debugtalk" - har_parser = HarParser(self.har_path, filter_str) - teststeps = har_parser._prepare_teststeps() - self.assertEqual(teststeps, []) - - def test_exclude(self): - exclude_str = "debugtalk" - har_parser = HarParser(self.har_path, exclude_str=exclude_str) - teststeps = har_parser._prepare_teststeps() - self.assertEqual( - teststeps[0]["request"]["url"], - "https://httprunner.top/api/v1/Account/Login", - ) - - exclude_str = "httprunner" - har_parser = HarParser(self.har_path, exclude_str=exclude_str) - teststeps = har_parser._prepare_teststeps() - self.assertEqual(teststeps, []) - - def test_exclude_multiple(self): - exclude_str = "httprunner|v2" - har_parser = HarParser(self.har_path, exclude_str=exclude_str) - teststeps = har_parser._prepare_teststeps() - self.assertEqual(teststeps, []) - - exclude_str = "http2|v1" - har_parser = HarParser(self.har_path, exclude_str=exclude_str) - teststeps = har_parser._prepare_teststeps() - self.assertEqual(teststeps, []) - - def test_make_request_data_params(self): - testcase_dict = {"name": "", "request": {}, "validate": []} - entry_json = { - "request": { - "method": "POST", - "postData": { - "mimeType": "application/x-www-form-urlencoded; charset=utf-8", - "params": [{"name": "a", "value": 1}, {"name": "b", "value": "2"}], - }, - } - } - self.har_parser._make_request_data(testcase_dict, entry_json) - self.assertEqual(testcase_dict["request"]["data"]["a"], 1) - self.assertEqual(testcase_dict["request"]["data"]["b"], "2") - - def test_make_request_data_json(self): - testcase_dict = {"name": "", "request": {}, "validate": []} - entry_json = { - "request": { - "method": "POST", - "postData": { - "mimeType": "application/json; charset=utf-8", - "text": '{"a":"1","b":"2"}', - }, - } - } - self.har_parser._make_request_data(testcase_dict, entry_json) - self.assertEqual(testcase_dict["request"]["json"], {"a": "1", "b": "2"}) - - def test_make_request_data_text_empty(self): - testcase_dict = {"name": "", "request": {}, "validate": []} - entry_json = { - "request": { - "method": "POST", - "postData": {"mimeType": "application/json; charset=utf-8", "text": ""}, - } - } - self.har_parser._make_request_data(testcase_dict, entry_json) - self.assertEqual(testcase_dict["request"]["data"], "") - - def test_make_validate(self): - testcase_dict = {"name": "", "request": {}, "validate": []} - entry_json = { - "request": {}, - "response": { - "status": 200, - "headers": [ - { - "name": "Content-Type", - "value": "application/json; charset=utf-8", - }, - ], - "content": { - "size": 71, - "mimeType": "application/json; charset=utf-8", - # raw response content text is application/jose type - "text": "ZXlKaGJHY2lPaUpTVTBFeFh6VWlMQ0psYm1NaU9pSkJNVEk0UTBKRExV", - "encoding": "base64", - }, - }, - } - self.har_parser._make_validate(testcase_dict, entry_json) - self.assertEqual(testcase_dict["validate"][0], {"eq": ["status_code", 200]}) - self.assertEqual( - testcase_dict["validate"][1], - {"eq": ["headers.Content-Type", "application/json; charset=utf-8"]}, - ) - - def test_make_testcase(self): - har_path = os.path.join( - self.data_dir, "demo-quickstart.har" - ) - har_parser = HarParser(har_path) - testcase = har_parser._make_testcase() - self.assertIsInstance(testcase, dict) - self.assertIn("config", testcase) - self.assertIn("teststeps", testcase) - self.assertEqual(len(testcase["teststeps"]), 2) diff --git a/httprunner/ext/har2case/utils.py b/httprunner/ext/har2case/utils.py deleted file mode 100644 index ead83b30..00000000 --- a/httprunner/ext/har2case/utils.py +++ /dev/null @@ -1,130 +0,0 @@ -import json -import sys -from json.decoder import JSONDecodeError -from urllib.parse import unquote - -import yaml -from loguru import logger - - -def load_har_log_entries(file_path): - """ load HAR file and return log entries list - - Args: - file_path (str) - - Returns: - list: entries - [ - { - "request": {}, - "response": {} - }, - { - "request": {}, - "response": {} - } - ] - - """ - with open(file_path, mode="rb") as f: - try: - content_json = json.load(f) - return content_json["log"]["entries"] - except (TypeError, JSONDecodeError) as ex: - logger.error(f"failed to load HAR file {file_path}: {ex}") - sys.exit(1) - except KeyError: - logger.error(f"log entries not found in HAR file: {content_json}") - sys.exit(1) - - -def x_www_form_urlencoded(post_data): - """ convert origin dict to x-www-form-urlencoded - - Args: - post_data (dict): - {"a": 1, "b":2} - - Returns: - str: - a=1&b=2 - - """ - if isinstance(post_data, dict): - return "&".join( - ["{}={}".format(key, value) for key, value in post_data.items()] - ) - else: - return post_data - - -def convert_x_www_form_urlencoded_to_dict(post_data): - """ convert x_www_form_urlencoded data to dict - - Args: - post_data (str): a=1&b=2 - - Returns: - dict: {"a":1, "b":2} - - """ - if isinstance(post_data, str): - converted_dict = {} - for k_v in post_data.split("&"): - try: - key, value = k_v.split("=") - except ValueError: - raise Exception( - "Invalid x_www_form_urlencoded data format: {}".format(post_data) - ) - converted_dict[key] = unquote(value) - return converted_dict - else: - return post_data - - -def convert_list_to_dict(origin_list): - """ convert HAR data list to mapping - - Args: - origin_list (list) - [ - {"name": "v", "value": "1"}, - {"name": "w", "value": "2"} - ] - - Returns: - dict: - {"v": "1", "w": "2"} - - """ - return {item["name"]: item.get("value") for item in origin_list} - - -def dump_yaml(testcase, yaml_file): - """ dump HAR entries to yaml testcase - """ - logger.info("dump testcase to YAML format.") - - with open(yaml_file, "w", encoding="utf-8") as outfile: - yaml.dump( - testcase, outfile, allow_unicode=True, default_flow_style=False, indent=4 - ) - - logger.info("Generate YAML testcase successfully: {}".format(yaml_file)) - - -def dump_json(testcase, json_file): - """ dump HAR entries to json testcase - """ - logger.info("dump testcase to JSON format.") - - with open(json_file, "w", encoding="utf-8") as outfile: - my_json_str = json.dumps(testcase, ensure_ascii=False, indent=4) - if isinstance(my_json_str, bytes): - my_json_str = my_json_str.decode("utf-8") - - outfile.write(my_json_str) - - logger.info("Generate JSON testcase successfully: {}".format(json_file)) diff --git a/httprunner/ext/har2case/utils_test.py b/httprunner/ext/har2case/utils_test.py deleted file mode 100644 index 6c414adc..00000000 --- a/httprunner/ext/har2case/utils_test.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -import os -import unittest - -from httprunner.ext.har2case import utils - - -class TestHar2CaseUtils(unittest.TestCase): - - data_dir = os.path.join(os.getcwd(), "examples", "data", "har2case") - - @staticmethod - def create_har_file(file_name, content): - file_path = os.path.join( - TestHar2CaseUtils.data_dir, "{}.har".format(file_name) - ) - with open(file_path, "w") as f: - f.write(json.dumps(content)) - - return file_path - - def test_load_har_log_entries(self): - har_path = os.path.join(TestHar2CaseUtils.data_dir, "demo.har") - log_entries = utils.load_har_log_entries(har_path) - self.assertIsInstance(log_entries, list) - self.assertIn("request", log_entries[0]) - self.assertIn("response", log_entries[0]) - - def test_load_har_log_key_error(self): - empty_json_file_path = TestHar2CaseUtils.create_har_file( - file_name="empty_json", content={} - ) - with self.assertRaises(SystemExit): - utils.load_har_log_entries(empty_json_file_path) - os.remove(empty_json_file_path) - - def test_load_har_log_empty_error(self): - empty_file_path = TestHar2CaseUtils.create_har_file( - file_name="empty", content="" - ) - with self.assertRaises(SystemExit): - utils.load_har_log_entries(empty_file_path) - os.remove(empty_file_path) - - # def test_x_www_form_urlencoded(self): - # origin_dict = {"a":1, "b": "2"} - # self.assertIn("a=1", utils.x_www_form_urlencoded(origin_dict)) - # self.assertIn("b=2", utils.x_www_form_urlencoded(origin_dict)) - - def test_convert_list_to_dict(self): - origin_list = [{"name": "v", "value": "1"}, {"name": "w", "value": "2"}] - self.assertEqual(utils.convert_list_to_dict(origin_list), {"v": "1", "w": "2"}) - - def test_convert_x_www_form_urlencoded_to_dict(self): - origin_str = "a=1&b=2" - converted_dict = utils.convert_x_www_form_urlencoded_to_dict(origin_str) - self.assertIsInstance(converted_dict, dict) - self.assertEqual(converted_dict["a"], "1") - self.assertEqual(converted_dict["b"], "2") diff --git a/httprunner/runner.py b/httprunner/runner.py index a4b5ec4c..3c5600ad 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -138,7 +138,7 @@ class HttpRunner(object): # parse functions = self.__project_meta.functions - # prepare_upload_step(step, functions) + prepare_upload_step(step, functions) request_dict = step.request.dict() request_dict.pop("upload", None) parsed_request_dict = parse_data( diff --git a/poetry.lock b/poetry.lock index 8a5e31a4..7b9eea4b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -252,7 +252,7 @@ reference = "tsinghua" [[package]] name = "jinja2" -version = "3.1.0" +version = "3.1.1" description = "A very fast and expressive template engine." category = "main" optional = false @@ -835,8 +835,8 @@ iniconfig = [ {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] jinja2 = [ - {file = "Jinja2-3.1.0-py3-none-any.whl", hash = "sha256:da424924c069a4013730d8dd010cbecac7e7bb752be388db3741688bffb48dc6"}, - {file = "Jinja2-3.1.0.tar.gz", hash = "sha256:a2f09a92f358b96b5f6ca6ecb4502669c4acb55d8733bbb2b2c9c4af5564c605"}, + {file = "Jinja2-3.1.1-py3-none-any.whl", hash = "sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119"}, + {file = "Jinja2-3.1.1.tar.gz", hash = "sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"}, ] jmespath = [ {file = "jmespath-0.9.5-py2.py3-none-any.whl", hash = "sha256:695cb76fa78a10663425d5b73ddc5714eb711157e52704d69be03b1a02ba4fec"}, diff --git a/pyproject.toml b/pyproject.toml index bb3973c5..745deb07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/httprunner/httprunner" repository = "https://github.com/httprunner/httprunner" documentation = "https://httprunner.com/docs" -keywords = ["HTTP", "apitest", "perftest", "DEM", "requests", "locustio"] +keywords = ["HTTP", "apitest", "perftest", "requests"] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -56,7 +56,6 @@ coverage = "^4.5.4" httprunner = "httprunner.cli:main" hrun = "httprunner.cli:main_hrun_alias" hmake = "httprunner.cli:main_make_alias" -har2case = "httprunner.cli:main_har2case_alias" [build-system] requires = ["poetry>=1.0.0"]