Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/domains/commits/Commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import type { CommitSha } from "#types/CommitSha.ts"
*/
export type Commit = {
sha: CommitSha
authorName: string | null
authorEmail: string | null
committerName: string | null
committerEmail: string | null
authorName: string
authorEmail: string
committerName: string
committerEmail: string
isMergeCommit: boolean
subjectLine: TokenisedLine
bodyLines: TokenisedLines
Expand Down
8 changes: 4 additions & 4 deletions src/domains/commits/CrudeCommit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import type { CometPlatform } from "#utilities/platform/CometPlatform.ts"
export type CrudeCommit = {
sha: CommitSha
parents: Array<CommitSha>
authorName: string | null
authorEmail: string | null
committerName: string | null
committerEmail: string | null
authorName: string
authorEmail: string
committerName: string
committerEmail: string
message: string
}

Expand Down
16 changes: 8 additions & 8 deletions src/domains/commits/git/GetGitBranchCrudeCommits.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ describe("when the author's name is absent", () => {

it("omits the author's name", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.authorName).toBeNull()
expect(commit?.authorName).toBe("")
})

it("preserves the author's email address", async () => {
Expand Down Expand Up @@ -325,7 +325,7 @@ describe("when the author's email address is absent", () => {

it("omits the author's email address", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.authorEmail).toBeNull()
expect(commit?.authorEmail).toBe("")
})
})

Expand All @@ -336,12 +336,12 @@ describe("when the author is absent", () => {

it("omits the author's name", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.authorName).toBeNull()
expect(commit?.authorName).toBe("")
})

it("omits the author's email address", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.authorEmail).toBeNull()
expect(commit?.authorEmail).toBe("")
})
})

Expand Down Expand Up @@ -420,7 +420,7 @@ describe("when the committer's name is absent", () => {

it("omits the committer's name", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.committerName).toBeNull()
expect(commit?.committerName).toBe("")
})

it("preserves the committer's email address", async () => {
Expand Down Expand Up @@ -485,7 +485,7 @@ describe("when the committer's email address is absent", () => {

it("omits the committer's email address", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.committerEmail).toBeNull()
expect(commit?.committerEmail).toBe("")
})
})

Expand All @@ -496,12 +496,12 @@ describe("when the committer is absent", () => {

it("omits the committer's name", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.committerName).toBeNull()
expect(commit?.committerName).toBe("")
})

it("omits the committer's email address", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.committerEmail).toBeNull()
expect(commit?.committerEmail).toBe("")
})
})

Expand Down
10 changes: 5 additions & 5 deletions src/domains/commits/git/GetGitBranchCrudeCommits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ function mapDtoToCrudeCommit(dto: GitLogCommitDto): CrudeCommit {
/**
* Parses a string on the form `name <email> timestamp timezone` into a pair of name and email.
*/
function parseUser(line: string | undefined): [name: string | null, email: string | null] {
function parseUser(line: string | undefined): [name: string, email: string] {
if (line === undefined) {
return [null, null]
return ["", ""]
}

const timezoneStartIndex = line.lastIndexOf(" ")
const timestampStartIndex = line.lastIndexOf(" ", timezoneStartIndex - 1)

if (timestampStartIndex === -1) {
return [null, null]
return ["", ""]
}

const userLine = line.slice(0, timestampStartIndex)
Expand All @@ -53,10 +53,10 @@ function parseUser(line: string | undefined): [name: string | null, email: strin
if (emailStartIndex !== -1 && emailEndIndex > emailStartIndex) {
const name = userLine.slice(0, emailStartIndex)
const email = userLine.slice(emailStartIndex + "<".length, emailEndIndex)
return [emailStartIndex > 0 ? trimTrailingSpace(name) : null, email]
return [emailStartIndex > 0 ? trimTrailingSpace(name) : "", email]
}

return [trimTrailingSpace(userLine), null]
return [trimTrailingSpace(userLine), ""]
}

function trimTrailingSpace(value: string): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ describe("when the author's name is absent", () => {

it("omits the author's name", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.authorName).toBeNull()
expect(commit?.authorName).toBe("")
})

it("preserves the author's email address", async () => {
Expand Down Expand Up @@ -374,7 +374,7 @@ describe("when the author's email address is absent", () => {

it("omits the author's email address", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.authorEmail).toBeNull()
expect(commit?.authorEmail).toBe("")
})
})

Expand All @@ -385,12 +385,12 @@ describe("when the author is absent", () => {

it("omits the author's name", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.authorName).toBeNull()
expect(commit?.authorName).toBe("")
})

it("omits the author's email address", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.authorEmail).toBeNull()
expect(commit?.authorEmail).toBe("")
})
})

