func TestGetPresenceMultiInvalid(t *testing.T) { c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { if !reflect.DeepEqual(in.Jid, []string{"*****@*****.**", "*****@*****.**"}) { return fmt.Errorf("bad request jids %#v", in.Jid) } out.PresenceResponse = []*pb.PresenceResponse{ newPresenceResponse(true, pb.PresenceResponse_EXTENDED_AWAY, true), newPresenceResponse(true, pb.PresenceResponse_CHAT, false), } return nil }) jids := []string{"*****@*****.**", "*****@*****.**"} presence, err := GetPresenceMulti(c, jids, "") wantErr := appengine.MultiError{nil, ErrInvalidJID} if !reflect.DeepEqual(err, wantErr) { t.Fatalf("GetPresenceMulti: got %#v, want %#v", err, wantErr) } want := []string{"xa", ""} if !reflect.DeepEqual(presence, want) { t.Errorf("GetPresenceMulti: got %#v, want %#v", presence, want) } }
func TestGetRequest(t *testing.T) { serviceCalled := false apiKey := "lyric" c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error { // Test request. if n := len(req.Key); n != 1 { t.Errorf("got %d want 1", n) return nil } if k := string(req.Key[0]); k != apiKey { t.Errorf("got %q want %q", k, apiKey) } serviceCalled = true return nil }) // Test the "forward" path from the API call parameters to the // protobuf request object. (The "backward" path from the // protobuf response object to the API call response, // including the error response, are handled in the next few // tests). Get(c, apiKey) if !serviceCalled { t.Error("Service was not called as expected") } }
func TestGetPresenceMultiFromJID(t *testing.T) { c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { if !reflect.DeepEqual(in.Jid, []string{"*****@*****.**", "*****@*****.**"}) { return fmt.Errorf("bad request jids %#v", in.Jid) } if jid := in.GetFromJid(); jid != "*****@*****.**" { return fmt.Errorf("bad from jid %q", jid) } out.PresenceResponse = []*pb.PresenceResponse{ newPresenceResponse(true, pb.PresenceResponse_NORMAL, true), newPresenceResponse(true, pb.PresenceResponse_CHAT, true), } return nil }) jids := []string{"*****@*****.**", "*****@*****.**"} presence, err := GetPresenceMulti(c, jids, "*****@*****.**") if err != nil { t.Fatalf("GetPresenceMulti: %v", err) } want := []string{"", "chat"} if !reflect.DeepEqual(presence, want) { t.Errorf("GetPresenceMulti: got %v, want %v", presence, want) } }
func TestNamespaceResetting(t *testing.T) { namec := make(chan *string, 1) c0 := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { namec <- req.NameSpace return errRPC }) // Check that wrapping c0 in a namespace twice works correctly. c1, err := appengine.Namespace(c0, "A") if err != nil { t.Fatalf("appengine.Namespace: %v", err) } c2, err := appengine.Namespace(c1, "") // should act as the original context if err != nil { t.Fatalf("appengine.Namespace: %v", err) } Get(c0, "key") if ns := <-namec; ns != nil { t.Errorf(`Get with c0: ns = %q, want nil`, *ns) } Get(c1, "key") if ns := <-namec; ns == nil { t.Error(`Get with c1: ns = nil, want "A"`) } else if *ns != "A" { t.Errorf(`Get with c1: ns = %q, want "A"`, *ns) } Get(c2, "key") if ns := <-namec; ns != nil { t.Errorf(`Get with c2: ns = %q, want nil`, *ns) } }
func TestNamespaceResetting(t *testing.T) { var nsField *string c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { nsField = req.NameSpace return errRPC }) // Check that wrapping c in a namespace twice works correctly. nc, err := appengine.Namespace(c, "A") if err != nil { t.Fatalf("appengine.Namespace: %v", err) } c0, err := appengine.Namespace(nc, "") // should act as the original context if err != nil { t.Fatalf("appengine.Namespace: %v", err) } Get(c, "key") if nsField != nil { t.Fatalf("Get with c yielded %q", *nsField) } Get(nc, "key") if nsField == nil || *nsField != "A" { t.Fatalf("Get with nc yielded %v", nsField) } Get(c0, "key") if nsField != nil && *nsField != "" { t.Fatalf("Get with c0 yielded %q", *nsField) } }
func TestAddRequest(t *testing.T) { var apiItem = &Item{ Key: "lyric", Value: []byte("Oh, give me a home"), } serviceCalled := false c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error { // Test request. pbItem := req.Item[0] if k := string(pbItem.Key); k != apiItem.Key { t.Errorf("got %q want %q", k, apiItem.Key) } if v := string(apiItem.Value); v != string(pbItem.Value) { t.Errorf("got %q want %q", v, string(pbItem.Value)) } if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_ADD { t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_ADD) } serviceCalled = true return nil }) Add(c, apiItem) if !serviceCalled { t.Error("Service was not called as expected") } }
func TestNamespaceResetting(t *testing.T) { // These environment variables are necessary because *Query.Run will // call internal.FullyQualifiedAppID which checks these variables or falls // back to the Metadata service that is not available in tests. environ := []struct { key, value string }{ {"GAE_LONG_APP_ID", "my-app-id"}, {"GAE_PARTITION", "1"}, } for _, v := range environ { old := os.Getenv(v.key) os.Setenv(v.key, v.value) v.value = old } defer func() { // Restore old environment after the test completes. for _, v := range environ { if v.value == "" { os.Unsetenv(v.key) continue } os.Setenv(v.key, v.value) } }() namec := make(chan *string, 1) c0 := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(req *pb.Query, res *pb.QueryResult) error { namec <- req.NameSpace return fmt.Errorf("RPC error") }) // Check that wrapping c0 in a namespace twice works correctly. c1, err := appengine.Namespace(c0, "A") if err != nil { t.Fatalf("appengine.Namespace: %v", err) } c2, err := appengine.Namespace(c1, "") // should act as the original context if err != nil { t.Fatalf("appengine.Namespace: %v", err) } q := NewQuery("SomeKind") q.Run(c0) if ns := <-namec; ns != nil { t.Errorf(`RunQuery with c0: ns = %q, want nil`, *ns) } q.Run(c1) if ns := <-namec; ns == nil { t.Error(`RunQuery with c1: ns = nil, want "A"`) } else if *ns != "A" { t.Errorf(`RunQuery with c1: ns = %q, want "A"`, *ns) } q.Run(c2) if ns := <-namec; ns != nil { t.Errorf(`RunQuery with c2: ns = %q, want nil`, *ns) } }
func TestSetRequest(t *testing.T) { var apiItem = &Item{ Key: "lyric", Value: []byte("Where the buffalo roam"), } serviceCalled := false c := aetesting.FakeSingleContext(t, "memcache", "Set", func(req *pb.MemcacheSetRequest, _ *pb.MemcacheSetResponse) error { // Test request. if n := len(req.Item); n != 1 { t.Errorf("got %d want 1", n) return nil } pbItem := req.Item[0] if k := string(pbItem.Key); k != apiItem.Key { t.Errorf("got %q want %q", k, apiItem.Key) } if v := string(pbItem.Value); v != string(apiItem.Value) { t.Errorf("got %q want %q", v, string(apiItem.Value)) } if p := *pbItem.SetPolicy; p != pb.MemcacheSetRequest_SET { t.Errorf("got %v want %v", p, pb.MemcacheSetRequest_SET) } serviceCalled = true return nil }) Set(c, apiItem) if !serviceCalled { t.Error("Service was not called as expected") } }
func TestPutAutoOrderID(t *testing.T) { index, err := Open("Doc") if err != nil { t.Fatalf("err from Open: %v", err) } c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { if len(in.Params.GetDocument()) < 1 { return fmt.Errorf("expected at least one Document, got %v", in) } got, want := in.Params.Document[0].GetOrderId(), int32(time.Since(orderIDEpoch).Seconds()) if d := got - want; -5 > d || d > 5 { return fmt.Errorf("got OrderId %d, want near %d", got, want) } *out = pb.IndexDocumentResponse{ Status: []*pb.RequestStatus{ {Code: pb.SearchServiceError_OK.Enum()}, }, DocId: []string{ "doc_id", }, } return nil }) if _, err := index.Put(c, "", &searchFields); err != nil { t.Fatal(err) } }
func TestGetPresenceMultiUnavailable(t *testing.T) { c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error { if !reflect.DeepEqual(in.Jid, []string{"*****@*****.**", "*****@*****.**"}) { return fmt.Errorf("bad request jids %#v", in.Jid) } out.PresenceResponse = []*pb.PresenceResponse{ newPresenceResponse(false, pb.PresenceResponse_AWAY, true), newPresenceResponse(false, pb.PresenceResponse_DO_NOT_DISTURB, true), } return nil }) jids := []string{"*****@*****.**", "*****@*****.**"} presence, err := GetPresenceMulti(c, jids, "") wantErr := appengine.MultiError{ ErrPresenceUnavailable, ErrPresenceUnavailable, } if !reflect.DeepEqual(err, wantErr) { t.Fatalf("GetPresenceMulti: got %#v, want %#v", err, wantErr) } want := []string{"", ""} if !reflect.DeepEqual(presence, want) { t.Errorf("GetPresenceMulti: got %#v, want %#v", presence, want) } }
func TestFieldSpec(t *testing.T) { index, err := Open("Doc") if err != nil { t.Fatalf("err from Open: %v", err) } errFoo := errors.New("foo") // sentinel error when there isn't one. testCases := []struct { desc string opts *SearchOptions want *pb.FieldSpec }{ { desc: "No options", want: &pb.FieldSpec{}, }, { desc: "Fields", opts: &SearchOptions{ Fields: []string{"one", "two"}, }, want: &pb.FieldSpec{ Name: []string{"one", "two"}, }, }, { desc: "Expressions", opts: &SearchOptions{ Expressions: []FieldExpression{ {Name: "one", Expr: "price * quantity"}, {Name: "two", Expr: "min(daily_use, 10) * rate"}, }, }, want: &pb.FieldSpec{ Expression: []*pb.FieldSpec_Expression{ {Name: proto.String("one"), Expression: proto.String("price * quantity")}, {Name: proto.String("two"), Expression: proto.String("min(daily_use, 10) * rate")}, }, }, }, } for _, tt := range testCases { c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, _ *pb.SearchResponse) error { params := req.Params if !reflect.DeepEqual(params.FieldSpec, tt.want) { t.Errorf("%s: params.FieldSpec=%v; want %v", tt.desc, params.FieldSpec, tt.want) } return errFoo // Always return some error to prevent response parsing. }) it := index.Search(c, "gopher", tt.opts) if _, err := it.Next(nil); err != errFoo { t.Fatalf("%s: got error %v; want %v", tt.desc, err, errFoo) } } }
func TestAddResponseRPCError(t *testing.T) { c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { return errRPC }) if err := Add(c, &Item{}); err != errRPC { t.Errorf("got %v want errRPC", err) } }
func TestGetResponseRPCError(t *testing.T) { c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { return errRPC }) if _, err := Get(c, "something"); err != errRPC { t.Errorf("got %v want errRPC", err) } }
func TestAddResponseNotStored(t *testing.T) { c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_NOT_STORED} return nil }) if err := Add(c, &Item{}); err != ErrNotStored { t.Errorf("got %v want ErrNotStored", err) } }
func TestGetResponseMiss(t *testing.T) { c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { // don't fill in any of the response return nil }) _, err := Get(c, "something") if err != ErrCacheMiss { t.Errorf("got %v want ErrCacheMiss", err) } }
func TestSetResponseError(t *testing.T) { c := aetesting.FakeSingleContext(t, "memcache", "Set", func(_ *pb.MemcacheSetRequest, res *pb.MemcacheSetResponse) error { res.SetStatus = []pb.MemcacheSetResponse_SetStatusCode{pb.MemcacheSetResponse_ERROR} return nil }) if err := Set(c, &Item{}); err != ErrServerError { t.Errorf("got %v want ErrServerError", err) } }
func TestAddWithEmptyPath(t *testing.T) { c := aetesting.FakeSingleContext(t, "taskqueue", "Add", func(req *pb.TaskQueueAddRequest, res *pb.TaskQueueAddResponse) error { if got, want := string(req.Url), "/_ah/queue/a-queue"; got != want { return fmt.Errorf("req.Url = %q; want %q", got, want) } return nil }) if _, err := Add(c, &Task{}, "a-queue"); err != nil { t.Fatalf("Add: %v", err) } }
func TestList(t *testing.T) { c := aetesting.FakeSingleContext(t, "modules", "GetModules", func(req *pb.GetModulesRequest, res *pb.GetModulesResponse) error { res.Module = []string{"default", "mod1"} return nil }) got, err := List(c) if err != nil { t.Fatalf("List: %v", err) } want := []string{"default", "mod1"} if !reflect.DeepEqual(got, want) { t.Errorf("List = %v, want %v", got, want) } }
func TestGetMultiEmpty(t *testing.T) { serviceCalled := false c := aetesting.FakeSingleContext(t, "memcache", "Get", func(req *pb.MemcacheGetRequest, _ *pb.MemcacheGetResponse) error { serviceCalled = true return nil }) // Test that the Memcache service is not called when // GetMulti is passed an empty slice of keys. GetMulti(c, []string{}) if serviceCalled { t.Error("Service was called but should not have been") } }
func TestMessageConstruction(t *testing.T) { var got *pb.MailMessage c := aetesting.FakeSingleContext(t, "mail", "Send", func(in *pb.MailMessage, out *basepb.VoidProto) error { got = in return nil }) msg := &Message{ Sender: "*****@*****.**", To: []string{"*****@*****.**"}, Body: "Hey, lunch time?", Attachments: []Attachment{ // Regression test for a prod bug. The address of a range variable was used when // constructing the outgoing proto, so multiple attachments used the same name. { Name: "att1.txt", Data: []byte("data1"), ContentID: "<att1>", }, { Name: "att2.txt", Data: []byte("data2"), }, }, } if err := Send(c, msg); err != nil { t.Fatalf("Send: %v", err) } want := &pb.MailMessage{ Sender: proto.String("*****@*****.**"), To: []string{"*****@*****.**"}, Subject: proto.String(""), TextBody: proto.String("Hey, lunch time?"), Attachment: []*pb.MailAttachment{ { FileName: proto.String("att1.txt"), Data: []byte("data1"), ContentID: proto.String("<att1>"), }, { FileName: proto.String("att2.txt"), Data: []byte("data2"), }, }, } if !proto.Equal(got, want) { t.Errorf("Bad proto for %+v\n got %v\nwant %v", msg, got, want) } }
func TestGetResponseHit(t *testing.T) { key := "lyric" value := "Where the buffalo roam" c := aetesting.FakeSingleContext(t, "memcache", "Get", func(_ *pb.MemcacheGetRequest, res *pb.MemcacheGetResponse) error { res.Item = []*pb.MemcacheGetResponse_Item{ {Key: []byte(key), Value: []byte(value)}, } return nil }) apiItem, err := Get(c, key) if apiItem == nil || apiItem.Key != key || string(apiItem.Value) != value { t.Errorf("got %q, %q want {%q,%q}, nil", apiItem, err, key, value) } }
func TestPut(t *testing.T) { index, err := Open("Doc") if err != nil { t.Fatalf("err from Open: %v", err) } c := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(in *pb.IndexDocumentRequest, out *pb.IndexDocumentResponse) error { expectedIn := &pb.IndexDocumentRequest{ Params: &pb.IndexDocumentParams{ Document: []*pb.Document{ {Field: protoFields}, // Omit OrderId since we'll check it explicitly. }, IndexSpec: &pb.IndexSpec{ Name: proto.String("Doc"), }, }, } if len(in.Params.GetDocument()) < 1 { return fmt.Errorf("expected at least one Document, got %v", in) } got, want := in.Params.Document[0].GetOrderId(), int32(time.Since(orderIDEpoch).Seconds()) if d := got - want; -5 > d || d > 5 { return fmt.Errorf("got OrderId %d, want near %d", got, want) } in.Params.Document[0].OrderId = nil if !proto.Equal(in, expectedIn) { return fmt.Errorf("unsupported argument:\ngot %v\nwant %v", in, expectedIn) } *out = pb.IndexDocumentResponse{ Status: []*pb.RequestStatus{ {Code: pb.SearchServiceError_OK.Enum()}, }, DocId: []string{ "doc_id", }, } return nil }) id, err := index.Put(c, "", &searchDoc) if err != nil { t.Fatal(err) } if want := "doc_id"; id != want { t.Errorf("Got doc ID %q, want %q", id, want) } }
func TestDefaultVersion(t *testing.T) { c := aetesting.FakeSingleContext(t, "modules", "GetDefaultVersion", func(req *pb.GetDefaultVersionRequest, res *pb.GetDefaultVersionResponse) error { if *req.Module != module { t.Errorf("Module = %v, want %v", req.Module, module) } res.Version = proto.String(version) return nil }) got, err := DefaultVersion(c, module) if err != nil { t.Fatalf("DefaultVersion: %v", err) } if got != version { t.Errorf("Version = %v, want %v", got, version) } }
func TestStart(t *testing.T) { c := aetesting.FakeSingleContext(t, "modules", "StartModule", func(req *pb.StartModuleRequest, res *pb.StartModuleResponse) error { if *req.Module != module { t.Errorf("Module = %v, want %v", req.Module, module) } if *req.Version != version { t.Errorf("Version = %v, want %v", req.Version, version) } return nil }) err := Start(c, module, version) if err != nil { t.Fatalf("Start: %v", err) } }
func TestVersions(t *testing.T) { c := aetesting.FakeSingleContext(t, "modules", "GetVersions", func(req *pb.GetVersionsRequest, res *pb.GetVersionsResponse) error { if *req.Module != module { t.Errorf("Module = %v, want %v", req.Module, module) } res.Version = []string{"v1", "v2", "v3"} return nil }) got, err := Versions(c, module) if err != nil { t.Fatalf("Versions: %v", err) } want := []string{"v1", "v2", "v3"} if !reflect.DeepEqual(got, want) { t.Errorf("Versions = %v, want %v", got, want) } }
func TestLimit(t *testing.T) { index, err := Open("Doc") if err != nil { t.Fatalf("err from Open: %v", err) } c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, res *pb.SearchResponse) error { limit := 20 // Default per page. if req.Params.Limit != nil { limit = int(*req.Params.Limit) } res.Status = &pb.RequestStatus{Code: pb.SearchServiceError_OK.Enum()} res.MatchedCount = proto.Int64(int64(limit)) for i := 0; i < limit; i++ { res.Result = append(res.Result, &pb.SearchResult{Document: &pb.Document{}}) res.Cursor = proto.String("moreresults") } return nil }) const maxDocs = 500 // Limit maximum number of docs. testCases := []struct { limit, want int }{ {limit: 0, want: maxDocs}, {limit: 42, want: 42}, {limit: 100, want: 100}, {limit: 1000, want: maxDocs}, } for _, tt := range testCases { it := index.Search(c, "gopher", &SearchOptions{Limit: tt.limit, IDsOnly: true}) count := 0 for ; count < maxDocs; count++ { _, err := it.Next(nil) if err == Done { break } if err != nil { t.Fatalf("err after %d: %v", count, err) } } if count != tt.want { t.Errorf("got %d results, expected %d", count, tt.want) } } }
func TestNamespaceApplication(t *testing.T) { internal.NamespaceMods["srv"] = func(m proto.Message, namespace string) { sm := m.(*basepb.StringProto) if strings.Contains(sm.GetValue(), "-") { // be idempotent return } sm.Value = proto.String(sm.GetValue() + "-" + namespace) } ctx := aetesting.FakeSingleContext(t, "srv", "mth", func(in, out *basepb.StringProto) error { out.Value = proto.String(in.GetValue()) return nil }) call := func(ctx context.Context, in string) (out string, ok bool) { inm := &basepb.StringProto{Value: &in} outm := &basepb.StringProto{} if err := internal.Call(ctx, "srv", "mth", inm, outm); err != nil { t.Errorf("RPC(in=%q) failed: %v", in, err) return "", false } return outm.GetValue(), true } // Check without a namespace. got, ok := call(ctx, "foo") if !ok { t.FailNow() } if got != "foo" { t.Errorf("Un-namespaced RPC returned %q, want %q", got, "foo") } // Now check by applying a namespace. nsCtx, err := Namespace(ctx, "myns") if err != nil { t.Fatal(err) } got, ok = call(nsCtx, "bar") if !ok { t.FailNow() } if got != "bar-myns" { t.Errorf("Namespaced RPC returned %q, want %q", got, "bar-myns") } }
func TestSetNumInstances(t *testing.T) { c := aetesting.FakeSingleContext(t, "modules", "SetNumInstances", func(req *pb.SetNumInstancesRequest, res *pb.SetNumInstancesResponse) error { if *req.Module != module { t.Errorf("Module = %v, want %v", req.Module, module) } if *req.Version != version { t.Errorf("Version = %v, want %v", req.Version, version) } if *req.Instances != instances { t.Errorf("Instances = %v, want %d", req.Instances, instances) } return nil }) err := SetNumInstances(c, module, version, instances) if err != nil { t.Fatalf("SetNumInstances: %v", err) } }
func TestGetPresence(t *testing.T) { c := aetesting.FakeSingleContext(t, "xmpp", "GetPresence", func(in *pb.PresenceRequest, out *pb.PresenceResponse) error { if jid := in.GetJid(); jid != "*****@*****.**" { return fmt.Errorf("bad jid %q", jid) } setPresenceResponse(out, true, pb.PresenceResponse_CHAT, true) return nil }) presence, err := GetPresence(c, "*****@*****.**", "") if err != nil { t.Fatalf("GetPresence: %v", err) } if presence != "chat" { t.Errorf("GetPresence: got %#v, want %#v", presence, pb.PresenceResponse_CHAT) } }
func TestAddErrors(t *testing.T) { var tests = []struct { err, want error sameErr bool // if true, should return err exactly }{ { err: &internal.APIError{ Service: "taskqueue", Code: int32(pb.TaskQueueServiceError_TASK_ALREADY_EXISTS), }, want: ErrTaskAlreadyAdded, }, { err: &internal.APIError{ Service: "taskqueue", Code: int32(pb.TaskQueueServiceError_TOMBSTONED_TASK), }, want: ErrTaskAlreadyAdded, }, { err: &internal.APIError{ Service: "taskqueue", Code: int32(pb.TaskQueueServiceError_UNKNOWN_QUEUE), }, want: errors.New("not used"), sameErr: true, }, } for _, tc := range tests { c := aetesting.FakeSingleContext(t, "taskqueue", "Add", func(req *pb.TaskQueueAddRequest, res *pb.TaskQueueAddResponse) error { // don't fill in any of the response return tc.err }) task := &Task{Path: "/worker", Method: "PULL"} _, err := Add(c, task, "a-queue") want := tc.want if tc.sameErr { want = tc.err } if err != want { t.Errorf("Add with tc.err = %v, got %#v, want = %#v", tc.err, err, want) } } }