$Data::Dumper::Sortkeys
= 1;
eval
"use DBD::SQLite 1.31"
;
if
($@) {
plan(
skip_all
=>
'DBD::SQLite not available'
);
exit
;
}
my
$test_domain
=
'example.com'
;
my
(
$report_id
,
$rr_id
,
$policy
,
$dkim
,
$spf
,
$reasons
,
$identifiers
,
$policy_evaluated
);
my
$begin
=
time
- 10000;
my
$end
=
time
- 100;
my
$mod
=
'Mail::DMARC::Report::Store::SQL'
;
use_ok(
$mod
);
my
$sql
=
$mod
->new;
isa_ok(
$sql
,
$mod
);
$sql
->config(
't/mail-dmarc.ini'
);
test_db_connect();
test_query_insert();
test_query_replace();
test_query_update();
test_query_delete();
test_query();
test_query_any();
test_ip_store_and_fetch();
test_get_report_id();
test_insert_policy_published();
test_get_report_policy_published();
test_insert_rr();
test_insert_rr_spf();
test_insert_rr_dkim();
test_insert_rr_reason();
test_retrieve();
test_retrieve_todo();
test_get_author_id(3);
test_get_report();
test_get_row_reason();
test_get_row_spf();
test_get_row_dkim();
test_populate_agg_metadata();
test_populate_agg_records();
done_testing();
exit
;
sub
test_populate_agg_records {
my
$agg
= Mail::DMARC::Report::Aggregate->new();
my
$r
=
$sql
->populate_agg_records( \
$agg
,
$report_id
);
ok(
$r
,
"populate_agg_records"
);
delete
$r
->[0]{auth_results}{dkim}[2]{human_result};
my
$expected
= [{
'auth_results'
=> {
'dkim'
=>
$dkim
,
'spf'
=>
$spf
,
},
'config_file'
=>
'mail-dmarc.ini'
,
'identifiers'
=>
$identifiers
,
'row'
=> {
'count'
=> 1,
'policy_evaluated'
=> {
%$policy_evaluated
,
'reason'
=>
$reasons
,
},
'source_ip'
=>
'192.1.1.1'
},
public_suffixes
=> {},
}];
is_deeply(
$r
,
$expected
,
"populate_agg_records, deeply"
);
};
sub
test_populate_agg_metadata {
my
$query
=
'SELECT id AS rid,begin,end FROM report WHERE id=?'
;
my
$report
=
$sql
->query(
$query
, [
$report_id
] )->[0];
my
$agg
= Mail::DMARC::Report::Aggregate->new();
ok(
$sql
->populate_agg_metadata( \
$agg
, \
$report
),
"populate_agg_metadata"
);
is_deeply(
$agg
->metadata,
{
'config_file'
=>
'mail-dmarc.ini'
,
'date_range'
=> {
'begin'
=>
$report
->{begin},
'end'
=>
$report
->{end},
},
'email'
=>
'noreply@example.com'
,
'org_name'
=>
'My Great Company'
,
'report_id'
=> 2,
'public_suffixes'
=> {},
},
"populate_agg_metadata, deeply"
) or diag Dumper(
$agg
);
};
sub
test_get_report_policy_published {
my
$pp
=
$sql
->get_report_policy_published(
$report_id
);
$pp
->apply_defaults;
$pp
->domain(
'recip.example.com'
);
foreach
(
qw/ sp pct /
) {
delete
$pp
->{
$_
}
if
!
defined
$pp
->
$_
;
};
delete
$pp
->{report_id};
delete
$policy
->{uri};
ok(
$pp
,
"get_report_policy_published"
);
is_deeply(
$pp
,
$policy
,
"get_report_policy_published, deeply"
)
or diag Dumper(
$pp
,
$policy
);
};
sub
test_retrieve {
my
$r
=
$sql
->retrieve;
ok(
scalar
@$r
,
"retrieve, "
.
scalar
@$r
);
my
%tests
= (
rid
=> 2,
author
=>
'Test Company'
,
from_domain
=>
'recip.example.com'
,
begin
=>
$begin
,
end
=>
$end
,
);
foreach
(
keys
%tests
) {
my
$r
=
$sql
->retrieve(
$_
=>
$tests
{
$_
} );
ok(
@$r
,
"retrieve, $_, "
.
scalar
@$r
);
};
};
sub
test_retrieve_todo {
my
$r
=
$sql
->retrieve_todo();
ok(
$r
,
"retrieve_todo"
);
};
sub
test_get_row_reason {
ok(
$sql
->get_row_reason(
$rr_id
),
'get_row_reason'
);
};
sub
test_get_row_spf {
ok(
$sql
->get_row_spf(
$rr_id
),
'get_row_spf'
);
};
sub
test_get_row_dkim {
ok(
$sql
->get_row_dkim(
$rr_id
),
'get_row_dkim'
);
};
sub
test_get_report {
my
$reports
=
$sql
->get_report(
rid
=>
$report_id
)->{rows};
ok(
scalar
@$reports
,
"get_report, no limits, "
.
scalar
@$reports
);
my
$limit
= 10;
my
$r
=
$sql
->get_report(
rows
=>
$limit
)->{rows};
if
( !
$r
|| !
scalar
@$r
||
scalar
@$r
<
$limit
) {
ok( 1,
"skipping author tests"
);
return
;
};
cmp_ok(
scalar
@$reports
,
'=='
,
$limit
,
"get_report, limit $limit"
);
my
@queries
= (
author
=>
'The Art Farm'
,
author
=>
'google.com'
,
from_domain
=>
'theartfarm.com'
,
recipient
=>
'google.com'
,
recipient
=>
'yahoo.com'
,
);
while
(
my
$key
=
shift
@queries
) {
my
$val
=
shift
@queries
;
$r
=
$sql
->get_report(
$key
=>
$val
);
$reports
=
$r
->{rows};
ok(
scalar
@$reports
,
"get_report, $key, $val, "
.
scalar
@$reports
);
};
$reports
=
$sql
->get_report(
rows
=> 1,
sord
=>
'desc'
,
sidx
=>
'rid'
);
ok(
$reports
->{rows},
"get_report, multisearch"
);
};
sub
test_get_author_id {
my
$times
=
shift
or
return
;
my
%meta
= (
org_name
=>
"Test $times Company"
,
email
=>
'dmarc-reporter@example.com'
,
extra_contact_info
=>
undef
,
report_id
=>
undef
,
begin
=>
time
,
end
=>
time
+ 10,
);
my
$report
= Mail::DMARC::Report->new();
foreach
(
keys
%meta
) {
next
if
!
defined
$_
;
next
if
!
defined
$meta
{
$_
};
ok(
$report
->aggregate->metadata->
$_
(
$meta
{
$_
} ),
"meta, $_, set"
);
}
my
$policy
= Mail::DMARC::Policy->new(
"v=DMARC1; p=reject"
);
ok(
$policy
->rua(
'mailto:'
.
$sql
->config->{organization}{email} ),
"policy, rua, set"
);
ok(
$policy
->domain(
'recip.example.com'
),
"policy, domain, set"
);
ok(
$report
->aggregate->policy_published(
$policy
),
"policy published, set"
);
my
$rid
=
$sql
->get_report_id(
$report
->aggregate );
ok(
$rid
,
"get_report_id, $rid"
);
my
$authors
=
$sql
->get_author_id(
$report
->aggregate->metadata );
test_get_author_id(
$times
- 1);
}
sub
test_get_report_id {
my
%meta
= (
org_name
=>
'Test Company'
,
email
=>
'dmarc-reporter@example.com'
,
begin
=>
$begin
,
end
=>
$end
,
);
my
$report
= Mail::DMARC::Report->new();
foreach
(
keys
%meta
) {
ok(
$report
->aggregate->metadata->
$_
(
$meta
{
$_
} ),
"meta, $_, set"
);
}
$policy
= Mail::DMARC::Policy->new(
"v=DMARC1; p=reject"
);
$policy
->apply_defaults;
ok(
$policy
->rua(
'mailto:'
.
$sql
->config->{organization}{email} ),
"policy, rua, set"
);
ok(
$policy
->domain(
'recip.example.com'
),
"policy, domain, set"
);
ok(
$report
->aggregate->policy_published(
$policy
),
"policy published, set"
);
$report_id
=
$sql
->get_report_id(
$report
->aggregate );
ok(
$report_id
,
"get_report_id, $report_id"
);
}
sub
test_insert_rr_reason {
my
@reasons
=
qw/ forwarded local_policy mailing_list other sampled_out trusted_forwarder /
;
foreach
my
$r
(
@reasons
) {
push
@$reasons
, {
type
=>
$r
,
comment
=>
"test $r comment"
};
ok(
$sql
->insert_rr_reason(
$rr_id
,
$r
,
"test $r comment"
),
"insert_rr_reason, $r"
);
}
}
sub
test_insert_rr_dkim {
$dkim
= [
{
domain
=>
'example.com'
,
selector
=>
'blah'
,
result
=>
'pass'
,
human_result
=>
'yay'
},
{
domain
=>
'example.com'
,
selector
=>
'blah'
,
result
=>
'pass'
,
human_result
=>
undef
,
},
{
domain
=>
'example.com'
,
selector
=>
'blah'
,
result
=>
'pass'
,
},
];
ok(
$sql
->insert_rr_dkim(
$rr_id
,
$dkim
->[0] ),
'insert_rr_dkim'
);
ok(
$sql
->insert_rr_dkim(
$rr_id
,
$dkim
->[1] ),
'insert_rr_dkim'
);
ok(
$sql
->insert_rr_dkim(
$rr_id
,
$dkim
->[2] ),
'insert_rr_dkim'
);
}
sub
test_insert_rr_spf {
$spf
= [
{
'domain'
=>
'example.com'
,
'result'
=>
'pass'
,
'scope'
=>
'helo'
},
{
'domain'
=>
'example.com'
,
'result'
=>
'pass'
,
'scope'
=>
'mfrom'
},
{
'domain'
=>
'example.com'
,
'result'
=>
'fail'
,
'scope'
=>
'mfrom'
}
];
foreach
(
@$spf
) {
ok(
$sql
->insert_rr_spf(
$rr_id
,
$_
),
'insert_rr_spf'
);
};
}
sub
test_insert_rr {
$identifiers
= {
header_from
=>
'from.com'
,
envelope_to
=>
'to.com'
,
envelope_from
=>
'from.com'
,
};
$policy_evaluated
= {
disposition
=>
'none'
,
dkim
=>
'fail'
,
spf
=>
'pass'
,
};
my
$record
= {
row
=> {
source_ip
=>
'192.1.1.1'
,
policy_evaluated
=>
$policy_evaluated
,
},
identifiers
=>
$identifiers
,
};
$rr_id
=
$sql
->insert_rr(
$report_id
,
$record
);
ok(
$rr_id
,
"insert_rr, $rr_id"
);
}
sub
test_insert_policy_published {
my
$pol
= Mail::DMARC::Policy->new(
'v=DMARC1; p=none'
);
$pol
->apply_defaults;
$pol
->rua(
'mailto:'
.
$sql
->config->{organization}{email} );
my
$r
=
$sql
->insert_policy_published(
$report_id
,
$pol
);
ok(
$r
,
'insert_policy_published'
);
}
sub
test_ip_store_and_fetch {
my
@test_ips
= (
'1.1.1.1'
,
'10.0.1.1'
,
'2002:4c79:6240::1610:9fff:fee5:fb5'
,
'2607:f060:b008:feed::6'
,
);
foreach
my
$ip
(
@test_ips
) {
my
$ipbin
=
$sql
->any_inet_pton(
$ip
);
ok(
$ipbin
,
"any_inet_pton, $ip"
);
my
$pres
=
$sql
->any_inet_ntop(
$ipbin
);
ok(
$pres
,
"any_inet_ntop, $ip"
);
compare_any_inet_round_trip(
$ip
,
$pres
);
my
$report_id
=
$sql
->query(
"INSERT INTO report_record ( report_id, source_ip, disposition, dkim,spf,header_from_did) VALUES (?,?,?,?,?,?)"
,
[ 1,
$ipbin
,
'none'
,
'pass'
,
'pass'
, 1 ]
) or
die
"failed to insert?"
;
my
$r_ref
=
$sql
->query(
"SELECT id,source_ip FROM report_record WHERE id=?"
,
[
$report_id
] );
compare_any_inet_round_trip(
$ip
,
$sql
->any_inet_ntop(
$r_ref
->[0]{source_ip} ),
);
}
}
sub
test_query {
ok(
$sql
->query(
"SELECT id FROM report LIMIT 1"
),
"query"
);
}
sub
test_query_insert {
my
$end
=
time
+ 86400;
my
$from_did
=
$sql
->query(
"INSERT INTO domain (domain) VALUES (?)"
, [
'ignore.test.com'
]
);
my
$rid
=
$sql
->query(
"INSERT INTO report (author_id, from_domain_id, begin, end) VALUES (??)"
,
[ 0,
$from_did
,
$begin
,
$end
]
);
ok(
$rid
,
"query_insert, report, $rid"
);
ok(
$sql
->query(
"DELETE FROM report WHERE id=?"
, [
$rid
] ),
"query_delete"
);
eval
{
$rid
=
$sql
->query(
"INSERT INTO reporting (domain, begin, end) VALUES (?,?,?)"
,
[
$test_domain
,
$begin
,
$end
] );
};
chomp
$@;
ok( $@,
"query_insert, report, neg: $@"
);
eval
{
$rid
=
$sql
->query(
"INSERT INTO report (domin, begin, end) VALUES (?,?,?)"
,
[
'a'
x 257,
'yellow'
,
$end
] );
};
chomp
$@;
ok( $@,
"query_insert, report, neg: $@"
);
}
sub
test_query_replace {
my
$end
=
time
+ 86400;
my
$snafus
=
$sql
->query(
"SELECT id FROM report WHERE begin='yellow'"
);
foreach
my
$s
(
@$snafus
) {
ok(
$sql
->query(
"REPLACE INTO report (id,domain, begin, end) VALUES (?,?,?,?)"
,
[
$s
->{id},
$test_domain
,
$begin
,
$end
]
),
"query_replace"
);
}
eval
{
$sql
->query(
"REPLACE INTO rep0rt (id,domain, begin, end) VALUES (?,?,?,?)"
,
[ 1, 1, 1, 1 ] );
};
chomp
$@;
ok( $@,
"replace, negative, $@"
);
}
sub
test_query_update {
my
$victims
=
$sql
->query(
"SELECT id FROM report LIMIT 1"
);
foreach
my
$v
(
@$victims
) {
my
$r
=
$sql
->query(
"UPDATE report SET end=? WHERE id=?"
,
[
time
,
$v
->{id} ] );
ok(
$r
,
"query_update, $r"
);
eval
{
$sql
->query(
"UPDATE report SET ed=? WHERE id=?"
,
[
time
,
$v
->{id} ] );
};
ok( $@,
"query_update, neg"
);
}
}
sub
test_query_delete {
my
$victims
=
$sql
->query(
"SELECT id FROM report LIMIT 1"
);
foreach
my
$v
(
@$victims
) {
my
$r
=
$sql
->query(
"DELETE FROM report WHERE id=?"
);
ok(
$r
,
"query_delete"
);
}
eval
{
$sql
->query(
"DELETE FROM repor WHERE id=?"
); };
chomp
$@;
ok( $@,
"delete, negative, $@"
);
}
sub
test_query_any {
foreach
my
$table
(
qw/ report author domain report_record /
) {
my
$r
=
$sql
->query(
"SELECT id FROM $table LIMIT 1"
);
ok(
$r
,
"query, select, $table"
);
}
eval
{
$sql
->query(
"SELECT id FROM rep0rt LIMIT 1"
) };
chomp
$@;
ok( $@,
"query, select, negative, $@"
);
}
sub
test_db_connect {
my
$dbh
=
$sql
->db_connect();
ok(
$dbh
,
"db_connect"
);
isa_ok(
$dbh
,
"DBIx::Simple"
);
}
sub
compare_any_inet_round_trip {
my
(
$ip
,
$pres
) =
@_
;
if
(
$pres
eq
$ip
) {
cmp_ok(
$pres
,
'eq'
,
$ip
,
"any_inet_ntop, round_trip, $ip"
);
}
else
{
my
$zero_filled
=
$ip
;
$zero_filled
=~ s/::/:0:/g;
cmp_ok(
$pres
,
'eq'
,
$zero_filled
,
"any_inet_ntop, round_trip, zero-pad, $ip"
)
or diag
"presentation: $zero_filled\nresult: $pres"
;
}
}