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 }
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, "") }) }) }
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) }) }
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") }) }) }
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`) }) }) }) }