Skip to content

Commit 5db2ba0

Browse files
wesmclaude
andcommitted
Merge main; fix captureStdout conflict; fall back to EXPUNGE without UIDPLUS
Merge origin/main to pick up search_test.go. Remove duplicate captureStdout from sync_test.go and adapt callers to the deferred- capture signature from search_test.go. DeleteMessage now checks UIDPLUS capability before using UID EXPUNGE and falls back to plain EXPUNGE on servers that lack the extension. TrashMessage already had a fallback via go-imap/v2's Move() method. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1199e8e commit 5db2ba0

File tree

2 files changed

+19
-35
lines changed

2 files changed

+19
-35
lines changed

cmd/msgvault/cmd/sync_test.go

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package cmd
22

33
import (
4-
"io"
54
"log/slog"
65
"os"
76
"path/filepath"
@@ -25,25 +24,6 @@ const fakeClientSecrets = `{
2524
}
2625
}`
2726

28-
// captureStdout runs fn while capturing os.Stdout output.
29-
func captureStdout(t *testing.T, fn func()) string {
30-
t.Helper()
31-
r, w, err := os.Pipe()
32-
if err != nil {
33-
t.Fatalf("create pipe: %v", err)
34-
}
35-
orig := os.Stdout
36-
os.Stdout = w
37-
38-
fn()
39-
40-
_ = w.Close()
41-
os.Stdout = orig
42-
out, _ := io.ReadAll(r)
43-
_ = r.Close()
44-
return string(out)
45-
}
46-
4727
// TestSyncCmd_DuplicateIdentifierRoutesCorrectly verifies that when
4828
// Gmail and IMAP sources share the same identifier, the single-arg
4929
// sync path resolves both and routes each to the correct backend.
@@ -123,10 +103,9 @@ func TestSyncCmd_DuplicateIdentifierRoutesCorrectly(t *testing.T) {
123103

124104
// Capture stdout: the sync command prints per-source errors
125105
// to stdout while the returned error is just the count.
126-
var execErr error
127-
output := captureStdout(t, func() {
128-
execErr = root.Execute()
129-
})
106+
getOutput := captureStdout(t)
107+
execErr := root.Execute()
108+
output := getOutput()
130109

131110
if execErr == nil {
132111
t.Fatal("expected error (no credentials/token)")
@@ -349,10 +328,9 @@ func TestSyncFullCmd_OAuthSkipDoesNotBlockIMAP(t *testing.T) {
349328
root.SetArgs([]string{"sync-full"})
350329

351330
// Capture stdout to check skip messages.
352-
var execErr error
353-
output := captureStdout(t, func() {
354-
execErr = root.Execute()
355-
})
331+
getOutput := captureStdout(t)
332+
execErr := root.Execute()
333+
output := getOutput()
356334

357335
// IMAP source should be attempted (and fail due to missing
358336
// config), but the command should NOT abort entirely because
@@ -461,10 +439,9 @@ func TestSyncCmd_BrokenOAuthDoesNotBlockIMAP(t *testing.T) {
461439
root.AddCommand(testCmd)
462440
root.SetArgs([]string{tc.name})
463441

464-
var execErr error
465-
output := captureStdout(t, func() {
466-
execErr = root.Execute()
467-
})
442+
getOutput := captureStdout(t)
443+
execErr := root.Execute()
444+
output := getOutput()
468445

469446
if execErr == nil {
470447
t.Fatal("expected error")

internal/imap/client.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,8 @@ func (c *Client) TrashMessage(ctx context.Context, messageID string) error {
849849
})
850850
}
851851

852-
// DeleteMessage permanently deletes a message using UID STORE \Deleted + UID EXPUNGE.
852+
// DeleteMessage permanently deletes a message using UID STORE \Deleted
853+
// followed by UID EXPUNGE (UIDPLUS) or plain EXPUNGE as a fallback.
853854
func (c *Client) DeleteMessage(ctx context.Context, messageID string) error {
854855
mailbox, uid, err := parseCompositeID(messageID)
855856
if err != nil {
@@ -868,8 +869,14 @@ func (c *Client) DeleteMessage(ctx context.Context, messageID string) error {
868869
}, nil).Close(); err != nil {
869870
return fmt.Errorf("UID STORE \\Deleted: %w", err)
870871
}
871-
if err := conn.UIDExpunge(uidSet).Close(); err != nil {
872-
return fmt.Errorf("UID EXPUNGE: %w", err)
872+
if conn.Caps().Has(imap.CapUIDPlus) {
873+
if err := conn.UIDExpunge(uidSet).Close(); err != nil {
874+
return fmt.Errorf("UID EXPUNGE: %w", err)
875+
}
876+
} else {
877+
if err := conn.Expunge().Close(); err != nil {
878+
return fmt.Errorf("EXPUNGE: %w", err)
879+
}
873880
}
874881
return nil
875882
})

0 commit comments

Comments
 (0)