improve unit testing

This commit is contained in:
lddsb
2019-03-08 17:29:57 +08:00
parent dc155b39c7
commit d8514a85d7
8 changed files with 230 additions and 347 deletions
+9 -3
View File
@@ -21,13 +21,19 @@ steps:
- master
status:
- success
event:
- push
settings:
repo: lddsb/drone-dingtalk-message
dockerfile: Dockerfile
tags: latest
tags:
- latest
- 1.0.0
username:
from_secret: docker_username
password:
from_secret: docker_password
trigger:
branch:
- master
event:
- tags
Generated
+9
View File
@@ -12,6 +12,14 @@
revision = "23d116af351c84513e1946b527c88823e476be13"
version = "v1.3.0"
[[projects]]
branch = "master"
digest = "1:9142979c770f3d0f3c42c2eec532048bbbe2571134da91e8946cd8610c85c04b"
name = "github.com/lddsb/dingtalk-webhook"
packages = ["."]
pruneopts = "UT"
revision = "b4abe34b5fa9af8ea7d5f28c02bd314558b21f7f"
[[projects]]
digest = "1:b24d38b282bacf9791408a080f606370efa3d364e4b5fd9ba0f7b87786d3b679"
name = "github.com/urfave/cli"
@@ -25,6 +33,7 @@
analyzer-version = 1
input-imports = [
"github.com/joho/godotenv/autoload",
"github.com/lddsb/dingtalk-webhook",
"github.com/urfave/cli",
]
solver-name = "gps-cdcl"
+4
View File
@@ -36,3 +36,7 @@
[prune]
go-tests = true
unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/lddsb/dingtalk-webhook"
+35 -31
View File
@@ -148,35 +148,36 @@ func main() {
if err := app.Run(os.Args); nil != err {
log.Println(err)
os.Exit(1)
}
}
// run with args
func run(c *cli.Context) {
plugin := Plugin{
// repo info
Repo: Repo{
FullName: c.String("repo.fullname"),
},
// build info
Build: Build{
Status: c.String("build.status"),
Link: c.String("build.link"),
},
Commit: Commit{
Sha: c.String("commit.sha"),
Branch: c.String("commit.branch"),
Message: c.String("commit.message"),
Link: c.String("commit.link"),
Authors: struct {
Avatar string
Email string
Name string
}{
Avatar: c.String("commit.author.avatar"),
Email: c.String("commit.author.email"),
Name: c.String("commit.author.name"),
Drone: Drone{
// repo info
Repo: Repo{
FullName: c.String("repo.fullname"),
},
// build info
Build: Build{
Status: c.String("build.status"),
Link: c.String("build.link"),
},
Commit: Commit{
Sha: c.String("commit.sha"),
Branch: c.String("commit.branch"),
Message: c.String("commit.message"),
Link: c.String("commit.link"),
Authors: struct {
Avatar string
Email string
Name string
}{
Avatar: c.String("commit.author.avatar"),
Email: c.String("commit.author.email"),
Name: c.String("commit.author.name"),
},
},
},
// custom config
@@ -189,18 +190,21 @@ func run(c *cli.Context) {
Debug: c.Bool("config.debug"),
},
Extra: Extra{
SuccessPicUrl: c.String("config.success.pic.url"),
FailurePicUrl: c.String("config.failure.pic.url"),
SuccessColor: c.String("config.success.color"),
FailureColor: c.String("config.failure.color"),
WithColor: c.Bool("config.message.color"),
WithPic: c.Bool("config.message.pic"),
LinkSha: c.Bool("config.message.sha.link"),
Pic: ExtraPic{
WithPic: c.Bool("config.message.pic"),
SuccessPicURL: c.String("config.success.pic.url"),
FailurePicURL: c.String("config.failure.pic.url"),
},
Color: ExtraColor{
SuccessColor: c.String("config.success.color"),
FailureColor: c.String("config.failure.color"),
WithColor: c.Bool("config.message.color"),
},
LinkSha: c.Bool("config.message.sha.link"),
},
}
if err := plugin.Exec(); nil != err {
fmt.Println(err)
os.Exit(1)
}
}
+10
View File
@@ -0,0 +1,10 @@
package main
import (
"testing"
)
func TestMain(t *testing.T) {
main()
t.Log("main testing finished")
}
+104 -70
View File
@@ -5,6 +5,8 @@ import (
"fmt"
"log"
"strings"
webhook "github.com/lddsb/dingtalk-webhook"
)
type (
@@ -12,58 +14,85 @@ type (
Repo struct {
FullName string // repository full name
}
// Build `build info`
Build struct {
Status string // providers the current build status
Link string // providers the current build link
}
// Commit `commit info`
Commit struct {
Branch string // providers the branch for the current commit
Link string // providers the http link to the current commit in the remote source code management system(e.g.GitHub)
Message string // providers the commit message for the current build
Sha string // providers the commit sha for the current build
// repo author info
Authors struct {
Avatar string // providers the author avatar for the current commit
Email string // providers the author email for the current commit
Name string // providers the author name for the current commit
}
Authors CommitAuthors
}
// CommitAuthors `commit author info`
CommitAuthors struct {
Avatar string // providers the author avatar for the current commit
Email string // providers the author email for the current commit
Name string // providers the author name for the current commit
}
// Drone `drone info`
Drone struct {
Repo Repo
Build Build
Commit Commit
}
// Config `plugin private config`
Config struct {
Debug bool
AccessToken string
IsAtALL bool
Mobiles string
Username string
MsgType string
Debug bool
AccessToken string
IsAtALL bool
Mobiles string
Username string
MsgType string
}
// MessageConfig `DingTalk message struct`
MessageConfig struct {
ActionCard ActionCard
}
// ActionCard `action card message struct`
ActionCard struct {
LinkUrls string
LinkTitles string
HideAvatar bool
BtnOrientation bool
}
// Extra `extra variables`
Extra struct {
PicURL string
MsgURL string
SuccessPicUrl string
FailurePicUrl string
SuccessColor string
FailureColor string
WithColor bool
Color ExtraColor
Pic ExtraPic
LinkSha bool
}
// ExtraPic `extra config for pic`
ExtraPic struct {
WithPic bool
LinkSha bool
SuccessPicURL string
FailurePicURL string
}
// ExtraColor `extra config for color`
ExtraColor struct {
WithColor bool
SuccessColor string
FailureColor string
}
// Plugin `plugin all config`
Plugin struct {
Commit Commit
Repo Repo
Build Build
Config Config
Extra Extra
WebHook *WebHook
Drone Drone
Config Config
Extra Extra
}
)
@@ -74,25 +103,30 @@ func (p *Plugin) Exec() error {
msg := "missing dingtalk access token"
return errors.New(msg)
}
p.WebHook = NewWebHook(p.Config.AccessToken)
if 6 > len(p.Drone.Commit.Sha) {
return errors.New("commit sha cannot short than 6")
}
newWebhook := webhook.NewWebHook(p.Config.AccessToken)
mobiles := strings.Split(p.Config.Mobiles, ",")
switch strings.ToLower(p.Config.MsgType) {
case "markdown":
err = p.WebHook.SendMarkdownMsg("You have a new message...", p.baseTpl(), p.Config.IsAtALL, mobiles...)
err = newWebhook.SendMarkdownMsg("You have a new message...", p.baseTpl(), p.Config.IsAtALL, mobiles...)
case "text":
err = p.WebHook.SendTextMsg(p.baseTpl(), p.Config.IsAtALL, mobiles...)
err = newWebhook.SendTextMsg(p.baseTpl(), p.Config.IsAtALL, mobiles...)
case "link":
err = p.WebHook.SendLinkMsg(p.Build.Status, p.baseTpl(), p.Commit.Authors.Avatar, p.Build.Link)
err = newWebhook.SendLinkMsg(p.Drone.Build.Status, p.baseTpl(), p.Drone.Commit.Authors.Avatar, p.Drone.Build.Link)
default:
msg := "not support message type"
err = errors.New(msg)
}
if err != nil {
return err
if err == nil {
log.Println("send message success!")
}
log.Println("send message success!")
return nil
return err
}
// markdownTpl `output the tpl of markdown`
@@ -101,44 +135,44 @@ func (p *Plugin) markdownTpl() string {
// title
title := fmt.Sprintf(" %s *Branch Build %s*",
strings.Title(p.Commit.Branch),
strings.Title(p.Build.Status))
strings.Title(p.Drone.Commit.Branch),
strings.Title(p.Drone.Build.Status))
// with color on title
if p.Extra.WithColor {
if p.Extra.Color.WithColor {
title = fmt.Sprintf("<font color=%s>%s</font>", p.getColor(), title)
}
tpl = fmt.Sprintf("# %s \n", title)
// with pic
if p.Extra.WithPic {
if p.Extra.Pic.WithPic {
tpl += fmt.Sprintf("![%s](%s)\n\n",
p.Build.Status,
p.getPicUrl())
p.Drone.Build.Status,
p.getPicURL())
}
// commit message
commitMsg := fmt.Sprintf("%s", p.Commit.Message)
if p.Extra.WithColor {
commitMsg := fmt.Sprintf("%s", p.Drone.Commit.Message)
if p.Extra.Color.WithColor {
commitMsg = fmt.Sprintf("<font color=%s>%s</font>", p.getColor(), commitMsg)
}
tpl += commitMsg + "\n\n"
// sha info
commitSha := p.Commit.Sha
commitSha := p.Drone.Commit.Sha
if p.Extra.LinkSha {
commitSha = fmt.Sprintf("[Click To %s Commit Detail Page](%s)", commitSha[:6], p.Commit.Link)
commitSha = fmt.Sprintf("[Click To %s Commit Detail Page](%s)", commitSha[:6], p.Drone.Commit.Link)
}
tpl += commitSha + "\n\n"
// author info
authorInfo := fmt.Sprintf("`%s(%s)`", p.Commit.Authors.Name, p.Commit.Authors.Email)
authorInfo := fmt.Sprintf("`%s(%s)`", p.Drone.Commit.Authors.Name, p.Drone.Commit.Authors.Email)
tpl += authorInfo + "\n\n"
// build detail link
buildDetail := fmt.Sprintf("[Click To The Build Detail Page %s](%s)",
p.getEmoticon(),
p.Build.Link)
p.Drone.Build.Link)
tpl += buildDetail
return tpl
}
@@ -154,20 +188,20 @@ func (p *Plugin) baseTpl() string {
@%s
%s (%s)
`,
p.Build.Status,
strings.TrimSpace(p.Commit.Message),
p.Repo.FullName,
p.Commit.Branch,
p.Commit.Sha,
p.Commit.Authors.Name,
p.Commit.Authors.Email)
p.Drone.Build.Status,
strings.TrimSpace(p.Drone.Commit.Message),
p.Drone.Repo.FullName,
p.Drone.Commit.Branch,
p.Drone.Commit.Sha,
p.Drone.Commit.Authors.Name,
p.Drone.Commit.Authors.Email)
case "link":
tpl = fmt.Sprintf(`%s(%s) @%s %s(%s)`,
p.Repo.FullName,
p.Commit.Branch,
p.Commit.Sha[:6],
p.Commit.Authors.Name,
p.Commit.Authors.Email)
p.Drone.Repo.FullName,
p.Drone.Commit.Branch,
p.Drone.Commit.Sha[:6],
p.Drone.Commit.Authors.Name,
p.Drone.Commit.Authors.Email)
case "actionCard":
// coming soon
@@ -184,7 +218,7 @@ func (p *Plugin) getEmoticon() string {
emoticons["success"] = ":)"
emoticons["failure"] = ":("
emoticon, ok := emoticons[p.Build.Status]
emoticon, ok := emoticons[p.Drone.Build.Status]
if ok {
return emoticon
}
@@ -195,20 +229,20 @@ func (p *Plugin) getEmoticon() string {
/**
get picture url
*/
func (p *Plugin) getPicUrl() string {
func (p *Plugin) getPicURL() string {
pics := make(map[string]string)
// success picture url
pics["success"] = "https://ws4.sinaimg.cn/large/006tNc79gy1fz05g5a7utj30he0bfjry.jpg"
if p.Extra.SuccessPicUrl != "" {
pics["success"] = p.Extra.SuccessPicUrl
if p.Extra.Pic.SuccessPicURL != "" {
pics["success"] = p.Extra.Pic.SuccessPicURL
}
// failure picture url
pics["failure"] = "https://ws1.sinaimg.cn/large/006tNc79gy1fz0b4fghpnj30hd0bdmxn.jpg"
if p.Extra.FailurePicUrl != "" {
pics["failure"] = p.Extra.FailurePicUrl
if p.Extra.Pic.FailurePicURL != "" {
pics["failure"] = p.Extra.Pic.FailurePicURL
}
url, ok := pics[p.Build.Status]
url, ok := pics[p.Drone.Build.Status]
if ok {
return url
}
@@ -223,16 +257,16 @@ func (p *Plugin) getColor() string {
colors := make(map[string]string)
// success color
colors["success"] = "#008000"
if p.Extra.SuccessColor != "" {
colors["success"] = "#" + p.Extra.SuccessColor
if p.Extra.Color.SuccessColor != "" {
colors["success"] = "#" + p.Extra.Color.SuccessColor
}
// failure color
colors["failure"] = "#FF0000"
if p.Extra.FailureColor != "" {
colors["failure"] = "#" + p.Extra.FailureColor
if p.Extra.Color.FailureColor != "" {
colors["failure"] = "#" + p.Extra.Color.FailureColor
}
color, ok := colors[p.Build.Status]
color, ok := colors[p.Drone.Build.Status]
if ok {
return color
}
+59
View File
@@ -0,0 +1,59 @@
package main
import (
"testing"
)
func TestPlugin(t *testing.T) {
p := Plugin{}
err := p.Exec()
if nil == err {
t.Error("access token empty error should be catch!")
}
p.Config.AccessToken = "example-access-token"
err = p.Exec()
if nil == err {
t.Error("commit sha length error should be catch!")
}
p.Drone.Commit.Sha = "53729847dfksj"
err = p.Exec()
if nil == err {
t.Error("not support message type error should be catch!")
}
p.Config.MsgType = "text"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
p.Config.MsgType = "link"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
p.Extra.Color.WithColor = true
p.Extra.Color.FailureColor = "#555555"
p.Extra.Color.SuccessColor = "#222222"
p.Extra.Pic.WithPic = true
p.Extra.Pic.FailurePicURL = "https://www.baidu.com"
p.Extra.Pic.SuccessPicURL = "https://www.baidu.com"
p.Extra.LinkSha = true
// p.Drone.Build.Status = "failure"
p.Config.MsgType = "markdown"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
p.Drone.Build.Status = "failure"
err = p.Exec()
if nil == err {
t.Error("access token invalid error should be catch!")
}
t.Log("plugin testing finished")
}
-243
View File
@@ -1,243 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
)
// LinkMsg `link message struct`
type LinkMsg struct {
Title string `json:"title"`
MessageURL string `json:"messageURL"`
PicURL string `json:"picURL"`
}
// ActionCard `action card message struct`
type ActionCard struct {
Text string `json:"text"`
Title string `json:"title"`
SingleTitle string `json:"singleTitle"`
SingleURL string `json:"singleURL"`
BtnOrientation string `json:"btnOrientation"`
HideAvatar string `json:"hideAvatar"` // robot message avatar
Buttons []struct {
Title string `json:"title"`
ActionURL string `json:"actionURL"`
} `json:"btns"`
}
// PayLoad payload
type PayLoad struct {
MsgType string `json:"msgtype"`
Text struct {
Content string `json:"content"`
} `json:"text"`
Link struct {
Title string `json:"title"`
Text string `json:"text"`
PicUrl string `json:"picUrl"`
MessageUrl string `json:"messageUrl"`
} `json:"link"`
Markdown struct {
Title string `json:"title"`
Text string `json:"text"`
} `json:"markdown"`
ActionCard ActionCard `json:"actionCard"`
FeedCard struct {
Links []LinkMsg `json:"links"`
} `json:"feedCard"`
At struct {
AtMobiles []string `json:"atMobiles"`
IsAtAll bool `json:"isAtAll"`
} `json:"at"`
}
// WebHook `web hook base config`
type WebHook struct {
AccessToken string `json:"accessToken"`
}
// NewWebHook `new a webhook`
func NewWebHook(accessToken string) *WebHook {
return &WebHook{AccessToken: accessToken}
}
// Response `dingtalk webhook response struct`
type Response struct {
ErrorCode int `json:"errcode"`
ErrorMessage string `json:"errmsg"`
}
var baseApi = "https://oapi.dingtalk.com/robot/send?access_token="
var reg = `^1([38][0-9]|14[57]|5[^4])\d{8}$`
var regx = regexp.MustCompile(reg)
// real send request to api
func (w *WebHook) sendPayload(payload *PayLoad) error {
// get config
bs, err := json.Marshal(payload)
if nil != err {
return err
}
// request api
resp, err := http.Post(baseApi+w.AccessToken, "application/json", bytes.NewReader(bs))
if nil != err {
return err
}
// read response body
body, err := ioutil.ReadAll(resp.Body)
if nil != err {
return err
}
// api unusual
if 200 != resp.StatusCode {
return fmt.Errorf("%d: %s", resp.StatusCode, string(body))
}
var result Response
// json decode
err = json.Unmarshal(body, &result)
if nil != err {
return err
}
if 0 != result.ErrorCode {
return fmt.Errorf("%d: %s", result.ErrorCode, result.ErrorMessage)
}
return nil
}
// SendTextMsg `send a text message`
func (w *WebHook) SendTextMsg(content string, isAtAll bool, mobiles ...string) error {
// send request
return w.sendPayload(&PayLoad{
MsgType: "text",
Text: struct {
Content string `json:"content"`
}{
Content: content,
},
At: struct {
AtMobiles []string `json:"atMobiles"`
IsAtAll bool `json:"isAtAll"`
}{
AtMobiles: mobiles,
IsAtAll: isAtAll,
},
})
}
// SendLinkMsg `send a link message`
func (w *WebHook) SendLinkMsg(title, content, picURL, msgURL string) error {
return w.sendPayload(&PayLoad{
MsgType: "link",
Link: struct {
Title string `json:"title"`
Text string `json:"text"`
PicUrl string `json:"picUrl"`
MessageUrl string `json:"messageUrl"`
}{
Title: title,
Text: content,
PicUrl: picURL,
MessageUrl: msgURL,
},
})
}
// SendMarkdownMsg `send a markdown msg`
func (w *WebHook) SendMarkdownMsg(title, content string, isAtAll bool, mobiles ...string) error {
firstLine := false
for _, mobile := range mobiles {
if regx.MatchString(mobile) {
if false == firstLine {
content += "#####"
}
content += " @" + mobile
firstLine = true
}
}
// send request
return w.sendPayload(&PayLoad{
MsgType: "markdown",
Markdown: struct {
Title string `json:"title"`
Text string `json:"text"`
}{
Title: title,
Text: content,
},
At: struct {
AtMobiles []string `json:"atMobiles"`
IsAtAll bool `json:"isAtAll"`
}{
AtMobiles: mobiles,
IsAtAll: isAtAll,
},
})
}
// SendActionCardMsg `send single action card message`
func (w *WebHook) SendActionCardMsg(title, content string, linkTitles, linkUrls []string, hideAvatar, btnOrientation bool) error {
// validation is empty
if 0 == len(linkTitles) || 0 == len(linkUrls) {
return errors.New("links or titles is empty")
}
// validation is equal
if len(linkUrls) != len(linkTitles) {
return errors.New("links length and titles length is not equal")
}
// hide robot avatar
var strHideAvatar = "0"
if hideAvatar {
strHideAvatar = "1"
}
// button sort
var strBtnOrientation = "0"
if btnOrientation {
strBtnOrientation = "1"
}
// button struct
var buttons []struct {
Title string `json:"title"`
ActionURL string `json:"actionURL"`
}
// inject to button
for i := 0; i < len(linkTitles); i++ {
buttons = append(buttons, struct {
Title string `json:"title"`
ActionURL string `json:"actionURL"`
}{
Title: linkTitles[i],
ActionURL: linkUrls[i],
})
}
// send request
return w.sendPayload(&PayLoad{
MsgType: "actionCard",
ActionCard: ActionCard{
Title: title,
Text: content,
HideAvatar: strHideAvatar,
BtnOrientation: strBtnOrientation,
Buttons: buttons,
},
})
}
// SendLinkCardMsg `send link card message`
func (w *WebHook) SendLinkCardMsg(messages []LinkMsg) error {
return w.sendPayload(&PayLoad{
MsgType: "feedCard",
FeedCard: struct {
Links []LinkMsg `json:"links"`
}{
Links: messages,
},
})
}