#!perl -w
# Copyright (C) all contributors
# License: AGPL-3.0+
use v5.12; use PublicInbox::TestCommon;
use PublicInbox::DS qw(now);
use PublicInbox::IO qw(write_file);
use autodie qw(open close truncate);
test_lei(sub {
ok(!lei(qw(import -F bogus), 't/plack-qp.eml'), 'fails with bogus format');
like($lei_err, qr/\bis `eml', not --in-format/, 'gave error message');
lei_ok(qw(q s:boolean), \'search miss before import');
unlike($lei_out, qr/boolean/i, 'no results, yet');
open my $fh, '<', 't/data/0001.patch';
lei_ok([qw(import -F eml -)], undef, { %$lei_opt, 0 => $fh },
\'import single file from stdin') or diag $lei_err;
close $fh;
lei_ok(qw(q s:boolean), \'search hit after import');
lei_ok(qw(q s:boolean -f mboxrd), \'blob accessible after import');
{
my $expect = [ eml_load('t/data/0001.patch') ];
require PublicInbox::MboxReader;
my @cmp;
open my $fh, '<', \$lei_out;
PublicInbox::MboxReader->mboxrd($fh, sub {
my ($eml) = @_;
$eml->header_set('Status');
push @cmp, $eml;
});
is_deeply(\@cmp, $expect, 'got expected message in mboxrd');
}
lei_ok(qw(import -F eml), 't/data/message_embed.eml',
\'import single file by path');
lei_ok(qw(q m:testmessage@example.com));
is($lei_out, "[null]\n", 'no results, yet');
my $oid = '9bf1002c49eb075df47247b74d69bcd555e23422';
my $eml = eml_load('t/utf8.eml');
my $in = 'From x@y Fri Oct 2 00:00:00 1993'."\n".$eml->as_string;
lei_ok([qw(import -F eml -)], undef, { %$lei_opt, 0 => \$in });
lei_ok(qw(q m:testmessage@example.com));
is(json_utf8->decode($lei_out)->[0]->{'blob'}, $oid,
'got expected OID w/o From');
my $eml_str = <<'';
From: a@b
Message-ID:
Status: RO
my $opt = { %$lei_opt, 0 => \$eml_str };
lei_ok([qw(import -F eml -)], undef, $opt,
\'import single file with keywords from stdin');
lei_ok(qw(q m:x@y));
my $res = json_utf8->decode($lei_out);
is($res->[1], undef, 'only one result');
is($res->[0]->{'m'}, 'x@y', 'got expected message');
is($res->[0]->{kw}, undef, 'Status ignored for eml');
lei_ok(qw(q -f mboxrd m:x@y));
unlike($lei_out, qr/^Status:/, 'no Status: in imported message');
lei_ok('blob', $res->[0]->{blob});
is($lei_out, "From: a\@b\nMessage-ID: \n", 'got blob back');
$eml->header_set('Message-ID', '');
$eml->header_set('Status', 'RO');
$in = 'From v@y Fri Oct 2 00:00:00 1993'."\n".$eml->as_string;
lei_ok([qw(import --no-kw -F mboxrd -)], undef, { %$lei_opt, 0 => \$in },
\'import single file with --no-kw from stdin');
lei(qw(q m:v@y));
$res = json_utf8->decode($lei_out);
is($res->[1], undef, 'only one result');
is($res->[0]->{'m'}, 'v@y', 'got expected message');
is($res->[0]->{kw}, undef, 'no keywords set');
$eml->header_set('Message-ID', '');
$in = 'From k@y Fri Oct 2 00:00:00 1993'."\n".$eml->as_string;
lei_ok([qw(import -F mboxrd /dev/fd/0)], undef, { %$lei_opt, 0 => \$in },
\'import single file with --kw (default) from stdin');
lei(qw(q m:k@y));
$res = json_utf8->decode($lei_out);
is($res->[1], undef, 'only one result');
is($res->[0]->{'m'}, 'k@y', 'got expected message');
is_deeply($res->[0]->{kw}, ['seen'], "`seen' keywords set");
# no From, Sender, or Message-ID
$eml_str = <<'EOM';
Subject: draft message with no sender
References:
Resent-Message-ID:
No use for a name
EOM
lei_ok([qw(import -F eml -)], undef, { %$lei_opt, 0 => \$eml_str });
lei_ok(['q', 's:draft message with no sender']);
my $draft_a = json_utf8->decode($lei_out);
ok(!exists $draft_a->[0]->{'m'}, 'no fake mid stored or exposed');
lei_ok([qw(tag -F eml - +kw:draft)], undef, { %$lei_opt, 0 => \$eml_str });
lei_ok(['q', 's:draft message with no sender']);
my $draft_b = json_utf8->decode($lei_out);
my $kw = delete $draft_b->[0]->{kw};
is_deeply($kw, ['draft'], 'draft kw set');
is_deeply($draft_a, $draft_b, 'fake Message-ID lookup') or
diag explain($draft_a, $draft_b);
lei_ok('blob', '--mail', $draft_b->[0]->{blob});
is($lei_out, $eml_str, 'draft retrieved by blob');
$eml_str = "Message-ID: \nSubject: label-this\n\n";
lei_ok([qw(import -F eml - +kw:seen +L:inbox)],
undef, { %$lei_opt, 0 => \$eml_str });
lei_ok(qw(q m:inbox@example.com));
$res = json_utf8->decode($lei_out);
is_deeply($res->[0]->{kw}, ['seen'], 'keyword set');
is_deeply($res->[0]->{L}, ['inbox'], 'label set');
# idempotent import can add label
lei_ok([qw(import -F eml - +L:boombox)],
undef, { %$lei_opt, 0 => \$eml_str });
lei_ok(qw(q m:inbox@example.com));
$res = json_utf8->decode($lei_out);
is_deeply($res->[0]->{kw}, ['seen'], 'keyword remains set');
is_deeply($res->[0]->{L}, [qw(boombox inbox)], 'new label added');
# idempotent import can add keyword
lei_ok([qw(import -F eml - +kw:answered)],
undef, { %$lei_opt, 0 => \$eml_str });
lei_ok(qw(q m:inbox@example.com));
$res = json_utf8->decode($lei_out);
is_deeply($res->[0]->{kw}, [qw(answered seen)], 'keyword added');
is_deeply($res->[0]->{L}, [qw(boombox inbox)], 'labels preserved');
# +kw:seen is not a location
open my $null, '<', '/dev/null';
ok(!lei([qw(import -F eml +kw:seen)], undef, { %$lei_opt, 0 => $null }),
'import fails w/ only kw arg');
like($lei_err, qr/\bLOCATION\.\.\. or --stdin must be set/s, 'error message');
lei_ok([qw(import -F eml +kw:flagged)], # no lone dash (`-')
undef, { %$lei_opt, 0 => \$eml_str },
'import succeeds with implicit --stdin');
lei_ok(qw(q m:inbox@example.com));
$res = json_utf8->decode($lei_out);
is_deeply($res->[0]->{kw}, [qw(answered flagged seen)], 'keyword added');
is_deeply($res->[0]->{L}, [qw(boombox inbox)], 'labels preserved');
lei_ok qw(import --commit-delay=1 +L:bin -F eml t/data/binary.patch);
lei_ok 'ls-label';
unlike($lei_out, qr/\bbin\b/, 'commit-delay delays label');
my $end = now + 10;
my $n = 1;
diag 'waiting for lei/store commit...';
do {
tick $n;
$n = 0.1;
} until (!lei('ls-label') || $lei_out =~ /\bbin\b/ || now > $end);
like($lei_out, qr/\bbin\b/, 'commit-delay eventually commits');
SKIP: {
my $strace = strace_inject(1); # skips if strace is old or non-Linux
my $tmpdir = tmpdir;
my $tr = "$tmpdir/tr";
my $cmd = [ $strace, '-q', "-o$tr", '-f',
"-P", File::Spec->rel2abs('t/plack-qp.eml'),
'-e', 'inject=readv,read:error=EIO'];
lei_ok qw(daemon-pid);
chomp(my $daemon_pid = $lei_out);
push @$cmd, '-p', $daemon_pid;
require PublicInbox::Spawn;
require PublicInbox::AutoReap;
my $pid = PublicInbox::Spawn::spawn($cmd, \%ENV);
my $ar = PublicInbox::AutoReap->new($pid);
tick; # wait for strace to attach
ok(!lei(qw(import -F eml t/plack-qp.eml)),
'-F eml import fails on pathname error injection');
my $IO = '[Ii](?:nput)?/[Oo](?:utput)?';
like($lei_err, qr!error reading t/plack-qp\.eml: .*?$IO error!,
'EIO noted in stderr');
open $fh, '<', 't/plack-qp.eml';
ok(!lei(qw(import -F eml -), undef, { %$lei_opt, 0 => $fh }),
'-F eml import fails on stdin error injection');
like($lei_err, qr!error reading .*?: .*?$IO error!,
'EIO noted in stderr');
}
{
local $ENV{PI_CONFIG} = "$ENV{HOME}/pi_config";
write_file '>', $ENV{PI_CONFIG}, <
List-Unsubscribe-Post: List-Unsubscribe=One-Click
Message-ID:
Subject: unsubscribe-1 example
From: u\@example.com
To: 2\@example.com
Date: Fri, 02 Oct 1993 00:00:00 +0000
EOM
lei_ok [qw(import -F eml +L:unsub)], undef, { %$lei_opt, 0 => \$in },
'import succeeds w/ List-Unsubscribe';
lei_ok qw(q L:unsub -f mboxrd);
like $lei_out, qr/some-UUID-here/,
'Unsubscribe header preserved despite PI_CONFIG dropping';
lei_ok qw(q L:unsub -o), "v2:$ENV{HOME}/v2-1";
lei_ok qw(q s:unsubscribe -f mboxrd --only), "$ENV{HOME}/v2-1";
unlike $lei_out, qr/some-UUID-here/,
'Unsubscribe header dropped w/ dropUniqueUnsubscribe';
like $lei_out, qr/Message-ID: /,
'wrote expected message to v2 output';
# the default for compatibility:
truncate $ENV{PI_CONFIG}, 0;
lei_ok qw(q L:unsub -o), "v2:$ENV{HOME}/v2-2";
lei_ok qw(q s:unsubscribe -f mboxrd --only), "$ENV{HOME}/v2-2";
like $lei_out, qr/some-UUID-here/,
'Unsubscribe header preserved by default :<';
# ensure we can fail
write_file '>', $ENV{PI_CONFIG}, <