func TestRecordsQueryTemplate(t *testing.T) { db := mem.NewDB() creds := make([]map[string]interface{}, 5) for i := 0; i < 5; i++ { _, c, err := user.Create(db, "username", "password") if err != nil { t.Fatal("user.Create error: %s", err) } m := make(map[string]interface{}) transfer.TransferAttrs(c, &m) creds[i] = m } s := &records.QueryData{ Model: models.Metis[models.CredentialKind], Records: creds, } var b bytes.Buffer if err := records.QueryTemplate.Execute(&b, s); err != nil { t.Fatalf("template.Execute error: %s", err) } o := b.String() t.Logf("TemplateOutput:\n%s", o) if got, want := strings.Contains(o, "username"), true; got != want { t.Errorf("strings.Contains(\"username\"): got %t, want %t", got, want) } if got, want := strings.Contains(o, "password"), true; got != want { t.Errorf("strings.Contains(\"password\"): got %t, want %t", got, want) } }
func BenchmarkRecordPostEvent(b *testing.B) { db := mem.NewDB() u, _, err := user.Create(db, "username", "password") if err != nil { b.Fatalf("user.Create error: %v", err) } ctx := user.NewContext(context.Background(), u) logger := services.NewTestLogger(b) for i := 0; i < b.N; i++ { rec := httptest.NewRecorder() req, err := http.NewRequest( "POST", "http://www.elos.pw/record/?"+url.Values{ "kind": []string{models.EventKind.String()}, }.Encode(), bytes.NewBuffer( []byte(`{ "name": "event name", "data": { "sensor1": 34, "sensor2": 4.3 }, "owner_id": "`+u.ID().String()+`" }`), )) if err != nil { b.Fatalf("http.NewRequest error: %v", err) } routes.RecordPOST(ctx, rec, req, logger, db) } }
func TestWebSensorsAgent(t *testing.T) { db := mem.NewDB() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatal(err) } changes := data.FilterKind(db.Changes(), models.EventKind) ctx, stop := context.WithCancel(context.Background()) go agents.WebSensorsAgent(ctx, db, u) defer stop() // give control to agent thread time.Sleep(1 * time.Millisecond) _, err = event.WebSensorLocation(db, u, 50, 50) if err != nil { t.Fatal(err) } // read off the event we just created <-*changes select { case eventChange := <-*changes: e := eventChange.Record.(*models.Event) if e.Name != "Location Update" { t.Fatal("Expected a location update to be produced") } case <-time.After(100 * time.Millisecond): t.Fatal("Timed out waiting for event creation") } }
func TestLocationAgent(t *testing.T) { db := mem.NewDB() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatal(err) } changes := data.FilterKind(db.Changes(), models.ProfileKind) ctx, stop := context.WithCancel(context.Background()) go agents.LocationAgent(ctx, db, u) defer stop() // give control to agent thread time.Sleep(1 * time.Millisecond) _, loc, err := event.LocationUpdate(db, u, 50, 50, 50) if err != nil { t.Fatal(err) } select { case profileChange := <-*changes: p := profileChange.Record.(*models.Profile) if loc.Id != p.LocationId { t.Fatal("Expected profile's location id to now match be the new location ") } case <-time.After(100 * time.Millisecond): t.Fatal("Timed out waiting for profile update") } }
func TestNewGET(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { records.NewGET(context.Background(), w, r, mem.NewDB(), services.NewTestLogger(t)) })) resp, err := http.Get(s.URL) if err != nil { t.Fatalf("http.Get error: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("ioutil.ReadAll error: %v", err) } t.Logf("resp.Body:\n%s", body) for name := range models.Metis { if name == "user" { continue } if got, want := bytes.Contains(body, []byte(name)), true; got != want { t.Errorf("strings.Contains(out, %q): got %t, want %t", name, got, want) } } }
func TestCalInadequateInitialization(t *testing.T) { // mock cli.Ui ui := new(cli.MockUi) // yes to init prompt to create a new calendar, 3 times ui.InputReader = bytes.NewBufferString("y\ny\ny\n") // memory db db := mem.NewDB() // a new user, stored in db user := newTestUser(t, db) // note: this CalCommand is missing a cli.Ui missingUI := &CalCommand{ UserID: user.ID().String(), DB: db, } // note: this CalCommand has no UserID missingUserID := &CalCommand{ UI: ui, DB: db, } // note: this CalCommand lacks a database (DB field) missingDB := &CalCommand{ UI: ui, UserID: user.ID().String(), } t.Log("Run command that doesn't have a UI") // expect missing a ui to fail if o := missingUI.Run([]string{"new"}); o != failure { t.Fatal("CalCommand without ui should fail on run") } t.Log("Completed") t.Log("Run command that doesn't have a user id") // expect missing a user id to fail if o := missingUserID.Run([]string{"new"}); o != failure { t.Fatal("CalCommand without user id should fail on run") } t.Log("Completed") t.Log("Run command that doesn't have a db") // expect missing a db to fail if o := missingDB.Run([]string{"new"}); o != failure { t.Fatal("CalCommand without db should fail on run") } t.Log("Completed") }
func newMockTagCommand(t *testing.T) (*cli.MockUi, data.DB, *models.User, *TagCommand) { ui := new(cli.MockUi) db := mem.NewDB() user := newTestUser(t, db) return ui, db, user, &TagCommand{ UI: ui, UserID: user.ID().String(), DB: db, } }
func TestEditGET(t *testing.T) { db := mem.NewDB() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create error: %v", err) } e := &models.Event{ Id: db.NewID().String(), OwnerId: u.Id, Name: "old name", Data: map[string]interface{}{ "sensor": 4, }, } if err := db.Save(e); err != nil { t.Fatalf("db.Save error: %v", err) } s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { records.EditGET(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t)) })) p := s.URL + "?" + url.Values{ "kind": []string{"event"}, "id": []string{e.Id}, }.Encode() resp, err := http.Get(p) if err != nil { t.Fatalf("http.Get(%q) error: %v", p, err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("ioutil.ReadAll error: %v", err) } t.Logf("resp.Body:\n%s", body) if got, want := bytes.Contains(body, []byte(`event`)), true; got != want { t.Fatalf("bytes.Contains(body, %q): got %t, want %t", "event", got, want) } if got, want := bytes.Contains(body, []byte(`old name`)), true; got != want { t.Fatalf("bytes.Contains(body, %q): got %t, want %t", "old name", got, want) } if got, want := bytes.Contains(body, []byte(`/records/view/?kind=event&id=3`)), true; got != want { t.Fatalf("bytes.Contains(body, %q): got %t, want %t", `/records/view/?kind=event&id=3`, got, want) } }
func TestRecordsNewGET(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, ok := Authenticate(ctx, w, r, logger, db) if !ok { t.Fatal("authentication failed") } Records.NewGET(ctx, w, r, db, logger) })) defer s.Close() _, _, err := user.Create(db, "username", "password") if err != nil { t.Fatal(err) } c := new(http.Client) req, err := http.NewRequest("GET", s.URL, nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("username", "password") resp, err := c.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } t.Logf("Body:\n%s", body) doc := string(body) contains := map[string]bool{ "user": false, "event": true, "New": true, } for c, want := range contains { if got := strings.Contains(doc, c); got != want { t.Fatalf("strings.Contains(%q): got %t, want %t", c, got, want) } } }
func TestDeletePOST(t *testing.T) { db := mem.NewDB() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create error: %v", err) } e := &models.Event{ Id: db.NewID().String(), OwnerId: u.Id, Name: "event name", Data: map[string]interface{}{ "sensor": 45.3, }, } if err := db.Save(e); err != nil { t.Fatalf("db.Save error: %v", err) } s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.RequestURI, "/records/delete/") { records.DeletePOST(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t)) } records.QueryGET(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t)) })) p := s.URL + "/records/delete/?" + url.Values{ "kind": []string{"event"}, "id": []string{e.Id}, }.Encode() resp, err := http.Post(p, "", new(bytes.Buffer)) if err != nil { t.Fatalf("http.Post error: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("ioutil.ReadAll error: %v", err) } t.Logf("resp.Body:\n%s", body) if got, want := db.PopulateByID(e), data.ErrNotFound; got != want { t.Fatalf("db.PopulateByID: got %v, want %v", got, want) } }
func TestRecordsQueryGET(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, ok := Authenticate(ctx, w, r, logger, db) if !ok { t.Fatal("authentication failed") } Records.QueryGET(ctx, w, r, db, logger) })) defer s.Close() _, _, err := user.Create(db, "username", "password") if err != nil { t.Fatal(err) } c := new(http.Client) req, err := http.NewRequest("GET", s.URL+"?"+url.Values{ "query/Kind": []string{"credential"}, }.Encode(), nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("username", "password") resp, err := c.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } t.Logf("Body:\n%s", body) doc := string(body) if got, want := strings.Contains(doc, "username"), true; got != want { t.Fatalf("strings.Contains(doc, \"username\"): got %t, want %t", got, want) } if got, want := strings.Contains(doc, "password"), true; got != want { t.Fatalf("strings.Contains(doc, \"password\"): got %t, want %t", got, want) } }
func TestMarshalTask(t *testing.T) { t.Skip() db := mem.NewDB() user, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create error: %v", err) } bytes, err := form.Marshal(user, "user") if err != nil { t.Fatalf("form.Marshal error: %v", err) } if got, want := string(bytes), ""; got != want { t.Fatalf("form.Marshal: got\n%s\nwant,\n%s", got, want) } }
func TestAuthenticateQueryParams(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) var userContext context.Context var authed bool s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userContext, authed = routes.Authenticate(ctx, w, r, logger, db) if authed { w.WriteHeader(http.StatusOK) } })) defer s.Close() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } client := new(http.Client) req, err := http.NewRequest("GET", s.URL, new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } client.Do(req) if got, want := authed, false; got != want { t.Fatalf("authed: got %t, want %t", got, want) } req, err = http.NewRequest("GET", s.URL+"?public=username&private=password", new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } client.Do(req) if got, want := authed, true; got != want { t.Fatalf("authed: got %t, want %t") } authedU, ok := user.FromContext(userContext) if got, want := ok, true; got != want { t.Fatalf("_, ok := user.FromContext: got %t, want %t") } if got, want := data.Equivalent(authedU, u), true; got != want { t.Errorf("data.Equivalent(authedU, u): got %t, want %t", got, want) } }
func testInstance(t *testing.T, ctx context.Context) (data.DB, *gaia.Gaia, *httptest.Server) { db := mem.NewDB() g := gaia.New( ctx, &gaia.Middleware{}, &gaia.Services{ Logger: services.NewTestLogger(t), DB: db, SMSCommandSessions: services.NewSMSMux(), WebCommandSessions: services.NewWebMux(), }, ) s := httptest.NewServer(g) return db, g, s }
func TestTagInadequateInitialization(t *testing.T) { // mock cli.Ui ui := new(cli.MockUi) // memory db db := mem.NewDB() // a new user, stored in db user := newTestUser(t, db) // note: this TagCommand is missing a cli.Ui missingUI := &TagCommand{ UserID: user.ID().String(), DB: db, } // note: this TagCommand has no UserID missingUserID := &TagCommand{ UI: ui, DB: db, } // note: this TagCommand lacks a database (DB field) missingDB := &TagCommand{ UI: ui, UserID: user.ID().String(), } // expect missing a ui to fail if o := missingUI.Run([]string{"new"}); o != failure { t.Fatal("TagCommand without ui should fail on run") } // expect missing a user id to fail if o := missingUserID.Run([]string{"new"}); o != failure { t.Fatal("TagCommand without user id should fail on run") } // expect missing a db to fail if o := missingDB.Run([]string{"new"}); o != failure { t.Fatal("TagCommand without db should fail on run") } }
func TestAuthenticatePostForm(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) var userContext context.Context var authed bool s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userContext, authed = routes.Authenticate(ctx, w, r, logger, db) if authed { w.WriteHeader(http.StatusOK) } })) defer s.Close() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } client := new(http.Client) client.PostForm(s.URL, url.Values{ "public": []string{"username"}, "private": []string{"password"}, }) if got, want := authed, true; got != want { t.Fatalf("authed: got %t, want %t", got, want) } authedU, ok := user.FromContext(userContext) if got, want := ok, true; got != want { t.Fatalf("_, ok := user.FromContext: got %t, want %t", got, want) } if got, want := data.Equivalent(authedU, u), true; got != want { t.Fatal("data.Equivalent(authedU, u): got %t ,want %t", got, want) } }
// TestEventPOST tests a POST request to the '/event/' endpoint. // in the happy case (i.e., all parameters present). // We verify: // * The event's name // * The event's data // * The event's tags func TestEventPOST(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, ok := routes.Authenticate(ctx, w, r, logger, db) if !ok { t.Error("routes.Authenticate failed") } routes.EventPOST(ctx, w, r, db, logger) })) defer s.Close() _, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } u := s.URL + "?" + url.Values{ "tags": []string{"tag1", "tag2"}, }.Encode() body := map[string]interface{}{ "name": "event name", "time": time.Now(), "data": map[string]interface{}{ "arbitrary": []string{"data"}, "here": 1.0, "foo": map[string]interface{}{ "bar": "there", }, }, } b, err := json.Marshal(body) if err != nil { t.Fatal("json.Marshal(body) error: %s", err) } req, err := http.NewRequest("POST", u, bytes.NewBuffer(b)) req.SetBasicAuth("username", "password") client := new(http.Client) resp, err := client.Do(req) if err != nil { t.Fatalf("client.Do(req) error: %s", err) } defer resp.Body.Close() if got, want := resp.StatusCode, http.StatusCreated; got != want { t.Fatalf("resp.StatusCode: got %d, want %d", got, want) } b, err = ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("ioutil.ReadAll(resp.Body) error: %s", err) } e := new(models.Event) if err := json.Unmarshal(b, e); err != nil { t.Fatalf("json.Unmarshal(b, e) error: %s", err) } t.Logf("Event:\n\t%v", e) if got, want := e.Name, "event name"; got != want { t.Errorf("e.Name: got %q, want %q", got, want) } if got, want := e.Data["foo"].(map[string]interface{})["bar"], "there"; got != want { t.Errorf("e.Data[\"foo\"][\"bar\"]: got %q, want %q", got, want) } if got, want := len(e.TagsIds), 2; got != want { t.Fatalf("len(e.TagsIds): got %d, want %d", got, want) } tags, err := e.Tags(db) if err != nil { t.Fatalf("e.Tags(db) error: %s", err) } if got, want := len(tags), 2; got != want { t.Fatalf("len(tags): got %d, want %d", got, want) } if got, want := tags[0].Name, "tag1"; got != want { t.Errorf("tags[0].Name: got %q, want %q", got, want) } if got, want := tags[1].Name, "tag2"; got != want { t.Errorf("tags[1].Name: got %q, want %q", got, want) } }
func TestLoginPOST(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) m := http.NewServeMux() m.Handle("/login/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, ok := routes.Authenticate(ctx, w, r, logger, db) if !ok { t.Fatal("bad authentication") } routes.LoginPOST(ctx, w, r, db, logger) })) m.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) })) s := httptest.NewServer(http.HandlerFunc(m.ServeHTTP)) defer s.Close() _, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } req, err := http.NewRequest("GET", s.URL+"/login/", new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } req.SetBasicAuth("username", "password") jar, err := cookiejar.New(new(cookiejar.Options)) if err != nil { t.Fatalf("cookiejar.New error: %s", err) } client := &http.Client{ Jar: jar, } resp, err := client.Do(req) if err != nil { t.Fatalf("error posting to LoginPOST: %s", err) } t.Logf("Response:\n\t%v", resp) if got, want := resp.StatusCode, http.StatusAccepted; got != want { t.Errorf("resp.StatusCode: got %d, want %d", got, want) } iter, err := db.Query(models.SessionKind).Execute() if err != nil { t.Fatalf("db.Query(models.SessionKind).Execute(): %s", err) } seshes := mem.Slice(iter, func() data.Record { return new(models.Session) }) if got, want := len(seshes), 1; got != want { t.Fatalf("len(seshes): got %d, want %d", got, want) } uri, err := url.Parse(s.URL) if err != nil { t.Fatalf("url.Prase(s.URL) error: %s", err) } t.Logf("URL: %s", uri) cookies := jar.Cookies(uri) if got, want := len(cookies), 1; got != want { t.Fatalf("len(cookies): got %d, want %d", got, want) } if got, want := cookies[0].Value, seshes[0].(*models.Session).Token; got != want { t.Errorf("cookies[0].Value: got %s, want %s", got, want) } }
func TestCal(t *testing.T) { //TODO(nclandolfi): fix test t.Skip() NOW := time.Now() cases := map[string]struct { prior data.State userID string args []string input io.Reader code int errput string output string // IF posterior is nil, prior will be used posterior data.State }{ "simple cal week": { prior: data.State{ models.Kind_USER: []*data.Record{ &data.Record{ Kind: models.Kind_USER, User: &models.User{ Id: "1", }, }, }, models.Kind_CREDENTIAL: []*data.Record{ &data.Record{ Kind: models.Kind_CREDENTIAL, Credential: &models.Credential{ Id: "2", OwnerId: "1", Type: models.Credential_PASSWORD, Public: "pu", Private: "pr", }, }, }, models.Kind_FIXTURE: []*data.Record{ &data.Record{ Kind: models.Kind_FIXTURE, Fixture: &models.Fixture{ Id: "3", OwnerId: "1", StartTime: models.TimestampFrom(NOW.Add(1 * time.Hour)), EndTime: models.TimestampFrom(NOW.Add(2 * time.Hour)), }, }, }, }, input: new(bytes.Buffer), userID: "1", args: []string{"week"}, }, } for n, c := range cases { t.Run(n, func(t *testing.T) { db := mem.NewDB() dbc, conn, err := data.DBBothLocal(db) if err != nil { t.Fatalf("data.DBBothLocal error: %v", err) } defer conn.Close() if err := data.Seed(context.Background(), dbc, c.prior); err != nil { t.Fatalf("data.Seed error: %v", err) } if c.input == nil { t.Fatal("c.input must be non-nil") } ui := &cli.MockUi{ InputReader: c.input, } cmd := &CalCommand{ UI: ui, UserID: c.userID, DB: data.DB(dbc), } if got, want := cmd.Run(c.args), c.code; got != want { t.Log(ui.ErrorWriter.String()) t.Fatalf("cmd.Run(%v): got %d, want %d", c.args, got, want) } if got, want := ui.ErrorWriter.String(), c.errput; got != want { t.Fatalf("ui.ErrorWriter.String(): got %q, want %q", got, want) } if got, want := ui.OutputWriter.String(), c.output; got != want { t.Fatalf("ui.OutputWriter.String(): got %q, want %q", got, want) } finalState := c.prior if c.posterior != nil { finalState = c.posterior } if got, want := data.CompareState(context.Background(), dbc, finalState), error(nil); got != want { t.Fatalf("data.CompareState: got %v, want %v", got, want) } }) } }
func TestRecords(t *testing.T) { cases := map[string]struct { args []string code int in, err, out []byte prior, posterior data.State }{ // elos records kinds {{{ "elos records kinds": { args: []string{"kinds"}, out: []byte(strings.Join(apply(s(models.Kinds), func(s string) string { return "* " + s }), "\n") + "\n"), prior: data.State{ models.Kind_USER: []*data.Record{ &data.Record{ Kind: models.Kind_USER, User: &models.User{ Id: "1", }, }, }, models.Kind_CREDENTIAL: []*data.Record{ &data.Record{ Kind: models.Kind_CREDENTIAL, Credential: &models.Credential{ Id: "2", Type: models.Credential_PASSWORD, Public: "pu", Private: "pr", OwnerId: "1", }, }, }, }, }, // }}} // elos records count {{{ "elos records count": { args: []string{"count"}, in: []byte("SESSION\n"), out: []byte("Which kind? [string]:2\n"), prior: data.State{ models.Kind_USER: []*data.Record{ &data.Record{ Kind: models.Kind_USER, User: &models.User{ Id: "1", }, }, &data.Record{ Kind: models.Kind_USER, User: &models.User{ Id: "2", }, }, }, models.Kind_CREDENTIAL: []*data.Record{ &data.Record{ Kind: models.Kind_CREDENTIAL, Credential: &models.Credential{ Id: "3", Type: models.Credential_PASSWORD, Public: "pu", Private: "pr", OwnerId: "1", }, }, &data.Record{ Kind: models.Kind_CREDENTIAL, Credential: &models.Credential{ Id: "4", Type: models.Credential_PASSWORD, Public: "2pu", Private: "pr", OwnerId: "2", }, }, }, models.Kind_SESSION: []*data.Record{ &data.Record{ Kind: models.Kind_SESSION, Session: &models.Session{ Id: "5", AccessToken: "non-empty", ExpiresAt: models.TimestampFrom(time.Now().Add(5 * time.Minute)).WithoutNanos(), OwnerId: "1", CredentialId: "3", }, }, &data.Record{ Kind: models.Kind_SESSION, Session: &models.Session{ Id: "4", AccessToken: "non-empty", ExpiresAt: models.TimestampFrom(time.Now().Add(5 * time.Minute)).WithoutNanos(), OwnerId: "1", CredentialId: "3", }, }, &data.Record{ Kind: models.Kind_SESSION, Session: &models.Session{ Id: "4", AccessToken: "non-empty", ExpiresAt: models.TimestampFrom(time.Now().Add(5 * time.Minute)).WithoutNanos(), OwnerId: "2", CredentialId: "4", }, }, }, }, }, // }}} // TODO(nclandolfi) test query and changes } for n, c := range cases { t.Run(n, func(t *testing.T) { db := mem.NewDB() dbc, conn, err := data.DBBothLocal(db) if err != nil { t.Fatalf("data.DBBothLocal error: %v", err) } defer conn.Close() if err := data.Seed(context.Background(), dbc, c.prior); err != nil { t.Fatalf("data.Seed error: %v", err) } ui := &cli.MockUi{ InputReader: bytes.NewBuffer(c.in), } cmd := &RecordsCommand{ UI: ui, UserID: c.prior[models.Kind_USER][0].User.Id, DBClient: dbc, } if got, want := cmd.Run(c.args), c.code; got != want { t.Log(ui.ErrorWriter.String()) t.Fatalf("cmd.Run(%v): got %d, want %d", c.args, got, want) } if got, want := ui.ErrorWriter.String(), string(c.err); got != want { t.Fatalf("ui.ErrorWriter.String(): got %q, want %q", got, want) } if got, want := ui.OutputWriter.String(), string(c.out); got != want { t.Fatalf("ui.OutputWriter.String(): got %q, want %q", got, want) } finalState := c.prior if c.posterior != nil { finalState = c.posterior } if got, want := data.CompareState(context.Background(), dbc, finalState), error(nil); got != want { t.Fatalf("data.CompareState: got %v, want %v", got, want) } }) } }
// --- TestAuthenticateSession {{{ func TestAuthenticateSession(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) var userContext context.Context var authed bool s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userContext, authed = routes.Authenticate(ctx, w, r, logger, db) if authed { w.WriteHeader(http.StatusOK) } })) defer s.Close() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create(db, \"username\", \"password\") error: %s", err) } client := new(http.Client) req, err := http.NewRequest("GET", s.URL, new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } req.AddCookie(&http.Cookie{ Name: "elos-session-token", Value: "garbage", }) client.Do(req) if got, want := authed, false; got != want { t.Errorf("authed: got %t, want %t", got, want) } sesh := models.NewSessionForUser(u) sesh.SetID(db.NewID()) if err := db.Save(sesh); err != nil { t.Fatalf("db.Save(sesh) error: %s", err) } req, err = http.NewRequest("GET", s.URL, new(bytes.Buffer)) if err != nil { t.Fatalf("http.NewRequest error: %s", err) } req.AddCookie(&http.Cookie{ Name: "elos-session-token", Value: sesh.Token, }) client.Do(req) if got, want := authed, true; got != want { t.Fatalf("authed: got %t, want %t", got, want) } authedU, ok := user.FromContext(userContext) if got, want := ok, true; got != want { t.Fatalf("_, ok := user.FromContext: got %t, want %t", got, want) } if got, want := data.Equivalent(authedU, u), true; got != want { t.Errorf("data.Equivalent(authedU, u): got %t, want %t", got, want) } }
func TestCommandSMSInput(t *testing.T) { db := mem.NewDB() sms := newMockSMS() mux := services.NewSMSMux() ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() go mux.Start(ctx, db, sms) ctx, cancelContext := context.WithCancel(context.Background()) defer cancelContext() g := gaia.New( ctx, &gaia.Middleware{}, &gaia.Services{ Logger: services.NewTestLogger(t), DB: db, SMSCommandSessions: mux, WebCommandSessions: services.NewWebMux(), }, ) s := httptest.NewServer(g) defer s.Close() u, _ := testUser(t, db) t.Log("Creating profile") p := models.NewProfile() p.SetID(db.NewID()) phone := "650 123 4567" p.Phone = phone p.SetOwner(u) if err := db.Save(p); err != nil { t.Fatal(err) } t.Log("Created") messageBody := "todo new" fakeSMS(t, s, phone, phone, messageBody) select { case m := <-sms.bus: t.Logf("Message:\n%+v", m) if !strings.Contains(m.body, "Name") { t.Fatal("The message should have asked for name of task") } case <-time.After(1 * time.Second): t.Fatal("Timed out waiting for sms message") } taskName := "task name" // for the name fakeSMS(t, s, phone, phone, taskName) select { case m := <-sms.bus: t.Logf("Message:\n%+v", m) if !strings.Contains(m.body, "deadline") { t.Fatal("The message should have asked if it had a deadline") } case <-time.After(1 * time.Second): t.Fatal("Timed out waiting for sms message") } fakeSMS(t, s, phone, phone, "n") // no deadline select { case m := <-sms.bus: t.Logf("Message:\n%+v", m) if !strings.Contains(m.body, "prerequisites") { t.Fatal("The message should have asked if it had a prereqs") } case <-time.After(1 * time.Second): t.Fatal("Timed out waiting for sms message") } fakeSMS(t, s, phone, phone, "n") // no prereqs select { case m := <-sms.bus: t.Logf("Message:\n%+v", m) if !strings.Contains(m.body, "created") { t.Fatal("Should have indicated task was created") } case <-time.After(1 * time.Second): t.Fatal("Timed out waiting for sms message") } task := models.NewTask() // ensure the task exists if err := db.PopulateByField("name", taskName, task); err != nil { t.Fatal(err) } t.Logf("Task:\n%+v", task) }
func TestCommandSMS(t *testing.T) { db := mem.NewDB() sms := newMockSMS() mux := services.NewSMSMux() ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() go mux.Start(ctx, db, sms) ctx, cancelContext := context.WithCancel(context.Background()) defer cancelContext() g := gaia.New( ctx, &gaia.Middleware{}, &gaia.Services{ Logger: services.NewTestLogger(t), DB: db, SMSCommandSessions: mux, WebCommandSessions: services.NewWebMux(), }, ) s := httptest.NewServer(g) defer s.Close() u, _ := testUser(t, db) t.Log("Creating profile") p := models.NewProfile() p.SetID(db.NewID()) phone := "650 123 4567" p.Phone = phone p.SetOwner(u) if err := db.Save(p); err != nil { t.Fatal(err) } t.Log("Created") messageBody := "todo" params := url.Values{} params.Set("To", phone) // /command/ ignores this, twilio sends it though params.Set("From", phone) params.Set("Body", messageBody) url := s.URL + "/command/sms/?" + params.Encode() t.Logf("Constructed URL: %s", url) req, err := http.NewRequest("POST", url, nil) if err != nil { t.Fatal(err) } resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } t.Logf("Code: %d", resp.StatusCode) t.Logf("Body:\n%s", body) if resp.StatusCode != http.StatusNoContent { t.Fatalf("Expected status code of %d", http.StatusNoContent) } select { case m := <-sms.bus: t.Logf("Message:\n%+v", m) if !strings.Contains(m.body, "elos") { t.Fatal("The message should have almost certainly contained the word elos") } case <-time.After(1 * time.Second): t.Fatal("Timed out waiting for sms message") } }
// --- 'elos setup' (context: need a new account) {{{ func TestSetupNewUser(t *testing.T) { f, err := ioutil.TempFile("", "conf") if err != nil { t.Fatal(err) } defer f.Close() db := mem.NewDB() dbc, closers, err := access.NewTestDB(db) if err != nil { t.Fatalf("access.NewTestDB error: %v", err) } defer func(cs []io.Closer) { for _, c := range cs { c.Close() } }(closers) authc, closers, err := auth.NewTestAuth(db) if err != nil { t.Fatalf("access.NewTestDB error: %v", err) } defer func(cs []io.Closer) { for _, c := range cs { c.Close() } }(closers) webui, conn, err := records.WebUIBothLocal(dbc, authc) if err != nil { t.Fatalf("records.WebUIBothLocal error: %v", err) } defer conn.Close() g := gaia.New( context.Background(), &gaia.Middleware{}, &gaia.Services{ SMSCommandSessions: services.NewSMSMux(), Logger: services.NewTestLogger(t), DB: db, WebUIClient: webui, }, ) s := httptest.NewServer(g) defer s.Close() ui, conf, c := newMockSetupCommand(t) conf.Path = f.Name() // no already account, then username input and password input ui.InputReader = bytes.NewBufferString(fmt.Sprintf("%s\nn\npublic\nprivate\n", s.URL)) t.Log("running: `elos setup`") code := c.Run([]string{}) t.Log("command `setup` terminated") t.Log("Reading outputs") errput := ui.ErrorWriter.String() output := ui.OutputWriter.String() t.Logf("Error output:\n%s", errput) t.Logf("Output:\n%s", output) // verify there were no errors if errput != "" { t.Fatalf("Expected no error output, got: %s", errput) } // verify success if code != 0 { t.Fatalf("Expected successful exit code along with empty error output.") } // verify some of the output if !strings.Contains(output, "account") { t.Fatalf("Output should have contained a 'account' for saying something about an account") } i, err := db.Query(data.Kind(models.Kind_CREDENTIAL.String())).Select(data.AttrMap{ "public": "public", "private": "private", }).Execute() if err != nil { t.Fatal(err) } cred := new(models.Credential) if ok := i.Next(cred); !ok { t.Fatal("no credentials found") } if got, want := cred.OwnerId, "1"; got != want { t.Fatalf("cred.OwnerId: got %q, want %q", got, want) } // verify conf was changed if got, want := conf.UserID, "1"; got != want { t.Fatalf("conf.UserID: got %q, want %q", got, want) } if conf.PublicCredential != "public" { t.Fatalf("public credential should be: public") } if conf.PrivateCredential != "private" { t.Fatalf("private credential should be: private") } }
func main() { flag.Parse() var db data.DB var err error log.Printf("== Setting Up Database ==") log.Printf("\tDatabase Type: %s", *dbtype) switch *dbtype { case "mem": db = mem.NewDB() case "mongo": db, err = models.MongoDB(*dbaddr) if err != nil { log.Fatal(err) } log.Printf("\tConnected to mongo@%s", *dbaddr) default: log.Fatal("Unrecognized database type: '%s'", *dbtype) } log.Printf("== Set up Database ==") // DB CLIENT conn, err := grpc.Dial(":3334", grpc.WithInsecure()) if err != nil { log.Fatalf("failed to dial: %v", err) } defer conn.Close() adbc := access.NewDBClient(conn) // AUTH CLIENT conn, err = grpc.Dial(":3333", grpc.WithInsecure()) if err != nil { log.Fatalf("failed to dial: %v", err) } defer conn.Close() ac := auth.NewAuthClient(conn) // WEBUI SERVER lis, err := net.Listen("tcp", ":1113") if err != nil { log.Fatalf("failed to listen on :1113: %v", err) } g := grpc.NewServer() records.RegisterWebUIServer(g, records.Logged(records.NewWebUI(adbc, ac))) go g.Serve(lis) // WEB UI CLIENT conn, err = grpc.Dial(":1113", grpc.WithInsecure()) if err != nil { log.Fatalf("failed to dial: %v", err) } defer conn.Close() webuiclient := records.NewWebUIClient(conn) // calendar WEBUI SERVER lis, err = net.Listen("tcp", ":1114") if err != nil { log.Fatalf("failed to listen on :1114: %v", err) } g = grpc.NewServer() cal.RegisterWebUIServer(g, cal.NewWebUI(adbc, ac)) go g.Serve(lis) // cal WEB UI CLIENT conn, err = grpc.Dial(":1114", grpc.WithInsecure()) if err != nil { log.Fatalf("failed to dial: %v", err) } defer conn.Close() calwebui := cal.NewWebUIClient(conn) if _, _, err := user.Create(db, "u", "p"); err != nil { log.Fatal("user.Create error: %s", err) } background := context.Background() log.Printf("== Connecting to Twilio ==") twilioClient := twilio.NewClient(TwilioAccountSid, TwilioAuthToken, nil) log.Printf("== Connected to Twilio ==") log.Printf("== Starting SMS Command Sessions ==") smsMux := services.NewSMSMux() go smsMux.Start( background, db, services.SMSFromTwilio(twilioClient, TwilioFromNumber), ) log.Printf("== Started SMS Command Sessions ==") log.Printf("== Initiliazing Gaia Core ==") ga := gaia.New( context.Background(), new(gaia.Middleware), &gaia.Services{ AppFileSystem: http.Dir(*appdir), SMSCommandSessions: smsMux, DB: db, Logger: services.NewLogger(os.Stderr), WebUIClient: webuiclient, CalWebUIClient: calwebui, }, ) log.Printf("== Initiliazed Gaia Core ==") log.Printf("== Starting Agents ===") user.Map(db, func(db data.DB, u *models.User) error { go agents.LocationAgent(background, db, u) go agents.TaskAgent(background, db, u) go agents.WebSensorsAgent(background, db, u) return nil }) log.Printf("== Started Agents ===") log.Printf("== Starting HTTP Server ==") host := fmt.Sprintf("%s:%d", *addr, *port) log.Printf("\tServing on %s", host) if *certFile != "" && *keyFile != "" { if *port != 443 { log.Print("WARNING: serving HTTPS on a port that isn't 443") } if err = http.ListenAndServeTLS(host, *certFile, *keyFile, ga); err != nil { log.Fatal(err) } } else { log.Print("NOT SERVING SECURELY") if *port != 80 { log.Print("WARNING: serving HTTP on a port that isn't 80") } if err = http.ListenAndServe(host, ga); err != nil { log.Fatal(err) } } log.Printf("== Started HTTP Server ==") }
func TestCommandWeb(t *testing.T) { db := mem.NewDB() sms := newMockSMS() smsMux := services.NewSMSMux() webMux := services.NewWebMux() ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() go smsMux.Start(ctx, db, sms) go webMux.Start(ctx, db) ctx, cancelContext := context.WithCancel(context.Background()) defer cancelContext() g := gaia.New( ctx, &gaia.Middleware{}, &gaia.Services{ Logger: services.NewTestLogger(t), DB: db, SMSCommandSessions: smsMux, WebCommandSessions: webMux, }, ) s := httptest.NewServer(g) defer s.Close() _, cred := testUser(t, db) serverURL := s.URL origin := serverURL wsURL := strings.Replace(serverURL, "http", "ws", 1) params := url.Values{} params.Set("public", cred.Public) params.Set("private", cred.Private) wsURL += "/command/web/?" + params.Encode() t.Logf("Constructed URL: %s", wsURL) t.Log("Opening websocket") ws, err := websocket.Dial(wsURL, "", origin) if err != nil { t.Fatal(err) } defer ws.Close() t.Log("Websocket openened") messageBody := "todo" // Sending a message t.Logf("Sending message: %s", messageBody) websocket.Message.Send(ws, messageBody) t.Logf("Sent") t.Log("Recieving a message") var received string websocket.Message.Receive(ws, &received) t.Logf("Recieved message: %s", received) t.Log("Verifying the response contained the word 'elos'") if !strings.Contains(received, "elos") { t.Fatal("The message should have almost certainly contained the word elos") } t.Log("Verified") }
func TestEditPOST(t *testing.T) { db := mem.NewDB() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatalf("user.Create error: %v", err) } e := &models.Event{ Id: db.NewID().String(), OwnerId: u.Id, Name: "old name", Data: map[string]interface{}{ "sensor": 4, }, Time: time.Now(), } if err := db.Save(e); err != nil { t.Fatalf("db.Save error: %v", err) } s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.RequestURI, "/records/edit/") { records.EditPOST(user.NewContext(context.Background(), u), w, r, db, services.NewTestLogger(t)) return } w.WriteHeader(http.StatusOK) w.Write([]byte(`default test handler`)) })) p := s.URL + "/records/edit/?" + url.Values{ "kind": []string{"event"}, "id": []string{e.Id}, "event/Name": []string{"new eman"}, "event/Data": []string{`{"sensor": 4, "sensor2": 9}`}, }.Encode() resp, err := http.Post(p, "", new(bytes.Buffer)) if err != nil { t.Fatalf("http.Post error: %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("iotuil.ReadAll error: %v", err) } t.Logf("resp.Body:\n%s", body) ue := &models.Event{Id: e.Id} if err := db.PopulateByID(ue); err != nil { t.Fatalf("db.PopulateByID error: %v", err) } if got, want := ue.Name, "new eman"; got != want { t.Errorf("ue.Name: got %q, want %q", got, want) } if got, want := ue.Data, map[string]interface{}{"sensor": 4.0, "sensor2": 9.0}; !reflect.DeepEqual(got, want) { t.Errorf("ue.Data: got %v, want %v", got, want) } if got, want := ue.Time, e.Time; got != want { t.Errorf("ue.Time: got %v, want %v", got, want) } if got, want := ue.OwnerId, e.OwnerId; got != want { t.Errorf("ue.OwnerId: got %q, want %q", got, want) } }
func TestRecordsEditGET(t *testing.T) { ctx := context.Background() db := mem.NewDB() logger := services.NewTestLogger(t) s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, ok := Authenticate(ctx, w, r, logger, db) if !ok { t.Fatal("authentication failed") } Records.EditGET(ctx, w, r, db, logger) })) defer s.Close() u, _, err := user.Create(db, "username", "password") if err != nil { t.Fatal(err) } e := new(models.Event) e.SetID(db.NewID()) e.SetOwner(u) e.Name = "eventname" if err := db.Save(e); err != nil { t.Fatal("db.Save(e) error: %s", err) } c := new(http.Client) req, err := http.NewRequest("GET", s.URL+"?"+url.Values{ "kind": []string{string(models.EventKind)}, "id": []string{e.Id}, }.Encode(), nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("username", "password") resp, err := c.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } t.Logf("Body:\n%s", body) doc := string(body) contains := map[string]bool{ "eventname": true, "name": true, } for c, want := range contains { if got := strings.Contains(doc, c); got != want { t.Fatalf("strings.Contains(%q): got %t, want %t", c, got, want) } } }