// NewRemoteContext returns a context that gives access to the production // APIs for the application at the given host. All communication will be // performed over SSL unless the host is localhost. func NewRemoteContext(host string, client *http.Client) (context.Context, error) { // Add an appcfg header to outgoing requests. t := client.Transport if t == nil { t = http.DefaultTransport } client.Transport = &headerAddingRoundTripper{t} url := url.URL{ Scheme: "https", Host: host, Path: "/_ah/remote_api", } if host == "localhost" || strings.HasPrefix(host, "localhost:") { url.Scheme = "http" } u := url.String() appID, err := getAppID(client, u) if err != nil { return nil, fmt.Errorf("unable to contact server: %v", err) } rc := &remoteContext{ client: client, url: u, } ctx := internal.WithCallOverride(context.Background(), rc.call) ctx = internal.WithLogOverride(ctx, rc.logf) ctx = internal.WithAppIDOverride(ctx, appID) return ctx, nil }
// NewRequest returns an *http.Request associated with this instance. func (i *instance) NewRequest(method, urlStr string, body io.Reader) (*http.Request, error) { req, err := http.NewRequest(method, urlStr, body) if err != nil { return nil, err } // Associate this request. release := internal.RegisterTestRequest(req, i.apiURL, func(ctx context.Context) context.Context { ctx = internal.WithAppIDOverride(ctx, "dev~"+i.appID) return ctx }) i.relFuncs = append(i.relFuncs, release) return req, nil }
func TestIncompleteKeyWithParent(t *testing.T) { c := internal.WithAppIDOverride(context.Background(), "s~some-app") // fadduh is a complete key. fadduh := NewKey(c, "Person", "", 1, nil) if fadduh.Incomplete() { t.Fatalf("fadduh is incomplete") } // robert is an incomplete key with fadduh as a parent. robert := NewIncompleteKey(c, "Person", fadduh) if !robert.Incomplete() { t.Fatalf("robert is complete") } // Both should be valid keys. if !fadduh.valid() { t.Errorf("fadduh is invalid: %v", fadduh) } if !robert.valid() { t.Errorf("robert is invalid: %v", robert) } }
func TestQueryToProto(t *testing.T) { // The context is required to make Keys for the test cases. var got *pb.Query NoErr := errors.New("No error") c := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(in *pb.Query, out *pb.QueryResult) error { got = in return NoErr // return a non-nil error so Run doesn't keep going. }) c = internal.WithAppIDOverride(c, "dev~fake-app") testCases := []struct { desc string query *Query want *pb.Query err string }{ { desc: "empty", query: NewQuery(""), want: &pb.Query{}, }, { desc: "standard query", query: NewQuery("kind").Order("-I").Filter("I >", 17).Filter("U =", "Dave").Limit(7).Offset(42), want: &pb.Query{ Kind: proto.String("kind"), Filter: []*pb.Query_Filter{ { Op: pb.Query_Filter_GREATER_THAN.Enum(), Property: []*pb.Property{ { Name: proto.String("I"), Value: &pb.PropertyValue{Int64Value: proto.Int64(17)}, Multiple: proto.Bool(false), }, }, }, { Op: pb.Query_Filter_EQUAL.Enum(), Property: []*pb.Property{ { Name: proto.String("U"), Value: &pb.PropertyValue{StringValue: proto.String("Dave")}, Multiple: proto.Bool(false), }, }, }, }, Order: []*pb.Query_Order{ { Property: proto.String("I"), Direction: pb.Query_Order_DESCENDING.Enum(), }, }, Limit: proto.Int32(7), Offset: proto.Int32(42), }, }, { desc: "ancestor", query: NewQuery("").Ancestor(NewKey(c, "kind", "Mummy", 0, nil)), want: &pb.Query{ Ancestor: &pb.Reference{ App: proto.String("dev~fake-app"), Path: &pb.Path{ Element: []*pb.Path_Element{{Type: proto.String("kind"), Name: proto.String("Mummy")}}, }, }, }, }, { desc: "projection", query: NewQuery("").Project("A", "B"), want: &pb.Query{ PropertyName: []string{"A", "B"}, }, }, { desc: "projection with distinct", query: NewQuery("").Project("A", "B").Distinct(), want: &pb.Query{ PropertyName: []string{"A", "B"}, GroupByPropertyName: []string{"A", "B"}, }, }, { desc: "keys only", query: NewQuery("").KeysOnly(), want: &pb.Query{ KeysOnly: proto.Bool(true), RequirePerfectPlan: proto.Bool(true), }, }, { desc: "empty filter", query: NewQuery("kind").Filter("=", 17), err: "empty query filter field nam", }, { desc: "bad filter type", query: NewQuery("kind").Filter("M =", map[string]bool{}), err: "bad query filter value type", }, { desc: "bad filter operator", query: NewQuery("kind").Filter("I <<=", 17), err: `invalid operator "<<=" in filter "I <<="`, }, { desc: "empty order", query: NewQuery("kind").Order(""), err: "empty order", }, { desc: "bad order direction", query: NewQuery("kind").Order("+I"), err: `invalid order: "+I`, }, } for _, tt := range testCases { got = nil if _, err := tt.query.Run(c).Next(nil); err != NoErr { if tt.err == "" || !strings.Contains(err.Error(), tt.err) { t.Errorf("%s: error %v, want %q", tt.desc, err, tt.err) } continue } if tt.err != "" { t.Errorf("%s: no error, want %q", tt.desc, tt.err) continue } // Fields that are common to all protos. tt.want.App = proto.String("dev~fake-app") tt.want.Compile = proto.Bool(true) if !proto.Equal(got, tt.want) { t.Errorf("%s:\ngot %v\nwant %v", tt.desc, got, tt.want) } } }
func TestSimpleQuery(t *testing.T) { struct1 := Gopher{Name: "George", Height: 32} struct2 := Gopher{Name: "Rufus"} pList1 := PropertyList{ { Name: "Name", Value: "George", }, { Name: "Height", Value: int64(32), }, } pList2 := PropertyList{ { Name: "Name", Value: "Rufus", }, } pMap1 := PropertyMap{ "Name": Property{ Name: "Name", Value: "George", }, "Height": Property{ Name: "Height", Value: int64(32), }, } pMap2 := PropertyMap{ "Name": Property{ Name: "Name", Value: "Rufus", }, } testCases := []struct { dst interface{} want interface{} }{ // The destination must have type *[]P, *[]S or *[]*S, for some non-interface // type P such that *P implements PropertyLoadSaver, or for some struct type S. {new([]Gopher), &[]Gopher{struct1, struct2}}, {new([]*Gopher), &[]*Gopher{&struct1, &struct2}}, {new([]PropertyList), &[]PropertyList{pList1, pList2}}, {new([]PropertyMap), &[]PropertyMap{pMap1, pMap2}}, // Any other destination type is invalid. {0, nil}, {Gopher{}, nil}, {PropertyList{}, nil}, {PropertyMap{}, nil}, {[]int{}, nil}, {[]Gopher{}, nil}, {[]PropertyList{}, nil}, {new(int), nil}, {new(Gopher), nil}, {new(PropertyList), nil}, // This is a special case. {new(PropertyMap), nil}, {new([]int), nil}, {new([]map[int]int), nil}, {new([]map[string]Property), nil}, {new([]map[string]interface{}), nil}, {new([]*int), nil}, {new([]*map[int]int), nil}, {new([]*map[string]Property), nil}, {new([]*map[string]interface{}), nil}, {new([]**Gopher), nil}, {new([]*PropertyList), nil}, {new([]*PropertyMap), nil}, } for _, tc := range testCases { nCall := 0 c := aetesting.FakeSingleContext(t, "datastore_v3", "RunQuery", func(in *pb.Query, out *pb.QueryResult) error { nCall++ return fakeRunQuery(in, out) }) c = internal.WithAppIDOverride(c, "dev~fake-app") var ( expectedErr error expectedNCall int ) if tc.want == nil { expectedErr = ErrInvalidEntityType } else { expectedNCall = 1 } keys, err := NewQuery("Gopher").GetAll(c, tc.dst) if err != expectedErr { t.Errorf("dst type %T: got error [%v], want [%v]", tc.dst, err, expectedErr) continue } if nCall != expectedNCall { t.Errorf("dst type %T: Context.Call was called an incorrect number of times: got %d want %d", tc.dst, nCall, expectedNCall) continue } if err != nil { continue } key1 := NewKey(c, "Gopher", "", 6, nil) expectedKeys := []*Key{ key1, NewKey(c, "Gopher", "", 8, key1), } if l1, l2 := len(keys), len(expectedKeys); l1 != l2 { t.Errorf("dst type %T: got %d keys, want %d keys", tc.dst, l1, l2) continue } for i, key := range keys { if key.AppID() != "s~test-app" { t.Errorf(`dst type %T: Key #%d's AppID = %q, want "s~test-app"`, tc.dst, i, key.AppID()) continue } if !keysEqual(key, expectedKeys[i]) { t.Errorf("dst type %T: got key #%d %v, want %v", tc.dst, i, key, expectedKeys[i]) continue } } if !reflect.DeepEqual(tc.dst, tc.want) { t.Errorf("dst type %T: Entities got %+v, want %+v", tc.dst, tc.dst, tc.want) continue } } }