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
24 changes: 24 additions & 0 deletions src/domains/commits/Commit.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,30 @@ describe.each`
})
})

describe("when the commit signature is non-empty", () => {
const crudeCommit = fakeCrudeCommit({
signature:
"-----BEGIN SSH SIGNATURE-----\n" +
"MzEwM2JkMTMtNmJiMy00N2YxLWEyNTUtOWMxZmFmYTAyMGZlNDI3MWYyMmEtMjU4MS00YTky\n" +
"LWFhNTEtMjI5YjRiZWIxYzMxNTZiMzAwYmMtYmU5ZC00NjUxLWFmODAtY2U3N2I2NmZmNDIy\n" +
"-----END SSH SIGNATURE-----",
})

it("has a signature", () => {
const commit = mapCrudeCommitToCommit(crudeCommit, configuration)
expect(commit.hasSignature).toBe(true)
})
})

describe("when the commit signature is empty", () => {
const crudeCommit = fakeCrudeCommit({ signature: "" })

it("does not have a signature", () => {
const commit = mapCrudeCommitToCommit(crudeCommit, configuration)
expect(commit.hasSignature).toBe(false)
})
})

describe("when the commit message is empty", () => {
const crudeCommit = fakeCrudeCommit({ message: "" })

Expand Down
2 changes: 2 additions & 0 deletions src/domains/commits/Commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type Commit = {
committerName: string
committerEmail: string
isMergeCommit: boolean
hasSignature: boolean
subjectLine: TokenisedLine
bodyLines: TokenisedLines
}
Expand All @@ -34,6 +35,7 @@ export function mapCrudeCommitToCommit(
return {
sha: crudeCommit.sha,
isMergeCommit: crudeCommit.parents.length > 1,
hasSignature: crudeCommit.signature !== "",
authorName: crudeCommit.authorName,
authorEmail: crudeCommit.authorEmail,
committerName: crudeCommit.committerName,
Expand Down
1 change: 1 addition & 0 deletions src/domains/commits/CrudeCommit.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function fakeCrudeCommit(overrides: CrudeCommitTemplate = {}): CrudeCommi
committerName: "Leonardo da Vinci",
committerEmail: "71091436+katanaturtle@users.noreply.github.com",
message: "Introduce a cool feature\n\nIt is really awesome!",
signature: "",
...overrides,
}
}
1 change: 1 addition & 0 deletions src/domains/commits/CrudeCommit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type CrudeCommit = {
committerName: string
committerEmail: string
message: string
signature: string
}

export type CrudeCommits = Array<CrudeCommit>
Expand Down
28 changes: 28 additions & 0 deletions src/domains/commits/git/GetGitBranchCrudeCommits.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,34 @@ describe("when the committer is absent", () => {
})
})

describe.each`
signature
${"-----BEGIN SSH SIGNATURE-----\nNjI1ZTMxMzctODU1ZS00YmVmLWI0MTMtY2Q4MmI4YjZlYzE0\n-----END SSH SIGNATURE-----"}
${"-----BEGIN PGP SIGNATURE-----\n\nNGU2NWQ0YzctN2I0OC00YWExLWE2NmUtZjg3MTZmNGY2MWJhODM4NTI1ZTEtZWY4OS00YjVjLTgwMWItOTgxNTBmOGJiOTZk\n-----END PGP SIGNATURE-----"}
`("when the commit has a signature of $signature", (props: { signature: string }) => {
const signature = props.signature

beforeEach(() => {
mockGitLog([{ gpgsig: [signature] }])
})

it("preserves the signature", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.signature).toBe(signature)
})
})

describe("when the commit does not have a signature", () => {
beforeEach(() => {
mockGitLog([{}])
})

it("omits the signature", async () => {
const [commit] = await getGitBranchCrudeCommits()
expect(commit?.signature).toBe("")
})
})

describe("when the Git log does not have any commits", () => {
const commitDtos = fakeGitLogCommitDtos(0)

Expand Down
1 change: 1 addition & 0 deletions src/domains/commits/git/GetGitBranchCrudeCommits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function mapDtoToCrudeCommit(dto: GitLogCommitDto): CrudeCommit {
committerName,
committerEmail,
message: dto.message[0],
signature: dto.gpgsig[0] ?? "",
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,45 @@ describe("when the committer is absent", () => {
})
})

describe.each`
signature
${"-----BEGIN SSH SIGNATURE-----\nN2ViNGVjNmYtMzk2NC00YjUxLTg5ZTItZmJmYmQ2M2YyZGZk\n-----END SSH SIGNATURE-----"}
${"-----BEGIN PGP SIGNATURE-----\n\nMmE2NDljNTctZTJkNi00YTg2LTllOGMtMjkyYjc3YjlhOGJiYzc3NGRiZGMtNjcxNC00NjI5LWI1ODMtNDFlMGI3YTI2ZmYw\n-----END PGP SIGNATURE-----"}
`("when the commit has a signature of $signature", (props: { signature: string }) => {
const signature = props.signature

beforeEach(() => {
mockGithubPullRequestCommitDtos([{ commit: { verification: { signature } } }])
})

it("preserves the signature", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.signature).toBe(signature)
})
})

describe("when the commit does not have a signature", () => {
beforeEach(() => {
mockGithubPullRequestCommitDtos([{ commit: { verification: { signature: null } } }])
})

it("omits the signature", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.signature).toBe("")
})
})

describe("when the commit verification object is absent", () => {
beforeEach(() => {
mockGithubPullRequestCommitDtos([{ commit: {} }])
})

it("omits the signature", async () => {
const [commit] = await getGithubPullRequestCrudeCommits()
expect(commit?.signature).toBe("")
})
})

describe("when the pull request does not have any commits", () => {
const commitDtos = fakeGithubCommitDtos(0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ function mapDtoToCrudeCommit(dto: GithubCommitDto): CrudeCommit {
committerEmail: dto.commit.committer?.email ?? "",
parents: dto.parents.map((parentDto) => parentDto.sha),
message: dto.commit.message,
signature: dto.commit.verification?.signature ?? "",
}
}
58 changes: 58 additions & 0 deletions src/domains/rules/NoMergeCommits.tests.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
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 { commitConcern } from "#rules/concerns/CommitConcern.ts"
import type { Concerns } from "#rules/concerns/Concern.ts"
import { noMergeCommits } from "#rules/NoMergeCommits.ts"
import type { RuleKey, RuleOptions } from "#rules/Rule.ts"
import { fakeCommitSha } from "#types/CommitSha.fixtures.ts"
import type { CommitSha } from "#types/CommitSha.ts"
import type { Vector } from "#types/Vector.ts"

const rule = "noMergeCommits" satisfies RuleKey
const enabled: RuleOptions<typeof rule> = {}
Expand Down Expand Up @@ -70,3 +72,59 @@ describe.each`
})
},
)

