// 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 }
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 }
// NewTestServer creates a new *TestServer func NewTestServer() *TestServer { checkLimits() instance, err := aetest.NewInstance(&aetest.Options{ AppID: "wcgapptest", StronglyConsistentDatastore: true, }) if err != nil { panic(fmt.Errorf("aetest.NewInstance returns an error: %v", err)) } req, err := instance.NewRequest("GET", "/", nil) if err != nil { instance.Close() panic(fmt.Errorf("cannot create a new request for aetest.Instance: %v", err)) } var ctx context.Context err = wcg.RetryUntil(func() error { var err error defer func() { if x := recover(); x != nil { err = fmt.Errorf("Unknown error: %v", x) } }() ctx = appengine.NewContext(req) return err }, 5*time.Second, 200*time.Millisecond) // reset sinks level, err := wcg.ParseLogLevel(os.Getenv("LOGGING_LEVEL")) if err == nil { wcg.DefaultLogConfig.Level = level } wcg.DefaultLogConfig.ConfigureSinks() wcg.DefaultLogConfig.AddSink( gae.NewLogSinkWithContext(gae.LogSinkConfig.Format, ctx), level, ) ts := &TestServer{ Context: ctx, instance: instance, } return ts }
// Run to run an async task by calling an AsyncAPI endpoint. func (runner *AsyncTaskTestRunner) Run(path string, query url.Values) *models.AsyncAPITask { type TriggerResponse struct { ID wcg.UUID `json:"id"` } type MonitorResponse struct { ID wcg.UUID `json:"id"` Status models.AsyncAPIStatus `json:"status"` } var t = runner.t var ts = runner.ts var router = runner.router var triggered TriggerResponse var monitor MonitorResponse var onTrigger = runner.onTrigger var onMonitor = runner.onMonitor if onTrigger == nil { onTrigger = func(_ *httptest.TestRequest, f func()) { f() } } if onMonitor == nil { onMonitor = func(_ *httptest.TestRequest, f func()) { f() } } // Request to the trigger endpoint assert := gaetest.NewAssert(t) triggerPath := path if query != nil { triggerPath = fmt.Sprintf("%s?%s", triggerPath, query.Encode()) } req := ts.POSTForm(triggerPath, nil) onTrigger(req, func() { res := req.RouteTo(router) assert.HTTPStatus(201, res) assert.JSONResponse(&triggered, res) }) // Reqeust to the execution endpoint since no module is loaded on test server. go func() { basePath := fmt.Sprintf("%s%s.json", path, triggered.ID) execPath := basePath if query != nil { execPath = fmt.Sprintf("%s?%s", basePath, query.Encode()) } for execPath != "" { req := ts.POSTForm(execPath, nil) req.AddHeader("X-AppEngine-TaskName", "runasynctasktest") res := req.RouteTo(router) if res.Code == 200 { var next url.Values if err := json.Unmarshal(res.Body, &next); err == nil { execPath = fmt.Sprintf("%s?%s", basePath, next.Encode()) } else { execPath = "" } } else { execPath = "" } } }() err := wcg.RetryUntil(func() error { req := ts.GET(fmt.Sprintf("%s%s.json", path, triggered.ID)) onMonitor(req, func() { res := req.RouteTo(router) assert.HTTPStatus(200, res) assert.JSONResponse(&monitor, res) }) if monitor.Status == models.AsyncAPIStatusSuccess || monitor.Status == models.AsyncAPIStatusFailure { return nil } return fmt.Errorf("Task is running") }, _runAsyncTaskTestTimeout, _runAsyncTaskTestInterval) if err != nil { assert.Fatalf("AsyncTask timed out") return nil } _, one := entities.AsyncAPITask.Get().Key(string(triggered.ID)).MustOne(ts.GET("/").Request) assert.NotNil(one) return one.(*models.AsyncAPITask) }