go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/bqexporter/bqexporter_test.go (about)

     1  // Copyright 2023 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package bqexporter
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	. "github.com/smartystreets/goconvey/convey"
    23  
    24  	"go.chromium.org/luci/bisection/model"
    25  	bqpb "go.chromium.org/luci/bisection/proto/bq"
    26  	bisectionpb "go.chromium.org/luci/bisection/proto/v1"
    27  	"go.chromium.org/luci/bisection/util/testutil"
    28  	"go.chromium.org/luci/common/clock"
    29  	"go.chromium.org/luci/common/clock/testclock"
    30  	"go.chromium.org/luci/gae/impl/memory"
    31  	"go.chromium.org/luci/gae/service/datastore"
    32  )
    33  
    34  func TestExport(t *testing.T) {
    35  	t.Parallel()
    36  	ctx := memory.Use(context.Background())
    37  	testutil.UpdateIndices(ctx)
    38  	cl := testclock.New(testclock.TestTimeUTC)
    39  	baseTime := time.Unix(3600*24*30, 0).UTC()
    40  	cl.Set(baseTime)
    41  	ctx = clock.Set(ctx, cl)
    42  
    43  	Convey("export", t, func() {
    44  		// Create 5 test analyses.
    45  		for i := 1; i <= 5; i++ {
    46  			tfa := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
    47  				ID:         int64(1000 + i),
    48  				RunStatus:  bisectionpb.AnalysisRunStatus_ENDED,
    49  				Status:     bisectionpb.AnalysisStatus(bisectionpb.AnalysisStatus_NOTFOUND),
    50  				CreateTime: clock.Now(ctx).Add(time.Hour * time.Duration(-i)),
    51  			})
    52  			testutil.CreateTestFailure(ctx, &testutil.TestFailureCreationOption{
    53  				ID:        int64(2000 + i),
    54  				Analysis:  tfa,
    55  				IsPrimary: true,
    56  			})
    57  		}
    58  
    59  		client := &fakeExportClient{}
    60  		err := export(ctx, client)
    61  		So(err, ShouldBeNil)
    62  		// Filtered out 3.
    63  		So(len(client.rows), ShouldEqual, 2)
    64  		So(client.rows[0].AnalysisId, ShouldEqual, 1002)
    65  		So(client.rows[1].AnalysisId, ShouldEqual, 1004)
    66  	})
    67  }
    68  func TestFetchTestAnalyses(t *testing.T) {
    69  	t.Parallel()
    70  	ctx := memory.Use(context.Background())
    71  	testutil.UpdateIndices(ctx)
    72  	cl := testclock.New(testclock.TestTimeUTC)
    73  	baseTime := time.Unix(3600*24*30, 0).UTC()
    74  	cl.Set(baseTime)
    75  	ctx = clock.Set(ctx, cl)
    76  
    77  	Convey("fetch test analyses", t, func() {
    78  		// Not ended, should be skipped.
    79  		tf1 := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
    80  			ID:         1001,
    81  			RunStatus:  bisectionpb.AnalysisRunStatus_STARTED,
    82  			CreateTime: clock.Now(ctx).Add(-time.Hour),
    83  		})
    84  		// Ended, but from a long time ago. Should be skipped.
    85  		tf2 := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
    86  			ID:         1002,
    87  			RunStatus:  bisectionpb.AnalysisRunStatus_ENDED,
    88  			CreateTime: clock.Now(ctx).Add(-15 * 24 * time.Hour),
    89  		})
    90  		// Ended, not found.
    91  		tf3 := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
    92  			ID:         1003,
    93  			RunStatus:  bisectionpb.AnalysisRunStatus_ENDED,
    94  			Status:     bisectionpb.AnalysisStatus(bisectionpb.AnalysisStatus_NOTFOUND),
    95  			CreateTime: clock.Now(ctx).Add(-time.Hour),
    96  		})
    97  		// Ended, found, but action not taken, ended recently, should be skipped.
    98  		tf4 := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
    99  			ID:         1004,
   100  			RunStatus:  bisectionpb.AnalysisRunStatus_ENDED,
   101  			Status:     bisectionpb.AnalysisStatus(bisectionpb.AnalysisStatus_FOUND),
   102  			CreateTime: clock.Now(ctx).Add(-time.Hour),
   103  			EndTime:    clock.Now(ctx).Add(-time.Minute),
   104  		})
   105  		createSuspect(ctx, tf4, false)
   106  		// Ended, found, actions taken.
   107  		tf5 := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
   108  			ID:         1005,
   109  			RunStatus:  bisectionpb.AnalysisRunStatus_ENDED,
   110  			Status:     bisectionpb.AnalysisStatus(bisectionpb.AnalysisStatus_FOUND),
   111  			CreateTime: clock.Now(ctx).Add(-2 * time.Hour),
   112  		})
   113  		createSuspect(ctx, tf5, true)
   114  		// Ended, found, but action not taken, ended long time ago.
   115  		tf6 := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
   116  			ID:         1006,
   117  			RunStatus:  bisectionpb.AnalysisRunStatus_ENDED,
   118  			Status:     bisectionpb.AnalysisStatus(bisectionpb.AnalysisStatus_FOUND),
   119  			CreateTime: clock.Now(ctx).Add(-26 * time.Hour),
   120  			EndTime:    clock.Now(ctx).Add(-25 * time.Hour),
   121  		})
   122  		createSuspect(ctx, tf6, false)
   123  		So(datastore.Put(ctx, []*model.TestFailureAnalysis{tf1, tf2, tf3, tf4, tf5, tf6}), ShouldBeNil)
   124  		datastore.GetTestable(ctx).CatchupIndexes()
   125  		tfas, err := fetchTestAnalyses(ctx)
   126  		So(err, ShouldBeNil)
   127  		So(len(tfas), ShouldEqual, 3)
   128  		So(tfas[0].ID, ShouldEqual, 1003)
   129  		So(tfas[1].ID, ShouldEqual, 1005)
   130  		So(tfas[2].ID, ShouldEqual, 1006)
   131  	})
   132  }
   133  
   134  func createSuspect(ctx context.Context, tfa *model.TestFailureAnalysis, hasTakenAction bool) {
   135  	suspect := &model.Suspect{
   136  		Id: tfa.ID,
   137  		ActionDetails: model.ActionDetails{
   138  			HasTakenActions: hasTakenAction,
   139  		},
   140  	}
   141  	So(datastore.Put(ctx, suspect), ShouldBeNil)
   142  	datastore.GetTestable(ctx).CatchupIndexes()
   143  	tfa.VerifiedCulpritKey = datastore.KeyForObj(ctx, suspect)
   144  }
   145  
   146  // Fake client.
   147  type fakeExportClient struct {
   148  	rows []*bqpb.TestAnalysisRow
   149  }
   150  
   151  func (cl *fakeExportClient) EnsureSchema(ctx context.Context) error {
   152  	return nil
   153  }
   154  
   155  func (cl *fakeExportClient) Insert(ctx context.Context, rows []*bqpb.TestAnalysisRow) error {
   156  	cl.rows = append(cl.rows, rows...)
   157  	return nil
   158  }
   159  
   160  func (cl *fakeExportClient) ReadTestFailureAnalysisRows(ctx context.Context) ([]*TestFailureAnalysisRow, error) {
   161  	return []*TestFailureAnalysisRow{
   162  		{
   163  			AnalysisID: 1001,
   164  		},
   165  		{
   166  			AnalysisID: 1003,
   167  		},
   168  		{
   169  			AnalysisID: 1005,
   170  		},
   171  	}, nil
   172  }