File Coverage

blib/lib/SimpleMock/Model/LWP_UA.pm
Criterion Covered Total %
statement 64 64 100.0
branch 12 12 100.0
condition 11 12 91.6
subroutine 11 11 100.0
pod 0 2 0.0
total 98 101 97.0


line stmt bran cond sub pod time code
1             package SimpleMock::Model::LWP_UA;
2 6     6   33 use strict;
  6         7  
  6         180  
3 6     6   22 use warnings;
  6         29  
  6         274  
4 6     6   19 use HTTP::Status qw(status_message);
  6         6  
  6         838  
5 6     6   23 use HTTP::Response;
  6         7  
  6         197  
6 6     6   20 use HTTP::Headers;
  6         7  
  6         144  
7 6     6   19 use URI::QueryParam;
  6         8  
  6         169  
8 6     6   17 use URI;
  6         7  
  6         90  
9 6     6   16 use Data::Dumper;
  6         8  
  6         335  
10              
11 6         3194 use SimpleMock::Util qw(
12             generate_args_sha
13 6     6   20 );
  6         9  
14              
15             our $VERSION = '0.04';
16              
17             sub mock_send_request {
18 14     14 0 11306 my ($request, $ua, $h) = @_;
19 14         38 my $method = $request->method;
20 14         135 my $url = $request->uri;
21              
22             # initially, only supporting 'application/x-www-form-urlencoded'
23 14         101 my %request_args;
24 14 100       52 if ($method eq 'POST') {
    100          
25 2         6 my $content = $request->content;
26 2         27 my $uri = URI->new("http:dummy/"); # dummy base to reuse parser
27 2         115 $uri->query($content);
28 2         39 %request_args = $uri->query_form;
29             }
30             elsif ($method eq 'GET') {
31 11         23 my $uri = URI->new($request->uri);
32 11         806 %request_args = $uri->query_form;
33             }
34              
35             # if the request has no args, make it undef so that generate_args_sha
36             # returns _default
37 14 100       321 my $sha_arg = %request_args ? \%request_args : undef;
38 14         44 my $args_sha = generate_args_sha($sha_arg);
39              
40             # remove QS from URL before lookup
41 14         363 $url =~ s/\?.*//;
42              
43 14         90 for my $layer (reverse @SimpleMock::MOCK_STACK) {
44 15 100       49 my $lwp = $layer->{LWP_UA} or next;
45             my $response = $lwp->{$url}->{$method}->{$args_sha}
46 14   100     32 || $lwp->{$url}->{$method}->{_default};
47 14 100       103 return $response if $response;
48             }
49              
50 2         7 die "No mock is defined (nor default with no args) for url ($url), method ($method), args: " . Dumper(\%request_args);
51             }
52              
53             sub validate_mocks {
54 6     6 0 21 my $mocks_data = shift;
55              
56 6         13 my $new_mocks = {};
57              
58 6         16 URL: foreach my $url (keys %$mocks_data) {
59 7         13 METHOD: foreach my $method (keys %{$mocks_data->{$url}}) {
  7         19  
60 8         11 MOCK: foreach my $mock (@{ $mocks_data->{$url}->{$method}}) {
  8         18  
61 10         21 my $response_arg_or_content = $mock->{response};
62 10 100       40 my $response_arg = ref $response_arg_or_content eq 'HASH'
63             ? $response_arg_or_content
64             : { content => $response_arg_or_content };
65              
66 10   100     56 $response_arg->{code} //= 200;
67 10   66     58 $response_arg->{message} //= status_message($response_arg->{code});
68 10   100     66 $response_arg->{content} //= '';
69 10   100     39 $response_arg->{headers} //= {};
70              
71             my $response = HTTP::Response->new(
72             $response_arg->{code},
73             $response_arg->{message},
74 10         67 HTTP::Headers->new( %{ $response_arg->{headers} } ),
75             $response_arg->{content},
76 10         17 );
77              
78 10         697 my $sha = generate_args_sha($mock->{args});
79 10         218 $new_mocks->{LWP_UA}->{$url}->{$method}->{$sha} = $response;
80             }
81             }
82             }
83 6         15 return $new_mocks;
84             }
85              
86             1;
87              
88             =head1 NAME
89              
90             SimpleMock::Model::LWP_UA - Mock model for LWP::UserAgent HTTP requests
91              
92             =head1 DESCRIPTION
93              
94             This module allows you to register HTTP mocks for LWP requests, enabling you to simulate various responses for testing purposes without making actual HTTP calls.
95              
96             =head1 USAGE
97              
98             You probably won't use this module directly. Instead, you will use the `SimpleMock` module to register your mocks. Here's an example of how to do that:
99              
100             use SimpleMock qw(register_mocks);
101             register_mocks(
102             LWP_UA => {
103             # URL
104             'http://example.com/api' => {
105             # HTTP method
106             GET => [
107              
108             # Each mock is a hashref with args and response
109             # args can be undef for default mock
110             # response can be a content string, or a hashref with code, message, content, and headers
111              
112             { args => { foo => 'bar' },
113             response => { code => 200, content => 'Success' }
114             },
115              
116             { args => { foo => 'bar2' },
117             response => 'Success'
118             },
119              
120             # Default mock for GET method (no args) or for any other args not explicitly defined
121             { response => { code => 404, content => 'Not Found' } },
122             ],
123             POST => [
124             { args => { data => 'test' },
125             response => { code => 201, content => 'Created' }
126             },
127             ],
128             },
129             },
130             );
131              
132             If args are not specified, the mock will be registered as a default mock for that URL and method.
133             If args are specified, they will be used to differentiate between different mocks for the same
134             URL and method that have different arguments.
135              
136             The response can be a simple content string, or a hashref with the following keys:
137              
138             =over
139              
140             =item * code - HTTP status code (default: 200)
141              
142             =item * message - HTTP status message (default: derived from code)
143              
144             =item * content - The body of the response (default: empty string)
145              
146             =item * headers - A hashref of HTTP headers to include in the response (default: empty)
147              
148             =back
149              
150             See the tests for more examples.
151              
152             =cut