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

     1  // Copyright 2022 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 pubsub
    16  
    17  import (
    18  	"bytes"
    19  	"compress/zlib"
    20  	"context"
    21  	"encoding/json"
    22  	"io"
    23  	"net/http"
    24  	"testing"
    25  
    26  	. "github.com/smartystreets/goconvey/convey"
    27  	"google.golang.org/protobuf/encoding/protojson"
    28  	"google.golang.org/protobuf/proto"
    29  	"google.golang.org/protobuf/types/known/structpb"
    30  
    31  	"go.chromium.org/luci/bisection/compilefailuredetection"
    32  	"go.chromium.org/luci/bisection/internal/config"
    33  	configpb "go.chromium.org/luci/bisection/proto/config"
    34  	taskpb "go.chromium.org/luci/bisection/task/proto"
    35  	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
    36  	. "go.chromium.org/luci/common/testing/assertions"
    37  	"go.chromium.org/luci/common/tsmon"
    38  	"go.chromium.org/luci/gae/impl/memory"
    39  	"go.chromium.org/luci/server/tq"
    40  )
    41  
    42  func TestBuildBucketPubsub(t *testing.T) {
    43  	t.Parallel()
    44  
    45  	Convey("Buildbucket Pubsub Handler", t, func() {
    46  		c := memory.Use(context.Background())
    47  		// Setup config.
    48  		projectCfg := config.CreatePlaceholderProjectConfig()
    49  		cfg := map[string]*configpb.ProjectConfig{"chromium": projectCfg}
    50  		So(config.SetTestProjectConfig(c, cfg), ShouldBeNil)
    51  
    52  		Convey("Should create new task", func() {
    53  			c, scheduler := tq.TestingContext(c, nil)
    54  			compilefailuredetection.RegisterTaskClass()
    55  			largeField, err := largeField("bg")
    56  			So(err, ShouldBeNil)
    57  
    58  			buildPubsub := &buildbucketpb.BuildsV2PubSub{
    59  				Build: &buildbucketpb.Build{
    60  					Id: 8000,
    61  					Builder: &buildbucketpb.BuilderID{
    62  						Project: "chromium",
    63  						Bucket:  "ci",
    64  					},
    65  					Status: buildbucketpb.Status_FAILURE,
    66  				},
    67  				BuildLargeFields: largeField,
    68  			}
    69  			r := &http.Request{Body: makeBBReq(buildPubsub)}
    70  			err = buildbucketPubSubHandlerImpl(c, r)
    71  			So(err, ShouldBeNil)
    72  			// Check that a task was created.
    73  			task := &taskpb.FailedBuildIngestionTask{
    74  				Bbid: 8000,
    75  			}
    76  			expected := proto.Clone(task).(*taskpb.FailedBuildIngestionTask)
    77  			So(scheduler.Tasks().Payloads()[0], ShouldResembleProto, expected)
    78  		})
    79  
    80  		Convey("Unsupported project", func() {
    81  			c, _ := tsmon.WithDummyInMemory(c)
    82  			buildPubsub := &buildbucketpb.BuildsV2PubSub{
    83  				Build: &buildbucketpb.Build{
    84  					Builder: &buildbucketpb.BuilderID{
    85  						Project: "chrome",
    86  						Bucket:  "ci",
    87  					},
    88  					Status: buildbucketpb.Status_FAILURE,
    89  				},
    90  			}
    91  			r := &http.Request{Body: makeBBReq(buildPubsub)}
    92  			err := buildbucketPubSubHandlerImpl(c, r)
    93  			So(err, ShouldBeNil)
    94  			So(bbCounter.Get(c, "chrome", "unsupported"), ShouldEqual, 1)
    95  		})
    96  
    97  		Convey("Excluded builder group", func() {
    98  			c, _ := tsmon.WithDummyInMemory(c)
    99  			largeField, err := largeField("chromium.clang")
   100  			So(err, ShouldBeNil)
   101  			buildPubsub := &buildbucketpb.BuildsV2PubSub{
   102  				Build: &buildbucketpb.Build{
   103  					Builder: &buildbucketpb.BuilderID{
   104  						Project: "chromium",
   105  						Bucket:  "ci",
   106  					},
   107  					Status: buildbucketpb.Status_FAILURE,
   108  				},
   109  				BuildLargeFields: largeField,
   110  			}
   111  			r := &http.Request{Body: makeBBReq(buildPubsub)}
   112  			err = buildbucketPubSubHandlerImpl(c, r)
   113  			So(err, ShouldBeNil)
   114  			So(bbCounter.Get(c, "chromium", "unsupported"), ShouldEqual, 1)
   115  		})
   116  
   117  		Convey("Rerun metrics captured", func() {
   118  			c, _ := tsmon.WithDummyInMemory(c)
   119  
   120  			// Receiving a pubsub message for a terminal status should increase counter.
   121  			buildPubsub := &buildbucketpb.BuildsV2PubSub{
   122  				Build: &buildbucketpb.Build{
   123  					Id: 8000,
   124  					Builder: &buildbucketpb.BuilderID{
   125  						Project: "chromium",
   126  						Bucket:  "findit",
   127  						Builder: "gofindit-culprit-verification",
   128  					},
   129  					Status: buildbucketpb.Status_INFRA_FAILURE,
   130  				},
   131  			}
   132  			r := &http.Request{Body: makeBBReq(buildPubsub)}
   133  			err := buildbucketPubSubHandlerImpl(c, r)
   134  			So(err, ShouldBeNil)
   135  			So(rerunCounter.Get(c, "chromium", "INFRA_FAILURE", "compile"), ShouldEqual, 1)
   136  
   137  			// Receiving a pubsub message for a terminal status should not increase counter.
   138  			buildPubsub = &buildbucketpb.BuildsV2PubSub{
   139  				Build: &buildbucketpb.Build{
   140  					Id: 8001,
   141  					Builder: &buildbucketpb.BuilderID{
   142  						Project: "chromium",
   143  						Bucket:  "findit",
   144  						Builder: "gofindit-culprit-verification",
   145  					},
   146  					Status: buildbucketpb.Status_SCHEDULED,
   147  				},
   148  			}
   149  			r = &http.Request{Body: makeBBReq(buildPubsub)}
   150  			err = buildbucketPubSubHandlerImpl(c, r)
   151  			So(err, ShouldBeNil)
   152  			So(rerunCounter.Get(c, "chromium", "SCHEDULED", "compile"), ShouldEqual, 0)
   153  		})
   154  	})
   155  }
   156  
   157  func makeBBReq(message *buildbucketpb.BuildsV2PubSub) io.ReadCloser {
   158  	bm, err := protojson.Marshal(message)
   159  	if err != nil {
   160  		panic(err)
   161  	}
   162  
   163  	attributes := map[string]any{
   164  		"version": "v2",
   165  	}
   166  
   167  	msg := struct {
   168  		Message struct {
   169  			Data       []byte
   170  			Attributes map[string]any
   171  		}
   172  	}{struct {
   173  		Data       []byte
   174  		Attributes map[string]any
   175  	}{Data: bm, Attributes: attributes}}
   176  	jmsg, _ := json.Marshal(msg)
   177  	return io.NopCloser(bytes.NewReader(jmsg))
   178  }
   179  
   180  func largeField(builderGroup string) ([]byte, error) {
   181  	large := &buildbucketpb.Build{
   182  		Input: &buildbucketpb.Build_Input{
   183  			Properties: &structpb.Struct{
   184  				Fields: map[string]*structpb.Value{
   185  					"builder_group": structpb.NewStringValue(builderGroup),
   186  				},
   187  			},
   188  		},
   189  	}
   190  	largeBytes, err := proto.Marshal(large)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	buf := &bytes.Buffer{}
   196  	zw := zlib.NewWriter(buf)
   197  	if _, err := zw.Write(largeBytes); err != nil {
   198  		return nil, err
   199  	}
   200  	if err := zw.Close(); err != nil {
   201  		return nil, err
   202  	}
   203  	return buf.Bytes(), nil
   204  }