describe("when verifying a set of multiple commits and some commits are merge commits", () => {
const commits: Vector<Commit, 6> = [
fakeCommit({ parents: [fakeCommitSha()] }),
fakeCommit({ parents: [fakeCommitSha(), fakeCommitSha()] }),
fakeCommit({ parents: [fakeCommitSha()] }),
fakeCommit({ parents: [fakeCommitSha(), fakeCommitSha(), fakeCommitSha()] }),
fakeCommit({ parents: [fakeCommitSha(), fakeCommitSha()] }),
fakeCommit({ parents: [] }),
]

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

it("raises concerns about the merge commits", () => {
expect(actualConcerns).toEqual<Concerns>([
commitConcern(rule, commits[1].sha),
commitConcern(rule, commits[3].sha),
commitConcern(rule, commits[4].sha),
])
})
})

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

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

describe("when verifying a set of multiple commits and no commits are merge commits", () => {
const commits: Vector<Commit, 4> = [
fakeCommit({ parents: [fakeCommitSha()] }),
fakeCommit({ parents: [fakeCommitSha()] }),
fakeCommit({ parents: [fakeCommitSha()] }),
fakeCommit({ parents: [] }),
]

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

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

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

it("does not raise any concerns", () => {
expect(actualConcerns).toEqual<Concerns>([])
})
})
})
147 changes: 147 additions & 0 deletions src/domains/rules/UseSignedCommits.tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
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 { commitConcern } from "#rules/concerns/CommitConcern.ts"
import type { Concerns } from "#rules/concerns/Concern.ts"
import type { RuleKey, RuleOptions } from "#rules/Rule.ts"
import { useSignedCommits } from "#rules/UseSignedCommits.ts"
import type { Vector } from "#types/Vector.ts"

const rule = "useSignedCommits" satisfies RuleKey
const enabled: RuleOptions<typeof rule> = {}

