Files
bluekeyes-go-gitdiff/gitdiff/patch_header_test.go
T
Billy Keyes 3f2ea5c16e Accept empty emails in ParsePatchIdentity (#42)
Git is actually more lenient here than I thought. As long as the
identity contains the "<>" delimiters, Git will allow an empty email, so
we should accept the same thing. I also discovered that an identity with
only an email set will use the email as the name, so I've implemented
that behavior as well.
2024-03-06 21:32:38 -08:00

646 lines
17 KiB
Go

package gitdiff
import (
"testing"
"time"
)
func TestParsePatchIdentity(t *testing.T) {
tests := map[string]struct {
Input string
Output PatchIdentity
Err interface{}
}{
"simple": {
Input: "Morton Haypenny <mhaypenny@example.com>",
Output: PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
},
},
"extraWhitespace": {
Input: " Morton Haypenny <mhaypenny@example.com > ",
Output: PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
},
},
"trailingCharacters": {
Input: "Morton Haypenny <mhaypenny@example.com> unrelated garbage",
Output: PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
},
},
"onlyEmail": {
Input: "<mhaypenny@example.com>",
Output: PatchIdentity{
Name: "mhaypenny@example.com",
Email: "mhaypenny@example.com",
},
},
"emptyEmail": {
Input: "Morton Haypenny <>",
Output: PatchIdentity{
Name: "Morton Haypenny",
Email: "",
},
},
"missingEmail": {
Input: "Morton Haypenny",
Err: "invalid identity",
},
"missingNameAndEmptyEmail": {
Input: "<>",
Err: "invalid identity",
},
"empty": {
Input: "",
Err: "invalid identity",
},
"unclosedEmail": {
Input: "Morton Haypenny <mhaypenny@example.com",
Err: "unclosed email",
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
id, err := ParsePatchIdentity(test.Input)
if test.Err != nil {
assertError(t, test.Err, err, "parsing identity")
return
}
if err != nil {
t.Fatalf("unexpected error parsing identity: %v", err)
}
if test.Output != id {
t.Errorf("incorrect identity: expected %#v, actual %#v", test.Output, id)
}
})
}
}
func TestParsePatchDate(t *testing.T) {
expected := time.Date(2020, 4, 9, 8, 7, 6, 0, time.UTC)
tests := map[string]struct {
Input string
Output time.Time
Err interface{}
}{
"default": {
Input: "Thu Apr 9 01:07:06 2020 -0700",
Output: expected,
},
"defaultLocal": {
Input: "Thu Apr 9 01:07:06 2020",
Output: time.Date(2020, 4, 9, 1, 7, 6, 0, time.Local),
},
"iso": {
Input: "2020-04-09 01:07:06 -0700",
Output: expected,
},
"isoStrict": {
Input: "2020-04-09T01:07:06-07:00",
Output: expected,
},
"rfc": {
Input: "Thu, 9 Apr 2020 01:07:06 -0700",
Output: expected,
},
"short": {
Input: "2020-04-09",
Output: time.Date(2020, 4, 9, 0, 0, 0, 0, time.Local),
},
"raw": {
Input: "1586419626 -0700",
Output: expected,
},
"unix": {
Input: "1586419626",
Output: expected,
},
"unknownFormat": {
Input: "4/9/2020 01:07:06 PDT",
Err: "unknown date format",
},
"empty": {
Input: "",
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
d, err := ParsePatchDate(test.Input)
if test.Err != nil {
assertError(t, test.Err, err, "parsing date")
return
}
if err != nil {
t.Fatalf("unexpected error parsing date: %v", err)
}
if !test.Output.Equal(d) {
t.Errorf("incorrect parsed date: expected %v, actual %v", test.Output, d)
}
})
}
}
func TestParsePatchHeader(t *testing.T) {
expectedSHA := "61f5cd90bed4d204ee3feb3aa41ee91d4734855b"
expectedIdentity := &PatchIdentity{
Name: "Morton Haypenny",
Email: "mhaypenny@example.com",
}
expectedDate := time.Date(2020, 04, 11, 15, 21, 23, 0, time.FixedZone("PDT", -7*60*60))
expectedTitle := "A sample commit to test header parsing"
expectedEmojiOneLineTitle := "🤖 Enabling auto-merging"
expectedEmojiMultiLineTitle := "[IA64] Put ia64 config files on the Uwe Kleine-König diet"
expectedBody := "The medium format shows the body, which\nmay wrap on to multiple lines.\n\nAnother body line."
expectedBodyAppendix := "CC: Joe Smith <joe.smith@company.com>"
tests := map[string]struct {
Input string
Options []PatchHeaderOption
Header PatchHeader
Err interface{}
}{
"prettyShort": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
A sample commit to test header parsing
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
Title: expectedTitle,
},
},
"prettyMedium": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Date: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle,
Body: expectedBody,
},
},
"prettyFull": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Commit: Morton Haypenny <mhaypenny@example.com>
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
Committer: expectedIdentity,
Title: expectedTitle,
Body: expectedBody,
},
},
"prettyFuller": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
AuthorDate: Sat Apr 11 15:21:23 2020 -0700
Commit: Morton Haypenny <mhaypenny@example.com>
CommitDate: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Committer: expectedIdentity,
CommitterDate: expectedDate,
Title: expectedTitle,
Body: expectedBody,
},
},
"prettyAppendix": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
AuthorDate: Sat Apr 11 15:21:23 2020 -0700
Commit: Morton Haypenny <mhaypenny@example.com>
CommitDate: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
---
CC: Joe Smith <joe.smith@company.com>
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Committer: expectedIdentity,
CommitterDate: expectedDate,
Title: expectedTitle,
Body: expectedBody + "\n---\n" + expectedBodyAppendix,
},
},
"mailbox": {
Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
From: Morton Haypenny <mhaypenny@example.com>
Date: Sat, 11 Apr 2020 15:21:23 -0700
Subject: [PATCH] A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle,
Body: expectedBody,
},
},
"mailboxPatchOnly": {
Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
From: Morton Haypenny <mhaypenny@example.com>
Date: Sat, 11 Apr 2020 15:21:23 -0700
Subject: [PATCH] [BUG-123] A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Options: []PatchHeaderOption{
WithSubjectCleanMode(SubjectCleanPatchOnly),
},
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: "[BUG-123] " + expectedTitle,
Body: expectedBody,
},
},
"mailboxEmojiOneLine": {
Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
From: Morton Haypenny <mhaypenny@example.com>
Date: Sat, 11 Apr 2020 15:21:23 -0700
Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20Enabling=20auto-merging?=
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedEmojiOneLineTitle,
Body: expectedBody,
},
},
"mailboxEmojiMultiLine": {
Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
From: Morton Haypenny <mhaypenny@example.com>
Date: Sat, 11 Apr 2020 15:21:23 -0700
Subject: [PATCH] =?UTF-8?q?[IA64]=20Put=20ia64=20config=20files=20on=20the=20?=
=?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20diet?=
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedEmojiMultiLineTitle,
Body: expectedBody,
},
},
"mailboxAppendix": {
Input: `From 61f5cd90bed4d204ee3feb3aa41ee91d4734855b Mon Sep 17 00:00:00 2001
From: Morton Haypenny <mhaypenny@example.com>
Date: Sat, 11 Apr 2020 15:21:23 -0700
Subject: [PATCH] A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
---
CC: Joe Smith <joe.smith@company.com>
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle,
Body: expectedBody,
BodyAppendix: expectedBodyAppendix,
},
},
"mailboxMinimalNoName": {
Input: `From: <mhaypenny@example.com>
Subject: [PATCH] A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
Author: &PatchIdentity{expectedIdentity.Email, expectedIdentity.Email},
Title: expectedTitle,
Body: expectedBody,
},
},
"mailboxMinimal": {
Input: `From: Morton Haypenny <mhaypenny@example.com>
Subject: [PATCH] A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
Author: expectedIdentity,
Title: expectedTitle,
Body: expectedBody,
},
},
"unwrapTitle": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Date: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing with a long
title that is wrapped.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle + " with a long title that is wrapped.",
},
},
"normalizeBodySpace": {
Input: `commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
Date: Sat Apr 11 15:21:23 2020 -0700
A sample commit to test header parsing
The medium format shows the body, which
may wrap on to multiple lines.
Another body line.
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
AuthorDate: expectedDate,
Title: expectedTitle,
Body: expectedBody,
},
},
"ignoreLeadingBlankLines": {
Input: `
` + " " + `
commit 61f5cd90bed4d204ee3feb3aa41ee91d4734855b
Author: Morton Haypenny <mhaypenny@example.com>
A sample commit to test header parsing
`,
Header: PatchHeader{
SHA: expectedSHA,
Author: expectedIdentity,
Title: expectedTitle,
},
},
"emptyHeader": {
Input: "",
Header: PatchHeader{},
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
h, err := ParsePatchHeader(test.Input, test.Options...)
if test.Err != nil {
assertError(t, test.Err, err, "parsing patch header")
return
}
if err != nil {
t.Fatalf("unexpected error parsing patch header: %v", err)
}
if h == nil {
t.Fatalf("expected non-nil header, but got nil")
}
exp := test.Header
act := *h
if exp.SHA != act.SHA {
t.Errorf("incorrect parsed SHA: expected %q, actual %q", exp.SHA, act.SHA)
}
assertPatchIdentity(t, "author", exp.Author, act.Author)
if !exp.AuthorDate.Equal(act.AuthorDate) {
t.Errorf("incorrect parsed author date: expected %v, but got %v", exp.AuthorDate, act.AuthorDate)
}
assertPatchIdentity(t, "committer", exp.Committer, act.Committer)
if !exp.CommitterDate.Equal(act.CommitterDate) {
t.Errorf("incorrect parsed committer date: expected %v, but got %v", exp.CommitterDate, act.CommitterDate)
}
if exp.Title != act.Title {
t.Errorf("incorrect parsed title:\n expected: %q\n actual: %q", exp.Title, act.Title)
}
if exp.Body != act.Body {
t.Errorf("incorrect parsed body:\n expected: %q\n actual: %q", exp.Body, act.Body)
}
if exp.BodyAppendix != act.BodyAppendix {
t.Errorf("incorrect parsed body appendix:\n expected: %q\n actual: %q",
exp.BodyAppendix, act.BodyAppendix)
}
})
}
}
func assertPatchIdentity(t *testing.T, kind string, exp, act *PatchIdentity) {
switch {
case exp == nil && act == nil:
case exp == nil && act != nil:
t.Errorf("incorrect parsed %s: expected nil, but got %+v", kind, act)
case exp != nil && act == nil:
t.Errorf("incorrect parsed %s: expected %+v, but got nil", kind, exp)
case exp.Name != act.Name || exp.Email != act.Email:
t.Errorf("incorrect parsed %s, expected %+v, bot got %+v", kind, exp, act)
}
}
func TestCleanSubject(t *testing.T) {
expectedSubject := "A sample commit to test header parsing"
tests := map[string]struct {
Input string
Mode SubjectCleanMode
Prefix string
Subject string
}{
"CleanAll/noPrefix": {
Input: expectedSubject,
Mode: SubjectCleanAll,
Subject: expectedSubject,
},
"CleanAll/patchPrefix": {
Input: "[PATCH] " + expectedSubject,
Mode: SubjectCleanAll,
Prefix: "[PATCH] ",
Subject: expectedSubject,
},
"CleanAll/patchPrefixNoSpace": {
Input: "[PATCH]" + expectedSubject,
Mode: SubjectCleanAll,
Prefix: "[PATCH]",
Subject: expectedSubject,
},
"CleanAll/patchPrefixContent": {
Input: "[PATCH 3/7] " + expectedSubject,
Mode: SubjectCleanAll,
Prefix: "[PATCH 3/7] ",
Subject: expectedSubject,
},
"CleanAll/spacePrefix": {
Input: " " + expectedSubject,
Mode: SubjectCleanAll,
Subject: expectedSubject,
},
"CleanAll/replyLowerPrefix": {
Input: "re: " + expectedSubject,
Mode: SubjectCleanAll,
Prefix: "re: ",
Subject: expectedSubject,
},
"CleanAll/replyMixedPrefix": {
Input: "Re: " + expectedSubject,
Mode: SubjectCleanAll,
Prefix: "Re: ",
Subject: expectedSubject,
},
"CleanAll/replyCapsPrefix": {
Input: "RE: " + expectedSubject,
Mode: SubjectCleanAll,
Prefix: "RE: ",
Subject: expectedSubject,
},
"CleanAll/replyDoublePrefix": {
Input: "Re: re: " + expectedSubject,
Mode: SubjectCleanAll,
Prefix: "Re: re: ",
Subject: expectedSubject,
},
"CleanAll/noPrefixSubjectHasRe": {
Input: "Reimplement parsing",
Mode: SubjectCleanAll,
Subject: "Reimplement parsing",
},
"CleanAll/patchPrefixSubjectHasRe": {
Input: "[PATCH 1/2] Reimplement parsing",
Mode: SubjectCleanAll,
Prefix: "[PATCH 1/2] ",
Subject: "Reimplement parsing",
},
"CleanAll/unclosedPrefix": {
Input: "[Just to annoy people",
Mode: SubjectCleanAll,
Subject: "[Just to annoy people",
},
"CleanAll/multiplePrefix": {
Input: " Re:Re: [PATCH 1/2][DRAFT] " + expectedSubject + " ",
Mode: SubjectCleanAll,
Prefix: "Re:Re: [PATCH 1/2][DRAFT] ",
Subject: expectedSubject,
},
"CleanPatchOnly/patchPrefix": {
Input: "[PATCH] " + expectedSubject,
Mode: SubjectCleanPatchOnly,
Prefix: "[PATCH] ",
Subject: expectedSubject,
},
"CleanPatchOnly/mixedPrefix": {
Input: "[PATCH] [TICKET-123] " + expectedSubject,
Mode: SubjectCleanPatchOnly,
Prefix: "[PATCH] ",
Subject: "[TICKET-123] " + expectedSubject,
},
"CleanPatchOnly/multiplePrefix": {
Input: "Re:Re: [PATCH 1/2][DRAFT] " + expectedSubject,
Mode: SubjectCleanPatchOnly,
Prefix: "Re:Re: [PATCH 1/2]",
Subject: "[DRAFT] " + expectedSubject,
},
"CleanWhitespace/leadingSpace": {
Input: " [PATCH] " + expectedSubject,
Mode: SubjectCleanWhitespace,
Subject: "[PATCH] " + expectedSubject,
},
"CleanWhitespace/trailingSpace": {
Input: "[PATCH] " + expectedSubject + " ",
Mode: SubjectCleanWhitespace,
Subject: "[PATCH] " + expectedSubject,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
prefix, subject := cleanSubject(test.Input, test.Mode)
if prefix != test.Prefix {
t.Errorf("incorrect prefix: expected %q, actual %q", test.Prefix, prefix)
}
if subject != test.Subject {
t.Errorf("incorrect subject: expected %q, actual %q", test.Subject, subject)
}
})
}
}