File Coverage

blib/lib/Test/WWW/Stub.pm
Criterion Covered Total %
statement 82 83 98.8
branch 11 14 78.5
condition 7 14 50.0
subroutine 29 30 96.6
pod 7 7 100.0
total 136 148 91.8


line stmt bran cond sub pod time code
1             package Test::WWW::Stub;
2 3     3   305679 use 5.010;
  3         31  
3 3     3   17 use strict;
  3         5  
  3         63  
4 3     3   15 use warnings;
  3         16  
  3         127  
5              
6             our $VERSION = "0.11";
7              
8 3     3   30 use Carp ();
  3         5  
  3         85  
9 3     3   1433 use Guard; # guard
  3         1476  
  3         164  
10 3     3   1449 use LWP::Protocol::PSGI;
  3         125090  
  3         141  
11 3     3   1041 use Plack::Request;
  3         128919  
  3         104  
12 3     3   24 use Test::More ();
  3         7  
  3         46  
13 3     3   1763 use List::MoreUtils ();
  3         42020  
  3         89  
14 3     3   24 use URI;
  3         7  
  3         70  
15              
16 3     3   1466 use Test::WWW::Stub::Intercepter;
  3         10  
  3         3508  
17              
18             my $Intercepter = Test::WWW::Stub::Intercepter->new;
19             our @Requests;
20              
21             my $register_g;
22             my $app;
23             my %import_options;
24 0     0   0 sub _app { $app; }
25              
26             $app = sub {
27             my ($env) = @_;
28             my $req = Plack::Request->new($env);
29              
30             push @Requests, $req;
31              
32             my $uri = _normalize_uri($req->uri);
33              
34             my $stubbed_res = $Intercepter->intercept($uri, $env, $req);
35             return $stubbed_res if $stubbed_res;
36              
37             my ($file, $line) = _trace_file_and_line();
38              
39             my $method = $req->method;
40             warn "Unexpected external access: $method $uri at $file line $line";
41              
42             return [ 499, [], [] ];
43             };
44              
45             sub import {
46 9     9   328246 my ($class, %options) = @_;
47 9 50 66     80 if (!defined $register_g || _diff(\%import_options, \%options)) {
48 9         30 %import_options = %options;
49 9         75 $register_g = LWP::Protocol::PSGI->register($app, %options);
50             }
51             }
52              
53             sub unimport {
54 1     1   4813 undef $register_g;
55 1         24 undef %import_options;
56             }
57              
58             sub register {
59 14     14 1 33966 my ($class, $uri_or_re, $app_or_res) = @_;
60 14   50     47 $app_or_res //= [200, [], []];
61              
62 14         48 my $handler = $Intercepter->register($uri_or_re, $app_or_res);
63             defined wantarray && return guard {
64 13     13   17238 $Intercepter->unregister($uri_or_re, $handler);
65 14 100       120 };
66             }
67              
68             sub last_request {
69 2 100   2 1 1752 return undef unless @Requests;
70 1         5 return $Requests[-1];
71             }
72              
73             sub last_request_for {
74 3     3 1 7449 my ($class, $method, $url) = @_;
75 3         9 my $reqs = { map { _request_signature($_) => $_ } @Requests };
  9         87  
76 3         48 my $signature = "$method $url";
77 3         13 return $reqs->{$signature};
78             }
79              
80             sub _request_signature {
81 9     9   15 my ($req) = @_;
82 9         25 my $normalized = _normalize_uri($req->uri);
83 9         93 return join ' ', $req->method, $normalized;
84             }
85              
86             # Don't use query part of URI for handler matching.
87             sub _normalize_uri {
88 49     49   11653 my ($uri) = @_;
89 49         136 my $cloned = $uri->clone;
90 49         366 $cloned->query(undef);
91 49         758 return $cloned;
92             }
93              
94 3     3 1 656 sub requests { @Requests }
95              
96             sub requested_ok {
97 3     3 1 10323 my ($class, $method, $url) = @_;
98 3         7 local $Test::Builder::Level = $Test::Builder::Level + 1;
99             Test::More::ok(
100             List::MoreUtils::any(sub {
101 5     5   37 my $req_url = _normalize_uri($_->uri);
102 5 100       18 $_->method eq $method && $req_url eq $url
103             }, @Requests),
104             "stubbed $method $url",
105 3 100       21 ) or Test::More::diag Test::More::explain [ map { $_->method . ' ' . $_->uri } @Requests ]
  4         1390  
106             }
107              
108             sub clear_requests {
109 3     3 1 7881 @Requests = ();
110             }
111              
112             sub _trace_file_and_line {
113 10     10   23 my $level = $Test::Builder::Level;
114 10         57 my (undef, $file, $line) = caller($level);
115             # assume "Actual" caller is test file named FOOBAR.t
116 10   66     114 while ($file && $file !~ m<\.t$>) {
117 80         499 (undef, $file, $line) = caller(++$level);
118             }
119 10         57 ($file, $line);
120             }
121              
122             sub unstub {
123 6 50   6 1 176750 Carp::croak 'guard is required' unless defined wantarray;
124 6         38 undef $register_g;
125              
126             # Copy options to restore the state at the time of unstub
127 6         187 my %options = %import_options;
128             return guard {
129 6     6   1519532 %import_options = %options;
130 6         67 $register_g = LWP::Protocol::PSGI->register($app, %options);
131             }
132 6         58 }
133              
134             sub _diff {
135 5     5   20 my ($a, $b) = @_;
136              
137 5   33 2   56 my @diff_a = List::MoreUtils::any { !(exists $b->{$_} && ($b->{$_} eq $a->{$_})) } keys %$a;
  2         15  
138 5   33 2   51 my @diff_b = List::MoreUtils::any { !(exists $a->{$_} && ($a->{$_} eq $b->{$_})) } keys %$b;
  2         14  
139              
140 5 50       51 @diff_a || @diff_b
141             }
142              
143             1;
144             __END__