You've already forked mpbot-github
mirror of
https://github.com/macports/mpbot-github.git
synced 2026-03-31 14:46:03 -07:00
Add tests
This commit is contained in:
@@ -2,12 +2,12 @@ package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"errors"
|
||||
// PostgreSQL driver
|
||||
_ "github.com/lib/pq"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Maintainer struct {
|
||||
@@ -22,30 +22,39 @@ type PortMaintainer struct {
|
||||
OpenMaintainer bool
|
||||
}
|
||||
|
||||
var tracDB *sql.DB
|
||||
var wwwDB *sql.DB
|
||||
var prDB *sql.DB
|
||||
|
||||
// Create connections to DBs
|
||||
func init() {
|
||||
var err error
|
||||
tracDB, err = sql.Open("postgres", os.Getenv("TRAC_DB"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
wwwDB, err = sql.Open("postgres", os.Getenv("WWW_DB"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
prDB, err = sql.Open("postgres", os.Getenv("PR_DB"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
type DBHelper interface {
|
||||
GetGitHubHandle(email string) (string, error)
|
||||
GetPortMaintainer(port string) (*PortMaintainer, error)
|
||||
}
|
||||
|
||||
func GetGitHubHandle(email string) (string, error) {
|
||||
func NewDBHelper() (DBHelper, error) {
|
||||
// TODO: move os.Getenv to main
|
||||
tracDB, err := sql.Open("postgres", os.Getenv("TRAC_DB"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wwwDB, err := sql.Open("postgres", os.Getenv("WWW_DB"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prDB, err := sql.Open("postgres", os.Getenv("PR_DB"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sqlDBHelper{
|
||||
tracDB: tracDB,
|
||||
wwwDB: wwwDB,
|
||||
prDB: prDB,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type sqlDBHelper struct {
|
||||
tracDB, wwwDB, prDB *sql.DB
|
||||
}
|
||||
|
||||
func (sqlDB *sqlDBHelper) GetGitHubHandle(email string) (string, error) {
|
||||
sid := ""
|
||||
err := tracDB.QueryRow("SELECT sid "+
|
||||
err := sqlDB.tracDB.QueryRow("SELECT sid "+
|
||||
"FROM trac_macports.session_attribute "+
|
||||
"WHERE value = $1 "+
|
||||
"AND name = 'email' "+
|
||||
@@ -59,8 +68,8 @@ func GetGitHubHandle(email string) (string, error) {
|
||||
}
|
||||
|
||||
// GetPortMaintainer returns the maintainers of a port
|
||||
func GetPortMaintainer(port string) (*PortMaintainer, error) {
|
||||
rows, err := wwwDB.Query("SELECT maintainer, is_primary "+
|
||||
func (sqlDB *sqlDBHelper) GetPortMaintainer(port string) (*PortMaintainer, error) {
|
||||
rows, err := sqlDB.wwwDB.Query("SELECT maintainer, is_primary "+
|
||||
"FROM public.maintainers "+
|
||||
"WHERE portfile = $1", port)
|
||||
if err != nil {
|
||||
@@ -87,9 +96,9 @@ func GetPortMaintainer(port string) (*PortMaintainer, error) {
|
||||
continue
|
||||
}
|
||||
if isPrimary {
|
||||
maintainer.Primary = parseMaintainer(maintainerCursor)
|
||||
maintainer.Primary = sqlDB.parseMaintainer(maintainerCursor)
|
||||
} else {
|
||||
maintainer.Others = append(maintainer.Others, parseMaintainer(maintainerCursor))
|
||||
maintainer.Others = append(maintainer.Others, sqlDB.parseMaintainer(maintainerCursor))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +109,17 @@ func GetPortMaintainer(port string) (*PortMaintainer, error) {
|
||||
return maintainer, nil
|
||||
}
|
||||
|
||||
func parseMaintainer(maintainerFullString string) *Maintainer {
|
||||
func (sqlDB *sqlDBHelper) parseMaintainer(maintainerFullString string) *Maintainer {
|
||||
maintainer := parseMaintainerString(maintainerFullString)
|
||||
if maintainer.GithubHandle == "" && maintainer.Email != "" {
|
||||
if handle, err := sqlDB.GetGitHubHandle(maintainer.Email); err == nil {
|
||||
maintainer.GithubHandle = handle
|
||||
}
|
||||
}
|
||||
return maintainer
|
||||
}
|
||||
|
||||
func parseMaintainerString(maintainerFullString string) *Maintainer {
|
||||
maintainerStrings := strings.Split(maintainerFullString, " ")
|
||||
maintainer := new(Maintainer)
|
||||
for _, maintainerString := range maintainerStrings {
|
||||
@@ -113,10 +132,5 @@ func parseMaintainer(maintainerFullString string) *Maintainer {
|
||||
maintainer.Email = maintainerString + "@macports.org"
|
||||
}
|
||||
}
|
||||
if maintainer.GithubHandle == "" && maintainer.Email != "" {
|
||||
if handle, err := GetGitHubHandle(maintainer.Email); err == nil {
|
||||
maintainer.GithubHandle = handle
|
||||
}
|
||||
}
|
||||
return maintainer
|
||||
}
|
||||
|
||||
17
pr/db/dbutil_test.go
Normal file
17
pr/db/dbutil_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package db
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseMaintainerString(t *testing.T) {
|
||||
l2dy := parseMaintainerString("l2dy @l2dy")
|
||||
if l2dy.Email != "l2dy@macports.org" {
|
||||
t.Error("Expected @macports.org email, got", l2dy.Email)
|
||||
}
|
||||
if l2dy.GithubHandle != "l2dy" {
|
||||
t.Error("Expected GitHub login, got", l2dy.GithubHandle)
|
||||
}
|
||||
jverne := parseMaintainerString("@jverne example.org:julesverne")
|
||||
if jverne.Email != "julesverne@example.org" {
|
||||
t.Error("Expected deobfuscated email, got", jverne.Email)
|
||||
}
|
||||
}
|
||||
34
pr/githubapi/client.go
Normal file
34
pr/githubapi/client.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package githubapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
ListChangedPortsAndFiles(owner, repo string, number int) (ports []string, commitFiles []*github.CommitFile, err error)
|
||||
CreateComment(owner, repo string, number int, body *string) error
|
||||
ReplaceLabels(owner, repo string, number int, labels []string) error
|
||||
ListLabels(owner, repo string, number int) ([]string, error)
|
||||
ListOrgMembers(org string) ([]*github.User, error)
|
||||
}
|
||||
|
||||
type githubClient struct {
|
||||
*github.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func NewClient(botSecret string) Client {
|
||||
ctx := context.Background()
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: botSecret},
|
||||
)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
|
||||
return &githubClient{
|
||||
Client: github.NewClient(tc),
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
8
pr/githubapi/org.go
Normal file
8
pr/githubapi/org.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package githubapi
|
||||
|
||||
import "github.com/google/go-github/github"
|
||||
|
||||
func (client *githubClient) ListOrgMembers(org string) ([]*github.User, error) {
|
||||
users, _, err := client.Organizations.ListMembers(client.ctx, org, nil)
|
||||
return users, err
|
||||
}
|
||||
@@ -5,28 +5,9 @@ import (
|
||||
"regexp"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*github.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func NewClient(botSecret string) *Client {
|
||||
ctx := context.Background()
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: botSecret},
|
||||
)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
|
||||
return &Client{
|
||||
Client: github.NewClient(tc),
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (client *Client) ListChangedPortsAndFiles(owner, repo string, number int) (ports []string, commitFiles []*github.CommitFile, err error) {
|
||||
func (client *githubClient) ListChangedPortsAndFiles(owner, repo string, number int) (ports []string, commitFiles []*github.CommitFile, err error) {
|
||||
files, _, err := client.PullRequests.ListFiles(
|
||||
context.Background(),
|
||||
owner,
|
||||
@@ -47,7 +28,7 @@ func (client *Client) ListChangedPortsAndFiles(owner, repo string, number int) (
|
||||
return
|
||||
}
|
||||
|
||||
func (client *Client) CreateComment(owner, repo string, number int, body *string) error {
|
||||
func (client *githubClient) CreateComment(owner, repo string, number int, body *string) error {
|
||||
_, _, err := client.Issues.CreateComment(
|
||||
client.ctx,
|
||||
owner,
|
||||
@@ -58,7 +39,7 @@ func (client *Client) CreateComment(owner, repo string, number int, body *string
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *Client) ReplaceLabels(owner, repo string, number int, labels []string) error {
|
||||
func (client *githubClient) ReplaceLabels(owner, repo string, number int, labels []string) error {
|
||||
_, _, err := client.Issues.ReplaceLabelsForIssue(
|
||||
client.ctx,
|
||||
owner,
|
||||
@@ -69,7 +50,7 @@ func (client *Client) ReplaceLabels(owner, repo string, number int, labels []str
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *Client) ListLabels(owner, repo string, number int) ([]string, error) {
|
||||
func (client *githubClient) ListLabels(owner, repo string, number int) ([]string, error) {
|
||||
labels, _, err := client.Issues.ListLabelsByIssue(
|
||||
client.ctx,
|
||||
owner,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/macports/mpbot-github/pr/db"
|
||||
"github.com/macports/mpbot-github/pr/webhook"
|
||||
)
|
||||
|
||||
@@ -22,10 +23,14 @@ func main() {
|
||||
}
|
||||
|
||||
prodFlag := false
|
||||
|
||||
if os.Getenv("BOT_ENV") == "production" {
|
||||
prodFlag = true
|
||||
}
|
||||
|
||||
webhook.NewReceiver(*webhookAddr, hookSecret, botSecret, prodFlag).Start()
|
||||
dbHelper, err := db.NewDBHelper()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
webhook.NewReceiver(*webhookAddr, hookSecret, botSecret, prodFlag, dbHelper).Start()
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/macports/mpbot-github/pr/db"
|
||||
)
|
||||
|
||||
var cveRegexp = regexp.MustCompile(`CVE-\d{4}-\d+`)
|
||||
@@ -49,7 +48,7 @@ func (receiver *Receiver) handlePullRequest(body []byte) {
|
||||
// If PR sender is maintainer of one of the ports changed
|
||||
isOneMaintainer := false
|
||||
for i, port := range ports {
|
||||
portMaintainer, err := db.GetPortMaintainer(port)
|
||||
portMaintainer, err := receiver.dbHelper.GetPortMaintainer(port)
|
||||
if err != nil {
|
||||
// TODO: handle submission of duplicate ports
|
||||
if err.Error() == "port not found" && *files[i].Status == "added" {
|
||||
@@ -168,7 +167,9 @@ func (receiver *Receiver) handlePullRequest(body []byte) {
|
||||
// fallthrough
|
||||
//case "synchronize":
|
||||
}
|
||||
log.Println("PR #" + strconv.Itoa(number) + " processed")
|
||||
if !receiver.testing {
|
||||
log.Println("PR #" + strconv.Itoa(number) + " processed")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use map to dedup
|
||||
|
||||
3
pr/webhook/pull_request_review.go
Normal file
3
pr/webhook/pull_request_review.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package webhook
|
||||
|
||||
func (receiver *Receiver) handlePullRequestReview(body []byte) {}
|
||||
188
pr/webhook/pull_request_test.go
Normal file
188
pr/webhook/pull_request_test.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/macports/mpbot-github/pr/db"
|
||||
)
|
||||
|
||||
var errNotFound = errors.New("404")
|
||||
|
||||
func TestCVERegexp(t *testing.T) {
|
||||
assert.Equal(t, "CVE-2017-0001", cveRegexp.FindString("Fixes CVE-2017-0001."))
|
||||
assert.Equal(t, "", cveRegexp.FindString("CVE-pending."))
|
||||
}
|
||||
|
||||
type PullRequestEventTest struct {
|
||||
number int
|
||||
sender string
|
||||
title string
|
||||
body string
|
||||
comment string
|
||||
labels []string
|
||||
}
|
||||
|
||||
func TestHandlePullRequest(t *testing.T) {
|
||||
stubClient := stubGitHubClient{}
|
||||
receiver := &Receiver{
|
||||
githubClient: &stubClient,
|
||||
dbHelper: &stubDBHelper{},
|
||||
testing: true,
|
||||
}
|
||||
var event github.PullRequestEvent
|
||||
json.Unmarshal([]byte(`{
|
||||
"action": "opened",
|
||||
"number": 1,
|
||||
"pull_request": {
|
||||
"number": 1,
|
||||
"state": "open",
|
||||
"title": "",
|
||||
"user": {
|
||||
"login": "l2dy"
|
||||
},
|
||||
"body": ""
|
||||
},
|
||||
"repository": {
|
||||
"name": "macports-ports",
|
||||
"owner": {
|
||||
"login": "macports"
|
||||
}
|
||||
},
|
||||
"sender": {
|
||||
"login": "l2dy"
|
||||
}
|
||||
}`), &event)
|
||||
prTests := []*PullRequestEventTest{
|
||||
{number: 1, sender: "l2dy", title: "z: update to 1.1", labels: []string{"maintainer: none", "type: update"}},
|
||||
{number: 1, sender: "l2dy", title: "z: update to 1.1", body: "[x] enhancement", labels: []string{"maintainer: none", "type: update", "type: enhancement"}},
|
||||
{number: 1, sender: "l2dy", title: "z: update to 1.1", body: "Fixes CVE-0000-0.", labels: []string{"maintainer: none", "type: update", "type: security fix"}},
|
||||
{number: 2, sender: "l2dy", title: "upx-devel: new port", labels: []string{"type: submission"}},
|
||||
{number: 3, sender: "l2dy", title: "upx: update to 1.1", labels: []string{"maintainer", "maintainer: open", "type: update"}},
|
||||
{number: 3, sender: "jverne", title: "upx: update to 1.1", comment: "Notifying maintainers:\n@_l2dy for port upx.\n\nBy a harmless bot.", labels: []string{"maintainer: open", "type: update"}},
|
||||
{number: 3, sender: "jverne", title: "upx: update to 1.1", body: "<!-- [skip notification] -->", labels: []string{"maintainer: open", "type: update"}},
|
||||
}
|
||||
for _, prt := range prTests {
|
||||
stubClient.newComment = ""
|
||||
stubClient.newLabels = nil
|
||||
event.Number = &prt.number
|
||||
event.Sender.Login = &prt.sender
|
||||
event.PullRequest.Title = &prt.title
|
||||
event.PullRequest.Body = &prt.body
|
||||
eventBody, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
receiver.handlePullRequest(eventBody)
|
||||
assert.Equal(t, prt.comment, stubClient.newComment)
|
||||
assert.Subset(t, stubClient.newLabels, prt.labels)
|
||||
assert.Subset(t, prt.labels, stubClient.newLabels)
|
||||
}
|
||||
}
|
||||
|
||||
type stubGitHubClient struct {
|
||||
newComment string
|
||||
newLabels []string
|
||||
}
|
||||
|
||||
func (stub *stubGitHubClient) ListChangedPortsAndFiles(owner, repo string, number int) (ports []string, commitFiles []*github.CommitFile, err error) {
|
||||
if owner != "macports" || repo != "macports-ports" {
|
||||
return nil, nil, errNotFound
|
||||
}
|
||||
switch number {
|
||||
case 1:
|
||||
return []string{"z"},
|
||||
[]*github.CommitFile{
|
||||
{
|
||||
Filename: ptrOfStr("sysutils/z/Portfile"),
|
||||
Status: ptrOfStr("modified"),
|
||||
Changes: ptrOfInt(6),
|
||||
},
|
||||
}, nil
|
||||
case 2:
|
||||
return []string{"upx-devel"},
|
||||
[]*github.CommitFile{
|
||||
{
|
||||
Filename: ptrOfStr("archivers/upx-devel/Portfile"),
|
||||
Status: ptrOfStr("added"),
|
||||
Changes: ptrOfInt(43),
|
||||
},
|
||||
}, nil
|
||||
case 3:
|
||||
return []string{"upx"},
|
||||
[]*github.CommitFile{
|
||||
{
|
||||
Filename: ptrOfStr("archivers/upx/Portfile"),
|
||||
Status: ptrOfStr("modified"),
|
||||
Changes: ptrOfInt(6),
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, nil, errNotFound
|
||||
}
|
||||
}
|
||||
|
||||
func (stub *stubGitHubClient) CreateComment(owner, repo string, number int, body *string) error {
|
||||
stub.newComment = *body
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *stubGitHubClient) ReplaceLabels(owner, repo string, number int, labels []string) error {
|
||||
stub.newLabels = labels
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *stubGitHubClient) ListLabels(owner, repo string, number int) ([]string, error) {
|
||||
if owner != "macports" || repo != "macports-ports" {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (stub *stubGitHubClient) ListOrgMembers(org string) ([]*github.User, error) {
|
||||
return []*github.User{
|
||||
{Login: ptrOfStr("l2dy")},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubDBHelper struct{}
|
||||
|
||||
func (stub *stubDBHelper) GetGitHubHandle(email string) (string, error) {
|
||||
if email == "l2dy@macports.org" {
|
||||
return "l2dy", nil
|
||||
}
|
||||
return "", errNotFound
|
||||
}
|
||||
|
||||
func (stub *stubDBHelper) GetPortMaintainer(port string) (*db.PortMaintainer, error) {
|
||||
if port == "upx" {
|
||||
return &db.PortMaintainer{
|
||||
Primary: &db.Maintainer{
|
||||
GithubHandle: "l2dy",
|
||||
Email: "l2dy@macports.org",
|
||||
},
|
||||
NoMaintainer: false,
|
||||
OpenMaintainer: true,
|
||||
}, nil
|
||||
}
|
||||
if port == "z" {
|
||||
return &db.PortMaintainer{
|
||||
Primary: nil,
|
||||
NoMaintainer: true,
|
||||
OpenMaintainer: false,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("port not found")
|
||||
}
|
||||
|
||||
func ptrOfStr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func ptrOfInt(s int) *int {
|
||||
return &s
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/macports/mpbot-github/pr/db"
|
||||
"github.com/macports/mpbot-github/pr/githubapi"
|
||||
)
|
||||
|
||||
@@ -15,15 +16,18 @@ type Receiver struct {
|
||||
listenAddr string
|
||||
hookSecret []byte
|
||||
production bool
|
||||
githubClient *githubapi.Client
|
||||
testing bool
|
||||
githubClient githubapi.Client
|
||||
dbHelper db.DBHelper
|
||||
}
|
||||
|
||||
func NewReceiver(listenAddr string, hookSecret []byte, botSecret string, production bool) *Receiver {
|
||||
func NewReceiver(listenAddr string, hookSecret []byte, botSecret string, production bool, dbHelper db.DBHelper) *Receiver {
|
||||
return &Receiver{
|
||||
listenAddr: listenAddr,
|
||||
hookSecret: hookSecret,
|
||||
production: production,
|
||||
githubClient: githubapi.NewClient(botSecret),
|
||||
dbHelper: dbHelper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +64,8 @@ func (receiver *Receiver) Start() {
|
||||
return
|
||||
case "pull_request":
|
||||
go receiver.handlePullRequest(body)
|
||||
case "pull_request_review":
|
||||
go receiver.handlePullRequestReview(body)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
Reference in New Issue
Block a user