diff --git a/internal/http/insightsservice.go b/internal/http/insightsservice.go index 96cf30230..2c4296694 100644 --- a/internal/http/insightsservice.go +++ b/internal/http/insightsservice.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "reflect" + "sort" "strconv" "time" @@ -70,11 +71,57 @@ func NewInsightsService(url string, creds iam.Credentials, timeout time.Duration // GetHistory returns job history from insights func (c *InsightsService) GetHistory(ctx context.Context, user iam.User, launchOrder config.LaunchOrder) (insights.JobHistory, error) { + vdc, err := c.doGetHistory(ctx, user, launchOrder, "vdc") + if err != nil { + return insights.JobHistory{}, err + } + rdc, err := c.doGetHistory(ctx, user, launchOrder, "rdc") + if err != nil { + return insights.JobHistory{}, err + } + + jobHistory := mergeJobHistories([]insights.JobHistory{vdc, rdc}) + return jobHistory, nil +} + +func mergeJobHistories(histories []insights.JobHistory) insights.JobHistory { + testCasesMap := map[string]insights.TestCase{} + for _, history := range histories { + for _, tc := range history.TestCases { + addOrReplaceTestCase(testCasesMap, tc) + } + } + var testCases []insights.TestCase + for _, tc := range testCasesMap { + testCases = append(testCases, tc) + } + sort.Slice(testCases, func(i, j int) bool { + return testCases[i].FailRate > testCases[j].FailRate + }) + return insights.JobHistory{ + TestCases: testCases, + } +} + +// addOrReplaceTestCase adds or replaces the insights.TestCase in the map[string]insights.TestCase +// If there is already one with the same name, only the highest fail rate is kept. +func addOrReplaceTestCase(mp map[string]insights.TestCase, tc insights.TestCase) { + tcRef, present := mp[tc.Name] + if !present { + mp[tc.Name] = tc + return + } + if tc.FailRate > tcRef.FailRate { + mp[tc.Name] = tc + } +} + +func (c *InsightsService) doGetHistory(ctx context.Context, user iam.User, launchOrder config.LaunchOrder, source string) (insights.JobHistory, error) { start := time.Now().AddDate(0, 0, -7).Unix() now := time.Now().Unix() var jobHistory insights.JobHistory - url := fmt.Sprintf("%s/v2/insights/vdc/test-cases", c.URL) + url := fmt.Sprintf("%s/v2/insights/%s/test-cases", c.URL, source) req, err := NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return jobHistory, err diff --git a/internal/http/insightsservice_test.go b/internal/http/insightsservice_test.go index c0a5f30f8..fca4e7de2 100644 --- a/internal/http/insightsservice_test.go +++ b/internal/http/insightsservice_test.go @@ -6,6 +6,8 @@ import ( "net/http/httptest" "testing" + "github.com/stretchr/testify/assert" + "github.com/saucelabs/saucectl/internal/iam" "github.com/saucelabs/saucectl/internal/insights" ) @@ -60,3 +62,241 @@ func TestInsightsService_PostTestRun(t *testing.T) { ts.Close() } } + +func Test_mergeJobHistories(t *testing.T) { + tests := []struct { + name string + histories []insights.JobHistory + want insights.JobHistory + }{ + { + name: "Empty Set", + histories: []insights.JobHistory{}, + want: insights.JobHistory{}, + }, + { + name: "Single Set", + histories: []insights.JobHistory{ + { + TestCases: []insights.TestCase{ + { + Name: "suite1", + FailRate: 0.15, + }, + { + Name: "suite2", + FailRate: 0.60, + }, + { + Name: "suite3", + FailRate: 0.30, + }, + { + Name: "suite4", + FailRate: 0.10, + }, + }, + }, + }, + want: insights.JobHistory{ + TestCases: []insights.TestCase{ + { + Name: "suite2", + FailRate: 0.60, + }, + { + Name: "suite3", + FailRate: 0.30, + }, + { + Name: "suite1", + FailRate: 0.15, + }, + { + Name: "suite4", + FailRate: 0.10, + }, + }, + }, + }, + { + name: "Multiple Set", + histories: []insights.JobHistory{ + { + TestCases: []insights.TestCase{ + { + Name: "suite11", + FailRate: 0.15, + }, + { + Name: "suite12", + FailRate: 0.60, + }, + { + Name: "suite13", + FailRate: 0.30, + }, + { + Name: "suite14", + FailRate: 0.10, + }, + }, + }, + { + TestCases: []insights.TestCase{ + { + Name: "suite21", + FailRate: 0.28, + }, + { + Name: "suite22", + FailRate: 0.34, + }, + { + Name: "suite23", + FailRate: 0.12, + }, + { + Name: "suite24", + FailRate: 0.68, + }, + }, + }, + }, + want: insights.JobHistory{ + TestCases: []insights.TestCase{ + { + Name: "suite24", + FailRate: 0.68, + }, + { + Name: "suite12", + FailRate: 0.60, + }, + { + Name: "suite22", + FailRate: 0.34, + }, + { + Name: "suite13", + FailRate: 0.30, + }, + { + Name: "suite21", + FailRate: 0.28, + }, + { + Name: "suite11", + FailRate: 0.15, + }, + { + Name: "suite23", + FailRate: 0.12, + }, + { + Name: "suite14", + FailRate: 0.10, + }, + }, + }, + }, + { + name: "Multiple Set - With Collisions", + histories: []insights.JobHistory{ + { + TestCases: []insights.TestCase{ + { + Name: "suite11", + FailRate: 0.15, + }, + { + Name: "suite12", + FailRate: 0.60, + }, + { + Name: "suite13", + FailRate: 0.30, + }, + { + Name: "suite14", + FailRate: 0.10, + }, + { + Name: "suite05", + FailRate: 0.12, + }, + }, + }, + { + TestCases: []insights.TestCase{ + { + Name: "suite21", + FailRate: 0.28, + }, + { + Name: "suite22", + FailRate: 0.34, + }, + { + Name: "suite23", + FailRate: 0.12, + }, + { + Name: "suite24", + FailRate: 0.68, + }, + { + Name: "suite05", + FailRate: 0.35, + }, + }, + }, + }, + want: insights.JobHistory{ + TestCases: []insights.TestCase{ + { + Name: "suite24", + FailRate: 0.68, + }, + { + Name: "suite12", + FailRate: 0.60, + }, + { + Name: "suite05", + FailRate: 0.35, + }, + { + Name: "suite22", + FailRate: 0.34, + }, + { + Name: "suite13", + FailRate: 0.30, + }, + { + Name: "suite21", + FailRate: 0.28, + }, + { + Name: "suite11", + FailRate: 0.15, + }, + { + Name: "suite23", + FailRate: 0.12, + }, + { + Name: "suite14", + FailRate: 0.10, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, mergeJobHistories(tt.histories), "mergeJobHistories(%v)", tt.histories) + }) + } +}