go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/sink/sink_test.go (about)

     1  // Copyright 2019 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 sink
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"testing"
    21  
    22  	"google.golang.org/grpc/codes"
    23  	"google.golang.org/grpc/status"
    24  
    25  	"go.chromium.org/luci/lucictx"
    26  
    27  	"go.chromium.org/luci/resultdb/pbutil"
    28  	sinkpb "go.chromium.org/luci/resultdb/sink/proto/v1"
    29  
    30  	. "github.com/smartystreets/goconvey/convey"
    31  	. "go.chromium.org/luci/common/testing/assertions"
    32  )
    33  
    34  func TestNewServer(t *testing.T) {
    35  	t.Parallel()
    36  
    37  	Convey("NewServer", t, func() {
    38  		ctx := context.Background()
    39  		cfg := testServerConfig(":42", "my_token")
    40  
    41  		Convey("succeeds", func() {
    42  			srv, err := NewServer(ctx, cfg)
    43  			So(err, ShouldBeNil)
    44  			So(srv, ShouldNotBeNil)
    45  		})
    46  		Convey("generates a random auth token, if missing", func() {
    47  			cfg.AuthToken = ""
    48  			srv, err := NewServer(ctx, cfg)
    49  			So(err, ShouldBeNil)
    50  			So(srv.cfg.AuthToken, ShouldNotEqual, "")
    51  		})
    52  		Convey("uses the default max leases, if missing or 0", func() {
    53  			srv, err := NewServer(ctx, cfg)
    54  			So(err, ShouldBeNil)
    55  			So(srv.cfg.ArtChannelMaxLeases, ShouldEqual, DefaultArtChannelMaxLeases)
    56  			So(srv.cfg.TestResultChannelMaxLeases, ShouldEqual, DefaultTestResultChannelMaxLeases)
    57  		})
    58  		Convey("use the custom max leases, if specified", func() {
    59  			cfg.ArtChannelMaxLeases, cfg.TestResultChannelMaxLeases = 123, 456
    60  			srv, err := NewServer(ctx, cfg)
    61  			So(err, ShouldBeNil)
    62  			So(srv.cfg.ArtChannelMaxLeases, ShouldEqual, 123)
    63  			So(srv.cfg.TestResultChannelMaxLeases, ShouldEqual, 456)
    64  			testServerConfig("", "my_token")
    65  		})
    66  		Convey("with TestLocationBase", func() {
    67  			// empty
    68  			cfg.TestLocationBase = ""
    69  			_, err := NewServer(ctx, cfg)
    70  			So(err, ShouldBeNil)
    71  
    72  			// valid
    73  			cfg.TestLocationBase = "//base"
    74  			_, err = NewServer(ctx, cfg)
    75  			So(err, ShouldBeNil)
    76  
    77  			// invalid - not starting with double slahes
    78  			cfg.TestLocationBase = "base"
    79  			_, err = NewServer(ctx, cfg)
    80  			So(err, ShouldErrLike, "TestLocationBase: doesn't start with //")
    81  		})
    82  		Convey("with BaseTags", func() {
    83  			// empty
    84  			cfg.BaseTags = nil
    85  			_, err := NewServer(ctx, cfg)
    86  			So(err, ShouldBeNil)
    87  
    88  			// valid - unique keys
    89  			cfg.BaseTags = pbutil.StringPairs("k1", "v1", "k2", "v2")
    90  			_, err = NewServer(ctx, cfg)
    91  			So(err, ShouldBeNil)
    92  
    93  			// valid - duplicate keys
    94  			cfg.BaseTags = pbutil.StringPairs("k1", "v1", "k1", "v2")
    95  			_, err = NewServer(ctx, cfg)
    96  			So(err, ShouldBeNil)
    97  
    98  			// valid - empty value
    99  			cfg.BaseTags = pbutil.StringPairs("k1", "")
   100  			_, err = NewServer(ctx, cfg)
   101  			So(err, ShouldBeNil)
   102  
   103  			// invalid - empty key
   104  			cfg.BaseTags = pbutil.StringPairs("", "v1")
   105  			_, err = NewServer(ctx, cfg)
   106  			So(err, ShouldErrLike, "key: unspecified")
   107  		})
   108  		Convey("with MaxBatchableArtifactSize", func() {
   109  			// default
   110  			cfg.MaxBatchableArtifactSize = 0
   111  			s, err := NewServer(ctx, cfg)
   112  			So(err, ShouldBeNil)
   113  			So(s.cfg.MaxBatchableArtifactSize, ShouldNotEqual, 0)
   114  
   115  			// valid
   116  			cfg.MaxBatchableArtifactSize = 512 * 1024
   117  			_, err = NewServer(ctx, cfg)
   118  			So(err, ShouldBeNil)
   119  			So(cfg.MaxBatchableArtifactSize, ShouldNotEqual, 0)
   120  
   121  			cfg.MaxBatchableArtifactSize = 10 * 1024 * 1024
   122  			_, err = NewServer(ctx, cfg)
   123  			So(err, ShouldBeNil)
   124  			So(cfg.MaxBatchableArtifactSize, ShouldNotEqual, 0)
   125  
   126  			// invalid - too big
   127  			cfg.MaxBatchableArtifactSize = 10*1024*1024 + 1
   128  			_, err = NewServer(ctx, cfg)
   129  			So(err, ShouldErrLike, "is greater than 10MiB")
   130  		})
   131  	})
   132  }
   133  
   134  func TestServer(t *testing.T) {
   135  	t.Parallel()
   136  
   137  	Convey("Server", t, func() {
   138  		req := &sinkpb.ReportTestResultsRequest{}
   139  		ctx := context.Background()
   140  
   141  		srvCfg := testServerConfig("", "secret")
   142  		srv, err := NewServer(ctx, srvCfg)
   143  		So(err, ShouldBeNil)
   144  
   145  		Convey("Start assigns a random port, if missing cfg.Address", func() {
   146  			So(srv.Config().Address, ShouldBeEmpty)
   147  			So(srv.Start(ctx), ShouldBeNil)
   148  			So(srv.Config().Address, ShouldNotBeEmpty)
   149  		})
   150  		Convey("Start fails", func() {
   151  			So(srv.Start(ctx), ShouldBeNil)
   152  
   153  			Convey("if called twice", func() {
   154  				So(srv.Start(ctx), ShouldErrLike, "cannot call Start twice")
   155  			})
   156  
   157  			Convey("after being closed", func() {
   158  				So(srv.Close(ctx), ShouldBeNil)
   159  				So(srv.Start(ctx), ShouldErrLike, "cannot call Start twice")
   160  			})
   161  		})
   162  
   163  		Convey("Close closes the HTTP server", func() {
   164  			So(srv.Start(ctx), ShouldBeNil)
   165  			So(srv.Close(ctx), ShouldBeNil)
   166  
   167  			_, err := reportTestResults(ctx, srv.Config().Address, "secret", req)
   168  			// The error could be a connection error or write-error.
   169  			// e.g.,
   170  			// "No connection could be made", "connection refused", "write: broken pipe"
   171  			//
   172  			// The error messages could be different by OS, and this test simply checks
   173  			// whether err != nil.
   174  			So(err, ShouldNotBeNil)
   175  		})
   176  
   177  		Convey("Close fails before Start being called", func() {
   178  			So(srv.Close(ctx), ShouldErrLike, ErrCloseBeforeStart)
   179  		})
   180  
   181  		Convey("Shutdown closes Done", func() {
   182  			isClosed := func() bool {
   183  				select {
   184  				case <-srv.Done():
   185  					return true
   186  				default:
   187  					return false
   188  				}
   189  			}
   190  			So(srv.Start(ctx), ShouldBeNil)
   191  
   192  			// wait until the server is up.
   193  			_, err := reportTestResults(ctx, srv.Config().Address, "secret", req)
   194  			So(err, ShouldBeNil)
   195  
   196  			So(isClosed(), ShouldBeFalse)
   197  			So(srv.Shutdown(ctx), ShouldBeNil)
   198  			So(isClosed(), ShouldBeTrue)
   199  		})
   200  
   201  		Convey("Run", func() {
   202  			handlerErr := make(chan error, 1)
   203  			runErr := make(chan error)
   204  			expected := errors.New("an error-1")
   205  
   206  			Convey("succeeds", func() {
   207  				// launch a go routine with Run
   208  				go func() {
   209  					runErr <- Run(ctx, srvCfg, func(ctx context.Context, cfg ServerConfig) error {
   210  						return <-handlerErr
   211  					})
   212  				}()
   213  
   214  				// finish the callback and verify that srv.Run returned what the callback
   215  				// returned.
   216  				handlerErr <- expected
   217  				So(<-runErr, ShouldEqual, expected)
   218  			})
   219  
   220  			Convey("serves requests", func() {
   221  				So(srv.Start(ctx), ShouldBeNil)
   222  
   223  				Convey("with 200 OK", func() {
   224  					res, err := reportTestResults(ctx, srv.Config().Address, "secret", req)
   225  					So(err, ShouldBeNil)
   226  					So(res, ShouldNotBeNil)
   227  				})
   228  
   229  				Convey("with 401 Unauthorized if the auth_token missing", func() {
   230  					_, err := reportTestResults(ctx, srv.Config().Address, "", req)
   231  					So(status.Code(err), ShouldEqual, codes.Unauthenticated)
   232  				})
   233  
   234  				Convey("with 403 Forbidden if auth_token mismatched", func() {
   235  					_, err := reportTestResults(ctx, srv.Config().Address, "not-a-secret", req)
   236  					So(status.Code(err), ShouldEqual, codes.PermissionDenied)
   237  				})
   238  			})
   239  		})
   240  	})
   241  }
   242  
   243  func TestServerExport(t *testing.T) {
   244  	t.Parallel()
   245  
   246  	Convey("Export returns the configured address and auth_token", t, func() {
   247  		ctx := context.Background()
   248  		srv, err := NewServer(ctx, testServerConfig(":42", "hello"))
   249  		So(err, ShouldBeNil)
   250  
   251  		ctx = srv.Export(ctx)
   252  		sink := lucictx.GetResultSink(ctx)
   253  		So(sink, ShouldNotBeNil)
   254  		So(sink, ShouldNotBeNil)
   255  		So(sink.Address, ShouldEqual, ":42")
   256  		So(sink.AuthToken, ShouldEqual, "hello")
   257  	})
   258  }