const fakeCommit = fakeCommitFactory(fakeConfiguration())

describe.each`
subjectLine
${"Teach the coffee machine to stop judging mugs"}
${"shipping a tiny fix before lunch"}
`(
"when the commit does not have a signature and has a subject line of $subjectLine",
(props: { subjectLine: string }) => {
const commit = fakeCommit({ message: props.subjectLine, signature: "" })

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

it("raises a concern about the entire commit", () => {
expect(actualConcerns).toEqual<Concerns>([commitConcern(rule, commit.sha)])
})
})

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

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

describe.each`
signature | subjectLine
${"-----BEGIN SSH SIGNATURE-----\nNWNhOTk2MWQtMWZiZS00NTg4LThiMmUtNzEzMmExMjJiM2Q5\n-----END SSH SIGNATURE-----"} | ${"Give the release notes a sensible haircut"}
${"-----BEGIN PGP SIGNATURE-----\n\nOTI4Zjk0OWMtNjJhOS00OTk1LTkwYzEtNmU1MWYxYWE0MzdkNThmMDM4NGQtOGFjYS00ZDk5LWJjYjMtZmRiNWI1NmEzNTU4\n-----END PGP SIGNATURE-----"} | ${"put the changelog back where it belongs"}
`(
"when the commit has a signature and has a subject line of $subjectLine",
(props: { signature: string; subjectLine: string }) => {
const commit = fakeCommit({ message: props.subjectLine, signature: props.signature })

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

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

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

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

describe("when verifying a set of multiple commits and some commits lack a signature", () => {
const commits: Vector<Commit, 7> = [
fakeCommit({
signature:
"-----BEGIN SSH SIGNATURE-----\nYzhmNjMwODktYzA3MS00YzM5LWEyYzAtNTYyYjNjMmExMjJi\n-----END SSH SIGNATURE-----",
}),
fakeCommit({ signature: "" }),
fakeCommit({ signature: "" }),
fakeCommit({
signature:
"-----BEGIN SSH SIGNATURE-----\nN2YwNDUwYzktYzRhNi00YzNiLWFiMzMtYWEyYjg3MThjMDc0\n-----END SSH SIGNATURE-----",
}),
fakeCommit({ signature: "" }),
fakeCommit({
signature:
"-----BEGIN PGP SIGNATURE-----\n\nZmMzMDA1ZjItYzdlNy00NzliLThmOTYtNjk1NWZkMzA0MGNjNmFhYTA2YzUtOWQ1Yi00YmViLWI0MzMtNzJjYmU3ZjA3OGVl\n-----END PGP SIGNATURE-----",
}),
fakeCommit({ signature: "" }),
]

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

it("raises concerns about the commits without a signature", () => {
expect(actualConcerns).toEqual<Concerns>([
commitConcern(rule, commits[1].sha),
commitConcern(rule, commits[2].sha),
commitConcern(rule, commits[4].sha),
commitConcern(rule, commits[6].sha),
])
})
})

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

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

describe("when verifying a set of multiple commits and all commits have signatures", () => {
const commits: Vector<Commit, 4> = [
fakeCommit({
signature:
"-----BEGIN SSH SIGNATURE-----\nOTFhZDVjOGUtMWQxYS00NTY4LWFmNDgtOWU4Y2U2MzU5NGE2\n-----END SSH SIGNATURE-----",
}),
fakeCommit({
signature:
"-----BEGIN PGP SIGNATURE-----\n\nODk5ZTUwYTAtZDIwNi00ODY2LWEyMGItOGY0NGEwYjlmZDNmMDFkOTQzOGEtNjZiNy00MTk2LTlkYzYtMTc2MDg4MTZkMjgw\n-----END PGP SIGNATURE-----",
}),
fakeCommit({
signature:
"-----BEGIN SSH SIGNATURE-----\nMmZjYWYxMDktNWUzYS00ZmNiLWFjNTQtMmUzYzA1ODQwMGY4\n-----END SSH SIGNATURE-----",
}),
fakeCommit({
signature:
"-----BEGIN PGP SIGNATURE-----\n\nODVlYmM5MjgtMDA3NC00MjNhLTlkYTQtYWZjNThkNTJhNzU2NjY4MThlZmYtMTFhYi00ODBmLThlOWEtYzAwNGE5MmMwNjVj\n-----END PGP SIGNATURE-----",
}),
]

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

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

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

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