fix #1220: parse step url with base url

This commit is contained in:
debugtalk
2022-04-05 13:13:12 +08:00
parent 1aefc9f698
commit 19695e76ff
4 changed files with 86 additions and 21 deletions

View File

@@ -4,6 +4,7 @@ import (
builtinJSON "encoding/json" builtinJSON "encoding/json"
"fmt" "fmt"
"net/url" "net/url"
"path"
"reflect" "reflect"
"regexp" "regexp"
"strings" "strings"
@@ -26,18 +27,29 @@ type Parser struct {
} }
func buildURL(baseURL, stepURL string) string { func buildURL(baseURL, stepURL string) string {
uConfig, err := url.Parse(baseURL) uStep, err := url.Parse(stepURL)
if err != nil { if err != nil {
log.Error().Str("baseURL", baseURL).Err(err).Msg("[buildURL] parse baseURL failed") log.Error().Str("stepURL", stepURL).Err(err).Msg("[buildURL] parse url failed")
return "" return ""
} }
uStep, err := uConfig.Parse(stepURL) // step url is absolute url
if uStep.Host != "" {
return stepURL
}
// step url is relative, based on base url
uConfig, err := url.Parse(baseURL)
if err != nil { if err != nil {
log.Error().Str("stepURL", stepURL).Err(err).Msg("[buildURL] parse stepURL failed") log.Error().Str("baseURL", baseURL).Err(err).Msg("[buildURL] parse url failed")
return "" return ""
} }
// merge url
uStep.Scheme = uConfig.Scheme
uStep.Host = uConfig.Host
uStep.Path = path.Join(uConfig.Path, uStep.Path)
// base url missed // base url missed
return uStep.String() return uStep.String()
} }

View File

