agola/internal/services/notification/notification_test.go

1259 lines
45 KiB
Go

// Copyright 2023 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.
package notification
import (
"bytes"
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"slices"
"testing"
"time"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/rs/zerolog"
"github.com/sorintlab/errors"
"gotest.tools/assert"
"gotest.tools/assert/cmp"
"agola.io/agola/internal/services/config"
serrors "agola.io/agola/internal/services/errors"
"agola.io/agola/internal/services/notification/action"
"agola.io/agola/internal/sqlg"
"agola.io/agola/internal/sqlg/sql"
"agola.io/agola/internal/testutil"
"agola.io/agola/internal/util"
"agola.io/agola/services/notification/types"
rsclient "agola.io/agola/services/runservice/client"
rstypes "agola.io/agola/services/runservice/types"
)
const (
webhookSecret = "secretkey"
webhookPayload = "payloadtest"
webhookURL = "testWebhookURL"
project01 = "project01id"
project02 = "project02id"
runWebhookDelivery01 = "runwebhookdelivery01id"
commitStatusDelivery01 = "commitstatusdelivery01id"
)
// setupNotificationService don't start the notification service but just create it to manually call its methods
func setupNotificationService(ctx context.Context, t *testing.T, log zerolog.Logger, dir string) *NotificationService {
dbType := testutil.DBType(t)
_, _, dbConnString := testutil.CreateDB(t, log, ctx, dir)
c := config.Config{
Notification: config.Notification{
DB: config.DB{
Type: dbType,
ConnString: dbConnString,
},
},
}
ns, err := NewNotificationService(ctx, log, &c)
testutil.NilError(t, err)
return ns
}
func TestRunWebhookDelivery(t *testing.T) {
t.Parallel()
t.Run("test run webhook delivery success", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
wr := setupWebhooksReceiver(ctx, t)
defer wr.stop()
t.Logf("starting webhooks client")
ns.c.WebhookURL = fmt.Sprintf("%s/%s", wr.exposedURL, "webhooks")
ns.c.WebhookSecret = webhookSecret
runWebhooks := make([]*types.RunWebhook, maxRunWebhookDeliveriesQueryLimit+10)
for i := 0; i < len(runWebhooks); i++ {
runWebhooks[i] = createRunWebhook(t, ctx, ns, project01)
createRunWebhookDelivery(t, ctx, ns, runWebhooks[i].ID, types.DeliveryStatusNotDelivered)
}
err := ns.runWebhookDeliveriesHandler(ctx)
testutil.NilError(t, err)
runWebhookDeliveries := getRunWebhookDeliveries(t, ctx, ns)
assert.Assert(t, cmp.Len(runWebhookDeliveries, len(runWebhooks)))
for i := 0; i < len(runWebhookDeliveries); i++ {
assert.Equal(t, runWebhookDeliveries[i].DeliveryStatus, types.DeliveryStatusDelivered)
}
webhooks, err := wr.webhooks.getWebhooks()
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(webhooks, len(runWebhooks)))
for i := 0; i < len(runWebhookDeliveries); i++ {
assert.Assert(t, bytes.Equal(webhooks[i].Payload, runWebhooks[i].Payload))
h256 := hmac.New(sha256.New, []byte(webhookSecret))
_, err = h256.Write([]byte(runWebhooks[i].Payload))
testutil.NilError(t, err)
expectedsignature := hex.EncodeToString(h256.Sum(nil))
assert.Equal(t, webhooks[i].Signature, expectedsignature)
}
// test run webhooks handled previously.
wr.webhooks.resetWebhooks()
err = ns.runWebhookDeliveriesHandler(ctx)
testutil.NilError(t, err)
webhooks, err = wr.webhooks.getWebhooks()
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(webhooks, 0))
})
t.Run("test run webhook delivery fail", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
runWebhook := createRunWebhook(t, ctx, ns, project01)
createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusNotDelivered)
err := ns.runWebhookDeliveriesHandler(ctx)
testutil.NilError(t, err)
runWebhookDeliveries := getRunWebhookDeliveries(t, ctx, ns)
assert.Assert(t, cmp.Len(runWebhookDeliveries, 1))
assert.Equal(t, runWebhookDeliveries[0].DeliveryStatus, types.DeliveryStatusDeliveryError)
})
}
func TestLastRunEventSequence(t *testing.T) {
t.Parallel()
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
ns.c.WebhookURL = webhookURL
runEventsSender := setupRunEventsSender(ctx, t)
defer runEventsSender.stop()
t.Logf("starting run events service")
ns.runserviceClient = rsclient.NewClient(runEventsSender.exposedURL, "")
lastRunEventSequenceValue := getLastRunEventSequenceValue(t, ctx, ns)
assert.Equal(t, lastRunEventSequenceValue, uint64(0))
// test runEventsHandler start from sequence 0
runEvents := make([]*rstypes.RunEvent, 0)
runEvents = append(runEvents, generateRunEvent(1, rstypes.RunPhaseChanged))
runEvents = append(runEvents, generateRunEvent(2, rstypes.RunPhaseChanged))
runEventsSender.runEvents.addRunEvent(runEvents[0])
runEventsSender.runEvents.addRunEvent(runEvents[1])
expectedLastRunEventSequenceValue := uint64(2)
err := ns.runEventsHandler(ctx)
testutil.NilError(t, err)
lastRunEventSequenceValue = getLastRunEventSequenceValue(t, ctx, ns)
assert.Equal(t, lastRunEventSequenceValue, expectedLastRunEventSequenceValue)
// test new events
runEvents = append(runEvents, generateRunEvent(3, rstypes.RunPhaseChanged))
runEvents = append(runEvents, generateRunEvent(4, rstypes.RunPhaseChanged))
runEventsSender.runEvents.addRunEvent(runEvents[2])
runEventsSender.runEvents.addRunEvent(runEvents[3])
err = ns.runEventsHandler(ctx)
testutil.NilError(t, err)
runWebhooks := getRunWebhooks(t, ctx, ns)
assert.Assert(t, cmp.Len(runWebhooks, len(runEvents)))
for i := range runEvents {
runWebhook := ns.generatewebhook(ctx, runEvents[i])
webhookPayload, err := json.Marshal(runWebhook)
testutil.NilError(t, err)
assert.Assert(t, bytes.Equal(runWebhooks[i].Payload, webhookPayload))
}
expectedLastRunEventSequenceValue = 4
lastRunEventSequenceValue = getLastRunEventSequenceValue(t, ctx, ns)
assert.Equal(t, lastRunEventSequenceValue, expectedLastRunEventSequenceValue)
}
func generateRunEvent(sequence uint64, runEventType rstypes.RunEventType) *rstypes.RunEvent {
return &rstypes.RunEvent{
Sequence: sequence,
RunEventType: runEventType,
DataVersion: rstypes.RunEventDataVersion,
Data: &rstypes.RunEventData{},
}
}
func getLastRunEventSequenceValue(t *testing.T, ctx context.Context, ns *NotificationService) uint64 {
var lastRunEventSequence uint64
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
l, err := ns.d.GetLastRunEventSequence(tx)
if err != nil {
return errors.WithStack(err)
}
if l != nil {
lastRunEventSequence = l.Value
}
return nil
})
testutil.NilError(t, err)
return lastRunEventSequence
}
func getRunWebhookDeliveries(t *testing.T, ctx context.Context, ns *NotificationService) []*types.RunWebhookDelivery {
var wd []*types.RunWebhookDelivery
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
var err error
wd, err = ns.d.GetRunWebhookDeliveriesAfterSequence(tx, 0, 0)
if err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return wd
}
func createRunWebhook(t *testing.T, ctx context.Context, ns *NotificationService, projectID string) *types.RunWebhook {
var wh *types.RunWebhook
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
wh = types.NewRunWebhook(tx)
wh.Payload = []byte(webhookPayload)
wh.ProjectID = projectID
if err := ns.d.InsertRunWebhook(tx, wh); err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return wh
}
func getRunWebhooks(t *testing.T, ctx context.Context, ns *NotificationService) []*types.RunWebhook {
var runWebhooks []*types.RunWebhook
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
var err error
runWebhooks, err = ns.d.GetRunWebhooks(tx, 0)
if err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return runWebhooks
}
func createRunWebhookDelivery(t *testing.T, ctx context.Context, ns *NotificationService, runWebhookID string, deliveryStatus types.DeliveryStatus) *types.RunWebhookDelivery {
var wd *types.RunWebhookDelivery
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
wd = types.NewRunWebhookDelivery(tx)
wd.DeliveryStatus = deliveryStatus
wd.RunWebhookID = runWebhookID
if err := ns.d.InsertRunWebhookDelivery(tx, wd); err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return wd
}
func updateRunWebhookCreationDate(t *testing.T, ctx context.Context, ns *NotificationService, runWebhookID string, creationTime time.Time) {
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
var err error
runWebhook, err := ns.d.GetRunWebhookByID(tx, runWebhookID)
if err != nil {
return errors.WithStack(err)
}
runWebhook.CreationTime = creationTime
if err := ns.d.UpdateRunWebhook(tx, runWebhook); err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
}
func TestCommitStatusDelivery(t *testing.T) {
t.Parallel()
t.Run("test commit status delivery success", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
cs := setupStubCommitStatusUpdater()
ns.u = cs
commitStatuses := make([]*types.CommitStatus, maxCommitStatusDeliveriesQueryLimit+10)
for i := 0; i < len(commitStatuses); i++ {
commitStatuses[i] = createCommitStatus(t, ctx, ns, i, fmt.Sprintf("projectID-%d", i))
createCommitStatusDelivery(t, ctx, ns, commitStatuses[i].ID, types.DeliveryStatusNotDelivered)
}
err := ns.commitStatusDeliveriesHandler(ctx)
testutil.NilError(t, err)
commitStatusDeliveries := getCommitStatusDeliveries(t, ctx, ns)
assert.Assert(t, cmp.Len(commitStatusDeliveries, len(commitStatuses)))
for i := 0; i < len(commitStatusDeliveries); i++ {
assert.Equal(t, commitStatusDeliveries[i].DeliveryStatus, types.DeliveryStatusDelivered)
}
commitStatusesReceived, err := cs.commitStatuses.getCommitStatuses()
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(commitStatusesReceived, len(commitStatuses)))
for i := 0; i < len(commitStatuses); i++ {
assert.Equal(t, commitStatusesReceived[i].Context, commitStatuses[i].Context)
assert.Equal(t, commitStatusesReceived[i].Description, commitStatuses[i].Description)
assert.Equal(t, commitStatusesReceived[i].State, commitStatuses[i].State)
}
// test commitstatuses handled previously.
cs.commitStatuses.resetCommitStatuses()
err = ns.commitStatusDeliveriesHandler(ctx)
testutil.NilError(t, err)
commitStatusesReceived, err = cs.commitStatuses.getCommitStatuses()
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(commitStatusesReceived, 0))
})
t.Run("test commit status delivery fail", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
s := setupStubCommitStatusUpdater()
s.setFailUpdateCommitStatus(true)
ns.u = s
commitStatus := createCommitStatus(t, ctx, ns, 1, project01)
createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusNotDelivered)
err := ns.commitStatusDeliveriesHandler(ctx)
testutil.NilError(t, err)
commitStatusDeliveries := getCommitStatusDeliveries(t, ctx, ns)
assert.Assert(t, cmp.Len(commitStatusDeliveries, 1))
assert.Equal(t, commitStatusDeliveries[0].DeliveryStatus, types.DeliveryStatusDeliveryError)
})
}
func createCommitStatus(t *testing.T, ctx context.Context, ns *NotificationService, runCounter int, projectID string) *types.CommitStatus {
var cs *types.CommitStatus
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
cs = types.NewCommitStatus(tx)
// CommitSHAuse and Context use dumb values
cs.CommitSHA = fmt.Sprintf("commitSHA-%d", runCounter)
cs.Context = fmt.Sprintf("context-%d", runCounter)
cs.Description = "The run finished successfully"
cs.ProjectID = projectID
cs.RunCounter = uint64(runCounter)
cs.State = types.CommitStateSuccess
if err := ns.d.InsertCommitStatus(tx, cs); err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return cs
}
func updateCommitStatusCreationDate(t *testing.T, ctx context.Context, ns *NotificationService, commitStatusID string, creationTime time.Time) {
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
var err error
commitStatus, err := ns.d.GetCommitStatusByID(tx, commitStatusID)
if err != nil {
return errors.WithStack(err)
}
commitStatus.CreationTime = creationTime
if err := ns.d.UpdateCommitStatus(tx, commitStatus); err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
}
func getCommitStatuses(t *testing.T, ctx context.Context, ns *NotificationService) []*types.CommitStatus {
var commitStatuses []*types.CommitStatus
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
var err error
commitStatuses, err = ns.d.GetCommitStatuses(tx, 0)
if err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return commitStatuses
}
func getCommitStatusDeliveries(t *testing.T, ctx context.Context, ns *NotificationService) []*types.CommitStatusDelivery {
var wd []*types.CommitStatusDelivery
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
var err error
wd, err = ns.d.GetCommitStatusDeliveriesAfterSequence(tx, 0, 0)
if err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return wd
}
func createCommitStatusDelivery(t *testing.T, ctx context.Context, ns *NotificationService, commitStatusID string, deliveryStatus types.DeliveryStatus) *types.CommitStatusDelivery {
var delivery *types.CommitStatusDelivery
err := ns.d.Do(ctx, func(tx *sql.Tx) error {
delivery = types.NewCommitStatusDelivery(tx)
delivery.DeliveryStatus = deliveryStatus
delivery.CommitStatusID = commitStatusID
if err := ns.d.InsertCommitStatusDelivery(tx, delivery); err != nil {
return errors.WithStack(err)
}
return nil
})
testutil.NilError(t, err)
return delivery
}
func TestGetProjectRunWebhookDeliveries(t *testing.T) {
t.Parallel()
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
runWebhooks := make([]*types.RunWebhook, 10)
project01RunWebhookDeliveries := make([]*types.RunWebhookDelivery, 0)
for i := 0; i < len(runWebhooks); i++ {
runWebhooks[i] = createRunWebhook(t, ctx, ns, project01)
project01RunWebhookDeliveries = append(project01RunWebhookDeliveries, createRunWebhookDelivery(t, ctx, ns, runWebhooks[i].ID, types.DeliveryStatusDelivered))
project01RunWebhookDeliveries = append(project01RunWebhookDeliveries, createRunWebhookDelivery(t, ctx, ns, runWebhooks[i].ID, types.DeliveryStatusNotDelivered))
}
for i := 0; i < len(runWebhooks); i++ {
runWebhooks[i] = createRunWebhook(t, ctx, ns, project02)
createRunWebhookDelivery(t, ctx, ns, runWebhooks[i].ID, types.DeliveryStatusDelivered)
createRunWebhookDelivery(t, ctx, ns, runWebhooks[i].ID, types.DeliveryStatusNotDelivered)
}
tests := []struct {
name string
limit int
sortDirection types.SortDirection
deliveryStatusFilter []types.DeliveryStatus
expectedRunWebhookDeliveriesNumber int
expectedCallsNumber int
}{
{
name: "test get run webhook deliveries with limit = 0 and no sortdirection",
expectedRunWebhookDeliveriesNumber: 20,
expectedCallsNumber: 1,
},
{
name: "test get run webhook deliveries with limit = 0",
sortDirection: types.SortDirectionAsc,
expectedRunWebhookDeliveriesNumber: 20,
expectedCallsNumber: 1,
},
{
name: "test get run webhook deliveries with deliverystatusfilter = delivered",
sortDirection: types.SortDirectionAsc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedRunWebhookDeliveriesNumber: 10,
expectedCallsNumber: 1,
},
{
name: "test get run webhook deliveries with deliverystatusfilter = delivered and limit less than run webhook deliveries",
limit: 2,
sortDirection: types.SortDirectionAsc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedRunWebhookDeliveriesNumber: 10,
expectedCallsNumber: 5,
},
{
name: "test get run webhook deliveries with limit less than run webhook deliveries",
sortDirection: types.SortDirectionAsc,
limit: 5,
expectedRunWebhookDeliveriesNumber: 20,
expectedCallsNumber: 4,
},
{
name: "test get run webhook deliveries with limit = 0 and sortDirection desc",
sortDirection: types.SortDirectionDesc,
expectedRunWebhookDeliveriesNumber: 20,
expectedCallsNumber: 1,
},
{
name: "test get run webhook deliveries with deliverystatusfilter = delivered and sortDirection desc",
sortDirection: types.SortDirectionDesc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedRunWebhookDeliveriesNumber: 10,
expectedCallsNumber: 1,
},
{
name: "test get run webhook deliveries with deliverystatusfilter = delivered and limit less than run webhook deliveries and sortDirection desc",
limit: 2,
sortDirection: types.SortDirectionDesc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedRunWebhookDeliveriesNumber: 10,
expectedCallsNumber: 5,
},
{
name: "test get run webhook deliveries with limit less than run webhook deliveries and sortDirection desc",
sortDirection: types.SortDirectionDesc,
limit: 5,
expectedRunWebhookDeliveriesNumber: 20,
expectedCallsNumber: 4,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// populate the expected run webhook deliveries
expectedProject01RunWebhookDeliveries := []*types.RunWebhookDelivery{}
for _, c := range project01RunWebhookDeliveries {
if len(tt.deliveryStatusFilter) > 0 && !slices.Contains(tt.deliveryStatusFilter, c.DeliveryStatus) {
continue
}
expectedProject01RunWebhookDeliveries = append(expectedProject01RunWebhookDeliveries, c)
}
// default sortdirection is asc
// reverse if sortDirection is desc
if tt.sortDirection == types.SortDirectionDesc {
slices.Reverse(expectedProject01RunWebhookDeliveries)
}
callsNumber := 0
var lastProjectRunWebhookDelivery uint64
var respAllProjectRunWebhookDeliveries []*types.RunWebhookDelivery
// fetch next results
for {
res, err := ns.ah.GetProjectRunWebhookDeliveries(ctx, &action.GetProjectRunWebhookDeliveriesRequest{
ProjectID: project01,
Limit: tt.limit,
SortDirection: tt.sortDirection,
DeliveryStatusFilter: tt.deliveryStatusFilter,
StartSequence: lastProjectRunWebhookDelivery,
})
testutil.NilError(t, err)
callsNumber++
respAllProjectRunWebhookDeliveries = append(respAllProjectRunWebhookDeliveries, res.RunWebhookDeliveries...)
if res.HasMore == false {
break
}
lastProjectRunWebhookDelivery = respAllProjectRunWebhookDeliveries[len(respAllProjectRunWebhookDeliveries)-1].Sequence
}
assert.Assert(t, cmpDiffObject(expectedProject01RunWebhookDeliveries, respAllProjectRunWebhookDeliveries))
assert.Assert(t, cmp.Equal(callsNumber, tt.expectedCallsNumber))
})
}
}
func TestDeliveryStatusFromStringSlice(t *testing.T) {
t.Parallel()
deliverystatus := []string{"notDelivered", "delivered", "deliveryError"}
expectedDeliveryStatus := []types.DeliveryStatus{
types.DeliveryStatusNotDelivered,
types.DeliveryStatusDelivered,
types.DeliveryStatusDeliveryError,
}
result, err := types.DeliveryStatusFromStringSlice(deliverystatus)
testutil.NilError(t, err)
assert.DeepEqual(t, result, expectedDeliveryStatus)
// test wrong deliverystatus
baddeliverystatus := "baddeliverystatus"
deliverystatus = []string{string(types.DeliveryStatusNotDelivered), string(types.DeliveryStatusDelivered), string(types.DeliveryStatusDeliveryError), baddeliverystatus}
_, err = types.DeliveryStatusFromStringSlice(deliverystatus)
expectedErr := fmt.Sprintf("invalid delivery status %q", baddeliverystatus)
assert.Error(t, err, expectedErr)
}
func TestRunWebhooksCleaner(t *testing.T) {
t.Parallel()
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
expectedRunWebhooks := make([]*types.RunWebhook, 0)
expectedRunWebhookDeliveries := make([]*types.RunWebhookDelivery, 0)
for i := 0; i < maxRunWebhooksQueryLimit+10; i++ {
runWebhook := createRunWebhook(t, ctx, ns, project01)
expectedRunWebhooks = append(expectedRunWebhooks, runWebhook)
expectedRunWebhookDeliveries = append(expectedRunWebhookDeliveries, createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusDelivered))
expectedRunWebhookDeliveries = append(expectedRunWebhookDeliveries, createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusNotDelivered))
}
for i := 0; i < maxRunWebhooksQueryLimit+10; i++ {
runWebhook := createRunWebhook(t, ctx, ns, project02)
expectedRunWebhooks = append(expectedRunWebhooks, runWebhook)
expectedRunWebhookDeliveries = append(expectedRunWebhookDeliveries, createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusDelivered))
expectedRunWebhookDeliveries = append(expectedRunWebhookDeliveries, createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusNotDelivered))
}
runWebhookCreationTime := time.Now().Add(-1 * time.Hour)
for i := 0; i < maxRunWebhooksQueryLimit+10; i++ {
runWebhook := createRunWebhook(t, ctx, ns, project01)
createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusDelivered)
createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusNotDelivered)
updateRunWebhookCreationDate(t, ctx, ns, runWebhook.ID, runWebhookCreationTime)
}
err := ns.runWebhooksCleaner(ctx, 30*time.Minute)
testutil.NilError(t, err)
runWebhooks := getRunWebhooks(t, ctx, ns)
assert.Assert(t, cmp.Len(runWebhooks, len(expectedRunWebhooks)))
assert.Assert(t, cmpDiffObject(runWebhooks, expectedRunWebhooks))
runWebhookDeliveries := getRunWebhookDeliveries(t, ctx, ns)
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(runWebhookDeliveries, len(expectedRunWebhookDeliveries)))
assert.Assert(t, cmpDiffObject(runWebhookDeliveries, expectedRunWebhookDeliveries))
}
func cmpDiffObject(x, y interface{}) cmp.Comparison {
// Since postgres has microsecond time precision while go has nanosecond time precision we should check times with a microsecond margin
return cmp.DeepEqual(x, y, cmpopts.IgnoreFields(sqlg.ObjectMeta{}, "TxID"), cmpopts.EquateApproxTime(1*time.Microsecond))
}
func TestProjectRunWebhookRedelivery(t *testing.T) {
t.Parallel()
t.Run("test project run webhook redelivery with deliverystatus = deliveryError", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
runWebhook := createRunWebhook(t, ctx, ns, project01)
runWebhookDelivery := createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusDeliveryError)
wr := setupWebhooksReceiver(ctx, t)
defer wr.stop()
t.Logf("starting webhooks client")
ns.c.WebhookSecret = webhookSecret
ns.c.WebhookURL = fmt.Sprintf("%s/%s", wr.exposedURL, "webhooks")
err := ns.ah.RunWebhookRedelivery(ctx, runWebhook.ProjectID, runWebhookDelivery.ID)
testutil.NilError(t, err)
err = ns.runWebhookDeliveriesHandler(ctx)
testutil.NilError(t, err)
res, err := ns.ah.GetProjectRunWebhookDeliveries(ctx, &action.GetProjectRunWebhookDeliveriesRequest{ProjectID: project01, SortDirection: types.SortDirectionAsc})
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(res.RunWebhookDeliveries, 2))
assert.Assert(t, res.RunWebhookDeliveries[0].DeliveryStatus == types.DeliveryStatusDeliveryError)
assert.Assert(t, res.RunWebhookDeliveries[1].DeliveryStatus == types.DeliveryStatusDelivered)
})
t.Run("test project run webhook redelivery with deliverystatus = delivered", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
runWebhook := createRunWebhook(t, ctx, ns, project01)
runWebhookDelivery := createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusDelivered)
wr := setupWebhooksReceiver(ctx, t)
defer wr.stop()
t.Logf("starting webhooks client")
ns.c.WebhookSecret = webhookSecret
ns.c.WebhookURL = fmt.Sprintf("%s/%s", wr.exposedURL, "webhooks")
err := ns.ah.RunWebhookRedelivery(ctx, runWebhook.ProjectID, runWebhookDelivery.ID)
testutil.NilError(t, err)
err = ns.runWebhookDeliveriesHandler(ctx)
testutil.NilError(t, err)
res, err := ns.ah.GetProjectRunWebhookDeliveries(ctx, &action.GetProjectRunWebhookDeliveriesRequest{ProjectID: project01, SortDirection: types.SortDirectionAsc})
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(res.RunWebhookDeliveries, 2))
assert.Assert(t, res.RunWebhookDeliveries[0].DeliveryStatus == types.DeliveryStatusDelivered)
assert.Assert(t, res.RunWebhookDeliveries[1].DeliveryStatus == types.DeliveryStatusDelivered)
})
t.Run("test redelivery not existing project run webhook delivery", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
runWebhook := createRunWebhook(t, ctx, ns, project01)
expectedErr := util.NewAPIError(util.ErrNotExist, util.WithAPIErrorMsgf("runWebhookDelivery %q doesn't exist", runWebhookDelivery01), serrors.RunWebhookDeliveryDoesNotExist())
err := ns.ah.RunWebhookRedelivery(ctx, runWebhook.ProjectID, runWebhookDelivery01)
assert.Error(t, err, expectedErr.Error())
})
t.Run("test project run webhook redelivery with projectID that belong to another project", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
runWebhook := createRunWebhook(t, ctx, ns, project01)
runWebhookDelivery := createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusDelivered)
runWebhook = createRunWebhook(t, ctx, ns, project02)
createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusDelivered)
expectedErr := util.NewAPIError(util.ErrNotExist, util.WithAPIErrorMsgf("runWebhookDelivery %q doesn't belong to project %q", runWebhookDelivery.ID, project02), serrors.RunWebhookDeliveryDoesNotExist())
err := ns.ah.RunWebhookRedelivery(ctx, project02, runWebhookDelivery.ID)
assert.Error(t, err, expectedErr.Error())
})
t.Run("test project run webhook redelivery with the last delivery that hasn't been delivered", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
runWebhook := createRunWebhook(t, ctx, ns, project01)
runWebhookDelivery := createRunWebhookDelivery(t, ctx, ns, runWebhook.ID, types.DeliveryStatusNotDelivered)
wr := setupWebhooksReceiver(ctx, t)
defer wr.stop()
t.Logf("starting webhooks client")
ns.c.WebhookSecret = webhookSecret
ns.c.WebhookURL = fmt.Sprintf("%s/%s", wr.exposedURL, "webhooks")
expectedErr := util.NewAPIError(util.ErrBadRequest, util.WithAPIErrorMsgf("the previous delivery of run webhook %q hasn't already been delivered", runWebhookDelivery.RunWebhookID), serrors.RunWebhookDeliveryAlreadyInProgress())
err := ns.ah.RunWebhookRedelivery(ctx, runWebhook.ProjectID, runWebhookDelivery.ID)
assert.Error(t, err, expectedErr.Error())
err = ns.runWebhookDeliveriesHandler(ctx)
testutil.NilError(t, err)
res, err := ns.ah.GetProjectRunWebhookDeliveries(ctx, &action.GetProjectRunWebhookDeliveriesRequest{ProjectID: project01, SortDirection: types.SortDirectionAsc})
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(res.RunWebhookDeliveries, 1))
assert.Assert(t, res.RunWebhookDeliveries[0].DeliveryStatus == types.DeliveryStatusDelivered)
})
}
func TestCommitStatusesCleaner(t *testing.T) {
t.Parallel()
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
expectedCommitStatuses := make([]*types.CommitStatus, 0)
expectedCommitStatusDeliveries := make([]*types.CommitStatusDelivery, 0)
for i := 0; i < maxCommitStatusesQueryLimit+10; i++ {
commitStatus := createCommitStatus(t, ctx, ns, 1, fmt.Sprintf("projectID-%d", i))
expectedCommitStatuses = append(expectedCommitStatuses, commitStatus)
expectedCommitStatusDeliveries = append(expectedCommitStatusDeliveries, createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusDelivered))
expectedCommitStatusDeliveries = append(expectedCommitStatusDeliveries, createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusNotDelivered))
}
commitStatusCreationTime := time.Now().Add(-1 * time.Hour)
for i := 0; i < maxCommitStatusesQueryLimit+10; i++ {
commitStatus := createCommitStatus(t, ctx, ns, i, fmt.Sprintf("projectID-%d", i))
createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusDelivered)
createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusNotDelivered)
updateCommitStatusCreationDate(t, ctx, ns, commitStatus.ID, commitStatusCreationTime)
}
err := ns.commitStatusesCleaner(ctx, 30*time.Minute)
testutil.NilError(t, err)
commitStatuses := getCommitStatuses(t, ctx, ns)
assert.Assert(t, cmp.Len(commitStatuses, len(expectedCommitStatuses)))
assert.Assert(t, cmpDiffObject(commitStatuses, expectedCommitStatuses))
commitStatusDeliveries := getCommitStatusDeliveries(t, ctx, ns)
testutil.NilError(t, err)
assert.Assert(t, cmp.Len(commitStatusDeliveries, len(expectedCommitStatusDeliveries)))
assert.Assert(t, cmpDiffObject(commitStatusDeliveries, expectedCommitStatusDeliveries))
}
func TestGetProjectCommitStatusDeliveries(t *testing.T) {
t.Parallel()
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
commitStatuses := make([]*types.CommitStatus, 10)
project01CommitStatusDeliveries := make([]*types.CommitStatusDelivery, 0)
for i := 0; i < len(commitStatuses); i++ {
commitStatuses[i] = createCommitStatus(t, ctx, ns, i, project01)
project01CommitStatusDeliveries = append(project01CommitStatusDeliveries, createCommitStatusDelivery(t, ctx, ns, commitStatuses[i].ID, types.DeliveryStatusDelivered))
project01CommitStatusDeliveries = append(project01CommitStatusDeliveries, createCommitStatusDelivery(t, ctx, ns, commitStatuses[i].ID, types.DeliveryStatusNotDelivered))
}
for i := 0; i < len(commitStatuses); i++ {
commitStatuses[i] = createCommitStatus(t, ctx, ns, i, project02)
createCommitStatusDelivery(t, ctx, ns, commitStatuses[i].ID, types.DeliveryStatusDelivered)
createCommitStatusDelivery(t, ctx, ns, commitStatuses[i].ID, types.DeliveryStatusNotDelivered)
}
tests := []struct {
name string
limit int
sortDirection types.SortDirection
deliveryStatusFilter []types.DeliveryStatus
expectedCommitStatusDeliveriesNumber int
expectedCallsNumber int
}{
{
name: "test get commit status deliveries with limit = 0 and no sortdirection",
expectedCommitStatusDeliveriesNumber: 20,
expectedCallsNumber: 1,
},
{
name: "test get commit status deliveries with limit = 0",
sortDirection: types.SortDirectionAsc,
expectedCommitStatusDeliveriesNumber: 20,
expectedCallsNumber: 1,
},
{
name: "test get commit status deliveries with deliverystatusfilter = delivered",
sortDirection: types.SortDirectionAsc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedCommitStatusDeliveriesNumber: 10,
expectedCallsNumber: 1,
},
{
name: "test get commit status deliveries with deliverystatusfilter = delivered and limit less than commit status deliveries",
limit: 2,
sortDirection: types.SortDirectionAsc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedCommitStatusDeliveriesNumber: 10,
expectedCallsNumber: 5,
},
{
name: "test get commit status deliveries with limit less than commit status deliveries",
sortDirection: types.SortDirectionAsc,
limit: 5,
expectedCommitStatusDeliveriesNumber: 20,
expectedCallsNumber: 4,
},
{
name: "test get commit status deliveries with limit = 0 and sortDirection desc",
sortDirection: types.SortDirectionDesc,
expectedCommitStatusDeliveriesNumber: 20,
expectedCallsNumber: 1,
},
{
name: "test get commit status deliveries with deliverystatusfilter = delivered and sortDirection desc",
sortDirection: types.SortDirectionDesc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedCommitStatusDeliveriesNumber: 10,
expectedCallsNumber: 1,
},
{
name: "test get commit status deliveries with deliverystatusfilter = delivered and limit less than commit status deliveries and sortDirection desc",
limit: 2,
sortDirection: types.SortDirectionDesc,
deliveryStatusFilter: []types.DeliveryStatus{types.DeliveryStatusDelivered},
expectedCommitStatusDeliveriesNumber: 10,
expectedCallsNumber: 5,
},
{
name: "test get commit status deliveries with limit less than commit status deliveries and sortDirection desc",
sortDirection: types.SortDirectionDesc,
limit: 5,
expectedCommitStatusDeliveriesNumber: 20,
expectedCallsNumber: 4,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// populate the expected commit status deliveries
expectedProject01CommitStatusDeliveries := []*types.CommitStatusDelivery{}
for _, c := range project01CommitStatusDeliveries {
if len(tt.deliveryStatusFilter) > 0 && !slices.Contains(tt.deliveryStatusFilter, c.DeliveryStatus) {
continue
}
expectedProject01CommitStatusDeliveries = append(expectedProject01CommitStatusDeliveries, c)
}
// default sortdirection is asc
// reverse if sortDirection is desc
if tt.sortDirection == types.SortDirectionDesc {
slices.Reverse(expectedProject01CommitStatusDeliveries)
}
callsNumber := 0
var lastProjectCommitStatusDelivery uint64
var respAllProjectCommitStatusDeliveries []*types.CommitStatusDelivery
// fetch next results
for {
res, err := ns.ah.GetProjectCommitStatusDeliveries(ctx, &action.GetProjectCommitStatusDeliveriesRequest{
ProjectID: project01,
Limit: tt.limit,
SortDirection: tt.sortDirection,
DeliveryStatusFilter: tt.deliveryStatusFilter,
StartSequence: lastProjectCommitStatusDelivery,
})
testutil.NilError(t, err)
callsNumber++
respAllProjectCommitStatusDeliveries = append(respAllProjectCommitStatusDeliveries, res.CommitStatusDeliveries...)
if res.HasMore == false {
break
}
lastProjectCommitStatusDelivery = respAllProjectCommitStatusDeliveries[len(respAllProjectCommitStatusDeliveries)-1].Sequence
}
assert.Assert(t, cmpDiffObject(expectedProject01CommitStatusDeliveries, respAllProjectCommitStatusDeliveries))
assert.Assert(t, cmp.Equal(callsNumber, tt.expectedCallsNumber))
})
}
}
func TestProjectCommitStatusRedelivery(t *testing.T) {
t.Parallel()
t.Run("test project commit status redelivery with deliverystatus = deliveryError", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
commitStatus := createCommitStatus(t, ctx, ns, 1, project01)
commitStatusDelivery := createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusDeliveryError)
t.Logf("starting ns")
cs := setupStubCommitStatusUpdater()
ns.u = cs
err := ns.ah.CommitStatusRedelivery(ctx, commitStatus.ProjectID, commitStatusDelivery.ID)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err := ns.commitStatusDeliveriesHandler(ctx); err != nil {
t.Fatalf("unexpected err: %v", err)
}
res, err := ns.ah.GetProjectCommitStatusDeliveries(ctx, &action.GetProjectCommitStatusDeliveriesRequest{ProjectID: project01, SortDirection: types.SortDirectionAsc})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if len(res.CommitStatusDeliveries) != 2 {
t.Fatalf("expected 2 CommitStatusDeliveries got: %d", len(res.CommitStatusDeliveries))
}
if res.CommitStatusDeliveries[0].DeliveryStatus != types.DeliveryStatusDeliveryError {
t.Fatalf("expected %q DeliveryStatus got: %q", types.DeliveryStatusDeliveryError, res.CommitStatusDeliveries[0].DeliveryStatus)
}
if res.CommitStatusDeliveries[1].DeliveryStatus != types.DeliveryStatusDelivered {
t.Fatalf("expected %q DeliveryStatus got: %q", types.DeliveryStatusDelivered, res.CommitStatusDeliveries[1].DeliveryStatus)
}
})
t.Run("test project commit status redelivery with deliverystatus = delivered", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
commitStatus := createCommitStatus(t, ctx, ns, 1, project01)
commitStatusDelivery := createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusDelivered)
t.Logf("starting ns")
cs := setupStubCommitStatusUpdater()
ns.u = cs
err := ns.ah.CommitStatusRedelivery(ctx, commitStatus.ProjectID, commitStatusDelivery.ID)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err := ns.commitStatusDeliveriesHandler(ctx); err != nil {
t.Fatalf("unexpected err: %v", err)
}
res, err := ns.ah.GetProjectCommitStatusDeliveries(ctx, &action.GetProjectCommitStatusDeliveriesRequest{ProjectID: project01, SortDirection: types.SortDirectionAsc})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if len(res.CommitStatusDeliveries) != 2 {
t.Fatalf("expected 2 CommitStatusDeliveries got: %d", len(res.CommitStatusDeliveries))
}
if res.CommitStatusDeliveries[0].DeliveryStatus != types.DeliveryStatusDelivered {
t.Fatalf("expected %q DeliveryStatus got: %q", types.DeliveryStatusDelivered, res.CommitStatusDeliveries[0].DeliveryStatus)
}
if res.CommitStatusDeliveries[1].DeliveryStatus != types.DeliveryStatusDelivered {
t.Fatalf("expected %q DeliveryStatus got: %q", types.DeliveryStatusDelivered, res.CommitStatusDeliveries[1].DeliveryStatus)
}
})
t.Run("test redelivery not existing project commit status delivery", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
commitStatus := createCommitStatus(t, ctx, ns, 1, project01)
expectedErr := util.NewAPIError(util.ErrNotExist, util.WithAPIErrorMsgf("commitStatusDelivery %q doesn't exist", commitStatusDelivery01), serrors.CommitStatusDeliveryDoesNotExist())
err := ns.ah.CommitStatusRedelivery(ctx, commitStatus.ProjectID, commitStatusDelivery01)
if err == nil {
t.Fatalf("expected error %v, got nil err", expectedErr)
}
if err.Error() != expectedErr.Error() {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
})
t.Run("test project commit status redelivery with projectID that belong to another project", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
commitStatus := createCommitStatus(t, ctx, ns, 1, project01)
commitStatusDelivery := createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusDelivered)
commitStatus = createCommitStatus(t, ctx, ns, 1, project02)
createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusDelivered)
expectedErr := util.NewAPIError(util.ErrNotExist, util.WithAPIErrorMsgf("commitStatusDelivery %q doesn't belong to project %q", commitStatusDelivery.ID, project02), serrors.CommitStatusDeliveryDoesNotExist())
err := ns.ah.CommitStatusRedelivery(ctx, project02, commitStatusDelivery.ID)
if err == nil {
t.Fatalf("expected error %v, got nil err", expectedErr)
}
if err.Error() != expectedErr.Error() {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
})
t.Run("test project commit status redelivery with the last delivery that hasn't been delivered", func(t *testing.T) {
dir := t.TempDir()
log := testutil.NewLogger(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ns := setupNotificationService(ctx, t, log, dir)
commitStatus := createCommitStatus(t, ctx, ns, 1, project01)
commitStatusDelivery := createCommitStatusDelivery(t, ctx, ns, commitStatus.ID, types.DeliveryStatusNotDelivered)
t.Logf("starting ns")
cs := setupStubCommitStatusUpdater()
ns.u = cs
expectedErr := util.NewAPIError(util.ErrBadRequest, util.WithAPIErrorMsgf("the previous delivery of commit status %q hasn't already been delivered", commitStatusDelivery.CommitStatusID), serrors.CommitStatusDeliveryAlreadyInProgress())
err := ns.ah.CommitStatusRedelivery(ctx, commitStatus.ProjectID, commitStatusDelivery.ID)
if err == nil {
t.Fatalf("expected error %v, got nil err", expectedErr)
}
if err.Error() != expectedErr.Error() {
t.Fatalf("expected err %v, got err: %v", expectedErr, err)
}
if err := ns.commitStatusDeliveriesHandler(ctx); err != nil {
t.Fatalf("unexpected err: %v", err)
}
res, err := ns.ah.GetProjectCommitStatusDeliveries(ctx, &action.GetProjectCommitStatusDeliveriesRequest{ProjectID: project01, SortDirection: types.SortDirectionAsc})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if len(res.CommitStatusDeliveries) != 1 {
t.Fatalf("expected 1 CommitStatusDeliveries got: %d", len(res.CommitStatusDeliveries))
}
if res.CommitStatusDeliveries[0].DeliveryStatus != types.DeliveryStatusDelivered {
t.Fatalf("expected %q DeliveryStatus got: %q", types.DeliveryStatusDelivered, res.CommitStatusDeliveries[0].DeliveryStatus)
}
})
}