mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 04:36:43 +00:00
195 lines
3.5 KiB
Go
195 lines
3.5 KiB
Go
package tests
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/cucumber/messages-go/v16"
|
|
"github.com/emersion/go-imap"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
type Message struct {
|
|
Subject string `bdd:"subject"`
|
|
|
|
From string `bdd:"from"`
|
|
To string `bdd:"to"`
|
|
CC string `bdd:"cc"`
|
|
BCC string `bdd:"bcc"`
|
|
|
|
Unread bool `bdd:"unread"`
|
|
}
|
|
|
|
func newMessageFromIMAP(msg *imap.Message) Message {
|
|
message := Message{
|
|
Subject: msg.Envelope.Subject,
|
|
Unread: slices.Contains(msg.Flags, imap.SeenFlag),
|
|
}
|
|
|
|
if len(msg.Envelope.From) > 0 {
|
|
message.From = msg.Envelope.From[0].Address()
|
|
}
|
|
|
|
if len(msg.Envelope.To) > 0 {
|
|
message.To = msg.Envelope.To[0].Address()
|
|
}
|
|
|
|
if len(msg.Envelope.Cc) > 0 {
|
|
message.CC = msg.Envelope.Cc[0].Address()
|
|
}
|
|
|
|
if len(msg.Envelope.Bcc) > 0 {
|
|
message.BCC = msg.Envelope.Bcc[0].Address()
|
|
}
|
|
|
|
return message
|
|
}
|
|
|
|
func matchMessages(have, want []Message) error {
|
|
if !IsSub(ToAny(have), ToAny(want)) {
|
|
return fmt.Errorf("missing messages: %v", want)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type Mailbox struct {
|
|
Name string `bdd:"name"`
|
|
Total int `bdd:"total"`
|
|
Unread int `bdd:"unread"`
|
|
}
|
|
|
|
func newMailboxFromIMAP(status *imap.MailboxStatus) Mailbox {
|
|
return Mailbox{
|
|
Name: status.Name,
|
|
Total: int(status.Messages),
|
|
Unread: int(status.Unseen),
|
|
}
|
|
}
|
|
|
|
func matchMailboxes(have, want []Mailbox) error {
|
|
slices.SortFunc(have, func(a, b Mailbox) bool {
|
|
return a.Name < b.Name
|
|
})
|
|
|
|
slices.SortFunc(want, func(a, b Mailbox) bool {
|
|
return a.Name < b.Name
|
|
})
|
|
|
|
if !IsSub(want, have) {
|
|
return fmt.Errorf("missing messages: %v", want)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func eventually(condition func() error, waitFor, tick time.Duration) error {
|
|
ch := make(chan error, 1)
|
|
|
|
timer := time.NewTimer(waitFor)
|
|
defer timer.Stop()
|
|
|
|
ticker := time.NewTicker(tick)
|
|
defer ticker.Stop()
|
|
|
|
for tick := ticker.C; ; {
|
|
select {
|
|
case <-timer.C:
|
|
return fmt.Errorf("timed out after %v", waitFor)
|
|
|
|
case <-tick:
|
|
tick = nil
|
|
|
|
go func() { ch <- condition() }()
|
|
|
|
case err := <-ch:
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
tick = ticker.C
|
|
}
|
|
}
|
|
}
|
|
|
|
func unmarshalTable[T any](table *messages.PickleTable) ([]T, error) {
|
|
if len(table.Rows) == 0 {
|
|
return nil, fmt.Errorf("empty table")
|
|
}
|
|
|
|
var res []T
|
|
|
|
for _, row := range table.Rows[1:] {
|
|
var v T
|
|
|
|
if err := unmarshalRow(table.Rows[0], row, &v); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res = append(res, v)
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func unmarshalRow(header, row *messages.PickleTableRow, v any) error {
|
|
typ := reflect.TypeOf(v).Elem()
|
|
|
|
for idx := 0; idx < typ.NumField(); idx++ {
|
|
field := typ.Field(idx)
|
|
|
|
if tag, ok := field.Tag.Lookup("bdd"); ok {
|
|
cell, ok := getCellValue(header, row, tag)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
switch field.Type.Kind() {
|
|
case reflect.String:
|
|
reflect.ValueOf(v).Elem().Field(idx).SetString(cell)
|
|
|
|
case reflect.Int:
|
|
reflect.ValueOf(v).Elem().Field(idx).SetInt(int64(mustParseInt(cell)))
|
|
|
|
case reflect.Bool:
|
|
reflect.ValueOf(v).Elem().Field(idx).SetBool(mustParseBool(cell))
|
|
|
|
default:
|
|
return fmt.Errorf("unsupported type %q", field.Type.Kind())
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getCellValue(header, row *messages.PickleTableRow, name string) (string, bool) {
|
|
for idx, cell := range header.Cells {
|
|
if cell.Value == name {
|
|
return row.Cells[idx].Value, true
|
|
}
|
|
}
|
|
|
|
return "", false
|
|
}
|
|
|
|
func mustParseInt(s string) int {
|
|
i, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return i
|
|
}
|
|
|
|
func mustParseBool(s string) bool {
|
|
v, err := strconv.ParseBool(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return v
|
|
}
|