1 # Copyright (C) 2019 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 # edit frontend behavior test (t/replace.t for backend)
7 use PublicInbox::TestCommon;
9 require PublicInbox::Inbox;
10 require PublicInbox::InboxWritable;
11 require PublicInbox::Config;
12 use PublicInbox::MID qw(mid_clean);
14 my @mods = qw(DBI DBD::SQLite);
15 foreach my $mod (@mods) {
17 plan skip_all => "missing $mod for $0" if $@;
20 my ($tmpdir, $for_destroy) = tmpdir();
21 my $inboxdir = "$tmpdir/v2";
22 my $ibx = PublicInbox::Inbox->new({
23 inboxdir => $inboxdir,
24 name => 'test-v2edit',
26 -primary_address => 'test@example.com',
27 indexlevel => 'basic',
29 $ibx = PublicInbox::InboxWritable->new($ibx, {nproc=>1});
30 my $cfgfile = "$tmpdir/config";
31 local $ENV{PI_CONFIG} = $cfgfile;
32 my $file = 't/data/0001.patch';
33 open my $fh, '<', $file or die "open: $!";
34 my $raw = do { local $/; <$fh> };
35 my $im = $ibx->importer(0);
36 my $mime = PublicInbox::MIME->new($raw);
37 my $mid = mid_clean($mime->header('Message-Id'));
38 ok($im->add($mime), 'add message to be edited');
40 my ($in, $out, $err, $cmd, $cur, $t);
41 my $git = PublicInbox::Git->new("$ibx->{inboxdir}/git/0.git");
42 my $opt = { 0 => \$in, 1 => \$out, 2 => \$err };
45 $in = $out = $err = '';
46 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/boolean prefix/bool pfx/'";
47 $cmd = [ '-edit', "-F$file", $inboxdir ];
48 ok(run_script($cmd, undef, $opt), "$t edit OK");
49 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
50 like($cur->header('Subject'), qr/bool pfx/, "$t message edited");
51 like($out, qr/[a-f0-9]{40}/, "$t shows commit on success");
54 $t = '-m MESSAGE_ID'; {
55 $in = $out = $err = '';
56 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/bool pfx/boolean prefix/'";
57 $cmd = [ '-edit', "-m$mid", $inboxdir ];
58 ok(run_script($cmd, undef, $opt), "$t edit OK");
59 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
60 like($cur->header('Subject'), qr/boolean prefix/, "$t message edited");
61 like($out, qr/[a-f0-9]{40}/, "$t shows commit on success");
64 $t = 'no-op -m MESSAGE_ID'; {
65 $in = $out = $err = '';
66 my $before = $git->qx(qw(rev-parse HEAD));
67 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/bool pfx/boolean prefix/'";
68 $cmd = [ '-edit', "-m$mid", $inboxdir ];
69 ok(run_script($cmd, undef, $opt), "$t succeeds");
71 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
72 is_deeply($cur, $prev, "$t makes no change");
73 like($cur->header('Subject'), qr/boolean prefix/,
74 "$t does not change message");
75 like($out, qr/NONE/, 'noop shows NONE');
76 my $after = $git->qx(qw(rev-parse HEAD));
77 is($after, $before, 'git head unchanged');
80 $t = 'no-op -m MESSAGE_ID w/Status: header'; { # because mutt does it
81 $in = $out = $err = '';
82 my $before = $git->qx(qw(rev-parse HEAD));
83 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/^Subject:.*/Status: RO\\n\$&/'";
84 $cmd = [ '-edit', "-m$mid", $inboxdir ];
85 ok(run_script($cmd, undef, $opt), "$t succeeds");
87 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
88 is_deeply($cur, $prev, "$t makes no change");
89 like($cur->header('Subject'), qr/boolean prefix/,
90 "$t does not change message");
91 is($cur->header('Status'), undef, 'Status header not added');
92 like($out, qr/NONE/, 'noop shows NONE');
93 my $after = $git->qx(qw(rev-parse HEAD));
94 is($after, $before, 'git head unchanged');
97 $t = '-m MESSAGE_ID can change Received: headers'; {
98 $in = $out = $err = '';
99 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/^Subject:.*/Received: x\\n\$&/'";
100 $cmd = [ '-edit', "-m$mid", $inboxdir ];
101 ok(run_script($cmd, undef, $opt), "$t succeeds");
102 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
103 like($cur->header('Subject'), qr/boolean prefix/,
104 "$t does not change Subject");
105 is($cur->header('Received'), 'x', 'added Received header');
109 $in = $out = $err = '';
110 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/boolean/FAIL/'";
111 $cmd = [ '-edit', "-m$mid-miss", $inboxdir ];
112 ok(!run_script($cmd, undef, $opt), "$t fails on invalid MID");
113 like($err, qr/No message found/, "$t shows error");
116 $t = 'non-interactive editor failure'; {
117 $in = $out = $err = '';
118 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 'END { exit 1 }'";
119 $cmd = [ '-edit', "-m$mid", $inboxdir ];
120 ok(!run_script($cmd, undef, $opt), "$t detected");
121 like($err, qr/END \{ exit 1 \}' failed:/, "$t shows error");
124 $t = 'mailEditor set in config'; {
125 $in = $out = $err = '';
126 my $rc = system(qw(git config), "--file=$cfgfile",
127 'publicinbox.maileditor',
128 "$^X -i -p -e 's/boolean prefix/bool pfx/'");
129 is($rc, 0, 'set publicinbox.mailEditor');
130 local $ENV{MAIL_EDITOR};
131 delete $ENV{MAIL_EDITOR};
132 local $ENV{GIT_EDITOR} = 'echo should not run';
133 $cmd = [ '-edit', "-m$mid", $inboxdir ];
134 ok(run_script($cmd, undef, $opt), "$t edited message");
135 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
136 like($cur->header('Subject'), qr/bool pfx/, "$t message edited");
137 unlike($out, qr/should not run/, 'did not run GIT_EDITOR');
140 $t = '--raw and mbox escaping'; {
141 $in = $out = $err = '';
142 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/^\$/\\nFrom not mbox\\n/'";
143 $cmd = [ '-edit', "-m$mid", '--raw', $inboxdir ];
144 ok(run_script($cmd, undef, $opt), "$t succeeds");
145 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
146 like($cur->body, qr/^From not mbox/sm, 'put "From " line into body');
148 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/^>From not/\$& an/'";
149 $cmd = [ '-edit', "-m$mid", $inboxdir ];
150 ok(run_script($cmd, undef, $opt), "$t succeeds with mbox escaping");
151 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
152 like($cur->body, qr/^From not an mbox/sm,
153 'changed "From " line unescaped');
155 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/^From not an mbox\\n//s'";
156 $cmd = [ '-edit', "-m$mid", '--raw', $inboxdir ];
157 ok(run_script($cmd, undef, $opt), "$t succeeds again");
158 $cur = PublicInbox::MIME->new($ibx->msg_by_mid($mid));
159 unlike($cur->body, qr/^From not an mbox/sm, "$t restored body");
162 $t = 'reuse Message-ID'; {
164 local $SIG{__WARN__} = sub { push @warn, @_ };
165 ok($im->add($mime), "$t and re-add");
167 like($warn[0], qr/reused for mismatched content/, "$t got warning");
170 $t = 'edit ambiguous Message-ID with -m'; {
171 $in = $out = $err = '';
172 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/bool pfx/boolean prefix/'";
173 $cmd = [ '-edit', "-m$mid", $inboxdir ];
174 ok(!run_script($cmd, undef, $opt), "$t fails w/o --force");
175 like($err, qr/Multiple messages with different content found matching/,
177 like($err, qr/GIT_DIR=.*git show/is, "$t shows git commands");
180 $t .= ' and --force'; {
181 $in = $out = $err = '';
182 local $ENV{MAIL_EDITOR} = "$^X -i -p -e 's/^Subject:.*/Subject:x/i'";
183 $cmd = [ '-edit', "-m$mid", '--force', $inboxdir ];
184 ok(run_script($cmd, undef, $opt), "$t succeeds");
185 like($err, qr/Will edit all of them/, "$t notes all will be edited");
186 my @dump = $git->qx(qw(cat-file --batch --batch-all-objects));
188 is_deeply([grep(/^Subject:/i, @dump)], [qw(Subject:x Subject:x)],
189 "$t edited both messages");