Skip to content
Merged
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
4 changes: 4 additions & 0 deletions internal/state/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (

// listSuperiors returns all names superior to the given name, if hierarchies are indicated with the given delimiter.
func listSuperiors(name, delimiter string) []string {
if delimiter == "" {
return nil
}

split := strings.Split(name, delimiter)
if len(split) == 0 {
return nil
Expand Down
66 changes: 48 additions & 18 deletions internal/state/paths_test.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,71 @@
package state

import (
"fmt"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

func TestListSuperiorNames(t *testing.T) {
tests := []struct {
for name, data := range map[string]struct {
name, delimiter string

want []string
want []string
}{
{
"no parents": {
name: "this",
delimiter: "/",
want: []string{},
want: nil,
},
{
"has parents": {
name: "this/is/a/test",
delimiter: "/",
want: []string{"this", "this/is", "this/is/a"},
},
{
name: "/this/is/a/test",
"wrong delimiter": {
name: "this.is.a.test",
delimiter: "/",
want: []string{"/this", "/this/is", "/this/is/a"},
want: nil,
},
"nil delimiter": {
name: "/nil/delimiter.used",
delimiter: "",
want: nil,
},
} {
t.Run(name, func(t *testing.T) {
require.Equal(t, data.want, listSuperiors(data.name, data.delimiter))
})
}
}

for _, tt := range tests {
tt := tt

t.Run(fmt.Sprintf("%#v", tt), func(t *testing.T) {
if res := listSuperiors(tt.name, tt.delimiter); strings.Join(res, "") != strings.Join(tt.want, "") {
t.Errorf("expected result of %v but got %v", tt.want, res)
}
func TestListInferiorNames(t *testing.T) {
for name, data := range map[string]struct {
parent string
delimiter string
names []string
want []string
}{
"no children": {
parent: "this",
delimiter: "/",
names: []string{"one", "two", "three", "this", "a/b", "c/d"},
want: []string{},
},
"has children": {
parent: "this",
delimiter: "/",
names: []string{"a/b", "this/one", "this", "this/two", "this/one/two/three", "c/d"},
want: []string{"this/two", "this/one/two/three", "this/one"},
},
"nil delimiter": {
parent: "this",
delimiter: "",
names: []string{"a/b", "this/one", "this", "this/two", "this/one/two/three", "c/d"},
want: []string{},
},
} {
t.Run(name, func(t *testing.T) {
require.Equal(t, data.want, listInferiors(data.parent, data.delimiter, data.names))
})
}
}
9 changes: 9 additions & 0 deletions internal/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package state

import (
"context"
"errors"
"fmt"
"strings"
"sync/atomic"
Expand Down Expand Up @@ -148,6 +149,14 @@ func (state *State) Examine(ctx context.Context, name string, fn func(*Mailbox)
}

func (state *State) Create(ctx context.Context, name string) error {
if strings.HasPrefix(name, state.delimiter) {
return errors.New("invalid mailbox name: begins with hierarchy separator")
}

if strings.Contains(name, state.delimiter+state.delimiter) {
return errors.New("invalid mailbox name: has adjacent hierarchy separators")
}

mboxesToCreate, err := db.ReadResult(ctx, state.db(), func(ctx context.Context, client *ent.Client) ([]string, error) {
var mboxesToCreate []string
// If the mailbox name is suffixed with the server's hierarchy separator, remove the separator and still create
Expand Down
24 changes: 24 additions & 0 deletions tests/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ func TestCreateWithDifferentHierarchySeparator(t *testing.T) {
})
}

func TestCreateWithNilHierarchySeparator(t *testing.T) {
runOneToOneTestClientWithAuth(t, defaultServerOptions(t, withDelimiter("")), func(client *client.Client, _ *testSession) {
matchMailboxNamesClient(t, client, "", "*", []string{"INBOX"})
require.NoError(t, client.Create("Folder/Bar"))
matchMailboxNamesClient(t, client, "", "*", []string{"INBOX", "Folder/Bar"})
require.NoError(t, client.Create("Folder"))
matchMailboxNamesClient(t, client, "", "*", []string{"INBOX", "Folder", "Folder/Bar"})
})
}

func TestCreatePreviousLevelHierarchyIfNonExisting(t *testing.T) {
runOneToOneTestClientWithAuth(t, defaultServerOptions(t), func(client *client.Client, _ *testSession) {
require.NoError(t, client.Create("Folder/Bar/ZZ"))
Expand Down Expand Up @@ -96,3 +106,17 @@ func TestEnsureNewMailboxWithDeletedNameHasGreaterId(t *testing.T) {

})
}

func TestCreateAdjacentSeparator(t *testing.T) {
runOneToOneTestWithAuth(t, defaultServerOptions(t), func(c *testConnection, _ *testSession) {
c.C(`A001 create foo//bar`)
c.Sx(`^A001 NO .*adjacent hierarchy separators\r\n$`)
})
}

func TestCreateBeginsWithSeparator(t *testing.T) {
runOneToOneTestWithAuth(t, defaultServerOptions(t), func(c *testConnection, _ *testSession) {
c.C(`A001 create /foo`)
c.Sx(`^A001 NO .*begins with hierarchy separator\r\n$`)
})
}
14 changes: 14 additions & 0 deletions tests/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,17 @@ func TestListSpecialUseAttributes(t *testing.T) {
c.OK(`a`)
})
}

func TestListNilDelimiter(t *testing.T) {
runOneToOneTestWithAuth(t, defaultServerOptions(t, withDelimiter("")), func(c *testConnection, s *testSession) {
s.mailboxCreated("user", []string{"INBOX"})
s.mailboxCreated("user", []string{"Folders/Custom"})

c.C(`a list "" "*"`)
c.S(
`* LIST (\Unmarked) NIL "INBOX"`,
`* LIST (\Unmarked) NIL "Folders/Custom"`,
)
c.OK(`a`)
})
}
14 changes: 14 additions & 0 deletions tests/select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,17 @@ func TestSelectUTF7(t *testing.T) {
c.C(`A005 LIST "" "*"`).Sxe(`&ZeVnLIqe-`).OK(`A005`)
})
}

func TestSelectWithNilDelimiter(t *testing.T) {
runOneToOneTestWithAuth(t, defaultServerOptions(t, withDelimiter("")), func(c *testConnection, _ *testSession) {
// Test we can create a mailbox with a UTF-7 name.
c.C("a CREATE A").OK("a")
c.C("a CREATE A/B").OK("a")
c.C("a CREATE A/B/C").OK("a")

// Test we can select the mailbox.
c.C("A001 SELECT A").OK("A001")
c.C("A002 SELECT A/B").OK("A002")
c.C("A003 SELECT A/B/C").OK("A003")
})
}