diff options
Diffstat (limited to 't/tests/sietima')
-rw-r--r-- | t/tests/sietima/cmdline.t | 82 | ||||
-rw-r--r-- | t/tests/sietima/mailstore.t | 186 | ||||
-rw-r--r-- | t/tests/sietima/message.t | 35 | ||||
-rw-r--r-- | t/tests/sietima/multi-role/debounce-moderate.t | 64 | ||||
-rw-r--r-- | t/tests/sietima/role/avoid-dups.t | 29 | ||||
-rw-r--r-- | t/tests/sietima/role/debounce.t | 37 | ||||
-rw-r--r-- | t/tests/sietima/role/headers.t | 56 | ||||
-rw-r--r-- | t/tests/sietima/role/nomail.t | 41 | ||||
-rw-r--r-- | t/tests/sietima/role/replyto.t | 143 | ||||
-rw-r--r-- | t/tests/sietima/role/subject-tag.t | 42 | ||||
-rw-r--r-- | t/tests/sietima/role/subscriberonly/drop.t | 39 | ||||
-rw-r--r-- | t/tests/sietima/role/subscriberonly/moderate.t | 131 | ||||
-rw-r--r-- | t/tests/sietima/subscriber.t | 41 |
13 files changed, 926 insertions, 0 deletions
diff --git a/t/tests/sietima/cmdline.t b/t/tests/sietima/cmdline.t new file mode 100644 index 0000000..bd24c84 --- /dev/null +++ b/t/tests/sietima/cmdline.t @@ -0,0 +1,82 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; +use Path::Tiny; +use Sietima; +use Sietima::CmdLine; + +subtest 'given instance' => sub { + my $s = Sietima->new({ + return_path => 'list@example.com', + }); + my $c = Sietima::CmdLine->new({ + sietima => $s, + }); + is( + $c, + object { + call app_spec => object { + call name => 'sietima'; + call subcommands => hash { + field send => object { + call name => 'send'; + }; + etc; + }; + }; + call runner => object { + call cmd => $s; + }; + }, + 'spec & runner should be built', + ); +}; + +subtest 'built via args' => sub { + my $c = Sietima::CmdLine->new({ + args => { + return_path => 'list@example.com', + }, + }); + is( + $c, + object { + call sietima => object { + call return_path => 'list@example.com'; + }; + }, + 'sietima should be built', + ); +}; + +subtest 'built via args & traits' => sub { + my $c = Sietima::CmdLine->new({ + traits => [ qw(ReplyTo) ], + args => { + return_path => 'list@example.com', + }, + }); + DOES_ok( + $c->sietima, + ['Sietima::Role::ReplyTo'], + 'sietima should be built with the given trait', + ); +}; + +subtest 'extra spec' => sub { + my $c = Sietima::CmdLine->new({ + extra_spec => { name => 'different' }, + args => { + return_path => 'list@example.com', + }, + }); + is( + $c->app_spec, + object { + call name => 'different'; + }, + 'spec fields should be overridden', + ); +}; + +done_testing; diff --git a/t/tests/sietima/mailstore.t b/t/tests/sietima/mailstore.t new file mode 100644 index 0000000..7a2aa08 --- /dev/null +++ b/t/tests/sietima/mailstore.t @@ -0,0 +1,186 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; +use Email::Stuffer; +use Path::Tiny; + +package TestClassWithMS { + use Moo; + use Sietima::Policy; + with 'Sietima::Role::WithMailStore'; +}; + +subtest 'Role::WithMailStore' => sub { + subtest 'plain instance' => sub { + require Test::Sietima::MailStore; + ok( + lives { + TestClassWithMS->new({ + mail_store => Test::Sietima::MailStore->new, + }) + }, + 'passing a test instance should work', + ); + }; + subtest 'type coercion' => sub { + my $tc; + my $root = Path::Tiny->tempdir; + ok( + lives { + $tc = TestClassWithMS->new({ + mail_store => { + class => 'Sietima::MailStore::FS', + root => $root, + }, + }) + }, + 'passing a hashref should work (and load the class)', + ); + is( + $tc->mail_store, + object { + prop blessed => 'Sietima::MailStore::FS'; + call root => $root; + }, + 'the mailstore should be built correctly', + ); + }; +}; + +sub mkmail($id) { + Email::Stuffer + ->from("from-${id}\@example.com") + ->to("to-${id}\@example.com") + ->subject("subject $id") + ->text_body("body $id \nbody body\n") + ->email; +} + +sub chkmail($id) { + object { + call [header=>'from'] => "from-${id}\@example.com"; + call [header=>'to'] => "to-${id}\@example.com"; + call [header=>'subject'] => "subject $id"; + call body => match qr{\bbody \Q$id\E\b}; + }; +} + +sub chk_multimail(@ids) { + return bag { + for my $id (@ids) { + item hash { + field id => D(); + field mail => chkmail($id); + end; + }; + } + end; + }; +} + +sub test_store($store) { + my %stored_id; + + subtest 'storing' => sub { + ok($stored_id{1}=$store->store(mkmail(1),'tag1','tag2')); + ok($stored_id{2}=$store->store(mkmail(2),'tag2')); + ok($stored_id{3}=$store->store(mkmail(3),'tag1')); + }; + + subtest 'retrieving by id' => sub { + is( + $store->retrieve_by_id($stored_id{$_}), + chkmail($_), + ) for 1..3; + }; + + subtest 'retrieving by tag' => sub { + my $tag1 = $store->retrieve_by_tags('tag1'); + is( + $tag1, + chk_multimail(1,3), + 'tag1 should have mails 1 & 3', + ); + + my $tag2 = $store->retrieve_by_tags('tag2'); + is( + $tag2, + chk_multimail(1,2), + 'tag1 should have mails 1 & 2', + ); + + my $tag12 = $store->retrieve_by_tags('tag2','tag1'); + is( + $tag12, + chk_multimail(1), + 'tag1+tag2 should have mail 1', + ); + + my $tag_all = $store->retrieve_by_tags(); + is( + $tag_all, + chk_multimail(1,2,3), + 'no tags should retrieve all mails', + ); + }; + + subtest 'retrieving ids by tag' => sub { + my $tag1 = $store->retrieve_ids_by_tags('tag1'); + is( + $tag1, + bag { item $stored_id{1}; item $stored_id{3}; end }, + 'tag1 should have ids 1 & 3', + ); + + my $tag2 = $store->retrieve_ids_by_tags('tag2'); + is( + $tag2, + bag { item $stored_id{1}; item $stored_id{2}; end }, + 'tag1 should have ids 1 & 2', + ); + + my $tag12 = $store->retrieve_ids_by_tags('tag2','tag1'); + is( + $tag12, + bag { item $stored_id{1}; end }, + 'tag1+tag2 should have id 1', + ); + + my $tag_all = $store->retrieve_ids_by_tags(); + is( + $tag_all, + bag { item $stored_id{1}; item $stored_id{2}; item $stored_id{3}; end }, + 'no tags should retrieve all ids', + ); + }; + + subtest 'removing' => sub { + $store->remove($stored_id{2}); + is( + $store->retrieve_by_tags('tag2'), + chk_multimail(1), + 'remove should remove', + ); + }; + + subtest 'clearing' => sub { + $store->clear; + is( + $store->retrieve_by_tags(), + [], + 'clear should clear', + ); + }; +} + +subtest 'test store' => sub { + test_store(Test::Sietima::MailStore->new); +}; + +subtest 'file store' => sub { + my $root = Path::Tiny->tempdir; + + test_store(Sietima::MailStore::FS->new({root => $root})); +}; + +done_testing; diff --git a/t/tests/sietima/message.t b/t/tests/sietima/message.t new file mode 100644 index 0000000..d1c548a --- /dev/null +++ b/t/tests/sietima/message.t @@ -0,0 +1,35 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; +use Email::Stuffer; +use Sietima::Message; + +my $mail = Email::Stuffer + ->from('one@example.com') + ->to('two@example, three@example.com') + ->text_body('test message')->email; + +my $message = Sietima::Message->new({ + mail => $mail, + from => 'one@envelope.example.com', + to => [ + 'two@envelope.example.com', + 'three@envelope.example.com', + ], +}); + +is( + $message->envelope, + { + from => 'one@envelope.example.com', + to => bag { + item 'two@envelope.example.com'; + item 'three@envelope.example.com'; + }, + }, + 'the envelope should be built from the attributes', +); + +# I'm not sure I'll need 'clone', so I won't test it for the moment + +done_testing; diff --git a/t/tests/sietima/multi-role/debounce-moderate.t b/t/tests/sietima/multi-role/debounce-moderate.t new file mode 100644 index 0000000..e61c551 --- /dev/null +++ b/t/tests/sietima/multi-role/debounce-moderate.t @@ -0,0 +1,64 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; +use Test::Sietima::MailStore; + +sub test_one($traits,$should_send=1) { + my @subscriber_addresses = ( + 'one@users.example.com', + 'two@users.example.com', + ); + my $owner = 'owner@lists.example.com'; + my $ms = Test::Sietima::MailStore->new(); + + my $s = make_sietima( + with_traits => $traits, + subscribers => \@subscriber_addresses, + owner => $owner, + mail_store => $ms, + ); + + test_sending( + sietima => $s, + mail => { from=>'someone@users.example.com' }, + mails => [{ + o => object { + call [header_str => 'subject'] => match qr{\bheld for moderation\b}; + }, + }], + ); + transport->clear_deliveries; + + my $to_moderate = $ms->retrieve_by_tags('moderation'); + my $msg_id = $to_moderate->[0]->{id}; + $s->resume($msg_id); + + if ($should_send) { + deliveries_are( + to => \@subscriber_addresses, + test_message => 'the resumed message should be sent', + ); + } + else { + deliveries_are( + mails => [], + test_message => 'the resumed message should be dropped', + ); + } +} + +# there's an ordering dependency between Debounce and Moderate: if we +# moderate a message that already has the X-Been-There header, it will +# be dropped when resumed; the simplest solution is to apply Debounce +# *before* Moderate, so messages are moderated *before* getting the +# anti-loop header + +subtest 'debounce first' => sub { + test_one(['Debounce','SubscriberOnly::Moderate'],1); +}; + +subtest 'moderate first' => sub { + test_one(['SubscriberOnly::Moderate','Debounce'],0); +}; + +done_testing; diff --git a/t/tests/sietima/role/avoid-dups.t b/t/tests/sietima/role/avoid-dups.t new file mode 100644 index 0000000..671d898 --- /dev/null +++ b/t/tests/sietima/role/avoid-dups.t @@ -0,0 +1,29 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; + +my $s = make_sietima( + with_traits => ['AvoidDups'], + subscribers => [ + 'one@users.example.com', + 'two@users.example.com', + ], +); + +subtest 'in cc' => sub { + test_sending( + sietima => $s, + mail => { cc => 'one@users.example.com' }, + to => ['two@users.example.com'], + ); +}; + +subtest 'in to' => sub { + test_sending( + sietima => $s, + mail => { to => $s->return_path . ' one@users.example.com' }, + to => ['two@users.example.com'], + ); +}; + +done_testing; diff --git a/t/tests/sietima/role/debounce.t b/t/tests/sietima/role/debounce.t new file mode 100644 index 0000000..ba7566c --- /dev/null +++ b/t/tests/sietima/role/debounce.t @@ -0,0 +1,37 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; + +my $s = make_sietima( + with_traits => ['Debounce'], + subscribers => [ + 'one@users.example.com', + 'two@users.example.com', + ], +); + +my $return_path = $s->return_path->address; + +subtest 'header should be added' => sub { + test_sending( + sietima => $s, + mails => [ + object { + call [ header_str => 'X-Been-There' ] => + match qr{\b\Q$return_path\E\b}; + }, + ], + ); +}; + +subtest 'header should inhibit sending' => sub { + test_sending( + sietima => $s, + mail => { + headers => { 'x-been-there' => $return_path }, + }, + to => [], + ); +}; + +done_testing; diff --git a/t/tests/sietima/role/headers.t b/t/tests/sietima/role/headers.t new file mode 100644 index 0000000..3052781 --- /dev/null +++ b/t/tests/sietima/role/headers.t @@ -0,0 +1,56 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; + +package Sietima::Role::ForTesting { + use Moo::Role; + use Sietima::Policy; + use Sietima::Types qw(AddressFromStr); + + around list_addresses => sub($orig,$self) { + return { + $self->$orig->%*, + test1 => AddressFromStr->coerce('name <someone@example.com>'), + 'test+2' => 'http://test.example.com', + }; + }; +}; + +my $s = make_sietima( + with_traits => ['Headers','WithOwner','ForTesting'], + name => 'test-list', + owner => 'owner@example.com', + subscribers => [ + 'one@users.example.com', + 'two@users.example.com', + ], +); + +subtest 'list headers should be added' => sub { + test_sending( + sietima => $s, + mails => [ + object { + call sub { +{ shift->header_str_pairs } } => hash { + field 'List-Id' => 'test-list <sietima-test.list.example.com>'; + field 'List-Owner' => '<mailto:owner@example.com>'; + field 'List-Post' => '<mailto:sietima-test@list.example.com>'; + field 'List-Test1' => '<mailto:someone@example.com>'; + field 'List-Test-2' => 'http://test.example.com'; + + field 'Date' => D(); + field 'MIME-Version' => D(); + field 'Content-Type' => D(); + field 'Content-Transfer-Encoding' => D(); + field 'From' => 'someone@users.example.com'; + field 'To' => 'sietima-test@list.example.com'; + field 'Subject' => 'Test Message'; + + end; + }; + }, + ], + ); +}; + +done_testing; diff --git a/t/tests/sietima/role/nomail.t b/t/tests/sietima/role/nomail.t new file mode 100644 index 0000000..2449f5a --- /dev/null +++ b/t/tests/sietima/role/nomail.t @@ -0,0 +1,41 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; + +subtest 'disabled' => sub { + my $s = make_sietima( + with_traits => ['NoMail'], + subscribers => [ + { + primary => 'one@users.example.com', + prefs => { wants_mail => 0 }, + }, + 'two@users.example.com', + ], + ); + + test_sending( + sietima => $s, + to => ['two@users.example.com'], + ); +}; + +subtest 'enabled' => sub { + my $s = make_sietima( + with_traits => ['NoMail'], + subscribers => [ + { + primary => 'one@users.example.com', + prefs => { wants_mail => 1 }, + }, + 'two@users.example.com', + ], + ); + + test_sending( + sietima => $s, + to => ['one@users.example.com','two@users.example.com'], + ); +}; + +done_testing; diff --git a/t/tests/sietima/role/replyto.t b/t/tests/sietima/role/replyto.t new file mode 100644 index 0000000..e39f8b3 --- /dev/null +++ b/t/tests/sietima/role/replyto.t @@ -0,0 +1,143 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; + +subtest 'disabled' => sub { + my $s = make_sietima( + with_traits => ['ReplyTo'], + munge_reply_to => 0, + subscribers => [ + 'one@users.example.com', + 'two@users.example.com', + ], + ); + + test_sending( + sietima => $s, + mails => [ + object { + call [ header_str => 'reply-to' ] => undef; + }, + ], + ); +}; + +subtest 'enabled' => sub { + my $s = make_sietima( + with_traits => ['ReplyTo'], + munge_reply_to => 1, + subscribers => [ + 'one@users.example.com', + 'two@users.example.com', + ], + ); + + test_sending( + sietima => $s, + mails => [ + object { + call [ header_str => 'reply-to' ] => $s->return_path->address; + }, + ], + ); +}; + +subtest 'enabled, custom post address' => sub { + my $post_address = 'the-list@example.com'; + my $s = make_sietima( + with_traits => ['ReplyTo'], + munge_reply_to => 1, + subscribers => [ + 'one@users.example.com', + 'two@users.example.com', + ], + post_address => $post_address, + ); + + is( + $s->list_addresses, + hash { + field return_path => $s->return_path; + field post => object { + call address => $post_address; + }; + }, + 'the custom post address should be set for the headers', + ); + + test_sending( + sietima => $s, + mails => [ + object { + call [ header_str => 'reply-to' ] => $post_address; + }, + ], + ); +}; + +subtest 'enabled for some' => sub { + my $s = make_sietima( + with_traits => ['ReplyTo'], + munge_reply_to => 0, + subscribers => [ + { + primary => 'one@users.example.com', + prefs => { munge_reply_to => 1 }, + }, + 'two@users.example.com', + ], + ); + + test_sending( + sietima => $s, + mails => [ + { + o => object { + call [ header_str => 'reply-to' ] => $s->return_path->address; + }, + to => [ 'one@users.example.com' ], + }, + { + o => object { + call [ header_str => 'reply-to' ] => undef; + }, + to => [ 'two@users.example.com' ], + }, + ], + ); +}; + + +subtest 'disabled for some' => sub { + my $s = make_sietima( + with_traits => ['ReplyTo'], + munge_reply_to => 1, + subscribers => [ + { + primary => 'one@users.example.com', + prefs => { munge_reply_to => 0 }, + }, + 'two@users.example.com', + ], + ); + + test_sending( + sietima => $s, + mails => [ + { + o => object { + call [ header_str => 'reply-to' ] => $s->return_path->address; + }, + to => [ 'two@users.example.com' ], + }, + { + o => object { + call [ header_str => 'reply-to' ] => undef; + }, + to => [ 'one@users.example.com' ], + }, + ], + ); +}; + +done_testing; diff --git a/t/tests/sietima/role/subject-tag.t b/t/tests/sietima/role/subject-tag.t new file mode 100644 index 0000000..e3a266b --- /dev/null +++ b/t/tests/sietima/role/subject-tag.t @@ -0,0 +1,42 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; + +my $s = make_sietima( + with_traits => ['SubjectTag'], + subscribers => [ + 'one@users.example.com', + 'two@users.example.com', + ], + subject_tag => 'foo', +); + +subtest 'adding tag' => sub { + test_sending( + sietima => $s, + mails => [ + object { + call [ header_str => 'Subject' ] => + '[foo] Test Message'; + }, + ], + ); +}; + +subtest 'tag already there' => sub { + my $subject = "[foo] \N{HEAVY BLACK HEART} test"; + test_sending( + sietima => $s, + mail => { + subject => $subject, + }, + mails => [ + object { + call [ header_str => 'Subject' ] => + $subject; + }, + ], + ); +}; + +done_testing; diff --git a/t/tests/sietima/role/subscriberonly/drop.t b/t/tests/sietima/role/subscriberonly/drop.t new file mode 100644 index 0000000..ac37346 --- /dev/null +++ b/t/tests/sietima/role/subscriberonly/drop.t @@ -0,0 +1,39 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; + +my @subscriber_addresses = ( + 'one@users.example.com', + { + primary => 'two@users.example.com', + aliases => [ 'two-two@users.example.com' ], + }, +); +my $s = make_sietima( + with_traits => ['SubscriberOnly::Drop'], + subscribers => \@subscriber_addresses, +); + +subtest 'from subscriber' => sub { + test_sending( + sietima => $s, + mail => { from=>'one@users.example.com' }, + ); +}; + +subtest 'from subscriber alias' => sub { + test_sending( + sietima => $s, + mail => { from=>'two-two@users.example.com' }, + ); +}; + +subtest 'from non-subscriber' => sub { + test_sending( + sietima => $s, + mail => { from=>'someone@users.example.com' }, + to => [], + ); +}; + +done_testing; diff --git a/t/tests/sietima/role/subscriberonly/moderate.t b/t/tests/sietima/role/subscriberonly/moderate.t new file mode 100644 index 0000000..44999bd --- /dev/null +++ b/t/tests/sietima/role/subscriberonly/moderate.t @@ -0,0 +1,131 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; +use Test::Sietima::MailStore; + +my @subscriber_addresses = ( + 'one@users.example.com', + 'two@users.example.com', +); +my $owner = 'owner@lists.example.com'; +my $ms = Test::Sietima::MailStore->new(); +my $s = make_sietima( + with_traits => ['SubscriberOnly::Moderate'], + subscribers => \@subscriber_addresses, + owner => $owner, + mail_store => $ms, +); + +subtest 'from subscriber' => sub { + $ms->clear; + test_sending( + sietima => $s, + mail => { from=>'one@users.example.com' }, + ); + is( + $ms->retrieve_by_tags('moderation'), + [], + 'no mails held for moderation', + ); +}; + +sub test_from_non_sub() { + my $from = $s->return_path->address; + test_sending( + sietima => $s, + mail => { from=>'someone@users.example.com' }, + mails => [{ + o => object { + call [header_str => 'subject'] => match qr{\bheld for moderation\b}; + call [header_str => 'from'] => match qr{\b\Q$from\E\b}; + call [header_str => 'to'] => match qr{\b\Q$owner\E\b}; + call_list parts => [ + object { + call body => match qr{Use id \S+ to refer to it}; + }, + object { + call sub {Email::MIME->new(shift->body)} => object { + call [header_str => 'subject'] => 'Test Message'; + }; + }, + ]; + }, + from => $from, + to => [$owner], + }], + ); +} + +subtest 'from non-subscriber' => sub { + $ms->clear; + test_from_non_sub; + + is( + my $to_moderate = $ms->retrieve_by_tags('moderation'), + [ + { + id => T(), + mail => object { + call [header_str => 'from'] => 'someone@users.example.com'; + call [header_str => 'to'] => $s->return_path->address, + }, + }, + ], + 'mails was held for moderation', + ); + + like( + run_cmdline_sub($s, 'list_mails_in_moderation_queue'), + hash { + field exit => 0; + field error => DNE; + field output => qr{\A + ^\N+\b1 \s+ message\N+$ \n + ^\* \s+ \w+ \s+ someone\@users\.example\.com + \s+ "Test[ ]Message" + \s+ \(\N+?\)$ + }smx; + }, + 'mails in queue should be listed from command line', + ); + + my $msg_id = $to_moderate->[0]->{id}; + + like( + run_cmdline_sub( + $s, 'show_mail_from_moderation_queue', + {}, { 'mail-id' => $msg_id }, + ), + hash { + field exit => 0; + field error => DNE; + field output => qr{\A + ^Message \s+ \w+:$ + .*? + ^From: \s+ someone\@users\.example\.com \s*$ + }smx; + }, + 'mail in queue should be shown from command line', + ); + + transport->clear_deliveries; + $s->resume($msg_id); + deliveries_are( + to => \@subscriber_addresses, + ); +}; + +subtest 'from non-subscriber, drop' => sub { + $ms->clear; + test_from_non_sub; + + my $msg_id = $ms->retrieve_by_tags('moderation')->[0]{id}; + $s->drop($msg_id); + is( + $ms->retrieve_by_tags('moderation'), + [], + 'message should be dropped', + ); +}; + +done_testing; diff --git a/t/tests/sietima/subscriber.t b/t/tests/sietima/subscriber.t new file mode 100644 index 0000000..6761410 --- /dev/null +++ b/t/tests/sietima/subscriber.t @@ -0,0 +1,41 @@ +#!perl +use lib 't/lib'; +use Test::Sietima; +use Sietima::Subscriber; + +subtest 'simple' => sub { + my $s = Sietima::Subscriber->new( + primary => 'Gino (pino) <gino@pino.example.com>', + ); + + is( + $s, + object { + call address => 'gino@pino.example.com'; + call name => 'Gino'; + call original => 'Gino (pino) <gino@pino.example.com>'; + call prefs => {}; + }, + 'construction and delegation should work', + ); +}; + +subtest 'aliases' => sub { + my $s = Sietima::Subscriber->new( + primary => 'Gino (pino) <gino@pino.example.com>', + aliases => [qw(also-gino@pino.example.com maybe-gino@rino.example.com)], + ); + + is( + $s, + object { + for my $a (qw(gino@pino also-gino@pino maybe-gino@rino)) { + call [match => "${a}.example.com"] => T(); + } + }, + 'all addresses should ->match()', + ); + +}; + +done_testing; |