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 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 createInNamespace(c context.Context, namespace string) { ns, _ := appengine.Namespace(c, namespace) p := new(pet) p.Value = 1 k := datastore.NewKey(ns, "pet", "", 1, nil) datastore.Put(ns, k, p) }
// CleanupDatastore is to remove all data in datastore func CleanupDatastore(ctx context.Context, namespaces ...string) error { var dummy []interface{} logger := wcg.NewLogger(nil) logger.Debugf("[Fixture] --------- CleanupDatastore ---------") namespaceList := append(namespaces, "") for _, ns := range namespaceList { logger.Debugf("[Fixture] Cleanup: ns=%q", ns) var _ctx = ctx if ns != "" { _ctx, _ = appengine.Namespace(_ctx, ns) } err := wcg.RetryUntil(func() error { var keys []*datastore.Key var err error if keys, err = datastore.NewQuery("").KeysOnly().GetAll(_ctx, dummy); err != nil { return err } if err := datastore.DeleteMulti(_ctx, keys); err != nil { return err } count, _ := datastore.NewQuery("").KeysOnly().Count(_ctx) if count == 0 { return nil } return fmt.Errorf("Still have %d keys.", count) }, 10*time.Second, 100*time.Millisecond) if err != nil { return err } } return nil }
// NewKey returns a new *datastore.Key for `kind` func (kind *Kind) NewKey(req *wcg.Request, key string, parent *datastore.Key) *datastore.Key { ctx, err := appengine.Namespace(gae.NewContext(req), kind.Namespace) if err != nil { panic(err) } return datastore.NewKey(ctx, string(kind.Name), key, 0, parent) }
func namespace(ctx context.Context) context.Context { // assumes the user is logged in. ctx, err := appengine.Namespace(ctx, user.Current(ctx).ID) if err != nil { // ... } return ctx }
// TestGetNamespacedKey ensures issue https://goo.gl/rXU8nK is fixed so that // memcache uses the namespace from the key instead of the context. func TestGetNamespacedKey(t *testing.T) { c, closeFunc := NewContext(t) defer closeFunc() const intVal = int64(12) type testEntity struct { IntVal int64 } namespacedCtx, err := appengine.Namespace(c, "keyNamespace") if err != nil { t.Fatal(err) } key := datastore.NewKey(c, "Entity", "", 1, nil) namespacedKey := datastore.NewKey(namespacedCtx, "Entity", "", key.IntID(), nil) entity := &testEntity{intVal} if namespacedKey, err = nds.Put(c, namespacedKey, entity); err != nil { t.Fatal(err) } // Prime cache. if err := nds.Get(namespacedCtx, namespacedKey, &testEntity{}); err != nil { t.Fatal(err) } // Ensure that we get a value back from the cache by checking if the // datastore is called at all. entityFromCache := true nds.SetDatastoreGetMulti(func(c context.Context, keys []*datastore.Key, vals interface{}) error { if len(keys) != 0 { entityFromCache = false } return nil }) if err := nds.Get(c, namespacedKey, &testEntity{}); err != nil { t.Fatal(err) } nds.SetDatastoreGetMulti(datastore.GetMulti) if !entityFromCache { t.Fatal("entity not obtained from cache") } if err := nds.Delete(namespacedCtx, namespacedKey); err != nil { t.Fatal(err) } entity = &testEntity{} if err := nds.Get(c, namespacedKey, entity); err == nil { t.Fatalf("expected no such entity error but got %+v", entity) } else if err != datastore.ErrNoSuchEntity { t.Fatal(err) } }
// Namespace set the namespace func (d *Driver) Namespace(namespace string) *Driver { var err error d.ctx, err = appengine.Namespace(d.ctx, namespace) d.namespace = namespace if err != nil { panic(err) } return d }
func loadJsonToDatastore(ctx context.Context, pkey *datastore.Key, data map[string]interface{}, logger wcg.Logger) error { var kind string var ns string var keyval interface{} var key *datastore.Key var ok bool var err error if _, ok = data["_kind"]; !ok { return fmt.Errorf("Missing key `_kind`") } kind = data["_kind"].(string) if keyval, ok = data["_key"]; !ok { return fmt.Errorf("Missing key `_key`") } if _, ok = data["_ns"]; ok { ns = data["_ns"].(string) ctx, err = appengine.Namespace(ctx, ns) if err != nil { return fmt.Errorf("Could not change the namespace of %q, check _ns value: ", ns, err) } } switch keyval.(type) { case int64: key = datastore.NewKey(ctx, kind, "", keyval.(int64), pkey) case string: key = datastore.NewKey(ctx, kind, keyval.(string), 0, pkey) default: return fmt.Errorf("Invalid `_key` type.") } if _, err := datastore.Put(ctx, key, jsonSaver(data)); err != nil { return err } // Check the data is actually stored. if err := wcg.RetryUntil(func() error { var v jsonSaver if err := datastore.Get(ctx, key, &v); err != nil { return fmt.Errorf( "fixture is not synched on '%s:[%s]': internal error?(%v) on ", kind, keyval, err, ) } return nil }, 5*time.Second, 500*time.Millisecond); err != nil { return err } logger.Debugf("[Fixture] %s%s -- %v", ns, key, data) if children, ok := data["_children"]; ok { for _, v := range children.([]interface{}) { if err := loadJsonToDatastore(ctx, key, v.(map[string]interface{}), logger); err != nil { return err } } } return nil }
func storeContext(ctx context.Context) context.Context { ctx, err := appengine.Namespace(ctx, Namespace) if err != nil { // NOTE: appengine.Namespace will only return an error if // the passed namespace name itself is invalid. panic(err) } return ctx }
func (g giImpl) Namespace(namespace string) (context.Context, error) { c, err := appengine.Namespace(g, namespace) if err != nil { return c, err } pc := *getProbeCache(g) pc.namespace = namespace return withProbeCache(c, &pc), nil }
// cachedCerts fetches public certificates info from DefaultCertURI and // caches it for the duration specified in Age header of a response. func cachedCerts(c context.Context) (*certsList, error) { namespacedContext, err := appengine.Namespace(c, certNamespace) if err != nil { return nil, err } var certs *certsList _, err = memcache.JSON.Get(namespacedContext, DefaultCertURI, &certs) if err == nil { return certs, nil } // Cache miss or server error. // If any error other than cache miss, it's proably not a good time // to use memcache. var cacheResults = err == memcache.ErrCacheMiss if !cacheResults { log.Debugf(c, "%s", err.Error()) } log.Debugf(c, "Fetching provider certs from: %s", DefaultCertURI) resp, err := newHTTPClient(c).Get(DefaultCertURI) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, errors.New("Could not reach Cert URI or bad response.") } certBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } err = json.Unmarshal(certBytes, &certs) if err != nil { return nil, err } if cacheResults { expiration := certExpirationTime(resp.Header) if expiration > 0 { item := &memcache.Item{ Key: DefaultCertURI, Value: certBytes, Expiration: expiration, } err = memcache.Set(namespacedContext, item) if err != nil { log.Errorf(c, "Error adding Certs to memcache: %v", err) } } } return certs, nil }
func (g giImpl) Namespace(namespace string) (context.Context, error) { aeCtx, err := appengine.Namespace(g.aeCtx, namespace) if err != nil { return g.usrCtx, err } usrCtx := context.WithValue(g.usrCtx, prodContextKey, aeCtx) pc := *getProbeCache(usrCtx) pc.namespace = namespace return withProbeCache(usrCtx, &pc), nil }
// this will generate some random data // on production this will create 96,768 entitied func createInNamespace(c context.Context, namespace string) { c, _ = appengine.Namespace(c, namespace) for m := 1; m <= 12; m++ { for d := 1; d <= 28; d++ { taken := time.Date(2015, time.Month(m), d, 0, 0, 0, 0, time.UTC) generateRandomFn.Call(c, taken) } } }
// Stat returns the BlobInfo for a provided blobKey. If no blob was found for // that key, Stat returns datastore.ErrNoSuchEntity. func Stat(c context.Context, blobKey appengine.BlobKey) (*BlobInfo, error) { c, _ = appengine.Namespace(c, "") // Blobstore is always in the empty string namespace dskey := datastore.NewKey(c, blobInfoKind, string(blobKey), 0, nil) bi := &BlobInfo{ BlobKey: blobKey, } if err := datastore.Get(c, dskey, bi); err != nil && !isErrFieldMismatch(err) { return nil, err } return bi, nil }
func TestDatastoreFixture(t *testing.T) { filepath := mkTempfile(`[{ "_kind": "FixtureKind", "_key": "key1", "IntValue": 10, "FloatValue": 2.4, "BoolValue": true, "StringValue": "foobar", "BytesValue": "[]bytesfoobar", "DateTimeValue": "2014-01-02T14:02:50Z", "DateValue": "2014-01-02", "Slice": ["a", "b", "c"], "Struct": { "Foo": "bar" } },{ "_kind": "FixtureKind", "_key": "key1", "_ns": "ns1", "StringValue": "withns1" } ]`) assert := wcg.NewAssert(t) var fk FixtureKind assert.Nil(DatastoreFixture(ts.Context, filepath, nil), "DatastoreFixture") key := datastore.NewKey(ts.Context, "FixtureKind", "key1", 0, nil) wcg.NewLogger(nil).Infof("GET: %s", key) assert.Nil(datastore.Get(ts.Context, key, &fk), "datastore.Get('key1') ") assert.EqInt(10, fk.IntValue, "IntValue should be 10") assert.EqFloat32(2.4, fk.FloatValue, "FloatValue should be 2.4") assert.EqStr("foobar", fk.StringValue, "StringValue should be 'foobar'") assert.EqStr("bytesfoobar", string(fk.BytesValue), "BytesValue should be 'foobar'") assert.EqInt(3, len(fk.Slice), "len(Slice) should be 3") assert.EqStr("a", string(fk.Slice[0]), "Slice[0] should be 'a'") assert.EqStr("b", string(fk.Slice[1]), "Slice[0] should be 'a'") assert.EqStr("c", string(fk.Slice[2]), "Slice[0] should be 'a'") assert.EqTime(time.Date(2014, 01, 02, 14, 02, 50, 0, time.UTC), fk.DateTimeValue, "DateTimeValue should be 2014-01-02T14:02:50Z") assert.EqTime(time.Date(2014, 01, 02, 0, 0, 0, 0, time.UTC), fk.DateValue, "DateTimeValue should be 2014-01-02T00:00:00Z") assert.EqStr("bar", string(fk.Struct.Foo), "Struct.Foo should be 'bar'") // namespace ns1, _ := appengine.Namespace(ts.Context, "ns1") key = datastore.NewKey(ns1, "FixtureKind", "key1", 0, nil) assert.Nil(datastore.Get(ns1, key, &fk), "datastore.Get('key1') /w ns1") assert.EqStr("withns1", fk.StringValue, "StringValue should be 'withns1'") }
// AEContextNoTxn retrieves the raw "google.golang.org/appengine" compatible // Context that's not part of a transaction. func AEContextNoTxn(c context.Context) context.Context { aeCtx, _ := c.Value(prodContextNoTxnKey).(context.Context) if aeCtx == nil { return nil } aeCtx, err := appengine.Namespace(aeCtx, info.Get(c).GetNamespace()) if err != nil { panic(err) } if deadline, ok := c.Deadline(); ok { aeCtx, _ = context.WithDeadline(aeCtx, deadline) } return aeCtx }
func TestNamespaceResetting(t *testing.T) { namec := make(chan *string, 1) c0 := aetesting.FakeSingleContext(t, "search", "IndexDocument", func(req *pb.IndexDocumentRequest, res *pb.IndexDocumentResponse) error { namec <- req.Params.IndexSpec.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) } i := (&Index{}) i.Put(c0, "something", &searchDoc) if ns := <-namec; ns != nil { t.Errorf(`Put with c0: ns = %q, want nil`, *ns) } i.Put(c1, "something", &searchDoc) if ns := <-namec; ns == nil { t.Error(`Put with c1: ns = nil, want "A"`) } else if *ns != "A" { t.Errorf(`Put with c1: ns = %q, want "A"`, *ns) } i.Put(c2, "something", &searchDoc) if ns := <-namec; ns != nil { t.Errorf(`Put with c2: ns = %q, want nil`, *ns) } }
func TestNamespaceResetting(t *testing.T) { 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) } }
// TestClearNamespacedLocks tests to make sure that locks are cleared when // RunInTransaction is using a namespace. func TestClearNamespacedLocks(t *testing.T) { c, closeFunc := NewContext(t, nil) defer closeFunc() c, err := appengine.Namespace(c, "testnamespace") if err != nil { t.Fatal(err) } type testEntity struct { Val int } key := datastore.NewKey(c, "TestEntity", "", 1, nil) // Prime cache. if err := nds.Get(c, key, &testEntity{}); err == nil { t.Fatal("expected no such entity") } else if err != datastore.ErrNoSuchEntity { t.Fatal(err) } if err := nds.RunInTransaction(c, func(tc context.Context) error { if err := nds.Get(tc, key, &testEntity{}); err == nil { return errors.New("expected no such entity") } else if err != datastore.ErrNoSuchEntity { return err } if _, err := nds.Put(tc, key, &testEntity{3}); err != nil { return err } return nil }, nil); err != nil { t.Fatal(err) } entity := &testEntity{} if err := nds.Get(c, key, entity); err != nil { t.Fatal(err) } if entity.Val != 3 { t.Fatal("incorrect val") } }
func TestVerifySignedJWT(t *testing.T) { r, _, closer := newTestRequest(t, "GET", "/", nil) defer closer() nc, err := appengine.Namespace(appengine.NewContext(r), certNamespace) if err != nil { t.Fatal(err) } item := &memcache.Item{Key: DefaultCertURI, Value: []byte(googCerts)} if err := memcache.Set(nc, item); err != nil { t.Fatal(err) } tts := []struct { token string now time.Time want *signedJWT }{ {jwtValidTokenString, jwtValidTokenTime, &jwtValidTokenObject}, {jwtValidTokenString, jwtValidTokenTime.Add(time.Hour * 24), nil}, {jwtValidTokenString, jwtValidTokenTime.Add(-time.Hour * 24), nil}, {jwtInvalidKeyToken, jwtValidTokenTime, nil}, {jwtInvalidAlgToken, jwtValidTokenTime, nil}, {"invalid.token", jwtValidTokenTime, nil}, {"another.invalid.token", jwtValidTokenTime, nil}, } ec := NewContext(r) for i, tt := range tts { jwt, err := verifySignedJWT(ec, tt.token, tt.now.Unix()) switch { case err != nil && tt.want != nil: t.Errorf("%d: verifySignedJWT(%q, %d) = %v; want %#v", i, tt.token, tt.now.Unix(), err, tt.want) case err == nil && tt.want == nil: t.Errorf("%d: verifySignedJWT(%q, %d) = %#v; want error", i, tt.token, tt.now.Unix(), jwt) case err == nil && tt.want != nil: if !reflect.DeepEqual(jwt, tt.want) { t.Errorf("%d: verifySignedJWT(%q, %d) = %v; want %#v", i, tt.token, tt.now.Unix(), jwt, tt.want) } } } }
func TestCachedCertsCacheHit(t *testing.T) { origTransport := httpTransportFactory defer func() { httpTransportFactory = origTransport }() httpTransportFactory = func(c context.Context) http.RoundTripper { return newTestRoundTripper() } req, _, closer := newTestRequest(t, "GET", "/", nil) defer closer() nc, err := appengine.Namespace(appengine.NewContext(req), certNamespace) if err != nil { t.Fatal(err) } tts := []struct { cacheValue string want *certsList }{ {"", nil}, {"{}", &certsList{}}, {`{"keyvalues": [{}]}`, &certsList{[]*certInfo{{}}}}, {`{"keyvalues": [ {"algorithm": "RS256", "exponent": "123", "keyid": "some-id", "modulus": "123"} ]}`, &certsList{[]*certInfo{{"RS256", "123", "some-id", "123"}}}}, } ec := NewContext(req) for i, tt := range tts { item := &memcache.Item{Key: DefaultCertURI, Value: []byte(tt.cacheValue)} if err := memcache.Set(nc, item); err != nil { t.Fatal(err) } out, err := cachedCerts(ec) switch { case err != nil && tt.want != nil: t.Errorf("%d: cachedCerts() error %v", i, err) case err == nil && tt.want == nil: t.Errorf("%d: cachedCerts() = %#v; want error", i, out) case err == nil && tt.want != nil && !reflect.DeepEqual(out, tt.want): t.Errorf("cachedCerts() = %#+v (%T); want %#+v (%T)", out, out, tt.want, tt.want) } } }
// dsF2R (DS fake-to-real) converts a DSKey back to an SDK *Key. func dsF2R(aeCtx context.Context, k *ds.Key) (*datastore.Key, error) { if k == nil { return nil, nil } // drop aid. _, ns, toks := k.Split() err := error(nil) aeCtx, err = appengine.Namespace(aeCtx, ns) if err != nil { return nil, err } ret := datastore.NewKey(aeCtx, toks[0].Kind, toks[0].StringID, toks[0].IntID, nil) for _, t := range toks[1:] { ret = datastore.NewKey(aeCtx, t.Kind, t.StringID, t.IntID, ret) } return ret, nil }
func someHandler(w http.ResponseWriter, r *http.Request) { ctx := appengine.NewContext(r) err := incrementCounter(ctx, "SomeRequest") if err != nil { // ... handle err } // temporarily use a new namespace { ctx, err := appengine.Namespace(ctx, "-global-") if err != nil { // ... handle err } err = incrementCounter(ctx, "SomeRequest") if err != nil { // ... handle err } } io.WriteString(w, "Updated counters.\n") }
func someRequest(w http.ResponseWriter, r *http.Request) { ctx := appengine.NewContext(r) // Perform asynchronous requests to update counter. // (missing error handling here.) t := taskqueue.NewPOSTTask("/_ah/counter", map[string][]string{ "counter_name": {"someRequest"}, }) taskqueue.Add(ctx, t, "") // temporarily use a new namespace { ctx, err := appengine.Namespace(ctx, "-global-") if err != nil { // ... handle err } taskqueue.Add(ctx, t, "") } io.WriteString(w, "Counters will be updated.\n") }
func memcacheContext(c context.Context) (context.Context, error) { return appengine.Namespace(c, memcacheNamespace) }
func TestCachedCertsCacheMiss(t *testing.T) { rt := newTestRoundTripper() origTransport := httpTransportFactory defer func() { httpTransportFactory = origTransport }() httpTransportFactory = func(c context.Context) http.RoundTripper { return rt } req, _, closer := newTestRequest(t, "GET", "/", nil) defer closer() nc, err := appengine.Namespace(appengine.NewContext(req), certNamespace) if err != nil { t.Fatal(err) } ec := NewContext(req) tts := []*struct { respStatus int respContent, cacheControl, age string want *certsList shouldCache bool }{ {200, `{"keyvalues":null}`, "max-age=3600", "600", &certsList{}, true}, {-1, "", "", "", nil, false}, {400, "", "", "", nil, false}, {200, `{"keyvalues":null}`, "", "", &certsList{}, false}, } for i, tt := range tts { if tt.respStatus > 0 { resp := &http.Response{ Status: fmt.Sprintf("%d", tt.respStatus), StatusCode: tt.respStatus, Body: ioutil.NopCloser(strings.NewReader(tt.respContent)), Header: make(http.Header), } resp.Header.Set("cache-control", tt.cacheControl) resp.Header.Set("age", tt.age) rt.Add(resp) } memcache.Delete(nc, DefaultCertURI) out, err := cachedCerts(ec) switch { case err != nil && tt.want != nil: t.Errorf("%d: cachedCerts() = %v", i, err) case err == nil && tt.want == nil: t.Errorf("%d: cachedCerts() = %#v; want error", i, out) default: if !reflect.DeepEqual(out, tt.want) { t.Errorf("%d: cachedCerts() = %#v; want %#v", i, out, tt.want) } if !tt.shouldCache { continue } item, err := memcache.Get(nc, DefaultCertURI) if err != nil { t.Errorf("%d: memcache.Get(%q) = %v", i, DefaultCertURI, err) continue } cert := string(item.Value) if tt.respContent != cert { t.Errorf("%d: memcache.Get(%q) = %q; want %q", i, DefaultCertURI, cert, tt.respContent) } } } }
func TestCurrentUser(t *testing.T) { const ( // Default values from user_service_stub.py of dev_appserver2. clientID = "123456789.apps.googleusercontent.com" bearerEmail = "*****@*****.**" validScope = "valid.scope" ) inst, err := aetest.NewInstance(nil) if err != nil { t.Fatalf("failed to create instance: %v", err) } defer inst.Close() req, err := inst.NewRequest("GET", "/", nil) nc, err := appengine.Namespace(appengine.NewContext(req), certNamespace) if err != nil { t.Fatal(err) } // googCerts are provided in jwt_test.go item := &memcache.Item{Key: DefaultCertURI, Value: []byte(googCerts)} if err := memcache.Set(nc, item); err != nil { t.Fatal(err) } origCurrentUTC := currentUTC defer func() { currentUTC = origCurrentUTC }() currentUTC = func() time.Time { return jwtValidTokenTime } jwtStr, jwt := jwtValidTokenString, jwtValidTokenObject tts := []struct { token string scopes, audiences, clientIDs []string wantEmail string }{ // success {jwtStr, []string{EmailScope}, []string{jwt.Audience}, []string{jwt.ClientID}, jwt.Email}, {"ya29.token", []string{EmailScope}, []string{clientID}, []string{clientID}, bearerEmail}, {"ya29.token", []string{EmailScope, validScope}, []string{clientID}, []string{clientID}, bearerEmail}, {"1/token", []string{validScope}, []string{clientID}, []string{clientID}, bearerEmail}, // failure {jwtStr, []string{EmailScope}, []string{"other-client"}, []string{"other-client"}, ""}, {"some.invalid.jwt", []string{EmailScope}, []string{jwt.Audience}, []string{jwt.ClientID}, ""}, {"", []string{validScope}, []string{clientID}, []string{clientID}, ""}, // The following test is commented for now because default implementation // of UserServiceStub in dev_appserver2 allows any scope. // TODO: figure out how to test this. //{"ya29.invalid", []string{"invalid.scope"}, []string{clientID}, []string{clientID}, ""}, {"doesn't matter", nil, []string{clientID}, []string{clientID}, ""}, {"doesn't matter", []string{EmailScope}, nil, []string{clientID}, ""}, {"doesn't matter", []string{EmailScope}, []string{clientID}, nil, ""}, } for i, tt := range tts { r, err := inst.NewRequest("GET", "/", nil) c := newContext(r, cachingAuthenticatorFactory) if tt.token != "" { r.Header.Set("authorization", "oauth "+tt.token) } user, err := CurrentUser(c, tt.scopes, tt.audiences, tt.clientIDs) switch { case tt.wantEmail == "" && err == nil: t.Errorf("%d: CurrentUser(%v, %v, %v) = %v; want error", i, tt.scopes, tt.audiences, tt.clientIDs, user) case tt.wantEmail != "" && user == nil: t.Errorf("%d: CurrentUser(%v, %v, %v) = %v; want email = %q", i, tt.scopes, tt.audiences, tt.clientIDs, err, tt.wantEmail) case tt.wantEmail != "" && tt.wantEmail != user.Email: t.Errorf("%d: CurrentUser(%v, %v, %v) = %v; want email = %q", i, tt.scopes, tt.audiences, tt.clientIDs, user, tt.wantEmail) } } }
/* Splits an arbitrary datastore query. This is used to shard queries within a namespace. This is done in one of two ways: 1. If the query contains an inequality filter, the lower and upper bounds are determined (this may involve querying the datastore) then the range is split naively. This works well when the property that is being queried on is uniformly distributed. 2. If the query does not contain an inequality filter. The query will be partitioned by the entity key. This is done by using the "__scatter__" property to get a random sample of the keyspace and partitioning based on that. This can result in a poor distribution if there are equality filters on the query that bias the selection with respect to certain regions of keyspace. The following clauses are not supported by this class: An inequality filter of unsupported type. Filters that are incompatible with datastore cursors such as: Combining multiple clauses with an OR. A filter on a value being NOT_EQUAL. TODO: add better explanation of the various splitting scenarios */ func (q *Query) split(c context.Context, shards, oversampling int) ([]*Query, error) { // NOTE: at this point the query will have a single namespace for the one we're processing // all the datastore operations we do for the query should be within that namespace c, _ = appengine.Namespace(c, q.namespaces[0]) equality, pr, err := q.toEqualityListAndRange() if err != nil { return nil, err } var propertyRanges []*propertyRange switch { case shards == 1: // no splitting required log.Debugf(c, "no split") propertyRanges = []*propertyRange{pr} case pr.empty(): // no property range, shard on key space only log.Debugf(c, "shard on keyspace") propertyRanges, err = q.getScatterSplitPoints(c, shards, oversampling, equality) if err != nil { return nil, err } default: log.Debugf(c, "shard on property range") if pr.lower == nil { // set lower filter predicate from datastore so we work out a meaningful range v, err := q.getExtremePropertyValue(c, equality, pr.upper.FieldName, Ascending) if err != nil { return nil, err } pr.lower = &filter{pr.upper.FieldName, greaterEq, v} log.Debugf(c, "got lower %s", pr.lower) } // set upper filter predicate from datastore so we work out a meaningful range if pr.upper == nil { v, err := q.getExtremePropertyValue(c, equality, pr.upper.FieldName, Descending) if err != nil { return nil, err } pr.upper = &filter{pr.upper.FieldName, lessEq, v} log.Debugf(c, "got upper %s", pr.upper) } propertyRanges = pr.split(shards) } // build query for the property ranges results := []*Query{} for _, pr := range propertyRanges { query := NewQuery(q.kind) query.selection = q.selection query.namespaces = q.namespaces for _, f := range equality { query.filter = append(query.filter, f) } if pr.lower != nil { query.filter = append(query.filter, *pr.lower) } if pr.upper != nil { query.filter = append(query.filter, *pr.upper) } results = append(results, query) } return results, nil }