X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=t%2Fimapd.t;h=59b95a6b83fdd0462c1dd6578c6e600125bb290d;hb=499af0138412496c2a0c84035d5d398fac178624;hp=b0caa8f17796c1540767f84345ce542486b212a1;hpb=9d154055ec44903052beaa4e2c1221f39d6d507a;p=public-inbox.git diff --git a/t/imapd.t b/t/imapd.t index b0caa8f1..59b95a6b 100644 --- a/t/imapd.t +++ b/t/imapd.t @@ -1,13 +1,15 @@ #!perl -w # Copyright (C) 2020 all contributors # License: AGPL-3.0+ +# end-to-end IMAP tests, see unit tests in t/imap.t, too use strict; use Test::More; use Time::HiRes (); use PublicInbox::TestCommon; use PublicInbox::Config; use PublicInbox::Spawn qw(which); -require_mods(qw(DBD::SQLite Mail::IMAPClient Linux::Inotify2)); +require_mods(qw(DBD::SQLite Mail::IMAPClient Mail::IMAPClient::BodyStructure)); + my $level = '-Lbasic'; SKIP: { require_mods('Search::Xapian', 1); @@ -81,6 +83,22 @@ ok(!$mic->select('foo') && ($e = $@), 'EXAMINE non-existent'); like($e, qr/\bNO\b/, 'got a NO on EXAMINE for non-existent'); ok($mic->select('inbox.i1'), 'SELECT succeeds'); ok($mic->examine('inbox.i1'), 'EXAMINE succeeds'); +my @raw = $mic->status('inbox.i1', qw(Messages uidnext uidvalidity)); +is(scalar(@raw), 2, 'got status response'); +like($raw[0], qr/\A\*\x20STATUS\x20inbox\.i1\x20 + \(MESSAGES\x20\d+\x20UIDNEXT\x20\d+\x20UIDVALIDITY\x20\d+\)\r\n/sx); +like($raw[1], qr/\A\S+ OK /, 'finished status response'); + +@raw = $mic->list; +like($raw[0], qr/^\* LIST \(.*?\) "\." inbox/, + 'got an inbox'); +like($raw[-1], qr/^\S+ OK /, 'response ended with OK'); +is(scalar(@raw), scalar(@V) + 2, 'default LIST response'); +@raw = $mic->list('', 'inbox.i1'); +is(scalar(@raw), 2, 'limited LIST response'); +like($raw[0], qr/^\* LIST \(.*?\) "\." inbox/, + 'got an inbox.i1'); +like($raw[-1], qr/^\S+ OK /, 'response ended with OK'); my $ret = $mic->search('all') or BAIL_OUT "SEARCH FAIL $@"; is_deeply($ret, [ 1 ], 'search all works'); @@ -125,6 +143,36 @@ for my $r ('1:*', '1') { $ret = $mic->fetch_hash($r, 'FLAGS') or BAIL_OUT "FETCH $@"; is_deeply($ret->{1}->{FLAGS}, '', 'no flags'); + + $ret = $mic->fetch_hash($r, 'BODY[1]') or BAIL_OUT "FETCH $@"; + like($ret->{1}->{'BODY[1]'}, qr/\AThis is a test message/, 'BODY[1]'); + + $ret = $mic->fetch_hash($r, 'BODY[1]<1>') or BAIL_OUT "FETCH $@"; + like($ret->{1}->{'BODY[1]<1>'}, qr/\Ahis is a test message/, + 'BODY[1]<1>'); + + $ret = $mic->fetch_hash($r, 'BODY[1]<2.3>') or BAIL_OUT "FETCH $@"; + is($ret->{1}->{'BODY[1]<2>'}, "is ", 'BODY[1]<2.3>'); + $ret = $mic->bodypart_string($r, 1, 3, 2) or + BAIL_OUT "bodypart_string $@"; + is($ret, "is ", 'bodypart string'); + + $ret = $mic->fetch_hash($r, 'BODY[HEADER.FIELDS.NOT (Message-ID)]') + or BAIL_OUT "FETCH $@"; + $ret = $ret->{1}->{'BODY[HEADER.FIELDS.NOT (MESSAGE-ID)]'}; + unlike($ret, qr/message-id/i, 'Message-ID excluded'); + like($ret, qr/\r\n\r\n\z/s, 'got header end'); + + $ret = $mic->fetch_hash($r, 'BODY[HEADER.FIELDS (Message-ID)]') + or BAIL_OUT "FETCH $@"; + is($ret->{1}->{'BODY[HEADER.FIELDS (MESSAGE-ID)]'}, + 'Message-ID: '."\r\n\r\n", + 'got only Message-ID'); + + my $bs = $mic->get_bodystructure($r) or BAIL_OUT("bodystructure: $@"); + ok($bs, 'got a bodystructure'); + is(lc($bs->bodytype), 'text', '->bodytype'); + is(lc($bs->bodyenc), '8bit', '->bodyenc'); } # Mail::IMAPClient ->compress creates cyclic reference: @@ -152,10 +200,12 @@ $pi_config->each_inbox(sub { my $ng = $ibx->{newsgroup}; my $mic = Mail::IMAPClient->new(%mic_opt); ok($mic && $mic->login && $mic->IsAuthenticated, "authed $name"); + my $uidnext = $mic->uidnext($ng); # we'll fetch BODYSTRUCTURE on this + ok($uidnext, 'got uidnext for later fetch'); is_deeply([$mic->has_capability('IDLE')], ['IDLE'], "IDLE capa $name"); ok(!$mic->idle, "IDLE fails w/o SELECT/EXAMINE $name"); ok($mic->examine($ng), "EXAMINE $ng succeeds"); - ok($mic->idle, "IDLE succeeds on $ng"); + ok(my $idle_tag = $mic->idle, "IDLE succeeds on $ng"); open(my $fh, '<', 't/data/message_embed.eml') or BAIL_OUT("open: $!"); run_script(['-mda', '--no-precheck'], $env, { 0 => $fh }) or @@ -215,7 +265,46 @@ $pi_config->each_inbox(sub { ok(@res = $mic->idle_data(11), "IDLE succeeds on $ng after HUP"); is(grep(/\A\* [0-9] EXISTS\b/, @res), 1, 'got EXISTS message'); ok((Time::HiRes::time() - $t0) < 10, 'IDLE client notified'); -}); + ok($mic->done($idle_tag), 'IDLE DONE'); + my $bs = $mic->get_bodystructure($uidnext); + ok($bs, 'BODYSTRUCTURE ok for deeply nested'); + $ret = $mic->fetch_hash($uidnext, 'BODY') or BAIL_OUT "FETCH $@"; + ok($ret->{$uidnext}->{BODY}, 'got something in BODY'); + + # this matches dovecot behavior + $ret = $mic->fetch_hash($uidnext, 'BODY[1]') or BAIL_OUT "FETCH $@"; + is($ret->{$uidnext}->{'BODY[1]'}, + "testing embedded message harder\r\n", 'BODY[1]'); + $ret = $mic->fetch_hash($uidnext, 'BODY[2]') or BAIL_OUT "FETCH $@"; + like($ret->{$uidnext}->{'BODY[2]'}, + qr/\ADate: Sat, 18 Apr 2020 22:20:20 /, 'BODY[2]'); + + $ret = $mic->fetch_hash($uidnext, 'BODY[2.1.1]') or BAIL_OUT "FETCH $@"; + is($ret->{$uidnext}->{'BODY[2.1.1]'}, + "testing embedded message\r\n", 'BODY[2.1.1]'); + + $ret = $mic->fetch_hash($uidnext, 'BODY[2.1.2]') or BAIL_OUT "FETCH $@"; + like($ret->{$uidnext}->{'BODY[2.1.2]'}, qr/\AFrom: /, + 'BODY[2.1.2] tip matched'); + like($ret->{$uidnext}->{'BODY[2.1.2]'}, + # trailing CRLF may vary depending on MIME parser + qr/done_testing;(?:\r\n){1,2}\z/, + 'BODY[2.1.2] tail matched'); + + $ret = $mic->fetch_hash($uidnext, 'BODY[2.HEADER]') or + BAIL_OUT "2.HEADER $@"; + like($ret->{$uidnext}->{'BODY[2.HEADER]'}, + qr/\ADate: Sat, 18 Apr 2020 22:20:20 /, + '2.HEADER of message/rfc822'); + + $ret = $mic->fetch_hash($uidnext, 'BODY[2.MIME]') or + BAIL_OUT "2.MIME $@"; + is($ret->{$uidnext}->{'BODY[2.MIME]'}, <kill; $td->join;