1package imap
2
3import (
4	"bytes"
5	"io"
6	"net/textproto"
7	"reflect"
8	"strings"
9	"testing"
10	"time"
11)
12
13// Note to myself: writing these boring tests actually fixed 2 bugs :P
14
15var searchSeqSet1, _ = ParseSeqSet("1:42")
16var searchSeqSet2, _ = ParseSeqSet("743:938")
17var searchDate1 = time.Date(1997, 11, 21, 0, 0, 0, 0, time.UTC)
18var searchDate2 = time.Date(1984, 11, 5, 0, 0, 0, 0, time.UTC)
19
20var searchCriteriaTests = []struct {
21	expected string
22	criteria *SearchCriteria
23}{
24	{
25		expected: `(1:42 UID 743:938 ` +
26			`SINCE "5-Nov-1984" BEFORE "21-Nov-1997" SENTSINCE "5-Nov-1984" SENTBEFORE "21-Nov-1997" ` +
27			`FROM "root@protonmail.com" BODY "hey there" TEXT "DILLE" ` +
28			`ANSWERED DELETED KEYWORD cc UNKEYWORD microsoft ` +
29			`LARGER 4242 SMALLER 4342 ` +
30			`NOT (SENTON "21-Nov-1997" HEADER "Content-Type" "text/csv") ` +
31			`OR (ON "5-Nov-1984" DRAFT FLAGGED UNANSWERED UNDELETED OLD) (UNDRAFT UNFLAGGED UNSEEN))`,
32		criteria: &SearchCriteria{
33			SeqNum:     searchSeqSet1,
34			Uid:        searchSeqSet2,
35			Since:      searchDate2,
36			Before:     searchDate1,
37			SentSince:  searchDate2,
38			SentBefore: searchDate1,
39			Header: textproto.MIMEHeader{
40				"From": {"root@protonmail.com"},
41			},
42			Body:         []string{"hey there"},
43			Text:         []string{"DILLE"},
44			WithFlags:    []string{AnsweredFlag, DeletedFlag, "cc"},
45			WithoutFlags: []string{"microsoft"},
46			Larger:       4242,
47			Smaller:      4342,
48			Not: []*SearchCriteria{{
49				SentSince:  searchDate1,
50				SentBefore: searchDate1.Add(24 * time.Hour),
51				Header: textproto.MIMEHeader{
52					"Content-Type": {"text/csv"},
53				},
54			}},
55			Or: [][2]*SearchCriteria{{
56				{
57					Since:        searchDate2,
58					Before:       searchDate2.Add(24 * time.Hour),
59					WithFlags:    []string{DraftFlag, FlaggedFlag},
60					WithoutFlags: []string{AnsweredFlag, DeletedFlag, RecentFlag},
61				},
62				{
63					WithoutFlags: []string{DraftFlag, FlaggedFlag, SeenFlag},
64				},
65			}},
66		},
67	},
68	{
69		expected: "(ALL)",
70		criteria: &SearchCriteria{},
71	},
72}
73
74func TestSearchCriteria_Format(t *testing.T) {
75	for i, test := range searchCriteriaTests {
76		fields := test.criteria.Format()
77
78		got, err := formatFields(fields)
79		if err != nil {
80			t.Fatal("Unexpected no error while formatting fields, got:", err)
81		}
82
83		if got != test.expected {
84			t.Errorf("Invalid search criteria fields for #%v: got \n%v\n instead of \n%v", i+1, got, test.expected)
85		}
86	}
87}
88
89func TestSearchCriteria_Parse(t *testing.T) {
90	for i, test := range searchCriteriaTests {
91		criteria := new(SearchCriteria)
92
93		b := bytes.NewBuffer([]byte(test.expected))
94		r := NewReader(b)
95		fields, _ := r.ReadFields()
96
97		if err := criteria.ParseWithCharset(fields[0].([]interface{}), nil); err != nil {
98			t.Errorf("Cannot parse search criteria for #%v: %v", i+1, err)
99		} else if !reflect.DeepEqual(criteria, test.criteria) {
100			t.Errorf("Invalid search criteria for #%v: got \n%+v\n instead of \n%+v", i+1, criteria, test.criteria)
101		}
102	}
103}
104
105var searchCriteriaParseTests = []struct {
106	fields   []interface{}
107	criteria *SearchCriteria
108	charset  func(io.Reader) io.Reader
109}{
110	{
111		fields:   []interface{}{"ALL"},
112		criteria: &SearchCriteria{},
113	},
114	{
115		fields: []interface{}{"NEW"},
116		criteria: &SearchCriteria{
117			WithFlags:    []string{RecentFlag},
118			WithoutFlags: []string{SeenFlag},
119		},
120	},
121	{
122		fields: []interface{}{"SUBJECT", strings.NewReader("café")},
123		criteria: &SearchCriteria{
124			Header: textproto.MIMEHeader{"Subject": {"café"}},
125		},
126		charset: func(r io.Reader) io.Reader {
127			return r
128		},
129	},
130}
131
132func TestSearchCriteria_Parse_others(t *testing.T) {
133	for i, test := range searchCriteriaParseTests {
134		criteria := new(SearchCriteria)
135		if err := criteria.ParseWithCharset(test.fields, test.charset); err != nil {
136			t.Errorf("Cannot parse search criteria for #%v: %v", i+1, err)
137		} else if !reflect.DeepEqual(criteria, test.criteria) {
138			t.Errorf("Invalid search criteria for #%v: got \n%+v\n instead of \n%+v", i+1, criteria, test.criteria)
139		}
140	}
141}
142