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 }