273 lines
12 KiB
Go
273 lines
12 KiB
Go
package helpers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func executeMailEdge(t *testing.T, caller *scriptedToolCaller, args ...string) error {
|
|
t.Helper()
|
|
previous, previousArgs := deps, os.Args
|
|
os.Args = append([]string{"dws", "mail"}, os.Args[1:]...)
|
|
InitDeps(caller)
|
|
deps.Out.w = io.Discard
|
|
deps.Out.errW = io.Discard
|
|
defer func() {
|
|
deps = previous
|
|
os.Args = previousArgs
|
|
}()
|
|
|
|
cmd := newMailCommand()
|
|
cmd.SilenceErrors = true
|
|
cmd.SilenceUsage = true
|
|
cmd.SetOut(io.Discard)
|
|
cmd.SetErr(io.Discard)
|
|
cmd.SetArgs(args)
|
|
return cmd.Execute()
|
|
}
|
|
|
|
func TestCrossPlatformCoverageMailSearchSendAndThreadEdges(t *testing.T) {
|
|
valid := [][]string{
|
|
{"message", "search", "--email", "a@example.com", "--query", "subject:x"},
|
|
{"message", "list", "--email", "a@example.com"},
|
|
{"message", "send", "--from", "a@example.com", "--to", "b@example.com", "--subject", "s", "--content", "body", "--cc", "c@example.com"},
|
|
}
|
|
for _, args := range valid {
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, args...); err != nil {
|
|
t.Fatalf("execute %v: %v", args, err)
|
|
}
|
|
}
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, "message", "send", "--from", "a@example.com", "--to", "b@example.com", "--subject", "s"); err == nil {
|
|
t.Fatal("missing content returned nil")
|
|
}
|
|
|
|
many := strings.TrimSuffix(strings.Repeat("id,", 101), ",")
|
|
for _, args := range [][]string{
|
|
{"thread", "batch-update", "--email", "a@example.com", "--ids", many, "--action", "markRead"},
|
|
{"thread", "trash", "--email", "a@example.com", "--id", "1"},
|
|
{"thread", "batch-trash", "--email", "a@example.com", "--ids", "1"},
|
|
{"thread", "batch-trash", "--email", "a@example.com", "--ids", many, "--yes"},
|
|
{"message", "batch-update", "--email", "a@example.com", "--ids", "1", "--action", "addTags"},
|
|
{"message", "batch-get", "--email", "a@example.com", "--ids", ","},
|
|
{"message", "batch-get", "--email", "a@example.com", "--ids", strings.TrimSuffix(strings.Repeat("id,", 21), ",")},
|
|
} {
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, args...); err == nil {
|
|
t.Fatalf("expected error for %v", args)
|
|
}
|
|
}
|
|
if err := executeMailEdge(t, &scriptedToolCaller{dry: true}, "message", "batch-get", "--email", "a@example.com", "--ids", "1,2"); err != nil {
|
|
t.Fatalf("dry batch get: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageMailReplyForwardAndDraftEdges(t *testing.T) {
|
|
draftSteps := func() []scriptedToolStep {
|
|
return []scriptedToolStep{{text: `{}`}, {text: `{"result":{"message":{"id":"message"}}}`}, {text: `{}`}}
|
|
}
|
|
for _, args := range [][]string{
|
|
{"message", "reply", "--from", "a@example.com", "--id", "1", "--to", "b@example.com", "--subject", "s", "--content", "body"},
|
|
{"message", "reply-all", "--from", "a@example.com", "--id", "1", "--to", "b@example.com", "--subject", "s", "--content", "body"},
|
|
{"message", "forward", "--from", "a@example.com", "--id", "1", "--to", "b@example.com", "--subject", "s", "--content", "body"},
|
|
} {
|
|
if err := executeMailEdge(t, &scriptedToolCaller{steps: draftSteps()}, args...); err != nil {
|
|
t.Fatalf("execute %v: %v", args, err)
|
|
}
|
|
}
|
|
|
|
attachment := filepath.Join(t.TempDir(), "attachment.txt")
|
|
if err := os.WriteFile(attachment, []byte("data"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
originalPut := mailPutAttachment
|
|
mailPutAttachment = func(context.Context, string, string, string, int64) error { return nil }
|
|
defer func() { mailPutAttachment = originalPut }()
|
|
upload := `{"uploadUrl":"https://upload.invalid"}`
|
|
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, "draft", "create", "--from", "a@example.com", "--subject", "s"); err != nil {
|
|
t.Fatalf("plain create draft: %v", err)
|
|
}
|
|
createSteps := []scriptedToolStep{{text: `{}`}, {text: `{"messageId":"message"}`}, {text: upload}, {text: `{}`}}
|
|
if err := executeMailEdge(t, &scriptedToolCaller{steps: createSteps}, "draft", "create", "--from", "a@example.com", "--subject", "s", "--attachment", attachment); err != nil {
|
|
t.Fatalf("attachment create draft: %v", err)
|
|
}
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, "draft", "update", "--from", "a@example.com", "--id", "message"); err != nil {
|
|
t.Fatalf("plain update draft: %v", err)
|
|
}
|
|
updateSteps := []scriptedToolStep{{text: `{}`}, {text: `{}`}, {text: upload}, {text: `{}`}}
|
|
if err := executeMailEdge(t, &scriptedToolCaller{steps: updateSteps}, "draft", "update", "--from", "a@example.com", "--id", "message", "--attachment", attachment); err != nil {
|
|
t.Fatalf("attachment update draft: %v", err)
|
|
}
|
|
|
|
sendSteps := []scriptedToolStep{{text: `{}`}, {text: `{"messageId":"message"}`}, {text: upload}, {text: `{}`}}
|
|
if err := executeMailEdge(t, &scriptedToolCaller{steps: sendSteps}, "message", "send", "--from", "a@example.com", "--to", "b@example.com", "--subject", "s", "--content", "body", "--attachment", attachment); err != nil {
|
|
t.Fatalf("attachment send: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageMailRulesRecallAndTemplateEdges(t *testing.T) {
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, "sent-message", "recall", "--email", "a@example.com", "--id", "1", "--subject", "s"); err == nil {
|
|
t.Fatal("recall without confirmation returned nil")
|
|
}
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, "template", "create", "--email", "a@example.com", "--subject", "s", "--name", "n"); err == nil {
|
|
t.Fatal("template missing content returned nil")
|
|
}
|
|
invalidParameter := `{"success":false,"message":"Invalid parameter"}`
|
|
if err := executeMailEdge(t, &scriptedToolCaller{steps: []scriptedToolStep{{text: invalidParameter}}}, "template", "update", "--email", "a@example.com", "--id", "1"); err == nil {
|
|
t.Fatal("template invalid parameter returned nil")
|
|
}
|
|
|
|
condition := `[{"object":"subject","or":[{"and":[{"operation":"include"}]}]}]`
|
|
actions := `[{"action":"ActFlagMail2","parameters":["asread"]}]`
|
|
for _, args := range [][]string{
|
|
{"rule", "create", "--email", "a@example.com", "--name", "n", "--enabled", "true", "--conditions", condition, "--actions", actions},
|
|
{"rule", "update", "--email", "a@example.com", "--id", "1", "--name", "n", "--enabled", "false", "--conditions", condition, "--actions", actions},
|
|
} {
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, args...); err != nil {
|
|
t.Fatalf("rule %v: %v", args, err)
|
|
}
|
|
}
|
|
for _, args := range [][]string{
|
|
{"rule", "create", "--email", "a@example.com", "--name", "n", "--enabled", "true", "--conditions", `bad`, "--actions", actions},
|
|
{"rule", "update", "--email", "a@example.com", "--id", "1", "--name", "n", "--enabled", "true", "--conditions", `bad`, "--actions", actions},
|
|
} {
|
|
if err := executeMailEdge(t, &scriptedToolCaller{}, args...); err == nil {
|
|
t.Fatalf("bad rule %v returned nil", args)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageMailDraftInlineFailureEdges(t *testing.T) {
|
|
previousArgs := os.Args
|
|
os.Args = []string{"dws", "mail"}
|
|
defer func() { os.Args = previousArgs }()
|
|
inline := filepath.Join(t.TempDir(), "image.png")
|
|
if err := os.WriteFile(inline, []byte("image"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mailboxes := `{"emailAccounts":[{"email":"a@example.com","type":"PERSONAL"}]}`
|
|
draft := `{"messageId":"message"}`
|
|
upload := `{"uploadUrl":"https://upload.invalid"}`
|
|
boom := errors.New("boom")
|
|
|
|
for _, steps := range [][]scriptedToolStep{
|
|
{{text: mailboxes}, {err: boom}},
|
|
{{text: mailboxes}, {text: draft}, {err: boom}},
|
|
{{text: mailboxes}, {text: draft}, {text: `{}`}},
|
|
} {
|
|
caller := &scriptedToolCaller{steps: steps}
|
|
previous := deps
|
|
InitDeps(caller)
|
|
deps.Out.w, deps.Out.errW = io.Discard, io.Discard
|
|
_, _ = runMailDraftWithAttachment("create_draft", map[string]any{"from": "a@example.com"}, "", "body", nil, []string{inline})
|
|
deps = previous
|
|
}
|
|
|
|
previousPut := mailPutAttachment
|
|
mailPutAttachment = func(context.Context, string, string, string, int64) error { return boom }
|
|
defer func() { mailPutAttachment = previousPut }()
|
|
previous := deps
|
|
InitDeps(&scriptedToolCaller{steps: []scriptedToolStep{{text: mailboxes}, {text: draft}, {text: upload}}})
|
|
deps.Out.w, deps.Out.errW = io.Discard, io.Discard
|
|
_, _ = runMailDraftWithAttachment("create_draft", map[string]any{"from": "a@example.com"}, "", "body", nil, []string{inline})
|
|
deps = previous
|
|
|
|
previous = deps
|
|
InitDeps(&scriptedToolCaller{steps: []scriptedToolStep{{text: mailboxes}, {err: boom}}})
|
|
deps.Out.w, deps.Out.errW = io.Discard, io.Discard
|
|
_, _ = runMailDraftWithAttachment("ignored", map[string]any{"from": "a@example.com"}, "message", "body", nil, nil)
|
|
deps = previous
|
|
}
|
|
|
|
func TestCrossPlatformCoverageMailDownloadWorkflowEdges(t *testing.T) {
|
|
previousArgs := os.Args
|
|
os.Args = []string{"dws", "mail"}
|
|
defer func() { os.Args = previousArgs }()
|
|
makeCmd := func() *cobra.Command {
|
|
cmd := &cobra.Command{Use: "download"}
|
|
cmd.Flags().String("email", "a@example.com", "")
|
|
cmd.Flags().String("message-id", "message", "")
|
|
cmd.Flags().String("attachment-id", "attachment", "")
|
|
cmd.Flags().String("name", "file.txt", "")
|
|
cmd.Flags().String("output", t.TempDir(), "")
|
|
return cmd
|
|
}
|
|
boom := errors.New("boom")
|
|
cases := [][]scriptedToolStep{
|
|
{{err: boom}},
|
|
{{text: `{}`}, {err: boom}},
|
|
{{text: `{}`}, {text: `{}`}},
|
|
}
|
|
for _, steps := range cases {
|
|
previous := deps
|
|
InitDeps(&scriptedToolCaller{steps: steps})
|
|
deps.Out.w, deps.Out.errW = io.Discard, io.Discard
|
|
_ = runMailAttachmentDownload(makeCmd())
|
|
deps = previous
|
|
}
|
|
|
|
previousGet := mailGetAttachment
|
|
defer func() { mailGetAttachment = previousGet }()
|
|
for _, getErr := range []error{boom, nil} {
|
|
mailGetAttachment = func(context.Context, string, string, string) error { return getErr }
|
|
previous := deps
|
|
InitDeps(&scriptedToolCaller{steps: []scriptedToolStep{{text: `{}`}, {text: `{"downloadUrl":"https://download.invalid"}`}}})
|
|
deps.Out.w, deps.Out.errW = io.Discard, io.Discard
|
|
_ = runMailAttachmentDownload(makeCmd())
|
|
deps = previous
|
|
}
|
|
}
|
|
|
|
type mailRoundTripFunc func(*http.Request) (*http.Response, error)
|
|
|
|
func (f mailRoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
|
|
|
|
type mailFailingReadCloser struct{}
|
|
|
|
func (mailFailingReadCloser) Read([]byte) (int, error) { return 0, errors.New("read") }
|
|
func (mailFailingReadCloser) Close() error { return nil }
|
|
|
|
func TestCrossPlatformCoverageMailHTTPAndLimitRemainingEdges(t *testing.T) {
|
|
file := filepath.Join(t.TempDir(), "file.txt")
|
|
if err := os.WriteFile(file, []byte("data"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := httpPutMailAttachment(context.Background(), "PERSONAL", "http://[::1", file, 4); err == nil {
|
|
t.Fatal("invalid upload URL returned nil")
|
|
}
|
|
if err := httpGetMailAttachment(context.Background(), "PERSONAL", "http://[::1", filepath.Join(t.TempDir(), "out")); err == nil {
|
|
t.Fatal("invalid download URL returned nil")
|
|
}
|
|
|
|
previousClient := mailHTTPClient
|
|
defer func() { mailHTTPClient = previousClient }()
|
|
mailHTTPClient = func() *http.Client {
|
|
return &http.Client{Transport: mailRoundTripFunc(func(*http.Request) (*http.Response, error) {
|
|
return nil, errors.New("round trip")
|
|
})}
|
|
}
|
|
if err := httpPutMailAttachment(context.Background(), "PERSONAL", "https://upload.invalid", file, 4); err == nil {
|
|
t.Fatal("upload transport error returned nil")
|
|
}
|
|
|
|
mailHTTPClient = func() *http.Client {
|
|
return &http.Client{Transport: mailRoundTripFunc(func(*http.Request) (*http.Response, error) {
|
|
return &http.Response{StatusCode: http.StatusOK, Body: mailFailingReadCloser{}}, nil
|
|
})}
|
|
}
|
|
if err := httpGetMailAttachment(context.Background(), "PERSONAL", "https://download.invalid", filepath.Join(t.TempDir(), "out")); err == nil {
|
|
t.Fatal("download copy error returned nil")
|
|
}
|
|
|
|
if _, err := validateMailboxThreadLimit(&cobra.Command{Use: "missing"}); err == nil {
|
|
t.Fatal("missing limit flag returned nil")
|
|
}
|
|
}
|