Example #1
0
func (m *mailImpl) sendImpl(msg *mail.Message) error {
	email := ""
	userSvc := user.Get(m.c)
	if u := userSvc.Current(); u != nil {
		email = u.Email
	}

	m.data.Lock()
	adminsPlain := m.data.adminsPlain[:]
	m.data.Unlock()

	testMsg := &mail.TestMessage{Message: *msg}

	if err := checkMessage(testMsg, adminsPlain, email); err != nil {
		return err
	}
	m.data.Lock()
	m.data.queue = append(m.data.queue, testMsg)
	m.data.Unlock()
	return nil
}
Example #2
0
func TestUser(t *testing.T) {
	t.Parallel()

	Convey("user", t, func() {
		c := Use(context.Background())
		user := userS.Get(c)

		Convey("default state is anonymous", func() {
			So(user.Current(), ShouldBeNil)

			usr, err := user.CurrentOAuth("something")
			So(err, ShouldBeNil)
			So(usr, ShouldBeNil)

			So(user.IsAdmin(), ShouldBeFalse)
		})

		Convey("can login (normal)", func() {
			user.Testable().Login("*****@*****.**", "", false)
			So(user.Current(), ShouldResemble, &userS.User{
				Email:      "*****@*****.**",
				AuthDomain: "world.com",
				ID:         "14628837901535854097",
			})

			usr, err := user.CurrentOAuth("scope")
			So(usr, ShouldBeNil)
			So(err, ShouldBeNil)

			Convey("and logout", func() {
				user.Testable().Logout()
				So(user.Current(), ShouldBeNil)

				usr, err := user.CurrentOAuth("scope")
				So(usr, ShouldBeNil)
				So(err, ShouldBeNil)
			})
		})

		Convey("can be admin", func() {
			user.Testable().Login("*****@*****.**", "", true)
			So(user.Current(), ShouldResemble, &userS.User{
				Email:      "*****@*****.**",
				AuthDomain: "world.com",
				ID:         "14628837901535854097",
				Admin:      true,
			})
			So(user.IsAdmin(), ShouldBeTrue)
		})

		Convey("can login (oauth)", func() {
			user.Testable().Login("*****@*****.**", "clientID", false)
			usr, err := user.CurrentOAuth("scope")
			So(err, ShouldBeNil)
			So(usr, ShouldResemble, &userS.User{
				Email:      "*****@*****.**",
				AuthDomain: "world.com",
				ID:         "14628837901535854097",
				ClientID:   "clientID",
			})

			So(user.Current(), ShouldBeNil)

			Convey("and logout", func() {
				user.Testable().Logout()
				So(user.Current(), ShouldBeNil)

				usr, err := user.CurrentOAuth("scope")
				So(usr, ShouldBeNil)
				So(err, ShouldBeNil)
			})
		})

		Convey("panics on bad email", func() {
			So(func() {
				user.Testable().Login("bademail", "", false)
			}, ShouldPanicLike, `mail: missing phrase`)
		})

		Convey("fake URLs", func() {
			url, err := user.LoginURL("https://funky.example.com")
			So(err, ShouldBeNil)
			So(url, ShouldEqual, "https://fakeapp.example.com/_ah/login?redirect=https%3A%2F%2Ffunky.example.com")

			url, err = user.LogoutURL("https://funky.example.com")
			So(err, ShouldBeNil)
			So(url, ShouldEqual, "https://fakeapp.example.com/_ah/logout?redirect=https%3A%2F%2Ffunky.example.com")
		})

		Convey("Some stuff is deprecated", func() {
			url, err := user.LoginURLFederated("https://something", "something")
			So(err, ShouldErrLike, "LoginURLFederated is deprecated")
			So(url, ShouldEqual, "")

			key, err := user.OAuthConsumerKey()
			So(err, ShouldErrLike, "OAuthConsumerKey is deprecated")
			So(key, ShouldEqual, "")
		})

	})
}
Example #3
0
func TestCount(t *testing.T) {
	t.Parallel()

	Convey("Test Count filter", t, func() {
		c, fb := featureBreaker.FilterRDS(memory.Use(context.Background()), nil)
		c, ctr := FilterRDS(c)

		So(c, ShouldNotBeNil)
		So(ctr, ShouldNotBeNil)

		ds := datastore.Get(c)
		vals := []datastore.PropertyMap{{
			"Val":  {datastore.MkProperty(100)},
			"$key": {datastore.MkPropertyNI(ds.NewKey("Kind", "", 1, nil))},
		}}

		Convey("Calling a ds function should reflect in counter", func() {
			So(ds.PutMulti(vals), ShouldBeNil)
			So(ctr.PutMulti.Successes(), ShouldEqual, 1)

			Convey("effects are cumulative", func() {
				So(ds.PutMulti(vals), ShouldBeNil)
				So(ctr.PutMulti.Successes(), ShouldEqual, 2)

				Convey("even within transactions", func() {
					die(ds.RunInTransaction(func(c context.Context) error {
						ds := datastore.Get(c)
						So(ds.PutMulti(append(vals, vals[0])), ShouldBeNil)
						return nil
					}, nil))
				})
			})
		})

		Convey("errors count against errors", func() {
			fb.BreakFeatures(nil, "GetMulti")

			So(ds.GetMulti(vals), ShouldErrLike, `"GetMulti" is broken`)
			So(ctr.GetMulti.Errors(), ShouldEqual, 1)

			fb.UnbreakFeatures("GetMulti")

			So(ds.PutMulti(vals), ShouldBeNil)

			die(ds.GetMulti(vals))
			So(ctr.GetMulti.Errors(), ShouldEqual, 1)
			So(ctr.GetMulti.Successes(), ShouldEqual, 1)
			So(ctr.GetMulti.Total(), ShouldEqual, 2)
		})
	})

	Convey("works for memcache", t, func() {
		c, ctr := FilterMC(memory.Use(context.Background()))
		So(c, ShouldNotBeNil)
		So(ctr, ShouldNotBeNil)
		mc := memcache.Get(c)

		die(mc.Set(mc.NewItem("hello").SetValue([]byte("sup"))))

		_, err := mc.Get("Wat")
		So(err, ShouldNotBeNil)

		_, err = mc.Get("hello")
		die(err)

		So(ctr.SetMulti, shouldHaveSuccessesAndErrors, 1, 0)
		So(ctr.GetMulti, shouldHaveSuccessesAndErrors, 2, 0)
		So(ctr.NewItem, shouldHaveSuccessesAndErrors, 3, 0)
	})

	Convey("works for taskqueue", t, func() {
		c, ctr := FilterTQ(memory.Use(context.Background()))
		So(c, ShouldNotBeNil)
		So(ctr, ShouldNotBeNil)
		tq := taskqueue.Get(c)

		die(tq.Add(&taskqueue.Task{Name: "wat"}, ""))
		So(tq.Add(&taskqueue.Task{Name: "wat"}, "DNE_QUEUE"),
			ShouldErrLike, "UNKNOWN_QUEUE")

		So(ctr.AddMulti, shouldHaveSuccessesAndErrors, 1, 1)
	})

	Convey("works for global info", t, func() {
		c, fb := featureBreaker.FilterGI(memory.Use(context.Background()), nil)
		c, ctr := FilterGI(c)
		So(c, ShouldNotBeNil)
		So(ctr, ShouldNotBeNil)

		gi := info.Get(c)

		_, err := gi.Namespace("foo")
		die(err)
		fb.BreakFeatures(nil, "Namespace")
		_, err = gi.Namespace("boom")
		So(err, ShouldErrLike, `"Namespace" is broken`)

		So(ctr.Namespace, shouldHaveSuccessesAndErrors, 1, 1)
	})

	Convey("works for user", t, func() {
		c, fb := featureBreaker.FilterUser(memory.Use(context.Background()), nil)
		c, ctr := FilterUser(c)
		So(c, ShouldNotBeNil)
		So(ctr, ShouldNotBeNil)

		u := user.Get(c)

		_, err := u.CurrentOAuth("foo")
		die(err)
		fb.BreakFeatures(nil, "CurrentOAuth")
		_, err = u.CurrentOAuth("foo")
		So(err, ShouldErrLike, `"CurrentOAuth" is broken`)

		So(ctr.CurrentOAuth, shouldHaveSuccessesAndErrors, 1, 1)
	})

	Convey("works for mail", t, func() {
		c, fb := featureBreaker.FilterMail(memory.Use(context.Background()), nil)
		c, ctr := FilterMail(c)
		So(c, ShouldNotBeNil)
		So(ctr, ShouldNotBeNil)

		m := mail.Get(c)

		err := m.Send(&mail.Message{
			Sender: "*****@*****.**",
			To:     []string{"*****@*****.**"},
			Body:   "hi",
		})
		die(err)

		fb.BreakFeatures(nil, "Send")
		err = m.Send(&mail.Message{
			Sender: "*****@*****.**",
			To:     []string{"*****@*****.**"},
			Body:   "hi",
		})
		So(err, ShouldErrLike, `"Send" is broken`)

		So(ctr.Send, shouldHaveSuccessesAndErrors, 1, 1)
	})
}
Example #4
0
func TestContextAccess(t *testing.T) {
	t.Parallel()

	// p is a function which recovers an error and then immediately panics with
	// the contained string. It's defer'd in each test so that we can use the
	// ShouldPanicWith assertion (which does an == comparison and not
	// a reflect.DeepEquals comparison).
	p := func() { panic(recover().(error).Error()) }

	Convey("Context Access", t, func() {
		c := context.Background()

		Convey("blank", func() {
			So(dsS.GetRaw(c), ShouldBeNil)
			So(mcS.GetRaw(c), ShouldBeNil)
			So(tqS.GetRaw(c), ShouldBeNil)
			So(infoS.Get(c), ShouldBeNil)
		})

		// needed for everything else
		c = infoS.Set(c, Info())

		Convey("Info", func() {
			So(infoS.Get(c), ShouldNotBeNil)
			So(func() {
				defer p()
				infoS.Get(c).Datacenter()
			}, ShouldPanicWith, "dummy: method Info.Datacenter is not implemented")
		})

		Convey("Datastore", func() {
			c = dsS.SetRaw(c, Datastore())
			So(dsS.Get(c), ShouldNotBeNil)
			So(func() {
				defer p()
				_, _ = dsS.Get(c).DecodeCursor("wut")
			}, ShouldPanicWith, "dummy: method Datastore.DecodeCursor is not implemented")
		})

		Convey("Memcache", func() {
			c = mcS.SetRaw(c, Memcache())
			So(mcS.Get(c), ShouldNotBeNil)
			So(func() {
				defer p()
				_ = mcS.Get(c).Add(nil)
			}, ShouldPanicWith, "dummy: method Memcache.AddMulti is not implemented")
		})

		Convey("TaskQueue", func() {
			c = tqS.SetRaw(c, TaskQueue())
			So(tqS.Get(c), ShouldNotBeNil)
			So(func() {
				defer p()
				_ = tqS.Get(c).Purge("")
			}, ShouldPanicWith, "dummy: method TaskQueue.Purge is not implemented")
		})

		Convey("User", func() {
			c = userS.Set(c, User())
			So(userS.Get(c), ShouldNotBeNil)
			So(func() {
				defer p()
				_ = userS.Get(c).IsAdmin()
			}, ShouldPanicWith, "dummy: method User.IsAdmin is not implemented")
		})

		Convey("Mail", func() {
			c = mailS.Set(c, Mail())
			So(mailS.Get(c), ShouldNotBeNil)
			So(func() {
				defer p()
				_ = mailS.Get(c).Send(nil)
			}, ShouldPanicWith, "dummy: method Mail.Send is not implemented")
		})

		Convey("Module", func() {
			c = modS.Set(c, Module())
			So(modS.Get(c), ShouldNotBeNil)
			So(func() {
				defer p()
				modS.Get(c).List()
			}, ShouldPanicWith, "dummy: method Module.List is not implemented")
		})
	})
}
Example #5
0
func TestMail(t *testing.T) {
	t.Parallel()

	Convey("mail", t, func() {
		c := Use(context.Background())
		user := userS.Get(c)
		mail := mailS.Get(c)

		Convey("good cases", func() {
			Convey("start with an empty set of messages", func() {
				So(mail.Testable().SentMessages(), ShouldBeEmpty)
			})

			Convey("can send a message from the admin", func() {
				So(mail.Send(&mailS.Message{
					Sender:  "*****@*****.**",
					To:      []string{"Valued Customer <*****@*****.**>"},
					Subject: "You are valued.",
					Body:    "We value you.",
				}), ShouldBeNil)

				Convey("and it shows up in sent messages", func() {
					So(mail.Testable().SentMessages(), ShouldResemble, []*mailS.TestMessage{
						{Message: mailS.Message{
							Sender:  "*****@*****.**",
							To:      []string{"Valued Customer <*****@*****.**>"},
							Subject: "You are valued.",
							Body:    "We value you.",
						}},
					})

					Convey("which can be reset", func() {
						mail.Testable().Reset()
						So(mail.Testable().SentMessages(), ShouldBeEmpty)
					})
				})
			})

			Convey("can send a message on behalf of a user", func() {
				user.Testable().Login("*****@*****.**", "", false)
				So(mail.Send(&mailS.Message{
					Sender:  "Friendly Person <*****@*****.**>",
					To:      []string{"Other Friendly Person <*****@*****.**>"},
					Subject: "Hi",
					Body:    "An app is sending a message for me. It's the future.",
				}), ShouldBeNil)
			})

			Convey("can send a message to the admins", func() {
				So(mail.SendToAdmins(&mailS.Message{
					Sender:  "*****@*****.**",
					Subject: "Reminder",
					Body:    "I forgot",
				}), ShouldBeNil)

				So(mail.Testable().SentMessages(), ShouldResemble, []*mailS.TestMessage{
					{Message: mailS.Message{
						Sender:  "*****@*****.**",
						To:      []string{"*****@*****.**"},
						Subject: "Reminder",
						Body:    "I forgot",
					}},
				})
			})

			Convey("can set admin emails", func() {
				mail.Testable().SetAdminEmails(
					"Friendly <*****@*****.**>",
					"Epic <*****@*****.**>",
				)

				So(mail.SendToAdmins(&mailS.Message{
					Sender:  "*****@*****.**",
					Subject: "Reminder",
					Body:    "I forgot",
				}), ShouldBeNil)

				So(mail.Testable().SentMessages(), ShouldResemble, []*mailS.TestMessage{
					{Message: mailS.Message{
						Sender: "*****@*****.**",
						To: []string{
							"Friendly <*****@*****.**>",
							"Epic <*****@*****.**>",
						},
						Subject: "Reminder",
						Body:    "I forgot",
					}},
				})
			})

			Convey("attachments get mimetypes assigned to them", func() {
				So(mail.SendToAdmins(&mailS.Message{
					Sender:  "*****@*****.**",
					Subject: "Reminder",
					Body:    "I forgot",
					Attachments: []mailS.Attachment{
						{Name: "reminder.txt", Data: []byte("bananas")},
						{Name: "coolthing", Data: []byte("bananas")},
					},
				}), ShouldBeNil)

				So(mail.Testable().SentMessages(), ShouldResemble, []*mailS.TestMessage{
					{
						Message: mailS.Message{
							Sender:  "*****@*****.**",
							To:      []string{"*****@*****.**"},
							Subject: "Reminder",
							Body:    "I forgot",
							Attachments: []mailS.Attachment{
								{Name: "reminder.txt", Data: []byte("bananas")},
								{Name: "coolthing", Data: []byte("bananas")},
							},
						},
						MIMETypes: []string{"text/plain", "application/octet-stream"}},
				})
			})

			Convey("can have headers", func() {
				So(mail.SendToAdmins(&mailS.Message{
					Sender:  "*****@*****.**",
					Subject: "Reminder",
					Body:    "I forgot",
					Headers: net_mail.Header{
						"in-reply-to": []string{"epicness"},
						"List-Id":     []string{"spam"},
					},
				}), ShouldBeNil)

				So(mail.Testable().SentMessages(), ShouldResemble, []*mailS.TestMessage{
					{Message: mailS.Message{
						Sender:  "*****@*****.**",
						To:      []string{"*****@*****.**"},
						Subject: "Reminder",
						Body:    "I forgot",
						Headers: net_mail.Header{
							"In-Reply-To": []string{"epicness"},
							"List-Id":     []string{"spam"},
						},
					}},
				})

			})
		})

		Convey("errors", func() {
			Convey("setting a non-email is a panic", func() {
				So(func() { mail.Testable().SetAdminEmails("i am a banana") },
					ShouldPanicLike, `invalid email ("i am a banana"): mail: missing phrase`)
			})

			Convey("sending from a non-user, non-admin is an error", func() {
				mail.Testable().SetAdminEmails("Friendly <*****@*****.**>")

				So(mail.Send(&mailS.Message{
					Sender:  "*****@*****.**",
					Subject: "Reminder",
					Body:    "I forgot",
				}), ShouldErrLike, "invalid Sender: [email protected]")
			})

			Convey("sending from a bogus address is a problem", func() {
				So(mail.Send(&mailS.Message{
					Sender: "lalal",
				}), ShouldErrLike, "unparsable Sender address: lalal: mail: missing phrase")
			})

			Convey("sending with no recipients is a problem", func() {
				So(mail.Send(&mailS.Message{
					Sender: "*****@*****.**",
				}), ShouldErrLike, "one of To, Cc or Bcc must be non-empty")
			})

			Convey("bad addresses are a problem", func() {
				So(mail.Send(&mailS.Message{
					Sender: "*****@*****.**",
					To:     []string{"wut"},
				}), ShouldErrLike, `invalid email ("wut"): mail: missing phrase`)

				So(mail.Send(&mailS.Message{
					Sender: "*****@*****.**",
					Cc:     []string{"wut"},
				}), ShouldErrLike, `invalid email ("wut"): mail: missing phrase`)

				So(mail.Send(&mailS.Message{
					Sender: "*****@*****.**",
					Bcc:    []string{"wut"},
				}), ShouldErrLike, `invalid email ("wut"): mail: missing phrase`)
			})

			Convey("no body is a problem", func() {
				So(mail.Send(&mailS.Message{
					Sender: "*****@*****.**",
					To:     []string{"*****@*****.**"},
				}), ShouldErrLike, `one of Body or HTMLBody must be non-empty`)
			})

			Convey("bad attachments are a problem", func() {
				So(mail.Send(&mailS.Message{
					Sender: "*****@*****.**",
					To:     []string{"*****@*****.**"},
					Body:   "nice thing",
					Attachments: []mailS.Attachment{
						{Name: "nice.exe", Data: []byte("boom")},
					},
				}), ShouldErrLike, `illegal attachment extension for "nice.exe"`)
			})

			Convey("bad headers are a problem", func() {
				So(mail.SendToAdmins(&mailS.Message{
					Sender:  "*****@*****.**",
					Subject: "Reminder",
					Body:    "I forgot",
					Headers: net_mail.Header{"x-spam-cool": []string{"value"}},
				}), ShouldErrLike, `disallowed header: x-spam-cool`)

			})

		})

	})
}