-
Notifications
You must be signed in to change notification settings - Fork 4
/
apexcov.go
158 lines (126 loc) · 3.77 KB
/
apexcov.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package main
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"github.com/urfave/cli"
)
// main is the entry point for the apexcov CLI application
func main() {
app := cli.NewApp()
app.Usage = "a Test Coverage Generator for Apex"
app.Version = "1.0.0"
app.Author = "Jean-Philippe Monette"
app.Email = "contact@jpmonette.net"
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "instance,i",
Value: "https://login.salesforce.com",
Usage: "instance to use",
},
cli.StringFlag{
Name: "username,u",
Value: os.Getenv("APEXCOV_USERNAME"),
Usage: "username of the Salesforge org",
},
cli.StringFlag{
Name: "password,p",
Value: os.Getenv("APEXCOV_PASSWORD"),
Usage: "password of the Salesforge org",
},
}
app.Action = apexcov
app.Run(os.Args)
}
// apexcov handles the code coverage command
func apexcov(c *cli.Context) error {
username := c.String("username")
password := c.String("password")
instance := c.String("instance")
if os.Getenv("APEXCOV_INSTANCE") != "" {
instance = os.Getenv("APEXCOV_INSTANCE")
}
if username == "" {
return cli.NewExitError("You must provide a username", 1)
} else if password == "" {
return cli.NewExitError("You must provide a password", 1)
} else if _, err := url.ParseRequestURI(instance); err != nil {
return cli.NewExitError("You must provide a valid instance URL", 1)
}
instanceUrl, sessionId, err := login(instance, username, password)
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
data, err := getCoverage(instanceUrl, sessionId)
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
body := "TN:\n"
dir, err := os.Getwd()
for _, class := range data.Records {
if strings.HasPrefix(class.Id, "01p") {
body += "SF:" + dir + "/src/classes/" + class.ApexClassOrTrigger.Name + ".cls\n"
} else {
body += "SF:" + dir + "/src/triggers/" + class.ApexClassOrTrigger.Name + ".cls\n"
}
for _, line := range class.Coverage.CoveredLines {
body += "DA:" + strconv.Itoa(line) + ",1\n"
}
for _, line := range class.Coverage.UncoveredLines {
body += "DA:" + strconv.Itoa(line) + ",0\n"
}
body += "end_of_record\n"
}
persistCoverage(body)
return nil
}
// getCoverage gets the Apex code coverage from the Salesforce instance
func getCoverage(instanceUrl, session string) (coverage CoverageResponse, err error) {
client := &http.Client{}
endpoint := instanceUrl + "/services/data/v39.0/tooling/query?q="
query := "SELECT ApexClassOrTriggerId, ApexClassorTrigger.Name, Coverage FROM ApexCodeCoverageAggregate"
req, err := http.NewRequest("GET", endpoint+url.QueryEscape(query), nil)
req.Header.Add("Authorization", "Bearer "+session)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("User-Agent", "apexcov")
response, err := client.Do(req)
if err != nil {
return coverage, err
}
defer response.Body.Close()
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
return coverage, err
}
err = json.Unmarshal(responseData, &coverage)
if err != nil {
return coverage, err
}
return
}
// persistCoverage stores the coverage in the lcov.info file
func persistCoverage(body string) error {
_, err := os.Stat("./coverage")
if os.IsNotExist(err) {
os.Mkdir("./coverage", 0777)
}
err = ioutil.WriteFile("./coverage/lcov.info", []byte(body), 0666)
return err
}
// CoverageResponse represents the format of the ApexCodeCoverageAggregate query response
type CoverageResponse struct {
Records []struct {
Id string `json:"ApexClassOrTriggerId"`
ApexClassOrTrigger struct {
Name string `json:"Name"`
} `json:"ApexClassOrTrigger"`
Coverage struct {
CoveredLines []int `json:"coveredLines"`
UncoveredLines []int `json:"uncoveredLines"`
} `json:"Coverage"`
} `json:"records"`
}