This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAttachmentDumpNormalWithFilename(t *testing.T) {
|
||||
a := MailAttachment{
|
||||
IsInline: false,
|
||||
ContentType: "text/plain",
|
||||
Filename: "hello.txt",
|
||||
Data: []byte("HelloWorld"),
|
||||
}
|
||||
|
||||
lines := a.dump()
|
||||
joined := strings.Join(lines, "\n")
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: text/plain; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Transfer-Encoding: base64"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, `Content-Disposition: attachment;filename="hello.txt"`))
|
||||
tst.AssertFalse(t, strings.Contains(joined, "Content-Disposition: inline"))
|
||||
|
||||
expectedB64 := base64.StdEncoding.EncodeToString([]byte("HelloWorld"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, expectedB64))
|
||||
}
|
||||
|
||||
func TestAttachmentDumpInlineWithFilename(t *testing.T) {
|
||||
a := MailAttachment{
|
||||
IsInline: true,
|
||||
ContentType: "image/png",
|
||||
Filename: "img.png",
|
||||
Data: []byte{0x01, 0x02, 0x03, 0x04},
|
||||
}
|
||||
|
||||
lines := a.dump()
|
||||
joined := strings.Join(lines, "\n")
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: image/png; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Transfer-Encoding: base64"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, `Content-Disposition: inline;filename="img.png"`))
|
||||
}
|
||||
|
||||
func TestAttachmentDumpNormalNoFilename(t *testing.T) {
|
||||
a := MailAttachment{
|
||||
IsInline: false,
|
||||
ContentType: "text/plain",
|
||||
Filename: "",
|
||||
Data: []byte("foo"),
|
||||
}
|
||||
|
||||
lines := a.dump()
|
||||
joined := strings.Join(lines, "\n")
|
||||
|
||||
tst.AssertTrue(t, langext.InArray("Content-Disposition: attachment", lines))
|
||||
tst.AssertFalse(t, strings.Contains(joined, "filename="))
|
||||
}
|
||||
|
||||
func TestAttachmentDumpInlineNoFilename(t *testing.T) {
|
||||
a := MailAttachment{
|
||||
IsInline: true,
|
||||
ContentType: "text/plain",
|
||||
Filename: "",
|
||||
Data: []byte("foo"),
|
||||
}
|
||||
|
||||
lines := a.dump()
|
||||
|
||||
tst.AssertTrue(t, langext.InArray("Content-Disposition: inline", lines))
|
||||
}
|
||||
|
||||
func TestAttachmentDumpNoContentType(t *testing.T) {
|
||||
a := MailAttachment{
|
||||
IsInline: false,
|
||||
ContentType: "",
|
||||
Filename: "x.bin",
|
||||
Data: []byte("x"),
|
||||
}
|
||||
|
||||
lines := a.dump()
|
||||
|
||||
for _, l := range lines {
|
||||
tst.AssertFalse(t, strings.HasPrefix(l, "Content-Type:"))
|
||||
}
|
||||
tst.AssertTrue(t, langext.InArray("Content-Transfer-Encoding: base64", lines))
|
||||
}
|
||||
|
||||
func TestAttachmentDumpEmptyData(t *testing.T) {
|
||||
a := MailAttachment{
|
||||
IsInline: false,
|
||||
ContentType: "application/octet-stream",
|
||||
Filename: "empty.bin",
|
||||
Data: []byte{},
|
||||
}
|
||||
|
||||
lines := a.dump()
|
||||
joined := strings.Join(lines, "\n")
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: application/octet-stream; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Transfer-Encoding: base64"))
|
||||
}
|
||||
|
||||
func TestAttachmentDumpLongDataLineWrapped(t *testing.T) {
|
||||
// Data needs to result in > 80 base64 chars to test the wrapping.
|
||||
// 100 bytes => 136 base64 chars => should wrap into 2 lines (80 + 56).
|
||||
data := make([]byte, 100)
|
||||
for i := range data {
|
||||
data[i] = byte(i)
|
||||
}
|
||||
|
||||
a := MailAttachment{
|
||||
IsInline: false,
|
||||
ContentType: "application/octet-stream",
|
||||
Filename: "big.bin",
|
||||
Data: data,
|
||||
}
|
||||
|
||||
lines := a.dump()
|
||||
|
||||
// Find the base64 lines (everything after the headers).
|
||||
b64Lines := make([]string, 0)
|
||||
foundFirstHeader := false
|
||||
for _, l := range lines {
|
||||
if !foundFirstHeader && (strings.HasPrefix(l, "Content-") || l == "") {
|
||||
foundFirstHeader = true
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(l, "Content-") {
|
||||
continue
|
||||
}
|
||||
b64Lines = append(b64Lines, l)
|
||||
}
|
||||
|
||||
full := strings.Join(b64Lines, "")
|
||||
expected := base64.StdEncoding.EncodeToString(data)
|
||||
tst.AssertEqual(t, full, expected)
|
||||
|
||||
// Each line (except possibly last) should be 80 chars.
|
||||
for i, l := range b64Lines {
|
||||
if i < len(b64Lines)-1 {
|
||||
tst.AssertEqual(t, len(l), 80)
|
||||
} else {
|
||||
tst.AssertTrue(t, len(l) <= 80)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMimeMailPlainOnly(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"Test Subject",
|
||||
MailBody{Plain: "Hello plain body"},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "From: from@example.com"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "To: to@example.com"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Subject: Test Subject"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: text/plain; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Transfer-Encoding: 7bit"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Hello plain body"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "MIME-Version: 1.0"))
|
||||
|
||||
// Each line must be terminated by CRLF.
|
||||
tst.AssertTrue(t, strings.Contains(mail, "\r\n"))
|
||||
}
|
||||
|
||||
func TestMimeMailHTMLOnly(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{HTML: "<p>Hi</p>"},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: text/html; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "<p>Hi</p>"))
|
||||
tst.AssertFalse(t, strings.Contains(mail, "multipart/"))
|
||||
}
|
||||
|
||||
func TestMimeMailAlternative(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{
|
||||
Plain: "Plain Body",
|
||||
HTML: "<p>HTML Body</p>",
|
||||
},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: multipart/alternative;"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Plain Body"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "<p>HTML Body</p>"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: text/plain; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: text/html; charset=UTF-8"))
|
||||
}
|
||||
|
||||
func TestMimeMailWithCC(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
[]string{"cc@example.com"},
|
||||
nil, nil,
|
||||
"S",
|
||||
MailBody{Plain: "x"},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "cc@example.com"))
|
||||
}
|
||||
|
||||
func TestMimeMailWithBCC(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil,
|
||||
[]string{"bcc@example.com"},
|
||||
nil,
|
||||
"S",
|
||||
MailBody{Plain: "x"},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Bcc: bcc@example.com"))
|
||||
}
|
||||
|
||||
func TestMimeMailWithReplyTo(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil,
|
||||
[]string{"reply@example.com"},
|
||||
"S",
|
||||
MailBody{Plain: "x"},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Reply-To: reply@example.com"))
|
||||
}
|
||||
|
||||
func TestMimeMailMultipleRecipients(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"a@example.com", "b@example.com", "c@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{Plain: "x"},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "a@example.com"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "b@example.com"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "c@example.com"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "a@example.com, b@example.com, c@example.com"))
|
||||
}
|
||||
|
||||
func TestMimeMailSubjectEncoding(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"Hällö Wörld",
|
||||
MailBody{Plain: "x"},
|
||||
nil)
|
||||
|
||||
// Non-ASCII subject must be quoted-printable encoded.
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Subject: =?UTF-8?q?"))
|
||||
}
|
||||
|
||||
func TestMimeMailWithNormalAttachment(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{Plain: "Body"},
|
||||
[]MailAttachment{
|
||||
{Data: []byte("attached"), Filename: "f.txt", IsInline: false, ContentType: "text/plain"},
|
||||
})
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: multipart/mixed;"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, `Content-Disposition: attachment;filename="f.txt"`))
|
||||
}
|
||||
|
||||
func TestMimeMailWithInlineAttachment(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{HTML: "<p>x</p>"},
|
||||
[]MailAttachment{
|
||||
{Data: []byte{1, 2, 3}, Filename: "img.png", IsInline: true, ContentType: "image/png"},
|
||||
})
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: multipart/related;"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, `Content-Disposition: inline;filename="img.png"`))
|
||||
}
|
||||
|
||||
func TestMimeMailWithBothAttachmentTypes(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{HTML: "<p>x</p>"},
|
||||
[]MailAttachment{
|
||||
{Data: []byte{1}, Filename: "img.png", IsInline: true, ContentType: "image/png"},
|
||||
{Data: []byte{2}, Filename: "f.txt", IsInline: false, ContentType: "text/plain"},
|
||||
})
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: multipart/mixed;"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Content-Type: multipart/related;"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, `Content-Disposition: inline;filename="img.png"`))
|
||||
tst.AssertTrue(t, strings.Contains(mail, `Content-Disposition: attachment;filename="f.txt"`))
|
||||
}
|
||||
|
||||
func TestMimeMailEmptyBody(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(mail, "From: from@example.com"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "To: to@example.com"))
|
||||
tst.AssertTrue(t, strings.Contains(mail, "Subject: S"))
|
||||
// No body type was set, so no Content-Type for body should be present.
|
||||
tst.AssertFalse(t, strings.Contains(mail, "Content-Type: text/"))
|
||||
tst.AssertFalse(t, strings.Contains(mail, "Content-Type: multipart/"))
|
||||
}
|
||||
|
||||
func TestMimeMailHasDateHeader(t *testing.T) {
|
||||
mail := encodeMimeMail(
|
||||
"from@example.com",
|
||||
[]string{"to@example.com"},
|
||||
nil, nil, nil,
|
||||
"S",
|
||||
MailBody{Plain: "x"},
|
||||
nil)
|
||||
|
||||
tst.AssertTrue(t, strings.HasPrefix(mail, "Date: "))
|
||||
}
|
||||
|
||||
func TestDumpMailBodyPlainOnly(t *testing.T) {
|
||||
lines := dumpMailBody(MailBody{Plain: "Plain"}, false, false, "BOUND", "BOUNDALT")
|
||||
joined := strings.Join(lines, "\n")
|
||||
tst.AssertTrue(t, strings.Contains(joined, "--BOUND"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: text/plain; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Plain"))
|
||||
}
|
||||
|
||||
func TestDumpMailBodyHTMLOnly(t *testing.T) {
|
||||
lines := dumpMailBody(MailBody{HTML: "<p>x</p>"}, false, false, "BOUND", "BOUNDALT")
|
||||
joined := strings.Join(lines, "\n")
|
||||
tst.AssertTrue(t, strings.Contains(joined, "--BOUND"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: text/html; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "<p>x</p>"))
|
||||
}
|
||||
|
||||
func TestDumpMailBodyEmpty(t *testing.T) {
|
||||
lines := dumpMailBody(MailBody{}, false, false, "BOUND", "BOUNDALT")
|
||||
joined := strings.Join(lines, "\n")
|
||||
// Default empty case still emits the boundary and a default Content-Type header.
|
||||
tst.AssertTrue(t, strings.Contains(joined, "--BOUND"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: text/plain; charset=UTF-8"))
|
||||
}
|
||||
|
||||
func TestDumpMailBodyMixedAlternative(t *testing.T) {
|
||||
// HTML+Plain with normal attachments and no inline → uses alternative sub-block.
|
||||
lines := dumpMailBody(MailBody{Plain: "P", HTML: "<p>H</p>"}, false, true, "BOUND", "BOUNDALT")
|
||||
joined := strings.Join(lines, "\n")
|
||||
tst.AssertTrue(t, strings.Contains(joined, "--BOUND"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: multipart/alternative; boundary=BOUNDALT"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "--BOUNDALT"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "P"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "<p>H</p>"))
|
||||
}
|
||||
|
||||
func TestDumpMailBodyMixedInline(t *testing.T) {
|
||||
// HTML+Plain with inline attachments → simplified to single HTML block.
|
||||
lines := dumpMailBody(MailBody{Plain: "P", HTML: "<p>H</p>"}, true, false, "BOUND", "BOUNDALT")
|
||||
tst.AssertEqual(t, len(lines), 2)
|
||||
tst.AssertEqual(t, lines[0], "--BOUND")
|
||||
tst.AssertEqual(t, lines[1], "<p>H</p>")
|
||||
}
|
||||
|
||||
func TestDumpMailBodyBothNoAttachments(t *testing.T) {
|
||||
lines := dumpMailBody(MailBody{Plain: "P", HTML: "<p>H</p>"}, false, false, "BOUND", "BOUNDALT")
|
||||
joined := strings.Join(lines, "\n")
|
||||
tst.AssertTrue(t, strings.Contains(joined, "--BOUND"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: text/plain; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "Content-Type: text/html; charset=UTF-8"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "P"))
|
||||
tst.AssertTrue(t, strings.Contains(joined, "<p>H</p>"))
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewGoogleOAuthReturnsNonNil(t *testing.T) {
|
||||
auth := NewGoogleOAuth("cid", "csecret", "rtok")
|
||||
tst.AssertTrue(t, auth != nil)
|
||||
}
|
||||
|
||||
func TestNewGoogleOAuthFieldsSet(t *testing.T) {
|
||||
auth := NewGoogleOAuth("cid", "csecret", "rtok")
|
||||
c, ok := auth.(*oauth)
|
||||
tst.AssertTrue(t, ok)
|
||||
tst.AssertEqual(t, c.clientID, "cid")
|
||||
tst.AssertEqual(t, c.clientSecret, "csecret")
|
||||
tst.AssertEqual(t, c.refreshToken, "rtok")
|
||||
tst.AssertTrue(t, c.accessToken == nil)
|
||||
tst.AssertTrue(t, c.expiryDate == nil)
|
||||
}
|
||||
|
||||
func TestOAuthAccessTokenCachedReturnsStored(t *testing.T) {
|
||||
c := &oauth{
|
||||
clientID: "cid",
|
||||
clientSecret: "csecret",
|
||||
refreshToken: "rtok",
|
||||
}
|
||||
|
||||
tok := "cached-token-value"
|
||||
expiry := time.Now().Add(1 * time.Hour)
|
||||
c.accessToken = &tok
|
||||
c.expiryDate = &expiry
|
||||
|
||||
got, err := c.AccessToken()
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, got, "cached-token-value")
|
||||
}
|
||||
|
||||
func TestOAuthAccessTokenCachedMultipleCalls(t *testing.T) {
|
||||
c := &oauth{
|
||||
clientID: "cid",
|
||||
clientSecret: "csecret",
|
||||
refreshToken: "rtok",
|
||||
}
|
||||
|
||||
tok := "another-token"
|
||||
expiry := time.Now().Add(30 * time.Minute)
|
||||
c.accessToken = &tok
|
||||
c.expiryDate = &expiry
|
||||
|
||||
for range 5 {
|
||||
got, err := c.AccessToken()
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, got, "another-token")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package googleapi
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewGoogleClientReturnsNonNil(t *testing.T) {
|
||||
auth := NewGoogleOAuth("cid", "csecret", "rtok")
|
||||
gc := NewGoogleClient(auth)
|
||||
tst.AssertTrue(t, gc != nil)
|
||||
}
|
||||
|
||||
func TestNewGoogleClientWiresOAuth(t *testing.T) {
|
||||
auth := NewGoogleOAuth("cid", "csecret", "rtok")
|
||||
gc := NewGoogleClient(auth)
|
||||
c, ok := gc.(*client)
|
||||
tst.AssertTrue(t, ok)
|
||||
tst.AssertTrue(t, c.oauth == auth)
|
||||
}
|
||||
|
||||
func TestMailBodyZeroValue(t *testing.T) {
|
||||
b := MailBody{}
|
||||
tst.AssertEqual(t, b.Plain, "")
|
||||
tst.AssertEqual(t, b.HTML, "")
|
||||
}
|
||||
Reference in New Issue
Block a user