Skip to content

Commit

Permalink
feat: add magic string check for linked issues (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
Namchee authored Dec 20, 2023
1 parent 8378fb2 commit b31518c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 1 deletion.
33 changes: 33 additions & 0 deletions internal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
// Designed this way for easier testing
type GithubClient interface {
GetPullRequest(context.Context, *entity.Meta, int) (*entity.PullRequest, error)
GetIssue(context.Context, *entity.Meta, int) (*entity.IssueReference, error)
GetIssueReferences(context.Context, *entity.Meta, int) ([]*entity.IssueReference, error)
GetCommits(context.Context, *entity.Meta, int) ([]*entity.Commit, error)
GetPermissions(context.Context, *entity.Meta, string) ([]string, error)
Expand Down Expand Up @@ -98,6 +99,38 @@ func (c *githubClient) GetPullRequest(
}, nil
}

func (c *githubClient) GetIssue(
ctx context.Context,
meta *entity.Meta,
issueNumber int,
) (*entity.IssueReference, error) {
var query struct {
Repository struct {
Issue struct {
Number int
} `graphql:"issue(number: $number)"`
} `graphql:"repository(owner: $owner, name: $name)"`
}

variables := map[string]interface{}{
"owner": githubv4.String(meta.Owner),
"name": githubv4.String(meta.Name),
"number": githubv4.Int(issueNumber),
}

var issue *entity.IssueReference

err := c.gqlClient.Query(ctx, &query, variables)
if err != nil {
return issue, err
}

return &entity.IssueReference{
Number: issueNumber,
Meta: *meta,
}, nil
}

func (c *githubClient) GetIssueReferences(
ctx context.Context,
meta *entity.Meta,
Expand Down
21 changes: 21 additions & 0 deletions internal/mocks/client_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,27 @@ func (m *githubClientMock) Close(
return errors.New("error")
}

func (m *githubClientMock) GetIssue(
_ context.Context,
_ *entity.Meta,
issueNumber int,
) (*entity.IssueReference, error) {
switch issueNumber {
case 2:
return &entity.IssueReference{
Number: 2,
Meta: entity.Meta{
Owner: "Namchee",
Name: "conventional-pr",
},
}, nil
case 3:
return &entity.IssueReference{}, nil
default:
return nil, errors.New("mock error")
}
}

func (m *githubClientMock) GetIssueReferences(
_ context.Context,
_ *entity.Meta,
Expand Down
37 changes: 37 additions & 0 deletions internal/validator/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ package validator

import (
"context"
"regexp"
"strconv"

"github.com/Namchee/conventional-pr/internal"
"github.com/Namchee/conventional-pr/internal/constants"
"github.com/Namchee/conventional-pr/internal/entity"
)

var (
keywordPattern = regexp.MustCompile(`(?mi)\b(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved) #(\d+)\b`)
)

type issueValidator struct {
Name string

Expand Down Expand Up @@ -65,9 +71,40 @@ func (v *issueValidator) IsValid(
}
}

if v.hasIssueMagicString(ctx, pullRequest) {
return &entity.ValidationResult{
Name: constants.IssueValidatorName,
Active: true,
Result: nil,
}
}

return &entity.ValidationResult{
Name: constants.IssueValidatorName,
Active: true,
Result: constants.ErrNoIssue,
}
}

func (v *issueValidator) hasIssueMagicString(
ctx context.Context,
pullRequest *entity.PullRequest,
) bool {
keywords := keywordPattern.FindAllStringSubmatch(pullRequest.Body, -1)

for _, number := range keywords {
num, _ := strconv.Atoi(number[2])

issue, _ := v.client.GetIssue(
context.Background(),
&pullRequest.Repository,
num,
)

if issue != nil {
return true
}
}

return false
}
21 changes: 20 additions & 1 deletion internal/validator/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func TestIssueValidator_IsValid(t *testing.T) {
config bool
meta *entity.Meta
prNumber int
body string
}
tests := []struct {
name string
Expand Down Expand Up @@ -85,10 +86,27 @@ func TestIssueValidator_IsValid(t *testing.T) {
Result: constants.ErrNoIssue,
},
},
{
name: "should pass if issue is referenced as magic string",
args: args{
prNumber: 2,
meta: &entity.Meta{
Name: "conventional-pr",
Owner: "namcheee",
},
body: "Closes #3",
config: true,
},
want: &entity.ValidationResult{
Name: constants.IssueValidatorName,
Active: true,
Result: nil,
},
},
{
name: "should pass if data fetching failed",
args: args{
prNumber: 3,
prNumber: 99,
meta: &entity.Meta{
Name: "conventional-pr",
Owner: "Namchee",
Expand All @@ -111,6 +129,7 @@ func TestIssueValidator_IsValid(t *testing.T) {
pullRequest := &entity.PullRequest{
Number: tc.args.prNumber,
Repository: *tc.args.meta,
Body: tc.args.body,
}

client := mocks.NewGithubClientMock()
Expand Down

0 comments on commit b31518c

Please sign in to comment.