func TestResetPasswordHandlerTokenMatchestUsedPasswordReset(t *testing.T) { pool := newConnPool(t) user := &data.User{ Name: data.NewString("test"), Email: data.NewString("*****@*****.**"), } SetPassword(user, "password") userID, err := data.CreateUser(pool, user) if err != nil { t.Fatalf("repo.CreateUser returned error: %v", err) } _, localhost, _ := net.ParseCIDR("127.0.0.1/32") pwr := &data.PasswordReset{ Token: data.NewString("0123456789abcdef"), Email: data.NewString("*****@*****.**"), UserID: data.NewInt32(userID), RequestTime: data.NewTime(time.Now()), RequestIP: data.NewIPNet(*localhost), CompletionTime: data.NewTime(time.Now()), CompletionIP: data.NewIPNet(*localhost), } err = data.InsertPasswordReset(pool, pwr) if err != nil { t.Fatalf("repo.CreatePasswordReset returned error: %v", err) } buf := bytes.NewBufferString(`{"token": "0123456789abcdef", "password": "******"}`) req, err := http.NewRequest("POST", "http://example.com/", buf) if err != nil { t.Fatalf("http.NewRequest returned error: %v", err) } env := &environment{pool: pool} w := httptest.NewRecorder() ResetPasswordHandler(w, req, env) if w.Code != 404 { t.Errorf("Expected HTTP status %d, instead received %d", 404, w.Code) } user, err = data.SelectUserByPK(pool, userID) if err != nil { t.Fatalf("repo.GetUser returned error: %v", err) } if IsPassword(user, "bigsecret") { t.Error("Expected password not to be changed but it was") } }
func TestResetPasswordHandlerTokenMatchestInvalidPasswordReset(t *testing.T) { pool := newConnPool(t) _, localhost, _ := net.ParseCIDR("127.0.0.1/32") pwr := &data.PasswordReset{ Token: data.NewString("0123456789abcdef"), Email: data.NewString("*****@*****.**"), RequestTime: data.NewTime(time.Now()), RequestIP: data.NewIPNet(*localhost), } err := data.InsertPasswordReset(pool, pwr) if err != nil { t.Fatalf("repo.CreatePasswordReset returned error: %v", err) } buf := bytes.NewBufferString(`{"token": "0123456789abcdef", "password": "******"}`) req, err := http.NewRequest("POST", "http://example.com/", buf) if err != nil { t.Fatalf("http.NewRequest returned error: %v", err) } env := &environment{pool: pool} w := httptest.NewRecorder() ResetPasswordHandler(w, req, env) if w.Code != 404 { t.Errorf("Expected HTTP status %d, instead received %d", 404, w.Code) } }
func TestDataDeleteSubscription(t *testing.T) { pool := newConnPool(t) userID, err := data.CreateUser(pool, newUser()) if err != nil { t.Fatal(err) } err = data.InsertSubscription(pool, userID, "http://foo") if err != nil { t.Fatal(err) } subscriptions, err := data.SelectSubscriptions(pool, userID) if err != nil { t.Fatal(err) } if len(subscriptions) != 1 { t.Fatalf("Found %d subscriptions, expected 1", len(subscriptions)) } feedID := subscriptions[0].FeedID.Value update := &data.ParsedFeed{Name: "baz", Items: []data.ParsedItem{ {URL: "http://baz/bar", Title: "Baz", PublicationTime: data.NewTime(time.Now())}, }} nullString := data.String{Status: data.Null} err = data.UpdateFeedWithFetchSuccess(pool, feedID, update, nullString, time.Now().Add(-20*time.Minute)) if err != nil { t.Fatal(err) } err = data.DeleteSubscription(pool, userID, feedID) if err != nil { t.Fatal(err) } subscriptions, err = data.SelectSubscriptions(pool, userID) if err != nil { t.Fatal(err) } if len(subscriptions) != 0 { t.Errorf("Found %d subscriptions, expected 0", len(subscriptions)) } // feed should have been deleted as it was the last user staleFeeds, err := data.GetFeedsUncheckedSince(pool, time.Now()) if err != nil { t.Fatal(err) } if len(staleFeeds) != 0 { t.Errorf("Found %d staleFeeds, expected 0", len(staleFeeds)) } }
// Try multiple time formats one after another until one works or all fail func parseTime(value string) (data.Time, error) { formats := []string{ "2006-01-02T15:04:05-07:00", "2006-01-02T15:04:05Z", time.RFC822, "02 Jan 2006 15:04 MST", // RFC822 with 4 digit year "02 Jan 2006 15:04:05 MST", // RFC822 with 4 digit year and seconds "Mon, _2 Jan 2006 15:04:05 MST", // RFC1123 with 1-2 digit days "Mon, _2 Jan 2006 15:04:05 -0700", // RFC1123 with numeric time zone and with 1-2 digit days "Mon, _2 Jan 2006", "2006-01-02", } for _, f := range formats { t, err := time.Parse(f, value) if err == nil { return data.NewTime(t), nil } } return data.Time{}, errors.New("Unable to parse time") }
func TestDataUpdateFeedWithFetchSuccess(t *testing.T) { pool := newConnPool(t) userID, err := data.CreateUser(pool, newUser()) if err != nil { t.Fatal(err) } now := time.Now() url := "http://bar" err = data.InsertSubscription(pool, userID, url) if err != nil { t.Fatal(err) } subscriptions, err := data.SelectSubscriptions(pool, userID) if err != nil { t.Fatal(err) } if len(subscriptions) != 1 { t.Fatalf("Found %d subscriptions, expected 1", len(subscriptions)) } feedID := subscriptions[0].FeedID.Value update := &data.ParsedFeed{Name: "baz", Items: []data.ParsedItem{ {URL: "http://baz/bar", Title: "Baz", PublicationTime: data.NewTime(now)}, }} nullString := data.String{Status: data.Null} err = data.UpdateFeedWithFetchSuccess(pool, feedID, update, nullString, now) if err != nil { t.Fatal(err) } buffer := &bytes.Buffer{} err = data.CopyUnreadItemsAsJSONByUserID(pool, buffer, userID) if err != nil { t.Fatal(err) } type UnreadItemsFromJSON struct { ID int32 `json:id` } var unreadItems []UnreadItemsFromJSON err = json.Unmarshal(buffer.Bytes(), &unreadItems) if err != nil { t.Fatal(err) } if len(unreadItems) != 1 { t.Fatalf("Found %d unreadItems, expected 1", len(unreadItems)) } // Update again and ensure item does not get created again err = data.UpdateFeedWithFetchSuccess(pool, feedID, update, nullString, now) if err != nil { t.Fatal(err) } buffer.Reset() err = data.CopyUnreadItemsAsJSONByUserID(pool, buffer, userID) if err != nil { t.Fatal(err) } err = json.Unmarshal(buffer.Bytes(), &unreadItems) if err != nil { t.Fatal(err) } if len(unreadItems) != 1 { t.Fatalf("Found %d unreadItems, expected 1", len(unreadItems)) } }
func TestResetPasswordHandlerTokenMatchestValidPasswordReset(t *testing.T) { pool := newConnPool(t) user := &data.User{ Name: data.NewString("test"), Email: data.NewString("*****@*****.**"), } SetPassword(user, "password") userID, err := data.CreateUser(pool, user) if err != nil { t.Fatalf("repo.CreateUser returned error: %v", err) } _, requestIP, _ := net.ParseCIDR("127.0.0.1/32") pwr := &data.PasswordReset{ Token: data.NewString("0123456789abcdef"), Email: data.NewString("*****@*****.**"), UserID: data.NewInt32(userID), RequestTime: data.NewTime(time.Now()), RequestIP: data.NewIPNet(*requestIP), } err = data.InsertPasswordReset(pool, pwr) if err != nil { t.Fatalf("repo.CreatePasswordReset returned error: %v", err) } buf := bytes.NewBufferString(`{"token": "0123456789abcdef", "password": "******"}`) req, err := http.NewRequest("POST", "http://example.com/", buf) if err != nil { t.Fatalf("http.NewRequest returned error: %v", err) } env := &environment{pool: pool} w := httptest.NewRecorder() ResetPasswordHandler(w, req, env) if w.Code != 200 { t.Errorf("Expected HTTP status %d, instead received %d", 200, w.Code) } user, err = data.SelectUserByPK(pool, userID) if err != nil { t.Fatalf("repo.GetUser returned error: %v", err) } if !IsPassword(user, "bigsecret") { t.Error("Expected password to be changed but it was not") } var response struct { Name string `json:"name"` SessionID string `json:"sessionID"` } decoder := json.NewDecoder(w.Body) if err := decoder.Decode(&response); err != nil { t.Errorf("Unable to decode response: %v", err) } }
func RequestPasswordResetHandler(w http.ResponseWriter, req *http.Request, env *environment) { pwr := &data.PasswordReset{} pwr.RequestTime = data.NewTime(time.Now()) if host, _, err := net.SplitHostPort(req.RemoteAddr); err == nil { if ip := net.ParseIP(host); ip != nil { mask := net.CIDRMask(len(ip)*8, len(ip)*8) pwr.RequestIP = data.NewIPNet(net.IPNet{IP: ip, Mask: mask}) } } token, err := genLostPasswordToken() if err != nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("getLostPasswordToken failed", "error", err) return } pwr.Token = data.NewString(token) var reset struct { Email string `json:"email"` } decoder := json.NewDecoder(req.Body) if err := decoder.Decode(&reset); err != nil { w.WriteHeader(422) fmt.Fprintf(w, "Error decoding request: %v", err) return } if reset.Email == "" { w.WriteHeader(422) fmt.Fprint(w, "Error decoding request: missing email") return } pwr.Email = data.NewString(reset.Email) user, err := data.SelectUserByEmail(env.pool, reset.Email) switch err { case nil: pwr.UserID = user.ID case data.ErrNotFound: default: w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) return } err = data.InsertPasswordReset(env.pool, pwr) if err != nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("repo.CreatePasswordReset failed", "error", err) return } if user == nil { env.logger.Warn("Password reset requested for missing email", "email", reset.Email) return } if env.mailer == nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("Mail is not configured -- cannot send password reset email") return } err = env.mailer.SendPasswordResetMail(reset.Email, token) if err != nil { w.WriteHeader(500) fmt.Fprintln(w, `Internal server error`) env.logger.Error("env.mailer.SendPasswordResetMail failed", "error", err) return } }
</item> <item> <title>Blizzard</title> <link>http://example.org/blizzard</link> <pubDate>Sat, 04 Jan 2014 08:15:00 GMT</pubDate> </item> </channel> </rss> </xml>`), &data.ParsedFeed{ Name: "News", Items: []data.ParsedItem{ { Title: "Snow Storm", URL: "http://example.org/snow-storm", PublicationTime: data.NewTime(time.Date(2014, 1, 3, 22, 45, 0, 0, time.UTC)), }, { Title: "Blizzard", URL: "http://example.org/blizzard", PublicationTime: data.NewTime(time.Date(2014, 1, 4, 8, 15, 0, 0, time.UTC)), }, }}, "", }, {"RSS - v1", []byte(`<?xml version='1.0' encoding='UTF-8'?> <rdf> <channel> <title>News</title> </channel>