Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CI #2

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# This workflow will run test and upload the coverage when push or pull_request is made on `dev` branch
name: build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.15

- name: Checkout the code
uses: actions/checkout@v2
with:
fetch-depth: 2

- name: Get dependencies
run: |
go get -v -t -d ./...

- name: Run Test and get coverage
run: |
go test -race -covermode atomic -coverprofile=covprofile
sed -i "s/$(pwd|sed 's/\//\\\//g')/./g" covprofile # convert absolute path to relative path

- name: Push test coverage
if: success()
continue-on-error: true
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: covprofile

18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
RawEml
=========
This is a wrapper for the AWS SES raw email and allows to set the message priority and the conversation topic to group emails with same topic.
This package is for a granual control of emails that are sent with AWS SES.
It supports setting the email priority, conversation topic for grouping and any other email header tags.

[![Build Status](https://github.com/boseca/raweml/workflows/build/badge.svg)](https://github.com/boseca/raweml/actions?query=workflow%3Abuild)
[![Coverage Status](https://coveralls.io/repos/github/boseca/raweml/badge.svg?branch=master)](https://coveralls.io/github/boseca/raweml?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/boseca/raweml)](https://goreportcard.com/report/github.com/boseca/raweml)
[![Documentation](https://godoc.org/gopkg.in/raweml?status.svg)](https://godoc.org/gopkg.in/raweml)

## Description

Expand Down Expand Up @@ -75,3 +81,13 @@ go test -v ./example
```bash
golint
```

- Show code coverage
```bash
go test -coverprofile=c.out
sed -i "s/$(pwd|sed 's/\//\\\//g')/./g" c.out # convert absolute path to relative path
go tool cover -html=c.out -o=c.html # optional
gcov2lcov -infile=c.out -outfile=c.lcov
genhtml -q --legend -o coverage_html --title='Raweml' c.lcov
x-www-browser file:///$(pwd)/coverage_html/index.html
```
14 changes: 13 additions & 1 deletion raweml.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/textproto"
"os"
"path/filepath"
"sort"
"strings"

"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -391,7 +392,9 @@ func addAttachments(w io.Writer, attachments []Attachment, boundary string) erro
// Header values will be trimmed but otherwise left alone.
// Headers with multiple values are not supported and will return an error.
func writeHeader(w io.Writer, header *textproto.MIMEHeader) error {
for k, vs := range *header {
// for k, vs := range *header {
for _, k := range sortedHeaders(header) {
vs := header.Values(k)
_, err := fmt.Fprintf(w, "%s: ", k)
if err != nil {
return err
Expand Down Expand Up @@ -468,6 +471,15 @@ func (priority EmailPriority) String() string {
return string(priority)
}

func sortedHeaders(header *textproto.MIMEHeader) (keys []string) {
// type MIMEHeader map[string][]string
for k := range *header {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}

// func win32TimeFromTar(key string, hdrs map[string]string, unixTime time.Time) Filetime {
// if s, ok := hdrs[key]; ok {
// n, err := strconv.ParseUint(s, 10, 64)
Expand Down
120 changes: 120 additions & 0 deletions raweml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package raweml

import (
"strings"
"testing"
)

// ---------------------------------------------------------------
// # TODO - create test for raweml
// ---------------------------------------------------------------
// - add test for `raweml` (test for GetSendRawEmailInput and NewRecipients)
// ---------------------------------------------------------------
var (
testEmailString = `Content-Language: en-US
Content-Type: multipart/mixed; boundary=*
From: NO REPLAY EMAIL ACCOUNT <no-reply@example.com>
Mime-Version: 1.0
References: MbfJRQw5X+qg8GSOJxjM2Q==
Subject: Simple Test
Thread-Index: *
Thread-Topic: Hello world
To: customer@example.com
X-Priority: 3

*
Content-Type: multipart/alternative; boundary=*
Mime-Version: 1.0

*
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=UTF-8

Amazon SES Test Email (AWS SDK for Go)
*
Content-Transfer-Encoding: 7bit
Content-Type: text/html; charset=UTF-8

<h1>Amazon SES Test Email (AWS SDK for Go)</h1>
*
*
*
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-ID: <1001>
X-Attachment-Id: 1001
Content-Disposition: attachment; filename="Mars.png"

*`
)

func TestRaweml(t *testing.T) {
t.Run("Test conversion of Email to raw data", func(t *testing.T) {
// create Email
const fromEmail = "NO REPLAY EMAIL ACCOUNT <no-reply@example.com>"
eml := Email{
From: fromEmail,
Recipients: NewRecipients("customer@example.com", "", ""),
Subject: "Simple Test",
TextBody: "Amazon SES Test Email (AWS SDK for Go)",
HTMLBody: "<h1>Amazon SES Test Email (AWS SDK for Go)</h1>",
Topic: "Hello world",
Attachments: []Attachment{{Name: "example/Mars.png", ContentID: "1001"}},
AwsRegion: "us-east-1",
}
eml.SetHeader("X-something", "test")

// get Email Raw data
r, err := eml.GetSendRawEmailInput()
if err != nil {
t.Error(err)
}

// validate the email
if got := eml.GetSource(); *got != fromEmail {
t.Errorf("Invalid Source!\nwant:%s\ngot:%s", fromEmail, *got)
}
if want := "customer@example.com"; *r.Destinations[0] != want {
t.Errorf("Invalid destination!\nwant:%v\ngot:%v", want, r.Destinations)
}

// line by line comparison
str := strings.Split(strings.ReplaceAll(string(r.RawMessage.Data), "\r", ""), "\n")
for i, line := range strings.Split(testEmailString, "\n") {
if line != "*" {
if strings.HasSuffix(line, "*") || strings.HasSuffix(line, "*\r") {
// compare first part
max := len(line) - 1
if strings.HasSuffix(line, "*\r") {
max = max - 1
}
if str[i][0:max] != line[0:max] {
t.Errorf("Invalid RawMessage data line with wildchar! (%v)\nwant:%s\ngot:%s", i, line, str[i])
}
} else {
if str[i] != line {
t.Errorf("Invalid RawMessage data line! (%v)\nwant:%s\ngot:%s", i, line, str[i])
}
}
}
}
})
t.Run("Test New Recipients", func(t *testing.T) {
to := "to_1@h.com,to_2@h.com"
cc := "cc_1@h.com,c_2@h.com"
bcc := "bcc_1@h.com,bcc_2@h.com"
email := Email{
Recipients: NewRecipients(to, cc, bcc),
}

// var want []*string
// for _, v := range strings.Split(to+","+cc+","+bcc, ",") {
// want = append(want, &v)
// }
want := to + "," + cc + "," + bcc
got := email.Recipients.String()
if want != got {
t.Errorf("Invalid Recipients!\nwant:%s\ngot:%s", want, got)
}
})
}
6 changes: 1 addition & 5 deletions thread.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,18 +373,14 @@ func unixToTimeStamp64(unixNanosecond int64) int64 {
return unixNanosecond/100 + 116444736000000000
}

// unixToTimeStamp converts unix time to time stamp uint64
func unixToTimeStamp(unixNanosecond uint64) uint64 {
return unixNanosecond/100 + 116444736000000000
}

// Helping functions (private)

func int64ToBytes(num int64) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(num))
return b
}

func bytesToInt64(b []byte) int64 {
return int64(binary.BigEndian.Uint64(b))
}
38 changes: 38 additions & 0 deletions thread_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package raweml

import (
"bytes"
"encoding/base64"
"encoding/hex"
"fmt"
Expand Down Expand Up @@ -266,6 +267,43 @@ func TestThread(t *testing.T) {
}
}
})
t.Run("Test New Thread", func(t *testing.T) {
thread := NewThread("Hello world")
thread.AddChildBlock()
want := thread.Bytes()
threadParm := NewEmailThreadFromParams(thread.DateUnixNano, thread.GetGUID(), thread.GetTopic(), thread.ChildBlocks)
got := threadParm.Bytes()
if bytes.Compare(got, want) != 0 {
t.Errorf("Invalid Filetime conversion!\ngot: %s\nwant: %s", got, want)
}
if got := thread.String(); got != thread.Index() {
t.Errorf("String does not match the index!\ngot: %s\nwant: %s", got, thread.Index())
}
if got := thread.Reference(); got != "MbfJRQw5X+qg8GSOJxjM2Q==" {
t.Errorf("Invalid reference!\ngot:%s\nwant:%s", got, "MbfJRQw5X+qg8GSOJxjM2Q==")
}
})
t.Run("Test converting Filetime to Unix nano seconds", func(t *testing.T) {
want := time.Now().UTC().UnixNano()
ft := UnixNanoToFiletime(want)
if got := ft.UnixNanoseconds(); got/100 != want/100 {
t.Errorf("Invalid Filetime conversion!\ngot: %v\nwant: %v", got, want)
}
})
t.Run("Test converting Bytes to Int", func(t *testing.T) {
want := int64(123456789)
b := int64ToBytes(want)
if got := bytesToInt64(b); got != want {
t.Errorf("Invalid bytes conversion!\ngot: %v\nwant: %v", got, want)
}
})
t.Run("Test conversion Hex to Base64", func(t *testing.T) {
b := []byte("hello world")
want := "aGVsbG8gd29ybGQ="
if got := hexToBase64(b); got != want {
t.Errorf("Invalid bytes conversion!\ngot: %v\nwant: %v", got, want)
}
})
}

// helping functions -----------------------
Expand Down