@@ -11,25 +11,44 @@ import (
func TestBuildURL(t *testing.T) { func TestBuildURL(t *testing.T) {
var url string var url string
url = buildURL("https://postman-echo.com", "/get") url = buildURL("https://postman-echo.com", "/get")
if url != "https://postman-echo.com/get" { if !assert.Equal(t, url, "https://postman-echo.com/get") {
t.Fatalf("buildURL error, %s != 'https://postman-echo.com/get'", url) t.Fail()
}
url = buildURL("https://postman-echo.com", "get")
if !assert.Equal(t, url, "https://postman-echo.com/get") {
t.Fail()
}
url = buildURL("https://postman-echo.com/", "/get")
if !assert.Equal(t, url, "https://postman-echo.com/get") {
t.Fail()
} }
url = buildURL("https://postman-echo.com/abc/", "/get?a=1&b=2") url = buildURL("https://postman-echo.com/abc/", "/get?a=1&b=2")
if url != "https://postman-echo.com/get?a=1&b=2" { if !assert.Equal(t, url, "https://postman-echo.com/abc/get?a=1&b=2") {
t.Fatalf("buildURL error, %s != 'https://postman-echo.com/get'", url) t.Fail()
}
url = buildURL("https://postman-echo.com/abc", "get?a=1&b=2")
if !assert.Equal(t, url, "https://postman-echo.com/abc/get?a=1&b=2") {
t.Fail()
}
// omit query string in base url
url = buildURL("https://postman-echo.com/abc?x=6&y=9", "/get?a=1&b=2")
if !assert.Equal(t, url, "https://postman-echo.com/abc/get?a=1&b=2") {
t.Fail()
} }
url = buildURL("", "https://postman-echo.com/get") url = buildURL("", "https://postman-echo.com/get")
if url != "https://postman-echo.com/get" { if !assert.Equal(t, url, "https://postman-echo.com/get") {
t.Fatalf("buildURL error, %s != 'https://postman-echo.com/get'", url) t.Fail()
} }
// notice: step request url > config base url // notice: step request url > config base url
url = buildURL("https://postman-echo.com", "https://httpbin.org/get") url = buildURL("https://postman-echo.com", "https://httpbin.org/get")
if url != "https://httpbin.org/get" { if !assert.Equal(t, url, "https://httpbin.org/get") {
t.Fatalf("buildURL error, %s != 'https://httpbin.org/get'", url) t.Fail()
} }
} }

View File

@@ -3,6 +3,7 @@ import builtins
import os import os
import re import re
from typing import Any, Callable, Dict, List, Set, Text from typing import Any, Callable, Dict, List, Set, Text
from urllib.parse import urljoin, urlparse
from loguru import logger from loguru import logger
from sentry_sdk import capture_exception from sentry_sdk import capture_exception
@@ -10,8 +11,6 @@ from sentry_sdk import capture_exception
from httprunner import exceptions, loader, utils from httprunner import exceptions, loader, utils
from httprunner.models import FunctionsMapping, VariablesMapping from httprunner.models import FunctionsMapping, VariablesMapping
absolute_http_url_regexp = re.compile(r"^https?://", re.I)
# use $$ to escape $ notation # use $$ to escape $ notation
dolloar_regex_compile = re.compile(r"\$\$") dolloar_regex_compile = re.compile(r"\$\$")
# variable notation, e.g. ${var} or $var # variable notation, e.g. ${var} or $var
@@ -37,15 +36,25 @@ def parse_string_value(str_value: Text) -> Any:
return str_value return str_value
def build_url(base_url, path): def build_url(base_url, step_url):
""" prepend url with base_url unless it's already an absolute URL """ """ prepend url with base_url unless it's already an absolute URL """
if absolute_http_url_regexp.match(path): o_step_url = urlparse(step_url)
return path if o_step_url.netloc != "":
elif base_url: # step url is absolute url
return "{}/{}".format(base_url.rstrip("/"), path.lstrip("/")) return step_url
else:
# step url is relative, based on base url
o_base_url = urlparse(base_url)
if o_base_url.netloc == "":
# missed base url
raise exceptions.ParamsError("base url missed!") raise exceptions.ParamsError("base url missed!")
path = o_base_url.path.rstrip("/") + "/" + o_step_url.path.lstrip("/")
o_step_url = o_step_url._replace(scheme=o_base_url.scheme) \
._replace(netloc=o_base_url.netloc) \
._replace(path=path)
return o_step_url.geturl()
def regex_findall_variables(raw_string: Text) -> List[Text]: def regex_findall_variables(raw_string: Text) -> List[Text]:
""" extract all variable names from content, which is in format $variable """ extract all variable names from content, which is in format $variable

View File

@@ -3,11 +3,36 @@ import time
import unittest import unittest
from httprunner import parser from httprunner import parser
from httprunner.exceptions import VariableNotFound, FunctionNotFound from httprunner.exceptions import FunctionNotFound, VariableNotFound
from httprunner.loader import load_project_meta from httprunner.loader import load_project_meta
class TestParserBasic(unittest.TestCase): class TestParserBasic(unittest.TestCase):
def test_build_url(self):
url = parser.build_url("https://postman-echo.com", "/get")
self.assertEqual(url, "https://postman-echo.com/get")
url = parser.build_url("https://postman-echo.com", "get")
self.assertEqual(url, "https://postman-echo.com/get")
url = parser.build_url("https://postman-echo.com/", "/get")
self.assertEqual(url, "https://postman-echo.com/get")
url = parser.build_url("https://postman-echo.com/abc/", "/get?a=1&b=2")
self.assertEqual(url, "https://postman-echo.com/abc/get?a=1&b=2")
url = parser.build_url("https://postman-echo.com/abc/", "get?a=1&b=2")
self.assertEqual(url, "https://postman-echo.com/abc/get?a=1&b=2")
# omit query string in base url
url = parser.build_url("https://postman-echo.com/abc?x=6&y=9", "/get?a=1&b=2")
self.assertEqual(url, "https://postman-echo.com/abc/get?a=1&b=2")
url = parser.build_url("", "https://postman-echo.com/get")
self.assertEqual(url, "https://postman-echo.com/get")
# notice: step request url > config base url
url = parser.build_url("https://postman-echo.com", "https://httpbin.org/get")
self.assertEqual(url, "https://httpbin.org/get")
def test_parse_variables_mapping(self): def test_parse_variables_mapping(self):
variables = {"varA": "$varB", "varB": "$varC", "varC": "123", "a": 1, "b": 2} variables = {"varA": "$varB", "varB": "$varC", "varC": "123", "a": 1, "b": 2}
parsed_variables = parser.parse_variables_mapping(variables) parsed_variables = parser.parse_variables_mapping(variables)