blob: 3d0070b55e6b52c0884b5033c5039b9cdbfc9f35 [file] [log] [blame]
package ingester
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
gerrit_mocks "go.skia.org/infra/go/gerrit/mocks"
"go.skia.org/infra/go/gitiles"
gitiles_mocks "go.skia.org/infra/go/gitiles/mocks"
"go.skia.org/infra/go/louhi"
louhi_mocks "go.skia.org/infra/go/louhi/mocks"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/vcsinfo"
)
const (
artifactLink = "http://artifacts/123"
branch = "main"
commit = "abc123"
flowID = "123-456-abc"
flowLink = "http://flows/" + id
flowName = "My Flow"
id = "flow123"
issueNum = int64(598597)
issueUrl = "https://skia-review.googlesource.com/c/buildbot/+/598597"
projectID = "my-project"
startedByLouhi = "Louhi"
triggerType = louhi.TriggerTypeCommit
)
// Timestamps are arbitrary, but finishedTs is after createdTs.
var createdTs = time.Unix(1667309176, 0)
var finishedTs = time.Unix(1667309500, 0)
func helper(t *testing.T, n *louhi.Notification, oldFlow, expect *louhi.FlowExecution, ts time.Time) {
ctx := context.Background()
db := &louhi_mocks.DB{}
db.On("GetFlowExecution", testutils.AnyContext, n.PipelineExecutionId).Return(oldFlow, nil)
db.On("PutFlowExecution", testutils.AnyContext, expect).Return(nil)
mockGerrit := &gerrit_mocks.GerritInterface{}
mockRepo := &gitiles_mocks.GitilesRepo{}
commitDetails := &vcsinfo.LongCommit{
Body: "Reviewed-on: " + issueUrl,
}
mockRepo.On("Details", testutils.AnyContext, commit).Return(commitDetails, nil)
mockGerrit.On("ExtractIssueFromCommit", commitDetails.Body).Return(issueNum, nil)
mockGerrit.On("Url", issueNum).Return(issueUrl)
repos := []gitiles.GitilesRepo{mockRepo}
ingester := NewIngester(db, mockGerrit, repos)
require.NoError(t, ingester.UpdateFlowFromNotification(ctx, n, ts))
}
func TestUpdateFlowFromNotification_NewFlow(t *testing.T) {
n := &louhi.Notification{
ProjectId: projectID,
FlowUniqueKey: flowID,
FlowName: flowName,
PipelineExecutionId: id,
EventSource: louhi.EventSource_PIPELINE,
EventAction: louhi.EventAction_STARTED,
Link: flowLink,
Branch: branch,
RefSha: commit,
TriggerType: triggerType,
StartedBy: startedByLouhi,
ArtifactLink: []string{artifactLink},
}
var oldFlow *louhi.FlowExecution = nil
expect := &louhi.FlowExecution{
Artifacts: []string{artifactLink},
CreatedAt: createdTs,
FinishedAt: time.Time{},
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: createdTs,
ProjectID: projectID,
Result: louhi.FlowResultUnknown,
SourceCL: issueUrl,
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
helper(t, n, oldFlow, expect, createdTs)
}
func TestUpdateFlowFromNotification_Success(t *testing.T) {
n := &louhi.Notification{
ProjectId: projectID,
FlowUniqueKey: flowID,
FlowName: flowName,
PipelineExecutionId: id,
EventSource: louhi.EventSource_PIPELINE,
EventAction: louhi.EventAction_FINISHED,
Link: flowLink,
Branch: branch,
RefSha: commit,
TriggerType: triggerType,
StartedBy: startedByLouhi,
ArtifactLink: []string{artifactLink},
}
oldFlow := &louhi.FlowExecution{
Artifacts: nil, // Should get filled in.
CreatedAt: createdTs,
FinishedAt: time.Time{}, // Should get filled in.
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: createdTs, // Should get updated.
ProjectID: projectID,
Result: louhi.FlowResultUnknown, // Should get updated.
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
expect := &louhi.FlowExecution{
Artifacts: []string{artifactLink},
CreatedAt: createdTs,
FinishedAt: finishedTs,
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs,
ProjectID: projectID,
Result: louhi.FlowResultSuccess,
SourceCL: issueUrl,
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
helper(t, n, oldFlow, expect, finishedTs)
}
func TestUpdateFlowFromNotification_Failure(t *testing.T) {
n := &louhi.Notification{
ProjectId: projectID,
FlowUniqueKey: flowID,
FlowName: flowName,
PipelineExecutionId: id,
EventSource: louhi.EventSource_PIPELINE,
EventAction: louhi.EventAction_FAILED,
Link: flowLink,
Branch: branch,
RefSha: commit,
TriggerType: triggerType,
StartedBy: startedByLouhi,
ArtifactLink: nil,
}
oldFlow := &louhi.FlowExecution{
Artifacts: nil, // Should stay nil.
CreatedAt: createdTs,
FinishedAt: time.Time{}, // Should get filled in.
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: createdTs, // Should get updated.
ProjectID: projectID,
Result: louhi.FlowResultUnknown, // Should get updated.
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
expect := &louhi.FlowExecution{
Artifacts: nil,
CreatedAt: createdTs,
FinishedAt: finishedTs,
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs,
ProjectID: projectID,
Result: louhi.FlowResultFailure,
SourceCL: issueUrl,
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
helper(t, n, oldFlow, expect, finishedTs)
}
func TestUpdateFlowFromNotification_FailureAfterFinished(t *testing.T) {
// This tests the case where we first receive a "finished" notification,
// which we interpret as success, but then receive a "failed" notification.
n := &louhi.Notification{
ProjectId: projectID,
FlowUniqueKey: flowID,
FlowName: flowName,
PipelineExecutionId: id,
EventSource: louhi.EventSource_PIPELINE,
EventAction: louhi.EventAction_FAILED,
Link: flowLink,
Branch: branch,
RefSha: commit,
TriggerType: triggerType,
StartedBy: startedByLouhi,
ArtifactLink: nil,
}
oldFlow := &louhi.FlowExecution{
Artifacts: nil, // Should stay nil.
CreatedAt: createdTs,
FinishedAt: finishedTs, // Should stay the same.
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs, // Should stay the same.
ProjectID: projectID,
Result: louhi.FlowResultSuccess, // Should get updated.
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
expect := &louhi.FlowExecution{
Artifacts: nil,
CreatedAt: createdTs,
FinishedAt: finishedTs,
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs,
ProjectID: projectID,
Result: louhi.FlowResultFailure,
SourceCL: issueUrl,
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
helper(t, n, oldFlow, expect, finishedTs)
}
func TestUpdateFlowFromNotification_FinishedAfterFailure(t *testing.T) {
// This tests the case where we first receive a "failed" notification,
// but then receive a "finished" notification. This occurs when a user
// manually retries the flow.
n := &louhi.Notification{
ProjectId: projectID,
FlowUniqueKey: flowID,
FlowName: flowName,
PipelineExecutionId: id,
EventSource: louhi.EventSource_PIPELINE,
EventAction: louhi.EventAction_FINISHED,
Link: flowLink,
Branch: branch,
RefSha: commit,
TriggerType: triggerType,
StartedBy: startedByLouhi,
ArtifactLink: nil,
}
oldFlow := &louhi.FlowExecution{
Artifacts: nil, // Should stay nil.
CreatedAt: createdTs,
FinishedAt: finishedTs, // Should stay the same.
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs, // Should stay the same.
ProjectID: projectID,
Result: louhi.FlowResultFailure, // Should stay the same.
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
expect := &louhi.FlowExecution{
Artifacts: nil,
CreatedAt: createdTs,
FinishedAt: finishedTs,
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs,
ProjectID: projectID,
Result: louhi.FlowResultSuccess,
SourceCL: issueUrl,
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
helper(t, n, oldFlow, expect, finishedTs)
}
func TestUpdateFlowFromNotification_StartedAfterFinished(t *testing.T) {
// This tests the case where we receive the "started" notification after we
// receive the "finished" notification. Pub/sub message ordering is not
// guaranteed.
n := &louhi.Notification{
ProjectId: projectID,
FlowUniqueKey: flowID,
FlowName: flowName,
PipelineExecutionId: id,
EventSource: louhi.EventSource_PIPELINE,
EventAction: louhi.EventAction_STARTED,
Link: flowLink,
Branch: branch,
RefSha: commit,
TriggerType: triggerType,
StartedBy: startedByLouhi,
ArtifactLink: nil,
}
oldFlow := &louhi.FlowExecution{
Artifacts: []string{artifactLink}, // Shouldn't be overwritten.
CreatedAt: finishedTs, // Should get updated.
FinishedAt: finishedTs,
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs, // Shouldn't change.
ProjectID: projectID,
Result: louhi.FlowResultSuccess, // Shouldn't change.
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
expect := &louhi.FlowExecution{
Artifacts: []string{artifactLink},
CreatedAt: createdTs,
FinishedAt: finishedTs,
FlowID: flowID,
FlowName: flowName,
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs,
ProjectID: projectID,
Result: louhi.FlowResultSuccess,
SourceCL: issueUrl,
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
helper(t, n, oldFlow, expect, createdTs)
}
func TestUpdateFlowFromNotification_GeneratedCLs(t *testing.T) {
n := &louhi.Notification{
ProjectId: projectID,
PipelineExecutionId: id,
EventSource: louhi.EventSource_PIPELINE,
EventAction: louhi.EventAction_CREATED_ARTIFACT,
GeneratedCls: []string{issueUrl},
}
oldFlow := &louhi.FlowExecution{
Artifacts: []string{artifactLink}, // Shouldn't be overwritten.
CreatedAt: createdTs, // Should get updated.
FinishedAt: time.Time{},
FlowID: flowID,
FlowName: flowName,
GeneratedCLs: nil, // Should get filled in.
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: createdTs, // Should get updated.
ProjectID: projectID,
Result: louhi.FlowResultUnknown, // Shouldn't change.
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
expect := &louhi.FlowExecution{
Artifacts: []string{artifactLink},
CreatedAt: createdTs,
FinishedAt: time.Time{},
FlowID: flowID,
FlowName: flowName,
GeneratedCLs: []string{issueUrl},
GitBranch: branch,
GitCommit: commit,
ID: id,
Link: flowLink,
ModifiedAt: finishedTs,
ProjectID: projectID,
Result: louhi.FlowResultUnknown,
// Not provided by the new notification but obtainable via GitCommit.
SourceCL: issueUrl,
StartedBy: startedByLouhi,
TriggerType: triggerType,
}
helper(t, n, oldFlow, expect, finishedTs)
}