| // Copyright 2020 The LUCI Authors. |
| // |
| // 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 casviewer |
| |
| import ( |
| "context" |
| "io" |
| "net/http/httptest" |
| "testing" |
| |
| "github.com/bazelbuild/remote-apis-sdks/go/pkg/client" |
| "github.com/bazelbuild/remote-apis-sdks/go/pkg/digest" |
| "github.com/bazelbuild/remote-apis-sdks/go/pkg/fakes" |
| repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2" |
| . "github.com/smartystreets/goconvey/convey" |
| "google.golang.org/grpc/codes" |
| "google.golang.org/protobuf/proto" |
| |
| "go.chromium.org/luci/grpc/grpcutil" |
| "go.chromium.org/luci/server/templates" |
| ) |
| |
| func TestBlobs(t *testing.T) { |
| t.Parallel() |
| |
| ctx := context.Background() |
| ctx = templates.Use(ctx, getTemplateBundle("test-version-1"), nil) |
| |
| w := httptest.NewRecorder() |
| |
| Convey("renderTree", t, func() { |
| cl := fakeClient(ctx, t) |
| |
| Convey("Not Found", func() { |
| // This blob doesn't exist on CAS. |
| bd := digest.NewFromBlob([]byte{1}) |
| |
| err := renderTree(ctx, w, cl, &bd, testInstance) |
| |
| So(grpcutil.Code(err), ShouldEqual, codes.NotFound) |
| So(w.Body.String(), ShouldEqual, "") |
| }) |
| |
| Convey("Must be Directory", func() { |
| // This blob exists on CAS, but isn't a Directory. |
| bd, err := cl.WriteBlob(context.Background(), []byte{1}) |
| So(err, ShouldBeNil) |
| |
| err = renderTree(ctx, w, cl, &bd, testInstance) |
| |
| So(grpcutil.Code(err), ShouldEqual, codes.InvalidArgument) |
| So(w.Body.String(), ShouldEqual, "") |
| }) |
| |
| Convey("OK", func() { |
| // Upload a directory node. |
| d := &repb.Directory{ |
| Directories: []*repb.DirectoryNode{ |
| { |
| Name: "subDir", |
| Digest: digest.NewFromBlob([]byte{}).ToProto(), |
| }, |
| }, |
| Files: []*repb.FileNode{ |
| { |
| Name: "foo", |
| Digest: digest.NewFromBlob([]byte{1}).ToProto(), |
| }, |
| }, |
| } |
| b, err := proto.Marshal(d) |
| So(err, ShouldBeNil) |
| bd, err := cl.WriteBlob(context.Background(), b) |
| So(err, ShouldBeNil) |
| |
| err = renderTree(ctx, w, cl, &bd, testInstance) |
| |
| So(err, ShouldBeNil) |
| body, err := io.ReadAll(w.Body) |
| So(err, ShouldBeNil) |
| // body should contain file name, hash, size. |
| So(string(body), ShouldContainSubstring, d.Files[0].Name) |
| So(string(body), ShouldContainSubstring, d.Files[0].Digest.Hash) |
| So(string(body), ShouldContainSubstring, "1 B") |
| }) |
| }) |
| |
| Convey("returnBlob", t, func() { |
| cl := fakeClient(ctx, t) |
| |
| Convey("Not Found", func() { |
| // This blob doesn't exist on CAS. |
| bd := digest.NewFromBlob([]byte{1}) |
| |
| err := returnBlob(ctx, w, cl, &bd, "") |
| |
| So(grpcutil.Code(err), ShouldEqual, codes.NotFound) |
| So(w.Body.String(), ShouldEqual, "") |
| }) |
| |
| Convey("OK", func() { |
| // Upload a blob. |
| b := []byte{1} |
| bd, err := cl.WriteBlob(context.Background(), b) |
| So(err, ShouldBeNil) |
| |
| err = returnBlob(ctx, w, cl, &bd, "test.txt") |
| |
| So(err, ShouldBeNil) |
| body, err := io.ReadAll(w.Body) |
| So(err, ShouldBeNil) |
| So(body, ShouldResemble, b) |
| }) |
| }) |
| } |
| |
| // fakeClient returns a Client for a fake CAS. |
| func fakeClient(ctx context.Context, t *testing.T) *client.Client { |
| casSrv, err := fakes.NewServer(t) |
| So(err, ShouldBeNil) |
| t.Cleanup(casSrv.Clear) |
| cl, err := casSrv.NewTestClient(ctx) |
| So(err, ShouldBeNil) |
| t.Cleanup(func() { |
| cl.Close() // ignore error. |
| }) |
| return cl |
| } |