Repository: cloudwego/hertz
Pull request #1108 (2024-05-10 04:40:03)

test: Increase pkg/common/utils coverage to more than 93%
#### What type of PR is this?
test
<!--
Add one of the following kinds:

build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
docs: Documentation only changes
feat: A new feature
optimize: A new optimization
fix: A bug fix
perf: A code change that improves performance
refactor: A code change that neither fixes a bug nor adds a feature
style: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc)
test: Adding missing tests or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
-->

#### Check the PR title.
<!--
The description of the title will be attached in Release Notes, 
so please describe it from user-oriented, what this PR does / why we need it.
Please check your PR title with the below requirements:
-->
- [x] This PR title match the format: \<type\>(optional scope): \<description\>
- [x] The description of this PR title is user-oriented and clear enough for others to understand.
- [ ] Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)


#### (Optional) Translate the PR title into Chinese.
提高对pkg/common/utils的覆盖率至93%以上

#### (Optional) More detailed description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review(e.g., it's recommended to provide perf data if this is a perf type PR).
-->
en:Increased unit test coverage for pkg/common/utils from 91.1% to 93.3%
zh(optional):提高对pkg/common/utils的单元测试，覆盖率从91.1%提高到了93.3%

#### (Optional) Which issue(s) this PR fixes:
<!--
Automatically closes linked issue when PR is merged.
Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->

#### (Optional) The PR that updates user documentation:
<!--
If the current PR requires user awareness at the usage level, please submit a PR to update user docs. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)
-->

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cloudwego/hertz/pull/1108?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 82.89%. Comparing base [(`9162dd1`)](https://app.codecov.io/gh/cloudwego/hertz/commit/9162dd1c04f5a957fc51d23d95b8c79855f2d567?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) to head [(`760f2b5`)](https://app.codecov.io/gh/cloudwego/hertz/commit/760f2b588cbd7af66e7e01aa649efe967c965927?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).
> Report is 8 commits behind head on develop.


<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop    #1108      +/-   ##
===========================================
+ Coverage    82.75%   82.89%   +0.14%     
===========================================
  Files           98       98              
  Lines        10044    10062      +18     
===========================================
+ Hits          8312     8341      +29     
+ Misses        1241     1232       -9     
+ Partials       491      489       -2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cloudwego/hertz/pull/1108?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).