Expand Down Expand Up @@ -488,7 +488,7 @@ describe("when the committer's name is absent", () => {

it("omits the committer's name", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.committerName).toBeNull()
expect(commit?.committerName).toBe("")
})

it("preserves the committer's email address", async () => {
Expand Down Expand Up @@ -567,7 +567,7 @@ describe("when the committer's email address is absent", () => {

it("omits the committer's email address", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.committerEmail).toBeNull()
expect(commit?.committerEmail).toBe("")
})
})

Expand All @@ -578,12 +578,12 @@ describe("when the committer is absent", () => {

it("omits the committer's name", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.committerName).toBeNull()
expect(commit?.committerName).toBe("")
})

it("omits the committer's email address", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.committerEmail).toBeNull()
expect(commit?.committerEmail).toBe("")
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export async function getGithubPullRequestCrudeCommits(): Promise<CrudeCommits>
function mapDtoToCrudeCommit(dto: GithubCommitDto): CrudeCommit {
return {
sha: dto.sha,
authorName: dto.commit.author?.name ?? null,
authorEmail: dto.commit.author?.email ?? null,
committerName: dto.commit.committer?.name ?? null,
committerEmail: dto.commit.committer?.email ?? null,
authorName: dto.commit.author?.name ?? "",
authorEmail: dto.commit.author?.email ?? "",
committerName: dto.commit.committer?.name ?? "",
committerEmail: dto.commit.committer?.email ?? "",
parents: dto.parents.map((parentDto) => parentDto.sha),
message: dto.commit.message,
}
Expand Down
28 changes: 24 additions & 4 deletions src/domains/configurations/Configuration.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,31 @@ export function fakeConfiguration(overrides: ConfigurationTemplate = {}): Config
noSquashMarkers: {},
noUnexpectedPunctuation: {},
noUnexpectedWhitespace: {},
useAuthorEmailPatterns: {},
useAuthorNamePatterns: {},
useAuthorEmailPatterns: {
patterns: [String.raw`\d+\+.+@users\.noreply\.github\.com`],
},
useAuthorNamePatterns: {
patterns: [
String.raw`\p{Lu}.*\s.+`,
String.raw`dependabot\[bot\]`,
String.raw`renovate\[bot\]`,
],
},
useCapitalisedSubjectLines: {},
useCommitterEmailPatterns: {},
useCommitterNamePatterns: {},
useCommitterEmailPatterns: {
patterns: [
String.raw`\d+\+.+@users\.noreply\.github\.com`,
String.raw`noreply@github\.com`,
],
},
useCommitterNamePatterns: {
patterns: [
String.raw`\p{Lu}.*\s.+`,
String.raw`dependabot\[bot\]`,
String.raw`renovate\[bot\]`,
String.raw`GitHub`,
],
},
useConciseSubjectLines: { maxLength: 50 },
useEmptyLineBeforeBodyLines: {},
useImperativeSubjectLines: { whitelist: new Set() },
Expand Down
141 changes: 141 additions & 0 deletions src/domains/rules/UseAuthorEmailPatterns.tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { describe, expect, it } from "vitest"
import { fakeCommitFactory } from "#commits/Commit.fixtures.ts"
import type { Commit } from "#commits/Commit.ts"
import { fakeConfiguration } from "#configurations/Configuration.fixtures.ts"
import type { Concerns } from "#rules/concerns/Concern.ts"
import { userIdentityConcern } from "#rules/concerns/UserIdentityConcern.ts"
import type { RuleKey, RuleOptions } from "#rules/Rule.ts"
import { useAuthorEmailPatterns } from "#rules/UseAuthorEmailPatterns.ts"
import type { Vector } from "#types/Vector.ts"

const rule = "useAuthorEmailPatterns" satisfies RuleKey
const enabled: RuleOptions<typeof rule> = {
patterns: [String.raw`\d+\+.+@users\.noreply\.github\.com`, String.raw`.+@fictivecompany\.com`],
}

const fakeCommit = fakeCommitFactory(fakeConfiguration())

describe.each`
authorEmail
${""}
${" "}
${"\t\t"}
${"claus@santasworkshop.com"}
${"12345678+unicorn@github.com"}
${"bunny@theeastercompany.com"}
${" tmnt@fastforward.com "}
${"baxter.stockman@fastforward.com"}
${" scl@fictivecompany.com "}
`(
"when the author's email address of $authorEmail does not satisfy any of the accepted patterns",
(props: { authorEmail: string }) => {
const commit = fakeCommit({ authorEmail: props.authorEmail })

describe("and the rule is enabled", () => {
const actualConcerns = useAuthorEmailPatterns([commit], enabled)

it("raises a concern about the author's email address", () => {
expect(actualConcerns).toEqual<Concerns>([
userIdentityConcern(rule, commit.sha, { field: "author:email" }),
])
})
})

describe("and the rule is disabled", () => {
const actualConcerns = useAuthorEmailPatterns([commit], null)

it("does not raise any concerns", () => {
expect(actualConcerns).toEqual<Concerns>([])
})
})
},
)

describe.each`
authorEmail
${"12345678+santaclaus@users.noreply.github.com"}
${"87654321+littlemermaid@users.noreply.github.com"}
${"25199993+noname@users.noreply.github.com"}
${"scl@fictivecompany.com"}
${"lme@fictivecompany.com"}
`(
"when the author's email address of $authorEmail satisfies some accepted pattern",
(props: { authorEmail: string }) => {
const commit = fakeCommit({ authorEmail: props.authorEmail })

describe("and the rule is enabled", () => {
const actualConcerns = useAuthorEmailPatterns([commit], enabled)

it("does not raise any concerns", () => {
expect(actualConcerns).toEqual<Concerns>([])
})
})

describe("and the rule is disabled", () => {
const actualConcerns = useAuthorEmailPatterns([commit], null)

it("does not raise any concerns", () => {
expect(actualConcerns).toEqual<Concerns>([])
})
})
},
)

describe("when verifying a set of multiple commits and some commits have author email addresses that do not satisfy any of the accepted patterns", () => {
const commits: Vector<Commit, 7> = [
fakeCommit({ authorEmail: "" }),
fakeCommit({ authorEmail: "bunny@theeastercompany.com" }),
fakeCommit({ authorEmail: "25199993+noname@users.noreply.github.com" }),
fakeCommit({ authorEmail: "op@fictivecompany.com" }),
fakeCommit({ authorEmail: " tmnt@fastforward.com " }),
fakeCommit({ authorEmail: "claus@santasworkshop.com" }),
fakeCommit({ authorEmail: "hello@fictivecompany.com" }),
]

describe("and the rule is enabled", () => {
const actualConcerns = useAuthorEmailPatterns(commits, enabled)

it("raises concerns about the commits with invalid author email addresses", () => {
expect(actualConcerns).toEqual<Concerns>([
userIdentityConcern(rule, commits[0].sha, { field: "author:email" }),
userIdentityConcern(rule, commits[1].sha, { field: "author:email" }),
userIdentityConcern(rule, commits[4].sha, { field: "author:email" }),
userIdentityConcern(rule, commits[5].sha, { field: "author:email" }),
])
})
})

describe("and the rule is disabled", () => {
const actualConcerns = useAuthorEmailPatterns(commits, null)

it("does not raise any concerns", () => {
expect(actualConcerns).toEqual<Concerns>([])
})
})
})

describe("when verifying a set of multiple commits and all commits have author email addresses that satisfy some accepted pattern", () => {
const commits: Vector<Commit, 5> = [
fakeCommit({ authorEmail: "12345678+santaclaus@users.noreply.github.com" }),
fakeCommit({ authorEmail: "87654321+littlemermaid@users.noreply.github.com" }),
fakeCommit({ authorEmail: "25199993+noname@users.noreply.github.com" }),
fakeCommit({ authorEmail: "scl@fictivecompany.com" }),
fakeCommit({ authorEmail: "lme@fictivecompany.com" }),
]

describe("and the rule is enabled", () => {
const actualConcerns = useAuthorEmailPatterns(commits, enabled)

it("does not raise any concerns", () => {
expect(actualConcerns).toEqual<Concerns>([])
})
})

describe("and the rule is disabled", () => {
const actualConcerns = useAuthorEmailPatterns(commits, null)

it("does not raise any concerns", () => {
expect(actualConcerns).toEqual<Concerns>([])
})
})
})
Loading
Loading