Files
go-imap/backend/backendutil/search_test.go

433 lines
9.6 KiB
Go

package backendutil
import (
"net/textproto"
"strings"
"testing"
"time"
"github.com/emersion/go-imap"
"github.com/emersion/go-message"
)
var testInternalDate = time.Unix(1483997966, 0)
var matchTests = []struct {
criteria *imap.SearchCriteria
seqNum uint32
uid uint32
date time.Time
flags []string
res bool
}{
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"From": {"Mitsuha"}},
},
res: true,
},
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"To": {"Mitsuha"}},
},
res: false,
},
{
criteria: &imap.SearchCriteria{SentBefore: testDate.Add(48 * time.Hour)},
res: true,
},
{
criteria: &imap.SearchCriteria{
Not: []*imap.SearchCriteria{{SentSince: testDate.Add(48 * time.Hour)}},
},
res: true,
},
{
criteria: &imap.SearchCriteria{
Not: []*imap.SearchCriteria{{Body: []string{"name"}}},
},
res: false,
},
{
criteria: &imap.SearchCriteria{
Text: []string{"name"},
},
res: true,
},
{
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{Text: []string{"i'm not in the text"}},
{Body: []string{"i'm not in the body"}},
}},
},
res: false,
},
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"Message-Id": {"42@example.org"}},
},
res: true,
},
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"Message-Id": {"43@example.org"}},
},
res: false,
},
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"Message-Id": {""}},
},
res: true,
},
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"Totally-Not-Reply-To": {""}},
},
res: false,
},
{
criteria: &imap.SearchCriteria{
Larger: 10,
},
res: true,
},
{
criteria: &imap.SearchCriteria{
Smaller: 10,
},
res: false,
},
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"Subject": {"your"}},
},
res: true,
},
{
criteria: &imap.SearchCriteria{
Header: textproto.MIMEHeader{"Subject": {"Taki"}},
},
res: false,
},
{
flags: []string{imap.SeenFlag},
criteria: &imap.SearchCriteria{
WithFlags: []string{imap.SeenFlag},
WithoutFlags: []string{imap.FlaggedFlag},
},
res: true,
},
{
flags: []string{imap.SeenFlag},
criteria: &imap.SearchCriteria{
WithFlags: []string{imap.DraftFlag},
WithoutFlags: []string{imap.FlaggedFlag},
},
res: false,
},
{
flags: []string{imap.SeenFlag, imap.FlaggedFlag},
criteria: &imap.SearchCriteria{
WithFlags: []string{imap.SeenFlag},
WithoutFlags: []string{imap.FlaggedFlag},
},
res: false,
},
{
flags: []string{imap.SeenFlag, imap.FlaggedFlag},
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{WithFlags: []string{imap.DraftFlag}},
{WithoutFlags: []string{imap.SeenFlag}},
}},
},
res: false,
},
{
flags: []string{imap.SeenFlag, imap.FlaggedFlag},
criteria: &imap.SearchCriteria{
Not: []*imap.SearchCriteria{
{WithFlags: []string{imap.SeenFlag}},
},
},
res: false,
},
{
seqNum: 42,
uid: 69,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Uid: new(imap.SeqSet),
Not: []*imap.SearchCriteria{{SeqNum: new(imap.SeqSet)}},
},
{
SeqNum: new(imap.SeqSet),
},
}},
},
res: false,
},
{
seqNum: 42,
uid: 69,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Uid: &imap.SeqSet{Set: []imap.Seq{{69, 69}}},
Not: []*imap.SearchCriteria{{SeqNum: new(imap.SeqSet)}},
},
{
SeqNum: new(imap.SeqSet),
},
}},
},
res: true,
},
{
seqNum: 42,
uid: 69,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Uid: &imap.SeqSet{Set: []imap.Seq{{69, 69}}},
Not: []*imap.SearchCriteria{{
SeqNum: &imap.SeqSet{Set: []imap.Seq{imap.Seq{42, 42}}},
}},
},
{
SeqNum: new(imap.SeqSet),
},
}},
},
res: false,
},
{
seqNum: 42,
uid: 69,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Uid: &imap.SeqSet{Set: []imap.Seq{{69, 69}}},
Not: []*imap.SearchCriteria{{
SeqNum: &imap.SeqSet{Set: []imap.Seq{{42, 42}}},
}},
},
{
SeqNum: &imap.SeqSet{Set: []imap.Seq{{42, 42}}},
},
}},
},
res: true,
},
{
date: testInternalDate,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Since: testInternalDate.Add(48 * time.Hour),
Not: []*imap.SearchCriteria{{
Since: testInternalDate.Add(48 * time.Hour),
}},
},
{
Before: testInternalDate.Add(-48 * time.Hour),
},
}},
},
res: false,
},
{
date: testInternalDate,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Since: testInternalDate.Add(-48 * time.Hour),
Not: []*imap.SearchCriteria{{
Since: testInternalDate.Add(48 * time.Hour),
}},
},
{
Before: testInternalDate.Add(-48 * time.Hour),
},
}},
},
res: true,
},
{
date: testInternalDate,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Since: testInternalDate.Add(-48 * time.Hour),
Not: []*imap.SearchCriteria{{
Since: testInternalDate.Add(-48 * time.Hour),
}},
},
{
Before: testInternalDate.Add(-48 * time.Hour),
},
}},
},
res: false,
},
{
date: testInternalDate,
criteria: &imap.SearchCriteria{
Or: [][2]*imap.SearchCriteria{{
{
Since: testInternalDate.Add(-48 * time.Hour),
Not: []*imap.SearchCriteria{{
Since: testInternalDate.Add(-48 * time.Hour),
}},
},
{
Before: testInternalDate.Add(48 * time.Hour),
},
}},
},
res: true,
},
}
func TestMatch(t *testing.T) {
for i, test := range matchTests {
e, err := message.Read(strings.NewReader(testMailString))
if err != nil {
t.Fatal("Expected no error while reading entity, got:", err)
}
ok, err := Match(e, test.seqNum, test.uid, test.date, test.flags, test.criteria)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if test.res && !ok {
t.Errorf("Expected #%v to match search criteria", i+1)
}
if !test.res && ok {
t.Errorf("Expected #%v not to match search criteria", i+1)
}
}
}
func TestMatchEncoded(t *testing.T) {
encodedTestMsg := `From: "fox.cpp" <foxcpp@foxcpp.dev>
To: "fox.cpp" <foxcpp@foxcpp.dev>
Subject: =?utf-8?B?0J/RgNC+0LLQtdGA0LrQsCE=?=
Date: Sun, 09 Jun 2019 00:06:43 +0300
MIME-Version: 1.0
Message-ID: <a2aeb99e-52dd-40d3-b99f-1fdaad77ed98@foxcpp.dev>
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: quoted-printable
=D0=AD=D1=82=D0=BE=D1=82 =D1=82=D0=B5=D0=BA=D1=81=D1=82 =D0=B4=D0=BE=D0=BB=
=D0=B6=D0=B5=D0=BD =D0=B1=D1=8B=D1=82=D1=8C =D0=B7=D0=B0=D0=BA=D0=BE=D0=B4=
=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD =D0=B2 base64 =D0=B8=D0=BB=D0=B8 quote=
d-encoding.`
e, err := message.Read(strings.NewReader(encodedTestMsg))
if err != nil {
t.Fatal("Expected no error while reading entity, got:", err)
}
// Check encoded header.
crit := imap.SearchCriteria{
Header: textproto.MIMEHeader{"Subject": []string{"Проверка!"}},
}
ok, err := Match(e, 0, 0, time.Now(), []string{}, &crit)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if !ok {
t.Error("Expected match for encoded header")
}
// Encoded body.
crit = imap.SearchCriteria{
Body: []string{"или"},
}
ok, err = Match(e, 0, 0, time.Now(), []string{}, &crit)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if !ok {
t.Error("Expected match for encoded body")
}
}
func TestMatchIssue298Regression(t *testing.T) {
raw1 := "Subject: 1\r\n\r\n1"
raw2 := "Subject: 2\r\n\r\n22"
raw3 := "Subject: 3\r\n\r\n333"
e1, err := message.Read(strings.NewReader(raw1))
if err != nil {
t.Fatal("Expected no error while reading entity, got:", err)
}
e2, err := message.Read(strings.NewReader(raw2))
if err != nil {
t.Fatal("Expected no error while reading entity, got:", err)
}
e3, err := message.Read(strings.NewReader(raw3))
if err != nil {
t.Fatal("Expected no error while reading entity, got:", err)
}
// Search for body size > 15 ("LARGER 15"), which should match messages #2 and #3
criteria := &imap.SearchCriteria{
Larger: 15,
}
ok1, err := Match(e1, 1, 101, time.Now(), nil, criteria)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if ok1 {
t.Errorf("Expected message #1 to not match search criteria")
}
ok2, err := Match(e2, 2, 102, time.Now(), nil, criteria)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if !ok2 {
t.Errorf("Expected message #2 to match search criteria")
}
ok3, err := Match(e3, 3, 103, time.Now(), nil, criteria)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if !ok3 {
t.Errorf("Expected message #3 to match search criteria")
}
// Search for body size < 17 ("SMALLER 17"), which should match messages #1 and #2
criteria = &imap.SearchCriteria{
Smaller: 17,
}
ok1, err = Match(e1, 1, 101, time.Now(), nil, criteria)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if !ok1 {
t.Errorf("Expected message #1 to match search criteria")
}
ok2, err = Match(e2, 2, 102, time.Now(), nil, criteria)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if !ok2 {
t.Errorf("Expected message #2 to match search criteria")
}
ok3, err = Match(e3, 3, 103, time.Now(), nil, criteria)
if err != nil {
t.Fatal("Expected no error while matching entity, got:", err)
}
if ok3 {
t.Errorf("Expected message #3 to not match search criteria")
}
}