Patch:
diff --git a/cmd/hz/generator/handler.go b/cmd/hz/generator/handler.go
index eeab0cf..6b353c6 100644
--- a/cmd/hz/generator/handler.go
+++ b/cmd/hz/generator/handler.go
@@ -97,7 +97,7 @@ func (pkgGen *HttpPackageGenerator) genHandler(pkg *HttpPackage, handlerDir, han
 			tmpHandlerPackage := handlerPackage
 			if len(s.ServiceGenDir) != 0 {
 				tmpHandlerDir = s.ServiceGenDir
-				tmpHandlerPackage = util.SubPackage(pkgGen.ProjPackage, tmpHandlerDir)
+				tmpHandlerPackage = util.SubPackage(pkgGen.ProjPackage, strings.TrimPrefix(tmpHandlerDir, "/"))
 			}
 			handler = Handler{
 				FilePath:    filepath.Join(tmpHandlerDir, util.ToSnakeCase(s.Name)+".go"),
diff --git a/cmd/hz/generator/router.go b/cmd/hz/generator/router.go
index 37366ea..c5e5cbb 100644
--- a/cmd/hz/generator/router.go
+++ b/cmd/hz/generator/router.go
@@ -225,6 +225,11 @@ func (routerNode *RouterNode) Insert(name string, method *HttpMethod, handlerTyp
 				method.RefPackageAlias = c.HandlerPackageAlias
 			} else { // generate handler by service
 				c.Handler = handlerType + "." + method.Name
+				if len(method.RefPackage) != 0 {
+					c.Handler = method.RefPackageAlias + "." + method.Name
+					c.HandlerPackageAlias = method.RefPackageAlias
+					c.HandlerPackage = method.RefPackage
+				}
 			}
 			c.HttpMethod = getHttpMethod(method.HTTPMethod)
 		}
@@ -409,15 +414,15 @@ func (pkgGen *HttpPackageGenerator) genRouter(pkg *HttpPackage, root *RouterNode
 		Router: root,
 	}
 
-	if pkgGen.HandlerByMethod {
-		handlerMap := make(map[string]string, 1)
-		hook := func(layer int, node *RouterNode) error {
-			if len(node.HandlerPackage) != 0 {
-				handlerMap[node.HandlerPackageAlias] = node.HandlerPackage
-			}
-			return nil
+	handlerMap := make(map[string]string)
+	hook := func(layer int, node *RouterNode) error {
+		if len(node.HandlerPackage) != 0 {
+			handlerMap[node.HandlerPackageAlias] = node.HandlerPackage
 		}
-		root.DFS(0, hook)
+		return nil
+	}
+	root.DFS(0, hook)
+	if len(handlerMap) != 0 {
 		router.HandlerPackages = handlerMap
 	}
 
diff --git a/pkg/protocol/request.go b/pkg/protocol/request.go
index d047318..8e4b40b 100644
--- a/pkg/protocol/request.go
+++ b/pkg/protocol/request.go
@@ -65,7 +65,7 @@ import (
 )
 
 var (
-	errMissingFile = errors.NewPublic("http: no such file")
+	ErrMissingFile = errors.NewPublic("http: no such file")
 
 	responseBodyPool bytebufferpool.Pool
 	requestBodyPool  bytebufferpool.Pool
@@ -313,7 +313,7 @@ func (req *Request) FormFile(name string) (*multipart.FileHeader, error) {
 	}
 	fhh := mf.File[name]
 	if fhh == nil {
-		return nil, errMissingFile
+		return nil, ErrMissingFile
 	}
 	return fhh[0], nil
 }
diff --git a/pkg/route/engine.go b/pkg/route/engine.go
index 30b899d..4881ee9 100644
--- a/pkg/route/engine.go
+++ b/pkg/route/engine.go
@@ -75,6 +75,7 @@ import (
 	"github.com/cloudwego/hertz/pkg/protocol/http1"
 	"github.com/cloudwego/hertz/pkg/protocol/http1/factory"
 	"github.com/cloudwego/hertz/pkg/protocol/suite"
+	"github.com/cloudwego/hertz/pkg/route/param"
 )
 
 const unknownTransporterName = "unknown"
@@ -749,6 +750,12 @@ func (engine *Engine) ServeHTTP(c context.Context, ctx *app.RequestContext) {
 		return
 	}
 
+	// if Params is re-assigned in HandlerFunc and the capacity is not enough we need to realloc
+	maxParams := int(engine.maxParams)
+	if cap(ctx.Params) < maxParams {
+		ctx.Params = make(param.Params, 0, maxParams)
+	}
+
 	// Find root of the tree for the given HTTP method
 	t := engine.trees
 	paramsPointer := &ctx.Params


Test patch:
diff --git a/pkg/common/utils/utils_test.go b/pkg/common/utils/utils_test.go
index 92873b5..225d8e4 100644
--- a/pkg/common/utils/utils_test.go
+++ b/pkg/common/utils/utils_test.go
@@ -49,7 +49,34 @@ import (
 
 // test assert func
 func TestUtilsAssert(t *testing.T) {
-	// nothing to test
+	assertPanic := func() (panicked bool) {
+		defer func() {
+			if r := recover(); r != nil {
+				panicked = true
+			}
+		}()
+		Assert(false, "should panic")
+		return false
+	}
+
+	// Checking if the assertPanic function results in a panic as expected.
+	// We expect a true value because it should panic.
+	assert.DeepEqual(t, true, assertPanic())
+
+	// Checking if a true assertion does not result in a panic.
+	// We create a wrapper around Assert to capture if it panics when it should not.
+	noPanic := func() (panicked bool) {
+		defer func() {
+			if r := recover(); r != nil {
+				panicked = true
+			}
+		}()
+		Assert(true, "should not panic")
+		return false
+	}
+
+	// We expect a false value because it should not panic.
+	assert.DeepEqual(t, false, noPanic())
 }
 
 func TestUtilsIsTrueString(t *testing.T) {
@@ -142,3 +169,18 @@ func TestFilterContentType(t *testing.T) {
 	contentType = FilterContentType(contentType)
 	assert.DeepEqual(t, "text/plain", contentType)
 }
+
+func TestNormalizeHeaderKeyEdgeCases(t *testing.T) {
+	empty := []byte("")
+	NormalizeHeaderKey(empty, false)
+	assert.DeepEqual(t, []byte(""), empty)
+	NormalizeHeaderKey(empty, true)
+	assert.DeepEqual(t, []byte(""), empty)
+}
+
+func TestFilterContentTypeEdgeCases(t *testing.T) {
+	simpleContentType := "text/plain"
+	assert.DeepEqual(t, "text/plain", FilterContentType(simpleContentType))
+	complexContentType := "text/html; charset=utf-8; format=flowed"
+	assert.DeepEqual(t, "text/html", FilterContentType(complexContentType))
+}
diff --git a/pkg/route/engine_test.go b/pkg/route/engine_test.go
index b3e0adb..ea1bc5f 100644
--- a/pkg/route/engine_test.go
+++ b/pkg/route/engine_test.go
@@ -1029,3 +1029,33 @@ func TestAcquireHijackConn(t *testing.T) {
 	assert.DeepEqual(t, engine, hijackConn.e)
 	assert.DeepEqual(t, conn, hijackConn.Conn)
 }
+
+func TestHandleParamsReassignInHandleFunc(t *testing.T) {
+	e := NewEngine(config.NewOptions(nil))
+	routes := []string{
+		"/:a/:b/:c",
+	}
+	for _, r := range routes {
+		e.GET(r, func(c context.Context, ctx *app.RequestContext) {
+			ctx.Params = make([]param.Param, 1)
+			ctx.String(consts.StatusOK, "")
+		})
+	}
+	testRoutes := []string{
+		"/aaa/bbb/ccc",
+		"/asd/alskja/alkdjad",
+		"/asd/alskja/alkdjad",
+		"/asd/alskja/alkdjad",
+		"/asd/alskja/alkdjad",
+		"/alksjdlakjd/ooo/askda",
+		"/alksjdlakjd/ooo/askda",
+		"/alksjdlakjd/ooo/askda",
+	}
+	ctx := e.ctxPool.Get().(*app.RequestContext)
+	for _, tr := range testRoutes {
+		r := protocol.NewRequest(http.MethodGet, tr, nil)
+		r.CopyTo(&ctx.Request)
+		e.ServeHTTP(context.Background(), ctx)
+		ctx.ResetWithoutConn()
+	}
+}