From 8e3209b09a284f1354809636ec63672c2aec5300 Mon Sep 17 00:00:00 2001 From: Kemal Zebari Date: Mon, 13 May 2024 22:35:17 -0700 Subject: [PATCH] Make commit trailer parsing public and add parameterized tests --- modules/util/commit_trailers.go | 42 +++++++++++++++++++++++ modules/util/commit_trailers_test.go | 37 ++++++++++++++++++++ services/repository/contributors_graph.go | 31 ++--------------- 3 files changed, 81 insertions(+), 29 deletions(-) create mode 100644 modules/util/commit_trailers.go create mode 100644 modules/util/commit_trailers_test.go diff --git a/modules/util/commit_trailers.go b/modules/util/commit_trailers.go new file mode 100644 index 0000000000..9c6547d0cd --- /dev/null +++ b/modules/util/commit_trailers.go @@ -0,0 +1,42 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package util + +import ( + "errors" + "net/mail" + "strings" +) + +var ErrInvalidCommitTrailerValueSyntax = errors.New("syntax error occurred while parsing a commit trailer value") + +// ParseCommitTrailerValueWithAuthor parses a commit trailer value that contains author data. +// Note that it only parses the value and does not consider the trailer key i.e. we just +// parse something like the following: +// +// Foo Bar +func ParseCommitTrailerValueWithAuthor(value string) (name, email string, err error) { + value = strings.TrimSpace(value) + if !strings.HasSuffix(value, ">") { + return "", "", ErrInvalidCommitTrailerValueSyntax + } + + closedBracketIdx := len(value) - 1 + openBracketIdx := strings.LastIndex(value, "<") + if openBracketIdx == -1 { + return "", "", ErrInvalidCommitTrailerValueSyntax + } + + email = value[openBracketIdx+1 : closedBracketIdx] + if _, err := mail.ParseAddress(email); err != nil { + return "", "", ErrInvalidCommitTrailerValueSyntax + } + + name = strings.TrimSpace(value[:openBracketIdx]) + if len(name) == 0 { + return "", "", ErrInvalidCommitTrailerValueSyntax + } + + return name, email, nil +} diff --git a/modules/util/commit_trailers_test.go b/modules/util/commit_trailers_test.go new file mode 100644 index 0000000000..3ba4b64ec9 --- /dev/null +++ b/modules/util/commit_trailers_test.go @@ -0,0 +1,37 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseCommitTrailerValueWithAuthor(t *testing.T) { + cases := []struct { + input string + shouldBeError bool + expectedName string + expectedEmail string + }{ + {"Foo Bar ", true, "", ""}, + {"Foo Bar <>", true, "", ""}, + {"Foo Bar ", true, "", ""}, + {"", true, "", ""}, + {" ", true, "", ""}, + {"Foo Bar ", false, "Foo Bar", "foobar@example.com"}, + {" Foo Bar ", false, "Foo Bar", "foobar@example.com"}, + // Account for edge case where name contains an open bracket. + {" Foo < Bar ", false, "Foo < Bar", "foobar@example.com"}, + } + + for n, c := range cases { + name, email, err := ParseCommitTrailerValueWithAuthor(c.input) + if c.shouldBeError { + assert.Error(t, err, "case %d should be a syntax error", n) + } else { + assert.Equal(t, c.expectedName, name, "case %d should have correct name", n) + assert.Equal(t, c.expectedEmail, email, "case %d should have correct email", n) + } + } +} diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index e1423d5989..b91d64e3b7 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -8,7 +8,6 @@ import ( "context" "errors" "fmt" - "net/mail" "os" "strconv" "strings" @@ -24,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" ) const ( @@ -159,7 +159,7 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int // There should be an empty line before we read the commit stats line. break } - coAuthorName, coAuthorEmail, err := parseCoAuthorTrailerValue(line) + coAuthorName, coAuthorEmail, err := util.ParseCommitTrailerValueWithAuthor(line) if err != nil { continue } @@ -220,33 +220,6 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int return extendedCommitStats, nil } -var errSyntax = errors.New("syntax error occurred") - -func parseCoAuthorTrailerValue(value string) (name, email string, err error) { - value = strings.TrimSpace(value) - if !strings.HasSuffix(value, ">") { - return "", "", errSyntax - } - - closedBracketIdx := len(value) - 1 - openBracketIdx := strings.LastIndex(value, "<") - if openBracketIdx == -1 { - return "", "", errSyntax - } - - email = value[openBracketIdx+1 : closedBracketIdx] - if _, err := mail.ParseAddress(email); err != nil { - return "", "", err - } - - name = strings.TrimSpace(value[:openBracketIdx]) - if len(name) == 0 { - return "", "", errSyntax - } - - return name, email, nil -} - func generateContributorStats(genDone chan struct{}, cache cache.StringCache, cacheKey string, repo *repo_model.Repository, revision string) { ctx := graceful.GetManager().HammerContext()