This commit expands the `Step` structure used with GitHub Actions workflow runs to include fields indicating when steps start and complete. This information is already provided by the GitHub API, so this only involves expanding the structure, fields exported, and the associated tests. In the future, I could see `gh` including the duration calculation which is used when viewing workflow or workflow run.
222 lines
6.7 KiB
Go
222 lines
6.7 KiB
Go
package shared
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/MakeNowJust/heredoc"
|
|
"github.com/cli/cli/v2/api"
|
|
"github.com/cli/cli/v2/internal/ghrepo"
|
|
"github.com/cli/cli/v2/pkg/httpmock"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPreciseAgo(t *testing.T) {
|
|
const form = "2006-Jan-02 15:04:05"
|
|
now, _ := time.Parse(form, "2021-Apr-12 14:00:00")
|
|
|
|
cases := map[string]string{
|
|
"2021-Apr-12 14:00:00": "0s ago",
|
|
"2021-Apr-12 13:59:30": "30s ago",
|
|
"2021-Apr-12 13:59:00": "1m0s ago",
|
|
"2021-Apr-12 13:30:15": "29m45s ago",
|
|
"2021-Apr-12 13:00:00": "1h0m0s ago",
|
|
"2021-Apr-12 02:30:45": "11h29m15s ago",
|
|
"2021-Apr-11 14:00:00": "24h0m0s ago",
|
|
"2021-Apr-01 14:00:00": "264h0m0s ago",
|
|
"2021-Mar-12 14:00:00": "Mar 12, 2021",
|
|
}
|
|
|
|
for createdAt, expected := range cases {
|
|
d, _ := time.Parse(form, createdAt)
|
|
got := preciseAgo(now, d)
|
|
if got != expected {
|
|
t.Errorf("expected %s but got %s for %s", expected, got, createdAt)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetAnnotations404(t *testing.T) {
|
|
reg := &httpmock.Registry{}
|
|
defer reg.Verify(t)
|
|
|
|
reg.Register(
|
|
httpmock.REST("GET", "repos/OWNER/REPO/check-runs/123456/annotations"),
|
|
httpmock.StatusStringResponse(404, "not found"))
|
|
|
|
httpClient := &http.Client{Transport: reg}
|
|
apiClient := api.NewClientFromHTTP(httpClient)
|
|
repo := ghrepo.New("OWNER", "REPO")
|
|
|
|
result, err := GetAnnotations(apiClient, repo, Job{ID: 123456, Name: "a job"})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, result, []Annotation{})
|
|
}
|
|
|
|
func TestRun_Duration(t *testing.T) {
|
|
now, _ := time.Parse(time.RFC3339, "2022-07-20T11:22:58Z")
|
|
|
|
tests := []struct {
|
|
name string
|
|
json string
|
|
wants string
|
|
}{
|
|
{
|
|
name: "no run_started_at",
|
|
json: heredoc.Doc(`
|
|
{
|
|
"created_at": "2022-07-20T11:20:13Z",
|
|
"updated_at": "2022-07-20T11:21:16Z",
|
|
"status": "completed"
|
|
}`),
|
|
wants: "1m3s",
|
|
},
|
|
{
|
|
name: "with run_started_at",
|
|
json: heredoc.Doc(`
|
|
{
|
|
"created_at": "2022-07-20T11:20:13Z",
|
|
"run_started_at": "2022-07-20T11:20:55Z",
|
|
"updated_at": "2022-07-20T11:21:16Z",
|
|
"status": "completed"
|
|
}`),
|
|
wants: "21s",
|
|
},
|
|
{
|
|
name: "in_progress",
|
|
json: heredoc.Doc(`
|
|
{
|
|
"created_at": "2022-07-20T11:20:13Z",
|
|
"run_started_at": "2022-07-20T11:20:55Z",
|
|
"updated_at": "2022-07-20T11:21:16Z",
|
|
"status": "in_progress"
|
|
}`),
|
|
wants: "2m3s",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var r Run
|
|
assert.NoError(t, json.Unmarshal([]byte(tt.json), &r))
|
|
assert.Equal(t, tt.wants, r.Duration(now).String())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRunExportData(t *testing.T) {
|
|
oldestStartedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:20:13Z")
|
|
oldestStepStartedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:20:15Z")
|
|
oldestStepCompletedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:21:10Z")
|
|
oldestCompletedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:21:16Z")
|
|
newestStartedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:20:55Z")
|
|
newestStepStartedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:21:01Z")
|
|
newestStepCompletedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:23:10Z")
|
|
newestCompletedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:23:16Z")
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields []string
|
|
run Run
|
|
output string
|
|
}{
|
|
{
|
|
name: "exports workflow run's single job",
|
|
fields: []string{"jobs"},
|
|
run: Run{
|
|
Jobs: []Job{
|
|
{
|
|
ID: 123456,
|
|
Status: "completed",
|
|
Conclusion: "success",
|
|
Name: "macos",
|
|
Steps: []Step{
|
|
{
|
|
Name: "Checkout",
|
|
Status: "completed",
|
|
Conclusion: "success",
|
|
Number: 1,
|
|
StartedAt: oldestStepStartedAt,
|
|
CompletedAt: oldestStepCompletedAt,
|
|
},
|
|
},
|
|
StartedAt: oldestStartedAt,
|
|
CompletedAt: oldestCompletedAt,
|
|
URL: "https://example.com/OWNER/REPO/actions/runs/123456",
|
|
},
|
|
},
|
|
},
|
|
output: `{"jobs":[{"completedAt":"2022-07-20T11:21:16Z","conclusion":"success","databaseId":123456,"name":"macos","startedAt":"2022-07-20T11:20:13Z","status":"completed","steps":[{"completedAt":"2022-07-20T11:21:10Z","conclusion":"success","name":"Checkout","number":1,"startedAt":"2022-07-20T11:20:15Z","status":"completed"}],"url":"https://example.com/OWNER/REPO/actions/runs/123456"}]}`,
|
|
},
|
|
{
|
|
name: "exports workflow run's multiple jobs",
|
|
fields: []string{"jobs"},
|
|
run: Run{
|
|
Jobs: []Job{
|
|
{
|
|
ID: 123456,
|
|
Status: "completed",
|
|
Conclusion: "success",
|
|
Name: "macos",
|
|
Steps: []Step{
|
|
{
|
|
Name: "Checkout",
|
|
Status: "completed",
|
|
Conclusion: "success",
|
|
Number: 1,
|
|
StartedAt: oldestStepStartedAt,
|
|
CompletedAt: oldestStepCompletedAt,
|
|
},
|
|
},
|
|
StartedAt: oldestStartedAt,
|
|
CompletedAt: oldestCompletedAt,
|
|
URL: "https://example.com/OWNER/REPO/actions/runs/123456",
|
|
},
|
|
{
|
|
ID: 234567,
|
|
Status: "completed",
|
|
Conclusion: "error",
|
|
Name: "windows",
|
|
Steps: []Step{
|
|
{
|
|
Name: "Checkout",
|
|
Status: "completed",
|
|
Conclusion: "error",
|
|
Number: 2,
|
|
StartedAt: newestStepStartedAt,
|
|
CompletedAt: newestStepCompletedAt,
|
|
},
|
|
},
|
|
StartedAt: newestStartedAt,
|
|
CompletedAt: newestCompletedAt,
|
|
URL: "https://example.com/OWNER/REPO/actions/runs/234567",
|
|
},
|
|
},
|
|
},
|
|
output: `{"jobs":[{"completedAt":"2022-07-20T11:21:16Z","conclusion":"success","databaseId":123456,"name":"macos","startedAt":"2022-07-20T11:20:13Z","status":"completed","steps":[{"completedAt":"2022-07-20T11:21:10Z","conclusion":"success","name":"Checkout","number":1,"startedAt":"2022-07-20T11:20:15Z","status":"completed"}],"url":"https://example.com/OWNER/REPO/actions/runs/123456"},{"completedAt":"2022-07-20T11:23:16Z","conclusion":"error","databaseId":234567,"name":"windows","startedAt":"2022-07-20T11:20:55Z","status":"completed","steps":[{"completedAt":"2022-07-20T11:23:10Z","conclusion":"error","name":"Checkout","number":2,"startedAt":"2022-07-20T11:21:01Z","status":"completed"}],"url":"https://example.com/OWNER/REPO/actions/runs/234567"}]}`,
|
|
},
|
|
{
|
|
name: "exports workflow run with attempt count",
|
|
fields: []string{"attempt"},
|
|
run: Run{
|
|
Attempt: 1,
|
|
Jobs: []Job{},
|
|
},
|
|
output: `{"attempt":1}`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
exported := tt.run.ExportData(tt.fields)
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
require.NoError(t, enc.Encode(exported))
|
|
assert.Equal(t, tt.output, strings.TrimSpace(buf.String()))
|
|
})
|
|
}
|
|
}
|