1
0
Fork 0
mirror of https://git.sr.ht/~rjarry/aerc synced 2025-02-22 23:23:57 +01:00
aerc/worker/notmuch/message_test.go
Jason Cox 1ce82f50d0 notmuch: add strategies for multi-file messages
A single notmuch message can represent multiple files. As a result,
file-based operations like move, copy, and delete can be ambiguous. Add
a new account config option, multi-file-strategy, to tell aerc how to
handle these ambiguous cases. Also add options to relevant commands to
set the multi-file strategy on a per-invocation basis.

If no multi-file strategy is set, refuse to take file-based actions on
multi-file messages. This default behavior is mostly the same as aerc's
previous behavior, but a bit stricter in some cases which previously
tried to be smart about multi-file operations (e.g., move and delete).

Applying multi-file strategies to cross-account copy and move operations
is not implemented. These operations will proceed as they have in the
past -- aerc will copy/move a single file. However, for cross-account
move operations, aerc will refuse to delete multiple files to prevent
data loss as not all of the files are added to the destination account.

See the changes to aerc-notmuch(5) for details on the currently
supported multi-file strategies.

Changelog-added: Tell aerc how to handle file-based operations
 on multi-file notmuch messages with the account config option
 `multi-file-strategy` and the `-m` flag to `:archive`, `:copy`,
 `:delete`, and `:move`.
Signed-off-by: Jason Cox <me@jasoncarloscox.com>
Tested-by: Maarten Aertsen <maarten@nlnetlabs.nl>
Acked-by: Robin Jarry <robin@jarry.cc>
2024-04-02 22:22:28 +02:00

264 lines
5.8 KiB
Go

//go:build notmuch
// +build notmuch
package notmuch
import (
"testing"
"git.sr.ht/~rjarry/aerc/worker/types"
"github.com/emersion/go-maildir"
)
func TestFilterForStrategy(t *testing.T) {
tests := []struct {
filenames []string
strategy types.MultiFileStrategy
curDir string
expectedAct []string
expectedDel []string
expectedErr bool
}{
// if there's only one file, always act on it
{
filenames: []string{"/h/j/m/A/cur/a.b.c:2,"},
strategy: types.Refuse,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
{
filenames: []string{"/h/j/m/A/cur/a.b.c:2,"},
strategy: types.ActAll,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
{
filenames: []string{"/h/j/m/A/cur/a.b.c:2,"},
strategy: types.ActOne,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
{
filenames: []string{"/h/j/m/A/cur/a.b.c:2,"},
strategy: types.ActOneDelRest,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
{
filenames: []string{"/h/j/m/A/cur/a.b.c:2,"},
strategy: types.ActDir,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
{
filenames: []string{"/h/j/m/A/cur/a.b.c:2,"},
strategy: types.ActDirDelRest,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
// follow strategy for multiple files
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.Refuse,
curDir: "/h/j/m/B",
expectedErr: true,
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActAll,
curDir: "/h/j/m/B",
expectedAct: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
expectedDel: []string{},
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActOne,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActOneDelRest,
curDir: "/h/j/m/B",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActDir,
curDir: "/h/j/m/B",
expectedAct: []string{
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
},
expectedDel: []string{},
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActDirDelRest,
curDir: "/h/j/m/B",
expectedAct: []string{
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
},
expectedDel: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/C/new/d.e.f",
},
},
// refuse to act on multiple files for ActDir and friends if
// no current dir is provided
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActDir,
curDir: "",
expectedErr: true,
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActDirDelRest,
curDir: "",
expectedErr: true,
},
// act on multiple files w/o current dir for other strategies
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActAll,
curDir: "",
expectedAct: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
expectedDel: []string{},
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActOne,
curDir: "",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{},
},
{
filenames: []string{
"/h/j/m/A/cur/a.b.c:2,",
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
strategy: types.ActOneDelRest,
curDir: "",
expectedAct: []string{"/h/j/m/A/cur/a.b.c:2,"},
expectedDel: []string{
"/h/j/m/B/new/b.c.d",
"/h/j/m/B/cur/c.d.e:2,S",
"/h/j/m/C/new/d.e.f",
},
},
}
for i, test := range tests {
act, del, err := filterForStrategy(test.filenames, test.strategy,
maildir.Dir(test.curDir))
if test.expectedErr && err == nil {
t.Errorf("[test %d] got nil, expected error", i)
}
if !test.expectedErr && err != nil {
t.Errorf("[test %d] got %v, expected nil", i, err)
}
if !arrEq(act, test.expectedAct) {
t.Errorf("[test %d] got %v, expected %v", i, act, test.expectedAct)
}
if !arrEq(del, test.expectedDel) {
t.Errorf("[test %d] got %v, expected %v", i, del, test.expectedDel)
}
}
}
func arrEq(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}