Test-LWP-UserAgent-0.022/000755 000767 000024 00000000000 12234623641 015246 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/advent_2012.pod000644 000767 000024 00000010031 12234623641 017672 0ustar00etherstaff000000 000000 =pod =head1 Perl Advent Calendar 2012 This article was originally published on the L. =head1 Testing networking client code using Test::LWP::UserAgent L is a module I wrote after writing several networking client libraries for $work with inconsistent and spotty test coverage -- what I most wanted to do was fully simulate the server end of a network connection, without having to delve deeply into L's internal implementation, nor mock a lot of methods, which the traditional mock object approach would require. Exploring the options available led me to Yury Zavarin's L, whose API I adapted into the initial version of L. It behaves exactly like L, one of the main networking client libraries in perl, in all respects except for the portion that actually sends the request out to the network - at that point it returns the first response that you have preconfigured that matches the outbound request. my $useragent = Test::LWP::UserAgent->new; $useragent->map_response(qr/example.com/, HTTP::Response->new(200)); my $response = $useragent->get('http://example.com'); # prints 200 say $response->code; $response = $useragent->get('http://google.com'); # prints 404 say $response->code; In the above example, no outbound request passing through this useragent will use the live network (this is the default behaviour). Any request whose URI matches C will receive an HTTP 200 response, and the remaining requests will return a 404. If, however, you wish to only capture some requests, while letting the remainder use the network normally, you can enable the C feature: my $useragent = Test::LWP::UserAgent->new; $useragent->network_fallback(1); $useragent->map_response(qr/example.com/, HTTP::Response->new(200)); my $response = $useragent->get('http://example.com'); # prints 200 say $response->code; $response = $useragent->get('http://google.com'); # prints 200 say $response->code; And indeed if you inspect the C<$response> object returned, you will see that it contains the actual network response from contacting L. Configuration can also be done globally, if you want all useragents in your program to use the same settings (or if you do not have direct control over the actual useragent object being used, but just its class): Test::LWP::UserAgent->map_response(...); my $response = Test::LWP::UserAgent->new->request(...); L inherits from L, so it satisfies C/C<@ISA> requirements that you may have via L or another system that uses type checking. This means that all the normal options available in L are still available to you, and work identically, for example: my $useragent = Test::LWP::UserAgent->new( timeout => 10, cookie_jar => { file => "$ENV{HOME}/.cookies.txt" }, ); You can also use L to connect to a local L application seamlessly, which can be very useful when you have a client and server installed on the same box but do not want to fuss with separate code for handling this case, or if you want more fine-grained control over what responses to send: my $app = Plack::Util::load_psgi('./myapp.psgi'); $useragent->register_psgi('mytestdomain.com', $app); my $response = $useragent->request(...); =head1 SUMMARY Use L to control data flow that you would otherwise receive over the network, to test your application's handling of that data. =head1 CODE EXAMPLES The code examples above are fleshed out as fully-working code in the examples/ directory under L, along with a detailed example of some unit tests for a hypothetical networking client library. =head1 AUTHOR Karen Etheridge (ether) L L =cut Test-LWP-UserAgent-0.022/Build.PL000644 000767 000024 00000000066 12234623641 016544 0ustar00etherstaff000000 000000 use 5.006; use Module::Build::Tiny 0.030; Build_PL(); Test-LWP-UserAgent-0.022/Changes000644 000767 000024 00000013060 12234623641 016541 0ustar00etherstaff000000 000000 Revision history for Test-LWP-UserAgent 0.022 2013-11-01 04:01:19Z - prevent optional test from failing on perls < 5.10 0.021 2013-10-31 02:24:49Z (Karen Etheridge) - skip testing examples/ code when optional/undeclared prereqs are not available 0.020 2013-10-26 16:31:48Z (Karen Etheridge) - fixed tests from using example.com to iana.org, to ensure we get a 302 response when needed - converted all uses of Test::NoWarnings to Test::Warnings - warnings tests bypassed during installation 0.019 2013-07-21 22:21:27 PDT-0700 (Karen Etheridge) - switched examples to using object-based syntax, to make it more clear that there is no monkey-patching (RT#86830), and other documentation/example cleanup 0.018 2013-03-22 11:08:50 PDT-0700 (Karen Etheridge) - fix test failures under newer perls by ensuring that serialized comparisons are always performed canonically 0.017 2013-03-22 10:48:34 PDT-0700 (Karen Etheridge) - really fix RT#84094 this time 0.016 2013-03-20 23:01:47 PDT-0700 (Karen Etheridge) - fixed handling of methods such as mirror() (RT#84094) - thanks for the report, Tom Hukins! 0.015 2013-02-09 16:55:18 PST-0800 (Karen Etheridge) - can now create a mapping on a class name or an object where that class or object implements the 'request' method (from a suggestion by mst) 0.014 2012-12-12 10:29:39 PST-0800 (Karen Etheridge) - fix advent link in pod 0.013 2012-12-12 10:24:49 PST-0800 (Karen Etheridge) - silly mistake in examples/application_client_test.pl fixed - link to today's Perl Advent Calendar entry, about this module :) 0.012 2012-12-01 22:57:01 PST-0800 (Karen Etheridge) - examples/ directory added, containing fleshed-out code snippets used in the article for the 2012 Perl Advent Calendar, and a detailed example of some unit tests for a client library 0.011 2012-10-16 11:37:48 PDT-0700 (Karen Etheridge) - remove additional options in constructor before passing to LWP::UserAgent (was causing a carp when $^W was set) - thanks for the report and patch, Nigel Gregoire and Michael Schulthess! - all remaining uses of eval eliminated, by using better heuristics for "can be used as a coderef" logic 0.010 2012-10-06 16:47:33 PDT-0700 (Karen Etheridge) - documentation on integration with XML::Compile::SOAP - questionable uses of eval { $foo->isa } converted to Safe::Isa - imported symbols are no longer available as methods 0.009 2012-08-29 09:54:34 PDT-0700 (Karen Etheridge) - fix test to work with older (pre-6.00) LWP::UserAgent, when _new_response did not take a content argument 0.008 2012-08-21 12:48:35 PDT-0700 (Karen Etheridge) - better handling when the response implementation dies, either with an error message or an HTTP::Response object - network_fallback and map_network_response features added, for sending any or all requests through to the real LWP::UserAgent - new feature: "last_useragent", for getting the last useragent object used globally - allow exact string matching against domains to work even for domains specified via a string-overloaded object - bug fix: properly mask existing mappings in $ua->map_response(..., undef) (v0.007) 0.007 2012-08-14 13:56:50 PDT-0700 (Karen Etheridge) - cleanup of domain->PSGI app functionality for stable release: - string mappings in map_response now match against the URI host, not the full URI string - register_domain is now called register_psgi; unregister_domain is now unregister_psgi; unregister_all is removed, as unmap_all will do the right thing - domain mappings no longer take priority over other mapped responses; all mappings are now tested in the order they were added (with instance mappings matched first over global mappings, as before) 0.006-TRIAL 2012-07-28 16:03:24 PDT-0700 (Karen Etheridge) - new domain->PSGI app functionality, and methods register_domain, unregister_domain, unregister_all 0.005 2012-07-20 17:37:26 PDT-0700 (Karen Etheridge) - additional error checking and tests - also support objects with &{} overloading, in place of subrefs - now doing the same end-of-response cleanup handling that the real LWPUA does: - saving the request on the response - adding Client-Date header - respect show_progress settings - runs handlers 0.004 2012-06-27 15:41:02 PDT-0700 (Karen Etheridge) - map_response now accepts a coderef in place of the HTTP::Response object to return, to allow basing the response data on what was in the request 0.003 2012-06-23 22:11:53 PDT-0700 (Karen Etheridge) - fix compile error on perls earlier than 5.9.5 (which did not have re::is_regexp) 0.002 2012-06-23 09:36:51 PDT-0700 (Karen Etheridge) - minor documentation fixes - renamed from Test::Mock::LWP::UserAgent to Test::LWP::UserAgent (thanks, mst!) 0.001 2012-06-22 17:11:20 PDT-0700 (Karen Etheridge) - Initial release, in all its questionable glory. Test-LWP-UserAgent-0.022/CONTRIBUTING000644 000767 000024 00000004565 12234623641 017112 0ustar00etherstaff000000 000000 CONTRIBUTING Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. The distribution is managed with Dist::Zilla. This means than many of the usual files you might expect are not in the repository, but are generated at release time (e.g. Makefile.PL). However, you can run tests directly using the 'prove' tool: $ prove -l $ prove -lv t/some_test_file.t $ prove -lvr t/ In most cases, 'prove' is entirely sufficent for you to test any patches you have. You may need to satisfy some dependencies. The easiest way to satisfy dependencies is to install the last release -- this is available at http::/metacpan.org/release/Test-LWP-UserAgent. If you use cpanminus, you can do it without downloading the tarball first: $ cpanm --reinstall --installdeps --with-recommends Test-LWP-UserAgent Dist::Zilla is a very powerful authoring tool, but requires a number of author-specific plugins. If you would like to use it for contributing, install it from CPAN, then run one of the following commands, depending on your CPAN client: $ cpan `dzil authordeps` $ dzil authordeps | cpanm You can also do this via cpanm directly: $ cpanm --reinstall --installdeps --with-develop --with-recommends Test-LWP-UserAgent Once installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil test --release $ dzil xtest $ dzil listdeps --json $ dzil build --not You can learn more about Dist::Zilla at http://dzil.org/. The code for this distribution is hosted at GitHub. The main repository is: https://github.com/karenetheridge/Test-LWP-UserAgent. You can submit code changes by forking the repository, pushing your code changes to your clone, and then submitting a pull request. Detailed instructions for doing that is available here: https://help.github.com/ https://help.github.com/articles/creating-a-pull-request If you have found a bug, but do not have an accompanying patch to fix it, you can submit an issue report here: https://rt.cpan.org/Public/Dist/Display.html?Name=Test-LWP-UserAgent or via bug-Test-LWP-UserAgent@rt.cpan.org. This is also a good place to send your questions about the usage of this distribution. This file was generated via Dist::Zilla::Plugin::GenerateFile::ShareDir 0.002 from a template file originating in Dist-Zilla-PluginBundle-Author-ETHER-0.035. Test-LWP-UserAgent-0.022/dist.ini000644 000767 000024 00000000711 12234623641 016711 0ustar00etherstaff000000 000000 name = Test-LWP-UserAgent author = Karen Etheridge copyright_holder = Karen Etheridge copyright_year = 2012 license = Perl_5 [@Author::ETHER] :version = 0.033 -remove = Git::CheckFor::MergeConflicts ; cannot handle unicode string content [Prereqs / DevelopRequires] Test::Warnings = 0 Test::CleanNamespaces = 0 HTTP::Message::PSGI = 0 Plack::Util = 0 ; authordep Pod::Weaver::Section::Contributors [ContributorsFromGit] Test-LWP-UserAgent-0.022/examples/000755 000767 000024 00000000000 12234623641 017064 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/INSTALL000644 000767 000024 00000001761 12234623641 016304 0ustar00etherstaff000000 000000 This is the Perl distribution Test-LWP-UserAgent. Installing Test-LWP-UserAgent is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm Test::LWP::UserAgent If you are installing into a system-wide directory, you may need to pass the "-S" flag to cpanm, which uses sudo to install the module: % cpanm -S Test::LWP::UserAgent ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan Test::LWP::UserAgent ## Manual installation As a last resort, you can manually install it. Download the tarball, untar it, then build it: % perl Build.PL % ./Build && ./Build test Then install it: % ./Build install If you are installing into a system-wide directory, you may need to run: % sudo ./Build install ## Documentation Test-LWP-UserAgent documentation is available as POD. You can run perldoc from a shell to read the documentation: % perldoc Test::LWP::UserAgent Test-LWP-UserAgent-0.022/lib/000755 000767 000024 00000000000 12234623641 016014 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/LICENSE000644 000767 000024 00000043664 12234623641 016270 0ustar00etherstaff000000 000000 This software is copyright (c) 2012 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2012 by Karen Etheridge. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2012 by Karen Etheridge. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Test-LWP-UserAgent-0.022/Makefile.PL000644 000767 000024 00000006722 12234623641 017227 0ustar00etherstaff000000 000000 # This Makefile.PL for Test-LWP-UserAgent was generated by # Dist::Zilla::Plugin::MakeMaker::Fallback 0.003. # Don't edit it but the dist.ini used to construct it. use strict; use warnings; BEGIN { my %configure_requires = ( 'ExtUtils::MakeMaker' => '6.30', 'Module::Build::Tiny' => '0.030', ); my @missing = grep { ! eval "require $_; $_->VERSION($configure_requires{$_}); 1" } keys %configure_requires; if (not @missing) { print "Congratulations, your toolchain understands 'configure_requires'!\n\n"; } else { warn <<'EOW'; *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** If you're seeing this warning, your toolchain is really, really old and you'll almost certainly have problems installing CPAN modules from this century. But never fear, dear user, for we have the technology to fix this! If you're using CPAN.pm to install things, then you can upgrade it using: cpan CPAN If you're using CPANPLUS to install things, then you can upgrade it using: cpanp CPANPLUS If you're using cpanminus, you shouldn't be seeing this message in the first place, so please file an issue on github. If you're installing manually, please retrain your fingers to run Build.PL when present instead. This public service announcement was brought to you by the Perl Toolchain Gang, the irc.perl.org #toolchain IRC channel, and the number 42. EOW sleep 10 if -t STDIN; } } use 5.006; use ExtUtils::MakeMaker 6.30; my %WriteMakefileArgs = ( "ABSTRACT" => "a LWP::UserAgent suitable for simulating and testing network calls", "AUTHOR" => "Karen Etheridge ", "BUILD_REQUIRES" => {}, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "6.30", "Module::Build::Tiny" => "0.030" }, "DISTNAME" => "Test-LWP-UserAgent", "EXE_FILES" => [], "LICENSE" => "perl", "NAME" => "Test::LWP::UserAgent", "PREREQ_PM" => { "Carp" => 0, "HTTP::Date" => 0, "HTTP::Request" => 0, "HTTP::Response" => 0, "HTTP::Status" => 0, "LWP::UserAgent" => 0, "Safe::Isa" => 0, "Scalar::Util" => 0, "Storable" => 0, "Try::Tiny" => 0, "URI" => 0, "namespace::clean" => 0, "parent" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "Class::Load" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec::Functions" => 0, "HTTP::Request::Common" => 0, "List::Util" => 0, "Path::Tiny" => 0, "Test::Deep" => "0.110", "Test::Fatal" => 0, "Test::More" => 0, "Test::Requires" => 0, "Test::TempDir" => 0, "Test::Warnings" => "0.005", "if" => 0, "overload" => 0 }, "VERSION" => "0.022", "test" => { "TESTS" => "t/*.t" } ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { my $tr = delete $WriteMakefileArgs{TEST_REQUIRES}; my $br = $WriteMakefileArgs{BUILD_REQUIRES}; for my $mod ( keys %$tr ) { if ( exists $br->{$mod} ) { $br->{$mod} = $tr->{$mod} if $tr->{$mod} > $br->{$mod}; } else { $br->{$mod} = $tr->{$mod}; } } } unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; my $pp = $WriteMakefileArgs{PREREQ_PM}; for my $mod ( keys %$br ) { if ( exists $pp->{$mod} ) { $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; } else { $pp->{$mod} = $br->{$mod}; } } } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Test-LWP-UserAgent-0.022/MANIFEST000644 000767 000024 00000002025 12234623641 016376 0ustar00etherstaff000000 000000 Build.PL CONTRIBUTING Changes INSTALL LICENSE MANIFEST META.json META.yml Makefile.PL README README.md advent_2012.pod dist.ini examples/MyApp/Client.pm examples/advent_2012_1.pl examples/advent_2012_2.pl examples/advent_2012_3.pl examples/application_client_test.t examples/call_psgi.t examples/myapp.psgi lib/Test/LWP/UserAgent.pm t/00-report-prereqs.t t/01-basic.t t/02-overload.t t/03-handlers.t t/04-psgi.t t/05-exceptions.t t/06-network-fallback.t t/07-mask-response.t t/08-isa-coderef.t t/09-dispatch-to-request-method.t t/10-request-args-network.t t/11-request-args-internal.t t/50-examples-application_client_test.t t/51-call_psgi.t weaver.ini xt/author/00-compile.t xt/author/pod-spell.t xt/release/changes_has_content.t xt/release/clean-namespaces.t xt/release/cpan-changes.t xt/release/distmeta.t xt/release/eol.t xt/release/kwalitee.t xt/release/minimum-version.t xt/release/mojibake.t xt/release/no-tabs.t xt/release/pod-coverage.t xt/release/pod-no404s.t xt/release/pod-syntax.t xt/release/test-version.t xt/release/unused-vars.t Test-LWP-UserAgent-0.022/META.json000644 000767 000024 00000045005 12234623641 016673 0ustar00etherstaff000000 000000 { "abstract" : "a LWP::UserAgent suitable for simulating and testing network calls", "author" : [ "Karen Etheridge " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 5.003, CPAN::Meta::Converter version 2.132830", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Test-LWP-UserAgent", "no_index" : { "directory" : [ "t", "xt", "examples" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.30", "Module::Build::Tiny" : "0.030" } }, "develop" : { "recommends" : { "Dist::Zilla::PluginBundle::Author::ETHER" : "0.035" }, "requires" : { "Dist::Zilla" : "5.003", "Dist::Zilla::Plugin::GitHub::Update" : "0", "Dist::Zilla::Plugin::GithubMeta" : "0", "Dist::Zilla::Plugin::MakeMaker::Fallback" : "0", "Dist::Zilla::Plugin::ModuleBuildTiny" : "0.004", "Dist::Zilla::PluginBundle::Author::ETHER" : "0.033", "File::Spec" : "0", "HTTP::Message::PSGI" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Plack::Util" : "0", "Pod::Coverage::TrustPod" : "0", "Test::CPAN::Changes" : "0.19", "Test::CPAN::Meta" : "0", "Test::CleanNamespaces" : "0", "Test::Kwalitee" : "1.12", "Test::More" : "0.94", "Test::NoTabs" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::Warnings" : "0" } }, "runtime" : { "requires" : { "Carp" : "0", "HTTP::Date" : "0", "HTTP::Request" : "0", "HTTP::Response" : "0", "HTTP::Status" : "0", "LWP::UserAgent" : "0", "Safe::Isa" : "0", "Scalar::Util" : "0", "Storable" : "0", "Try::Tiny" : "0", "URI" : "0", "namespace::clean" : "0", "parent" : "0", "perl" : "5.006", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "0", "CPAN::Meta::Requirements" : "0" }, "requires" : { "Class::Load" : "0", "ExtUtils::MakeMaker" : "0", "File::Spec::Functions" : "0", "HTTP::Request::Common" : "0", "List::Util" : "0", "Path::Tiny" : "0", "Test::Deep" : "0.110", "Test::Fatal" : "0", "Test::More" : "0", "Test::Requires" : "0", "Test::TempDir" : "0", "Test::Warnings" : "0.005", "if" : "0", "overload" : "0" } } }, "provides" : { "Test::LWP::UserAgent" : { "file" : "lib/Test/LWP/UserAgent.pm", "version" : "0.022" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "bug-Test-LWP-UserAgent@rt.cpan.org", "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=Test-LWP-UserAgent" }, "homepage" : "https://github.com/karenetheridge/Test-LWP-UserAgent", "repository" : { "type" : "git", "url" : "https://github.com/karenetheridge/Test-LWP-UserAgent.git", "web" : "https://github.com/karenetheridge/Test-LWP-UserAgent" } }, "version" : "0.022", "x_Dist_Zilla" : { "perl" : { "version" : "5.019005" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::Git::NextVersion", "name" : "@Author::ETHER/Git::NextVersion", "version" : "2.017" }, { "class" : "Dist::Zilla::Plugin::PromptIfStale", "name" : "@Author::ETHER/build", "version" : "0.008" }, { "class" : "Dist::Zilla::Plugin::PromptIfStale", "name" : "@Author::ETHER/release", "version" : "0.008" }, { "class" : "Dist::Zilla::Plugin::ExecDir", "name" : "@Author::ETHER/ExecDir", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::ShareDir", "name" : "@Author::ETHER/ShareDir", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::FileFinder::ByName", "name" : "@Author::ETHER/Examples", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Git::GatherDir", "name" : "@Author::ETHER/Git::GatherDir", "version" : "2.017" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "@Author::ETHER/MetaYAML", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "@Author::ETHER/MetaJSON", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "@Author::ETHER/License", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Readme", "name" : "@Author::ETHER/Readme", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "@Author::ETHER/Manifest", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::GenerateFile::ShareDir", "config" : { "Dist::Zilla::Plugin::GenerateFile::ShareDir" : { "destination_filename" : "CONTRIBUTING", "dist" : "Dist-Zilla-PluginBundle-Author-ETHER", "encoding" : "UTF-8", "source_filename" : "CONTRIBUTING" } }, "name" : "@Author::ETHER/GenerateFile::ShareDir", "version" : "0.002" }, { "class" : "Dist::Zilla::Plugin::Test::Compile", "config" : { "Dist::Zilla::Plugin::Test::Compile" : { "filename" : "xt/author/00-compile.t", "module_finder" : [ ":InstallModules" ], "script_finder" : [ ":ExecFiles", "@Author::ETHER/Examples" ] } }, "name" : "@Author::ETHER/Test::Compile", "version" : "2.037" }, { "class" : "Dist::Zilla::Plugin::Test::NoTabs", "config" : { "Dist::Zilla::Plugin::Test::NoTabs" : { "module_finder" : [ ":InstallModules" ], "script_finder" : [ ":ExecFiles", "@Author::ETHER/Examples" ] } }, "name" : "@Author::ETHER/Test::NoTabs", "version" : "0.05" }, { "class" : "Dist::Zilla::Plugin::EOLTests", "name" : "@Author::ETHER/EOLTests", "version" : "0.02" }, { "class" : "Dist::Zilla::Plugin::MetaTests", "name" : "@Author::ETHER/MetaTests", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Test::Version", "name" : "@Author::ETHER/Test::Version", "version" : "0.002004" }, { "class" : "Dist::Zilla::Plugin::Test::CPAN::Changes", "name" : "@Author::ETHER/Test::CPAN::Changes", "version" : "0.008" }, { "class" : "Dist::Zilla::Plugin::Test::ChangesHasContent", "name" : "@Author::ETHER/Test::ChangesHasContent", "version" : "0.006" }, { "class" : "Dist::Zilla::Plugin::Test::UnusedVars", "name" : "@Author::ETHER/Test::UnusedVars", "version" : "2.000005" }, { "class" : "Dist::Zilla::Plugin::Test::MinimumVersion", "name" : "@Author::ETHER/Test::MinimumVersion", "version" : "2.000005" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "@Author::ETHER/PodSyntaxTests", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::PodCoverageTests", "name" : "@Author::ETHER/PodCoverageTests", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Test::PodSpelling", "name" : "@Author::ETHER/Test::PodSpelling", "version" : "2.006001" }, { "class" : "Dist::Zilla::Plugin::Test::Pod::No404s", "name" : "@Author::ETHER/Test::Pod::No404s", "version" : "1.001" }, { "class" : "Dist::Zilla::Plugin::Test::Kwalitee", "name" : "@Author::ETHER/Test::Kwalitee", "version" : "2.07" }, { "class" : "Dist::Zilla::Plugin::MojibakeTests", "name" : "@Author::ETHER/MojibakeTests", "version" : "0.5" }, { "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", "name" : "@Author::ETHER/Test::ReportPrereqs", "version" : "0.010" }, { "class" : "Dist::Zilla::Plugin::PruneCruft", "name" : "@Author::ETHER/PruneCruft", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::ManifestSkip", "name" : "@Author::ETHER/ManifestSkip", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Authority", "name" : "@Author::ETHER/Authority", "version" : "1.006" }, { "class" : "Dist::Zilla::Plugin::Git::Describe", "name" : "@Author::ETHER/Git::Describe", "version" : "0.003" }, { "class" : "Dist::Zilla::Plugin::PkgVersion", "name" : "@Author::ETHER/PkgVersion", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::PodWeaver", "config" : { "Dist::Zilla::Plugin::PodWeaver" : { "finder" : [ ":InstallModules", ":ExecFiles" ] } }, "name" : "@Author::ETHER/PodWeaver", "version" : "4.000" }, { "class" : "Dist::Zilla::Plugin::NextRelease", "name" : "@Author::ETHER/NextRelease", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "name" : "@Author::ETHER/ReadmeAnyFromPod", "version" : "0.133030" }, { "class" : "Dist::Zilla::Plugin::GithubMeta", "name" : "@Author::ETHER/GithubMeta", "version" : "0.42" }, { "class" : "Dist::Zilla::Plugin::AutoMetaResources", "name" : "@Author::ETHER/AutoMetaResources", "version" : "1.20" }, { "class" : "Dist::Zilla::Plugin::MetaNoIndex", "name" : "@Author::ETHER/MetaNoIndex", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "@Author::ETHER/MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Package", "config" : { "Dist::Zilla::Plugin::MetaProvides::Package" : {}, "Dist::Zilla::Role::MetaProvider::Provider" : { "inherit_missing" : "1", "inherit_version" : "1", "meta_noindex" : "1" } }, "name" : "@Author::ETHER/MetaProvides::Package", "version" : "1.15000000" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "@Author::ETHER/MetaConfig", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::AutoPrereqs", "name" : "@Author::ETHER/AutoPrereqs", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::MinimumPerl", "name" : "@Author::ETHER/MinimumPerl", "version" : "1.003" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "requires" } }, "name" : "@Author::ETHER/installer_requirements", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "recommends" } }, "name" : "@Author::ETHER/pluginbundle_version", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "name" : "@Author::ETHER/RunExtraTests", "version" : "0.015" }, { "class" : "Dist::Zilla::Plugin::MakeMaker::Fallback", "name" : "@Author::ETHER/MakeMaker::Fallback", "version" : "0.003" }, { "class" : "Dist::Zilla::Plugin::ModuleBuildTiny", "name" : "@Author::ETHER/ModuleBuildTiny", "version" : "0.005" }, { "class" : "Dist::Zilla::Plugin::InstallGuide", "name" : "@Author::ETHER/InstallGuide", "version" : "1.200001" }, { "class" : "Dist::Zilla::Plugin::CheckSelfDependency", "name" : "@Author::ETHER/CheckSelfDependency", "version" : "0.005" }, { "class" : "Dist::Zilla::Plugin::Run::AfterBuild", "name" : "@Author::ETHER/Run::AfterBuild", "version" : "0.020" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "name" : "@Author::ETHER/Git::Check", "version" : "2.017" }, { "class" : "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch", "name" : "@Author::ETHER/Git::CheckFor::CorrectBranch", "version" : "0.007" }, { "class" : "Dist::Zilla::Plugin::Git::Remote::Check", "name" : "@Author::ETHER/Git::Remote::Check", "version" : "0.1.2" }, { "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed", "name" : "@Author::ETHER/CheckPrereqsIndexed", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "@Author::ETHER/TestRelease", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "@Author::ETHER/UploadToCPAN", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "name" : "@Author::ETHER/CopyFilesFromRelease", "version" : "0.001" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "name" : "@Author::ETHER/Git::Commit", "version" : "2.017" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "name" : "@Author::ETHER/Git::Tag", "version" : "2.017" }, { "class" : "Dist::Zilla::Plugin::GitHub::Update", "name" : "@Author::ETHER/GitHub::Update", "version" : "0.35" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "name" : "@Author::ETHER/Git::Push", "version" : "2.017" }, { "class" : "Dist::Zilla::Plugin::InstallRelease", "name" : "@Author::ETHER/InstallRelease", "version" : "0.008" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "@Author::ETHER/ConfirmRelease", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "requires" } }, "name" : "@Author::ETHER/via_options", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::Prereqs", "config" : { "Dist::Zilla::Plugin::Prereqs" : { "phase" : "develop", "type" : "requires" } }, "name" : "DevelopRequires", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::ContributorsFromGit", "name" : "ContributorsFromGit", "version" : "0.006" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "5.003" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "5.003" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : "0" }, "version" : "5.003" } }, "x_authority" : "cpan:ETHER" } Test-LWP-UserAgent-0.022/META.yml000644 000767 000024 00000026763 12234623641 016535 0ustar00etherstaff000000 000000 --- abstract: 'a LWP::UserAgent suitable for simulating and testing network calls' author: - 'Karen Etheridge ' build_requires: Class::Load: 0 ExtUtils::MakeMaker: 0 File::Spec::Functions: 0 HTTP::Request::Common: 0 List::Util: 0 Path::Tiny: 0 Test::Deep: 0.110 Test::Fatal: 0 Test::More: 0 Test::Requires: 0 Test::TempDir: 0 Test::Warnings: 0.005 if: 0 overload: 0 configure_requires: ExtUtils::MakeMaker: 6.30 Module::Build::Tiny: 0.030 dynamic_config: 0 generated_by: 'Dist::Zilla version 5.003, CPAN::Meta::Converter version 2.132830' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Test-LWP-UserAgent no_index: directory: - t - xt - examples provides: Test::LWP::UserAgent: file: lib/Test/LWP/UserAgent.pm version: 0.022 requires: Carp: 0 HTTP::Date: 0 HTTP::Request: 0 HTTP::Response: 0 HTTP::Status: 0 LWP::UserAgent: 0 Safe::Isa: 0 Scalar::Util: 0 Storable: 0 Try::Tiny: 0 URI: 0 namespace::clean: 0 parent: 0 perl: 5.006 strict: 0 warnings: 0 resources: bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Test-LWP-UserAgent homepage: https://github.com/karenetheridge/Test-LWP-UserAgent repository: https://github.com/karenetheridge/Test-LWP-UserAgent.git version: 0.022 x_Dist_Zilla: perl: version: 5.019005 plugins: - class: Dist::Zilla::Plugin::Git::NextVersion name: '@Author::ETHER/Git::NextVersion' version: 2.017 - class: Dist::Zilla::Plugin::PromptIfStale name: '@Author::ETHER/build' version: 0.008 - class: Dist::Zilla::Plugin::PromptIfStale name: '@Author::ETHER/release' version: 0.008 - class: Dist::Zilla::Plugin::ExecDir name: '@Author::ETHER/ExecDir' version: 5.003 - class: Dist::Zilla::Plugin::ShareDir name: '@Author::ETHER/ShareDir' version: 5.003 - class: Dist::Zilla::Plugin::FileFinder::ByName name: '@Author::ETHER/Examples' version: 5.003 - class: Dist::Zilla::Plugin::Git::GatherDir name: '@Author::ETHER/Git::GatherDir' version: 2.017 - class: Dist::Zilla::Plugin::MetaYAML name: '@Author::ETHER/MetaYAML' version: 5.003 - class: Dist::Zilla::Plugin::MetaJSON name: '@Author::ETHER/MetaJSON' version: 5.003 - class: Dist::Zilla::Plugin::License name: '@Author::ETHER/License' version: 5.003 - class: Dist::Zilla::Plugin::Readme name: '@Author::ETHER/Readme' version: 5.003 - class: Dist::Zilla::Plugin::Manifest name: '@Author::ETHER/Manifest' version: 5.003 - class: Dist::Zilla::Plugin::GenerateFile::ShareDir config: Dist::Zilla::Plugin::GenerateFile::ShareDir: destination_filename: CONTRIBUTING dist: Dist-Zilla-PluginBundle-Author-ETHER encoding: UTF-8 source_filename: CONTRIBUTING name: '@Author::ETHER/GenerateFile::ShareDir' version: 0.002 - class: Dist::Zilla::Plugin::Test::Compile config: Dist::Zilla::Plugin::Test::Compile: filename: xt/author/00-compile.t module_finder: - ':InstallModules' script_finder: - ':ExecFiles' - '@Author::ETHER/Examples' name: '@Author::ETHER/Test::Compile' version: 2.037 - class: Dist::Zilla::Plugin::Test::NoTabs config: Dist::Zilla::Plugin::Test::NoTabs: module_finder: - ':InstallModules' script_finder: - ':ExecFiles' - '@Author::ETHER/Examples' name: '@Author::ETHER/Test::NoTabs' version: 0.05 - class: Dist::Zilla::Plugin::EOLTests name: '@Author::ETHER/EOLTests' version: 0.02 - class: Dist::Zilla::Plugin::MetaTests name: '@Author::ETHER/MetaTests' version: 5.003 - class: Dist::Zilla::Plugin::Test::Version name: '@Author::ETHER/Test::Version' version: 0.002004 - class: Dist::Zilla::Plugin::Test::CPAN::Changes name: '@Author::ETHER/Test::CPAN::Changes' version: 0.008 - class: Dist::Zilla::Plugin::Test::ChangesHasContent name: '@Author::ETHER/Test::ChangesHasContent' version: 0.006 - class: Dist::Zilla::Plugin::Test::UnusedVars name: '@Author::ETHER/Test::UnusedVars' version: 2.000005 - class: Dist::Zilla::Plugin::Test::MinimumVersion name: '@Author::ETHER/Test::MinimumVersion' version: 2.000005 - class: Dist::Zilla::Plugin::PodSyntaxTests name: '@Author::ETHER/PodSyntaxTests' version: 5.003 - class: Dist::Zilla::Plugin::PodCoverageTests name: '@Author::ETHER/PodCoverageTests' version: 5.003 - class: Dist::Zilla::Plugin::Test::PodSpelling name: '@Author::ETHER/Test::PodSpelling' version: 2.006001 - class: Dist::Zilla::Plugin::Test::Pod::No404s name: '@Author::ETHER/Test::Pod::No404s' version: 1.001 - class: Dist::Zilla::Plugin::Test::Kwalitee name: '@Author::ETHER/Test::Kwalitee' version: 2.07 - class: Dist::Zilla::Plugin::MojibakeTests name: '@Author::ETHER/MojibakeTests' version: 0.5 - class: Dist::Zilla::Plugin::Test::ReportPrereqs name: '@Author::ETHER/Test::ReportPrereqs' version: 0.010 - class: Dist::Zilla::Plugin::PruneCruft name: '@Author::ETHER/PruneCruft' version: 5.003 - class: Dist::Zilla::Plugin::ManifestSkip name: '@Author::ETHER/ManifestSkip' version: 5.003 - class: Dist::Zilla::Plugin::Authority name: '@Author::ETHER/Authority' version: 1.006 - class: Dist::Zilla::Plugin::Git::Describe name: '@Author::ETHER/Git::Describe' version: 0.003 - class: Dist::Zilla::Plugin::PkgVersion name: '@Author::ETHER/PkgVersion' version: 5.003 - class: Dist::Zilla::Plugin::PodWeaver config: Dist::Zilla::Plugin::PodWeaver: finder: - ':InstallModules' - ':ExecFiles' name: '@Author::ETHER/PodWeaver' version: 4.000 - class: Dist::Zilla::Plugin::NextRelease name: '@Author::ETHER/NextRelease' version: 5.003 - class: Dist::Zilla::Plugin::ReadmeAnyFromPod name: '@Author::ETHER/ReadmeAnyFromPod' version: 0.133030 - class: Dist::Zilla::Plugin::GithubMeta name: '@Author::ETHER/GithubMeta' version: 0.42 - class: Dist::Zilla::Plugin::AutoMetaResources name: '@Author::ETHER/AutoMetaResources' version: 1.20 - class: Dist::Zilla::Plugin::MetaNoIndex name: '@Author::ETHER/MetaNoIndex' version: 5.003 - class: Dist::Zilla::Plugin::FinderCode name: '@Author::ETHER/MetaProvides::Package/AUTOVIV/:InstallModulesPM' version: 5.003 - class: Dist::Zilla::Plugin::MetaProvides::Package config: Dist::Zilla::Plugin::MetaProvides::Package: {} Dist::Zilla::Role::MetaProvider::Provider: inherit_missing: 1 inherit_version: 1 meta_noindex: 1 name: '@Author::ETHER/MetaProvides::Package' version: 1.15000000 - class: Dist::Zilla::Plugin::MetaConfig name: '@Author::ETHER/MetaConfig' version: 5.003 - class: Dist::Zilla::Plugin::AutoPrereqs name: '@Author::ETHER/AutoPrereqs' version: 5.003 - class: Dist::Zilla::Plugin::MinimumPerl name: '@Author::ETHER/MinimumPerl' version: 1.003 - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: develop type: requires name: '@Author::ETHER/installer_requirements' version: 5.003 - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: develop type: recommends name: '@Author::ETHER/pluginbundle_version' version: 5.003 - class: Dist::Zilla::Plugin::RunExtraTests name: '@Author::ETHER/RunExtraTests' version: 0.015 - class: Dist::Zilla::Plugin::MakeMaker::Fallback name: '@Author::ETHER/MakeMaker::Fallback' version: 0.003 - class: Dist::Zilla::Plugin::ModuleBuildTiny name: '@Author::ETHER/ModuleBuildTiny' version: 0.005 - class: Dist::Zilla::Plugin::InstallGuide name: '@Author::ETHER/InstallGuide' version: 1.200001 - class: Dist::Zilla::Plugin::CheckSelfDependency name: '@Author::ETHER/CheckSelfDependency' version: 0.005 - class: Dist::Zilla::Plugin::Run::AfterBuild name: '@Author::ETHER/Run::AfterBuild' version: 0.020 - class: Dist::Zilla::Plugin::Git::Check name: '@Author::ETHER/Git::Check' version: 2.017 - class: Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch name: '@Author::ETHER/Git::CheckFor::CorrectBranch' version: 0.007 - class: Dist::Zilla::Plugin::Git::Remote::Check name: '@Author::ETHER/Git::Remote::Check' version: 0.1.2 - class: Dist::Zilla::Plugin::CheckPrereqsIndexed name: '@Author::ETHER/CheckPrereqsIndexed' version: 0.009 - class: Dist::Zilla::Plugin::TestRelease name: '@Author::ETHER/TestRelease' version: 5.003 - class: Dist::Zilla::Plugin::UploadToCPAN name: '@Author::ETHER/UploadToCPAN' version: 5.003 - class: Dist::Zilla::Plugin::CopyFilesFromRelease name: '@Author::ETHER/CopyFilesFromRelease' version: 0.001 - class: Dist::Zilla::Plugin::Git::Commit name: '@Author::ETHER/Git::Commit' version: 2.017 - class: Dist::Zilla::Plugin::Git::Tag name: '@Author::ETHER/Git::Tag' version: 2.017 - class: Dist::Zilla::Plugin::GitHub::Update name: '@Author::ETHER/GitHub::Update' version: 0.35 - class: Dist::Zilla::Plugin::Git::Push name: '@Author::ETHER/Git::Push' version: 2.017 - class: Dist::Zilla::Plugin::InstallRelease name: '@Author::ETHER/InstallRelease' version: 0.008 - class: Dist::Zilla::Plugin::ConfirmRelease name: '@Author::ETHER/ConfirmRelease' version: 5.003 - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: develop type: requires name: '@Author::ETHER/via_options' version: 5.003 - class: Dist::Zilla::Plugin::Prereqs config: Dist::Zilla::Plugin::Prereqs: phase: develop type: requires name: DevelopRequires version: 5.003 - class: Dist::Zilla::Plugin::ContributorsFromGit name: ContributorsFromGit version: 0.006 - class: Dist::Zilla::Plugin::FinderCode name: ':InstallModules' version: 5.003 - class: Dist::Zilla::Plugin::FinderCode name: ':IncModules' version: 5.003 - class: Dist::Zilla::Plugin::FinderCode name: ':TestFiles' version: 5.003 - class: Dist::Zilla::Plugin::FinderCode name: ':ExecFiles' version: 5.003 - class: Dist::Zilla::Plugin::FinderCode name: ':ShareFiles' version: 5.003 - class: Dist::Zilla::Plugin::FinderCode name: ':MainModule' version: 5.003 zilla: class: Dist::Zilla::Dist::Builder config: is_trial: 0 version: 5.003 x_authority: cpan:ETHER Test-LWP-UserAgent-0.022/README000644 000767 000024 00000000525 12234623641 016130 0ustar00etherstaff000000 000000 This archive contains the distribution Test-LWP-UserAgent, version 0.022: a LWP::UserAgent suitable for simulating and testing network calls This software is copyright (c) 2012 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Test-LWP-UserAgent-0.022/README.md000644 000767 000024 00000040744 12234623641 016536 0ustar00etherstaff000000 000000 # NAME Test::LWP::UserAgent - a LWP::UserAgent suitable for simulating and testing network calls # VERSION version 0.022 # SYNOPSIS In your application code: use URI; use HTTP::Request::Common; use LWP::UserAgent; my $useragent = $self->useragent || LWP::UserAgent->new; my $uri = URI->new('http://example.com'); $uri->port('3000'); $uri->path('success'); my $request = POST($uri, a => 1); my $response = $useragent->request($request); Then, in your tests: use Test::LWP::UserAgent; use Test::More; my $useragent = Test::LWP::UserAgent->new; $useragent->map_response( qr{example.com/success}, HTTP::Response->new('200', 'OK', ['Content-Type' => 'text/plain'], '')); $useragent->map_response( qr{example.com/fail}, HTTP::Response->new('500', 'ERROR', ['Content-Type' => 'text/plain'], '')); # now, do something that sends a request, and test how your application # responds to that response # DESCRIPTION This module is a subclass of [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) which overrides a few key low-level methods that are concerned with actually sending your request over the network, allowing an interception of that request and simulating a particular response. This greatly facilitates testing of client networking code where the server follows a known protocol. The synopsis describes a classic case where you want to test how your application reacts to various responses from the server. This module will let you send back various responses depending on the request, without having to set up a real server to test against. This can be invaluable when you need to test edge cases or error conditions that do not normally arise from the server. There are a lot of different ways you can set up the response mappings, and hook into this module; see the documentation for the individual interface methods. You can use a [PSGI](http://search.cpan.org/perldoc?PSGI) app to handle the requests - see `examples/call\_psgi.t` in this dist, and also ["register\_psgi"](#register\_psgi) below. OR, you can route some or all requests through the network as normal, but still gain the hooks provided by this class to test what was sent and received: my $useragent = Test::LWP::UserAgent->new(network_fallback => 1); or: $useragent->map_network_response(qr/real.network.host/); # ... generate a request... # and then in your tests: is( $useragent->last_useragent->timeout, 180, 'timeout was overridden properly', ); is( $useragent->last_http_request_sent->uri, 'uri my code should have constructed', ); is( $useragent->last_http_response_received->code, '200', 'I should have gotten an OK response', ); ## Ensuring the right useragent is used Note that [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) itself is not monkey-patched - you must use this module (or a subclass) to send your request, or it cannot be caught and processed. One common mechanism to swap out the useragent implementation is via a lazily-built Moose attribute; if no override is provided at construction time, default to `LWP::UserAgent->new(%options)`. Additionally, most methods can be called as class methods, which will store the settings globally, so that any instance of [Test::LWP::UserAgent](http://search.cpan.org/perldoc?Test::LWP::UserAgent) can use them, which can simplify some of your application code. # METHODS - `new` Accepts all options as in [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent), including `use_eval`, an undocumented boolean which is enabled by default. When set, sending the HTTP request is wrapped in an `eval {}`, allowing all exceptions to be caught and an appropriate error response (usually HTTP 500) to be returned. You may want to unset this if you really want to test extraordinary errors within your networking code. Normally, you should leave it alone, as [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) and this module are capable of handling normal errors. Plus, this option is added: - `network_fallback => ` If true, requests passing through this object that do not match a previously-configured mapping or registration will be directed to the network. (To only divert _matched_ requests rather than unmatched requests, use `map_network_response`, see below.) This option is also available as a read/write accessor via `$useragent->network_fallback()`. __All other methods below may be called on a specific object instance, or as a class method.__ If called as on a blessed object, the action performed or data returned is limited to just that object; if called as a class method, the action or data is global. - `map_response($request_specification, $http_response)` With this method, you set up what [HTTP::Response](http://search.cpan.org/perldoc?HTTP::Response) should be returned for each request received. The request match specification can be described in multiple ways: - string The string is matched identically against the `host` field of the [URI](http://search.cpan.org/perldoc?URI) in the request. $test_ua->map_response('example.com', HTTP::Response->new('500')); - regexp The regexp is matched against the URI in the request. $test_ua->map_response(qr{foo/bar}, HTTP::Response->new('200')); $test_ua->map_response(qr{baz/quux}, HTTP::Response->new('500')); - code The provided coderef is passed a single argument, the [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request), and returns a boolean indicating if there is a match. # matches all GET and POST requests $test_ua->map_response(sub { my $request = shift; return 1 if $request->method eq 'GET' || $request->method eq 'POST'; }, HTTP::Response->new('200'), ); - [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request) object The [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request) object is matched identically (including all query parameters, headers etc) against the provided object. The response can be represented either as a literal [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request) object, or as a coderef that is run at the time of matching, with the request passed as the single argument: HTTP::Response->new(...); or sub { my $request = shift; HTTP::Response->new(...); } Instance mappings take priority over global (class method) mappings - if no matches are found from mappings added to the instance, the global mappings are then examined. When no matches have been found, a 404 response is returned. - `map_network_response($request_description)` Same as `map_response` above, only requests that match this description will not use a response that you specify, but instead uses a real [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) to dispatch your request to the network. If called on an instance, all options passed to the constructor (e.g. timeout) are used for making the real network call. If called as a class method, a pristine [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) object with no customized options will be used instead. - `unmap_all(instance_only?)` When called as a class method, removes all mappings set up globally (across all objects). Mappings set up on an individual object will still remain. When called as an object method, removes _all_ mappings both globally and on this instance, unless a true value is passed as an argument, in which only mappings local to the object will be removed. (Any true value will do, so you can pass a meaningful string.) - `register_psgi($domain, $app)` Register a particular [PSGI](http://search.cpan.org/perldoc?PSGI) app (code reference) to be used when requests for a domain are received (matches are made exactly against `$request->uri->host`). The request is passed to the `$app` for processing, and the [PSGI](http://search.cpan.org/perldoc?PSGI) response is converted back to an [HTTP::Response](http://search.cpan.org/perldoc?HTTP::Response) (you must already have loaded [HTTP::Message::PSGI](http://search.cpan.org/perldoc?HTTP::Message::PSGI) or equivalent, as this is not done for you). You can also use `register_psgi` with a regular expression as the first argument, or any of the other forms used by `map_response`, if you wish, as calling `$test_ua->register_psgi($domain, $app)` is equivalent to: $test_ua->map_response( $domain, sub { HTTP::Response->from_psgi($app->($_[0]->to_psgi)) }, ); This feature is useful for testing your PSGI applications, or for simulating a server so as to test your client code. You might find using [Plack::Test](http://search.cpan.org/perldoc?Plack::Test) or [Plack::Test::ExternalServer](http://search.cpan.org/perldoc?Plack::Test::ExternalServer) easier for your needs, so check those out as well. - `unregister_psgi($domain, instance_only?)` When called as a class method, removes a domain->PSGI app entry that had been registered globally. Some mappings set up on an individual object may still remain. When called as an object method, removes a domain registration that was made both globally and locally, unless a true value was passed as the second argument, in which case only the registration local to the object will be removed. This allows a different mapping made globally to take over. If you want to mask a global registration on just one particular instance, then add `undef` as a mapping on your instance: $useragent->map_response($domain, undef); - `last_http_request_sent` The last [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request) object that this object (if called on an object) or module (if called as a class method) processed, whether or not it matched a mapping you set up earlier. Note that this is also available via `last_http_response_received->request`. - `last_http_response_received` The last [HTTP::Response](http://search.cpan.org/perldoc?HTTP::Response) object that this module returned, as a result of a mapping you set up earlier with `map_response`. You shouldn't normally need to use this, as you know what you responded with - you should instead be testing how your code reacted to receiving this response. - `last_useragent` The last Test::LWP::UserAgent object that was used to send a request. Obviously this only provides new information if called as a class method; you can use this if you don't have direct control over the useragent itself, to get the object that was used, to verify options such as the network timeout. - `network_fallback` Getter/setter method for the network\_fallback preference that will be used on this object (if called as an instance method), or globally, if called as a class method. Note that the actual behaviour used on an object is the ORed value of the instance setting and the global setting. - `send_request($request)` This is the only method from [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) that has been overridden, which processes the [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request), sends to the network, then creates the [HTTP::Response](http://search.cpan.org/perldoc?HTTP::Response) object from the reply received. Here, we loop through your local and global domain registrations, and local and global mappings (in this order) and returns the first match found; otherwise, a simple 404 response is returned (unless `network_fallback` was specified as a constructor option, in which case unmatched requests will be delivered to the network.) All other methods from [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) are available unchanged. # Usage with SOAP requests - [SOAP::Lite](http://search.cpan.org/perldoc?SOAP::Lite) To use this module when communicating via [SOAP::Lite](http://search.cpan.org/perldoc?SOAP::Lite) with a SOAP server (either a real one, with live network requests, [see above](#network\_fallback) or with one simulated with mapped responses), simply do this: use SOAP::Lite; use SOAP::Transport::HTTP; $SOAP::Transport::HTTP::Client::USERAGENT_CLASS = 'Test::LWP::UserAgent'; You must then make all your configuration changes and mappings globally. See also ["CHANGING THE DEFAULT USERAGENT CLASS" in SOAP::Transport](http://search.cpan.org/perldoc?SOAP::Transport#CHANGING THE DEFAULT USERAGENT CLASS). - [XML::Compile::SOAP](http://search.cpan.org/perldoc?XML::Compile::SOAP) When using [XML::Compile::SOAP](http://search.cpan.org/perldoc?XML::Compile::SOAP) with a compiled WSDL, you can change the useragent object via [XML::Compile::Transport::SOAPHTTP](http://search.cpan.org/perldoc?XML::Compile::Transport::SOAPHTTP): my $call = $wsdl->compileClient( $interface_name, transport => XML::Compile::Transport::SOAPHTTP->new( user_agent => $useragent, address => $wsdl->endPoint, ), ); See also ["Adding HTTP headers" in XML::Compile::SOAP::FAQ](http://search.cpan.org/perldoc?XML::Compile::SOAP::FAQ#Adding HTTP headers). # MOTIVATION Most mock libraries on the CPAN use [Test::MockObject](http://search.cpan.org/perldoc?Test::MockObject), which is widely considered not good practice (among other things, `@ISA` is violated, it requires knowing far too much about the module's internals, and is very clumsy to work with). ([This blog entry](http://search.cpan.org/perldoc?hashbang.ca#2011/09/23/mocking-lwpuseragent) is one of many that chronicles its issues.) This module is a direct descendant of [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent), exports nothing into your namespace, and all access is via method calls, so it is fully inheritable should you desire to add more features or override some bits of functionality. (Aside from the constructor), it only overrides the one method in [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) that issues calls to the network, so real [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request) and [HTTP::Headers](http://search.cpan.org/perldoc?HTTP::Headers) objects are used throughout. It provides a method (`last_http_request_sent`) to access the last [HTTP::Request](http://search.cpan.org/perldoc?HTTP::Request), for testing things like the URI and headers that your code sent to [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent). # SUPPORT Bugs may be submitted through [the RT bug tracker](https://rt.cpan.org/Public/Dist/Display.html?Name=Test-LWP-UserAgent) (or [bug-Test-LWP-UserAgent@rt.cpan.org](http://search.cpan.org/perldoc?bug-Test-LWP-UserAgent@rt.cpan.org)). I am also usually active on irc, as 'ether' at `irc.perl.org`. # ACKNOWLEDGEMENTS [AirG Inc.](http://corp.airg.com), my former employer, and the first user of this distribution. mst - Matt S. Trout , for the better name of this distribution, and for the PSGI registration concept. Also Yury Zavarin, whose [Test::Mock::LWP::Dispatch](http://search.cpan.org/perldoc?Test::Mock::LWP::Dispatch) inspired me to write this module, and from where I borrowed some aspects of the API. # SEE ALSO - [Perl advent article, 2012](http://www.perladvent.org/2012/2012-12-12.html) - [Test::Mock::LWP::Dispatch](http://search.cpan.org/perldoc?Test::Mock::LWP::Dispatch) - [Test::Mock::LWP::UserAgent](http://search.cpan.org/perldoc?Test::Mock::LWP::UserAgent) - [LWP::UserAgent](http://search.cpan.org/perldoc?LWP::UserAgent) - [PSGI](http://search.cpan.org/perldoc?PSGI), [HTTP::Message::PSGI](http://search.cpan.org/perldoc?HTTP::Message::PSGI), [LWP::Protocol::PSGI](http://search.cpan.org/perldoc?LWP::Protocol::PSGI), - [Plack::Test](http://search.cpan.org/perldoc?Plack::Test), [Plack::Test::ExternalServer](http://search.cpan.org/perldoc?Plack::Test::ExternalServer) # AUTHOR Karen Etheridge # COPYRIGHT AND LICENSE This software is copyright (c) 2012 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Test-LWP-UserAgent-0.022/t/000755 000767 000024 00000000000 12234623641 015511 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/weaver.ini000644 000767 000024 00000000131 12234623641 017233 0ustar00etherstaff000000 000000 [@Default] [-Transformer] transformer = List [-StopWords] [-Encoding] [Contributors] Test-LWP-UserAgent-0.022/xt/000755 000767 000024 00000000000 12234623641 015701 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/xt/author/000755 000767 000024 00000000000 12234623641 017203 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/xt/release/000755 000767 000024 00000000000 12234623641 017321 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/xt/release/changes_has_content.t000644 000767 000024 00000002011 12234623641 023475 0ustar00etherstaff000000 000000 #!perl use Test::More tests => 2; note 'Checking Changes'; my $changes_file = 'Changes'; my $newver = '0.022'; my $trial_token = '-TRIAL'; SKIP: { ok(-e $changes_file, "$changes_file file exists") or skip 'Changes is missing', 1; ok(_get_changes($newver), "$changes_file has content for $newver"); } done_testing; # _get_changes copied and adapted from Dist::Zilla::Plugin::Git::Commit # by Jerome Quelin sub _get_changes { my $newver = shift; # parse changelog to find commit message open(my $fh, '<', $changes_file) or die "cannot open $changes_file: $!"; my $changelog = join('', <$fh>); close $fh; my @content = grep { /^$newver(?:$trial_token)?(?:\s+|$)/ ... /^\S/ } # from newver to un-indented split /\n/, $changelog; shift @content; # drop the version line # drop unindented last line and trailing blank lines pop @content while ( @content && $content[-1] =~ /^(?:\S|\s*$)/ ); # return number of non-blank lines return scalar @content; } Test-LWP-UserAgent-0.022/xt/release/clean-namespaces.t000644 000767 000024 00000000172 12234623641 022705 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use Test::CleanNamespaces; all_namespaces_clean; done_testing; Test-LWP-UserAgent-0.022/xt/release/cpan-changes.t000644 000767 000024 00000000263 12234623641 022036 0ustar00etherstaff000000 000000 #!perl use strict; use warnings; use Test::More 0.96 tests => 2; use_ok('Test::CPAN::Changes'); subtest 'changes_ok' => sub { changes_file_ok('Changes'); }; done_testing(); Test-LWP-UserAgent-0.022/xt/release/distmeta.t000644 000767 000024 00000000217 12234623641 021320 0ustar00etherstaff000000 000000 #!perl use Test::More; eval "use Test::CPAN::Meta"; plan skip_all => "Test::CPAN::Meta required for testing META.yml" if $@; meta_yaml_ok(); Test-LWP-UserAgent-0.022/xt/release/eol.t000644 000767 000024 00000000240 12234623641 020261 0ustar00etherstaff000000 000000 use strict; use warnings; use Test::More; eval 'use Test::EOL'; plan skip_all => 'Test::EOL required' if $@; all_perl_files_ok({ trailing_whitespace => 1 }); Test-LWP-UserAgent-0.022/xt/release/kwalitee.t000644 000767 000024 00000000166 12234623641 021316 0ustar00etherstaff000000 000000 # this test was generated with Dist::Zilla::Plugin::Test::Kwalitee 2.07 use strict; use warnings; use Test::Kwalitee; Test-LWP-UserAgent-0.022/xt/release/minimum-version.t000644 000767 000024 00000000271 12234623641 022644 0ustar00etherstaff000000 000000 #!perl use Test::More; eval "use Test::MinimumVersion"; plan skip_all => "Test::MinimumVersion required for testing minimum versions" if $@; all_minimum_version_ok( qq{5.008008} ); Test-LWP-UserAgent-0.022/xt/release/mojibake.t000644 000767 000024 00000000406 12234623641 021267 0ustar00etherstaff000000 000000 #!perl use strict; use warnings qw(all); use Test::More; ## no critic (ProhibitStringyEval, RequireCheckingReturnValueOfEval) eval q(use Test::Mojibake); plan skip_all => q(Test::Mojibake required for source encoding testing) if $@; all_files_encoding_ok(); Test-LWP-UserAgent-0.022/xt/release/no-tabs.t000644 000767 000024 00000000714 12234623641 021053 0ustar00etherstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.05 use Test::More 0.88; use Test::NoTabs; my @files = ( 'examples/MyApp/Client.pm', 'examples/advent_2012_1.pl', 'examples/advent_2012_2.pl', 'examples/advent_2012_3.pl', 'examples/application_client_test.t', 'examples/call_psgi.t', 'examples/myapp.psgi', 'lib/Test/LWP/UserAgent.pm' ); notabs_ok($_) foreach @files; done_testing; Test-LWP-UserAgent-0.022/xt/release/pod-coverage.t000644 000767 000024 00000000527 12234623641 022065 0ustar00etherstaff000000 000000 #!perl use Test::More; eval "use Test::Pod::Coverage 1.08"; plan skip_all => "Test::Pod::Coverage 1.08 required for testing POD coverage" if $@; eval "use Pod::Coverage::TrustPod"; plan skip_all => "Pod::Coverage::TrustPod required for testing POD coverage" if $@; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); Test-LWP-UserAgent-0.022/xt/release/pod-no404s.t000644 000767 000024 00000000527 12234623641 021321 0ustar00etherstaff000000 000000 #!perl use strict; use warnings; use Test::More; foreach my $env_skip ( qw( SKIP_POD_NO404S AUTOMATED_TESTING ) ){ plan skip_all => "\$ENV{$env_skip} is set, skipping" if $ENV{$env_skip}; } eval "use Test::Pod::No404s"; if ( $@ ) { plan skip_all => 'Test::Pod::No404s required for testing POD'; } else { all_pod_files_ok(); } Test-LWP-UserAgent-0.022/xt/release/pod-syntax.t000644 000767 000024 00000000212 12234623641 021607 0ustar00etherstaff000000 000000 #!perl use Test::More; eval "use Test::Pod 1.41"; plan skip_all => "Test::Pod 1.41 required for testing POD" if $@; all_pod_files_ok(); Test-LWP-UserAgent-0.022/xt/release/test-version.t000644 000767 000024 00000000643 12234623641 022153 0ustar00etherstaff000000 000000 use strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::Version 0.002004 BEGIN { eval "use Test::Version; 1;" or die $@; } my @imports = ( 'version_all_ok' ); my $params = { is_strict => 1, has_version => 1, }; push @imports, $params if version->parse( $Test::Version::VERSION ) >= version->parse('1.002'); Test::Version->import(@imports); version_all_ok; done_testing; Test-LWP-UserAgent-0.022/xt/release/unused-vars.t000644 000767 000024 00000000207 12234623641 021761 0ustar00etherstaff000000 000000 #!perl use Test::More; eval "use Test::Vars"; plan skip_all => "Test::Vars required for testing unused vars" if $@; all_vars_ok(); Test-LWP-UserAgent-0.022/xt/author/00-compile.t000644 000767 000024 00000004207 12234623641 021240 0ustar00etherstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.037 use Test::More 0.94 tests => 8 + ($ENV{AUTHOR_TESTING} ? 1 : 0); my @module_files = ( 'Test/LWP/UserAgent.pm' ); my @scripts = ( 'examples/MyApp/Client.pm', 'examples/advent_2012_1.pl', 'examples/advent_2012_2.pl', 'examples/advent_2012_3.pl', 'examples/application_client_test.t', 'examples/call_psgi.t', 'examples/myapp.psgi' ); # no fake home requested my $inc_switch = -d 'blib' ? '-Mblib' : '-Ilib'; use File::Spec; use IPC::Open3; use IO::Handle; my @warnings; for my $lib (@module_files) { # see L open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my $stderr = IO::Handle->new; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } foreach my $file (@scripts) { SKIP: { open my $fh, '<', $file or warn("Unable to open $file: $!"), next; my $line = <$fh>; close $fh and skip("$file isn't perl", 1) unless $line =~ /^#!.*?\bperl\b\s*(.*)$/; my @flags = $1 ? split(/\s+/, $1) : (); open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my $stderr = IO::Handle->new; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, @flags, '-c', $file); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$file compiled ok"); # in older perls, -c output is simply the file portion of the path being tested if (@_warnings = grep { !/\bsyntax OK$/ } grep { chomp; $_ ne (File::Spec->splitpath($file))[2] } @_warnings) { warn @_warnings; push @warnings, @_warnings; } } } is(scalar(@warnings), 0, 'no warnings found') if $ENV{AUTHOR_TESTING}; BAIL_OUT("Compilation problems") if !Test::More->builder->is_passing; Test-LWP-UserAgent-0.022/xt/author/pod-spell.t000644 000767 000024 00000000422 12234623641 021265 0ustar00etherstaff000000 000000 use strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::PodSpelling 2.006001 use Test::Spelling 0.12; use Pod::Wordlist; add_stopwords(); all_pod_files_spelling_ok( qw( bin lib ) ); __DATA__ Karen Etheridge ether lib Test LWP UserAgent Test-LWP-UserAgent-0.022/t/00-report-prereqs.t000644 000767 000024 00000007267 12234623641 021121 0ustar00etherstaff000000 000000 #!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.010 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec::Functions; use List::Util qw/max/; my @modules = qw( CPAN::Meta CPAN::Meta::Requirements Carp Class::Load ExtUtils::MakeMaker File::Spec::Functions HTTP::Date HTTP::Request HTTP::Request::Common HTTP::Response HTTP::Status LWP::UserAgent List::Util Module::Build::Tiny Path::Tiny Safe::Isa Scalar::Util Storable Test::Deep Test::Fatal Test::More Test::Requires Test::TempDir Test::Warnings Try::Tiny URI if namespace::clean overload parent perl strict warnings ); my %exclude = map {; $_ => 1 } qw( ); my ($source) = grep { -f $_ } qw/MYMETA.json MYMETA.yml META.json/; $source = "META.yml" unless defined $source; # replace modules with dynamic results from MYMETA.json if we can # (hide CPAN::Meta from prereq scanner) my $cpan_meta = "CPAN::Meta"; my $cpan_meta_req = "CPAN::Meta::Requirements"; my $all_requires; if ( -f $source && eval "require $cpan_meta" ) { ## no critic if ( my $meta = eval { CPAN::Meta->load_file($source) } ) { # Get ALL modules mentioned in META (any phase/type) my $prereqs = $meta->prereqs; delete $prereqs->{develop} if not $ENV{AUTHOR_TESTING}; my %uniq = map {$_ => 1} map { keys %$_ } map { values %$_ } values %$prereqs; $uniq{$_} = 1 for @modules; # don't lose any static ones @modules = sort grep { ! $exclude{$_} } keys %uniq; # If verifying, merge 'requires' only for major phases if ( 1 ) { $prereqs = $meta->effective_prereqs; # get the object, not the hash if (eval "require $cpan_meta_req; 1") { ## no critic $all_requires = $cpan_meta_req->new; for my $phase ( qw/configure build test runtime/ ) { $all_requires->add_requirements( $prereqs->requirements_for($phase, 'requires') ); } } } } } my @reports = [qw/Version Module/]; my @dep_errors; my $req_hash = defined($all_requires) ? $all_requires->as_string_hash : {}; for my $mod ( @modules ) { next if $mod eq 'perl'; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e catfile($_, $file) } @INC; if ( $prefix ) { my $ver = MM->parse_version( catfile($prefix, $file) ); $ver = "undef" unless defined $ver; # Newer MM should do this anyway push @reports, [$ver, $mod]; if ( 1 && $all_requires ) { my $req = $req_hash->{$mod}; if ( defined $req && length $req ) { if ( ! defined eval { version->parse($ver) } ) { push @dep_errors, "$mod version '$ver' cannot be parsed (version '$req' required)"; } elsif ( ! $all_requires->accepts_module( $mod => $ver ) ) { push @dep_errors, "$mod version '$ver' is not in required range '$req'"; } } } } else { push @reports, ["missing", $mod]; if ( 1 && $all_requires ) { my $req = $req_hash->{$mod}; if ( defined $req && length $req ) { push @dep_errors, "$mod is not installed (version '$req' required)"; } } } } if ( @reports ) { my $vl = max map { length $_->[0] } @reports; my $ml = max map { length $_->[1] } @reports; splice @reports, 1, 0, ["-" x $vl, "-" x $ml]; diag "\nVersions for all modules listed in $source (including optional ones):\n", map {sprintf(" %*s %*s\n",$vl,$_->[0],-$ml,$_->[1])} @reports; } if ( @dep_errors ) { diag join("\n", "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n", "The following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass; # vim: ts=2 sts=2 sw=2 et: Test-LWP-UserAgent-0.022/t/01-basic.t000644 000767 000024 00000014402 12234623641 017176 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::Deep 0.110; use Scalar::Util 'refaddr'; # simulates real code that we are testing { package MyApp; use strict; use warnings; use URI; use HTTP::Request::Common; use LWP::UserAgent; # in real code, you might want a Moose lazy _build_ua sub for this our $useragent = LWP::UserAgent->new; sub send_to_url { my ($self, $method, $base_url, $port, $path, %params) = @_; my $uri = URI->new($base_url); $uri->port($port); $uri->query_form(%params) if keys %params and $method eq 'GET'; $uri->path($path); my $request_sub = HTTP::Request::Common->can($method); my $request = $request_sub->( $uri, $method eq 'POST' ? \%params : (), ); my $response = $useragent->request($request); } } use Test::LWP::UserAgent; my $class = 'Test::LWP::UserAgent'; cmp_deeply( $class, methods( last_http_request_sent => undef, last_http_response_received => undef, last_useragent => undef, ), 'initial state (class)', ); cmp_deeply( $class->new, all( isa($class), isa('LWP::UserAgent'), methods( last_http_request_sent => undef, last_http_response_received => undef, last_useragent => undef, ), noclass(superhashof({ __last_http_request_sent => undef, __last_http_response_received => undef, __response_map => [], })), ), 'initial state (object)', ); # class methods { $class->map_response('foo.b.com', HTTP::Response->new('201')); $class->map_response(qr{foo.+success}, HTTP::Response->new('200')); $class->map_response(qr{foo.+fail}, HTTP::Response->new('500')); $class->map_response(sub { ::isa_ok($_[0], 'HTTP::Request'); $_[0]->method eq 'HEAD' }, HTTP::Response->new('304')); $class->map_response(HTTP::Request->new('DELETE', 'http://foo.a.com:3003/blah'), HTTP::Response->new('202')); $class->map_response(qr{conditional}, sub { ::isa_ok($_[0], 'HTTP::Request'); HTTP::Response->new(shift->uri =~ /success/ ? '200' : '550') } ); $MyApp::useragent = $class->new; foreach my $test ( [ 'regexp success', 'GET', 'http://foo.a.com', '3000', 'success', { a => 1 }, str('http://foo.a.com:3000/success?a=1'), '', '200' ], [ 'regexp fail', 'POST', 'http://foo.a.com', '3000', 'fail', { a => 1 }, str('http://foo.a.com:3000/fail'), 'a=1', '500' ], [ 'string success', 'GET', 'http://foo.b.com', '3001', 'success', { a => 1 }, str('http://foo.b.com:3001/success?a=1'), '', '201' ], [ 'subref redirect', 'HEAD', 'http://foo.a.com', '3002', 'blah', {}, str('http://foo.a.com:3002/blah'), '', '304' ], [ 'literal object', 'DELETE', 'http://foo.a.com', '3003', 'blah', {}, str('http://foo.a.com:3003/blah'), '', '202' ], [ 'response is coderef (success)', 'GET', 'http://conditional', '3004', 'success', {}, str('http://conditional:3004/success'), '', '200' ], [ 'response is coderef (fail)', 'GET', 'http://conditional', '3004', 'fail', {}, str('http://conditional:3004/fail'), '', '550' ], ) { test_send_request(@$test); } } # object methods { $MyApp::useragent = $class->new; cmp_deeply( $MyApp::useragent, all( isa($class), isa('LWP::UserAgent'), methods( last_http_request_sent => undef, last_http_response_received => undef, last_useragent => isa('LWP::UserAgent'), ), noclass(superhashof({ __last_http_request_sent => undef, __last_http_response_received => undef, __response_map => [], })), ), 'initial state of object after sending requests with another instance', ); # create one new mapping on this instance, and confirm it takes priority $MyApp::useragent->map_response(qr{foo.+fail}, HTTP::Response->new('401')); test_send_request( 'regexp fail', 'POST', 'http://foo', '3000', 'fail', { a => 1 }, str('http://foo:3000/fail'), 'a=1', '401', # globally, returning 500 ); $MyApp::useragent->unmap_all('this_instance_only'); test_send_request( 'global mappings are still in effect', 'GET', 'http://foo', '3000', 'success', { a => 1 }, str('http://foo:3000/success?a=1'), '', '200', ); $MyApp::useragent->unmap_all; test_send_request( 'all mappings are now gone', 'GET', 'http://foo', '3000', 'success', { a => 1 }, str('http://foo:3000/success?a=1'), '', '404', ); } sub test_send_request { my ($name, $method, $uri_base, $port, $path, $params, $expected_uri, $expected_content, $expected_code) = @_; local $Test::Builder::Level = $Test::Builder::Level + 1; note "\n", $name; my $response = MyApp->send_to_url($method, $uri_base, $port, $path, %$params); # response is what we stored in the useragent isa_ok($response, 'HTTP::Response'); is( refaddr($MyApp::useragent->last_http_response_received), refaddr($response), 'last_http_response_received', ); cmp_deeply( $MyApp::useragent->last_http_request_sent, all( isa('HTTP::Request'), methods( uri => $expected_uri, ), ), "$name request", ); is( refaddr($MyApp::useragent->last_useragent), refaddr($MyApp::useragent), 'last_useragent (class method)', ); cmp_deeply( refaddr(Test::LWP::UserAgent->last_useragent), refaddr($MyApp::useragent), 'last_useragent (instance method)', ); cmp_deeply( $response, methods( code => $expected_code, request => $MyApp::useragent->last_http_request_sent, ), "$name response", ); ok( HTTP::Date::parse_date($response->header('Client-Date')), 'Client-Date is a timestamp', ); } done_testing; Test-LWP-UserAgent-0.022/t/02-overload.t000644 000767 000024 00000004046 12234623641 017734 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use Test::Deep; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::LWP::UserAgent; use Class::Load 'try_load_class'; { package MyRequest; use overload '&{}' => sub { sub { ::isa_ok($_[0], 'HTTP::Request'); $_[0]->method eq 'GET' } }; } { package MyResponse; use overload '&{}' => sub { sub { ::isa_ok($_[0], 'HTTP::Request'); HTTP::Response->new('202') } }; } { package MyHost; sub new { my ($class, $string) = @_; bless { _string => $string }, $class; } use overload '""' => sub { my $self = shift; $self->{_string}; }; use overload 'cmp' => sub { my ($self, $other, $swap) = @_; $self->{_string} cmp $other; }; } { # mapped response is a thingy that quacks like a coderef my $useragent = Test::LWP::UserAgent->new; $useragent->map_response(bless({}, 'MyRequest'), bless({}, 'MyResponse')); my $response = $useragent->get('http://localhost'); isa_ok($response, 'HTTP::Response'); is($response->code, '202', 'response from overload'); } SKIP: { try_load_class('HTTP::Message::PSGI') or skip('HTTP::Message::PSGI is required for the remainder of these tests', 3); # mapped response is a coderef that turns a PSGI $env into an HTTP response my $useragent = Test::LWP::UserAgent->new; $useragent->register_psgi(MyHost->new('localhost'), sub { [ '200', [], ['home sweet home'] ] }); my $response = $useragent->get('http://localhost'); isa_ok($response, 'HTTP::Response'); cmp_deeply( $response, methods( code => '200', content => 'home sweet home', ), 'response from string overload', ); $useragent->unregister_psgi(MyHost->new('localhost')); $response = $useragent->get('http://localhost'); is($response->code, '404', 'mapping removed via str overload comparison'); } done_testing; Test-LWP-UserAgent-0.022/t/03-handlers.t000644 000767 000024 00000003060 12234623641 017715 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::Deep; use Test::LWP::UserAgent; # all phases as defined in the LWP::UserAgent documentation my @request_phases = qw(request_preprepare request_prepare request_send); # TODO: I'm not sure how to get response_header and response_data to be called... my @response_phases = qw(response_done response_redirect); { my $useragent = Test::LWP::UserAgent->new; $useragent->map_response( qr/localhost/, HTTP::Response->new('200', 'OK', ['Content-Type' => 'text/plain'], 'all good!'), ); my %phase_called; foreach my $phase (@request_phases) { $useragent->add_handler($phase => sub { my ($request, $ua, $h, $data) = @_; isa_ok($request, 'HTTP::Request'); isa_ok($ua, 'LWP::UserAgent'); $phase_called{$phase} = 1; return; }, m_host => 'localhost'); } foreach my $phase (@response_phases) { $useragent->add_handler($phase => sub { my ($response, $ua, $h, $data) = @_; isa_ok($response, 'HTTP::Response'); isa_ok($ua, 'LWP::UserAgent'); $phase_called{$phase} = 1; return; }, m_host => 'localhost'); } my $response = $useragent->get('http://localhost'); cmp_deeply( \%phase_called, { map { ( $_ => 1 ) } @request_phases, @response_phases }, 'all handlers called', ); } done_testing; Test-LWP-UserAgent-0.022/t/04-psgi.t000644 000767 000024 00000011740 12234623641 017064 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::Requires 'HTTP::Message::PSGI'; use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::Deep; use Test::LWP::UserAgent; use Scalar::Util 'refaddr'; use HTTP::Request::Common; my $app_foo = sub { my $env = shift; return [ '200', ['Content-Type' => 'text/plain' ], [ 'this is the foo app' ]]; }; my $app_foo2 = sub { my $env = shift; return [ '200', ['Content-Type' => 'text/html' ], [ 'this is the alternative foo app' ]]; }; my $app_bar = sub { my $env = shift; return [ '200', ['Content-Type' => 'text/html' ], [ 'this is the bar app' ]]; }; my $app_bar2 = sub { my $env = shift; return [ '200', ['Content-Type' => 'text/plain' ], [ 'this is the alternative bar app' ]]; }; my $app_baz = sub { my $env = shift; return [ '200', ['Content-Type' => 'image/jpeg' ], [ 'this is the baz app' ]]; }; { my $useragent = Test::LWP::UserAgent->new; my $useragent2 = Test::LWP::UserAgent->new; Test::LWP::UserAgent->register_psgi('foo', $app_foo); $useragent->register_psgi('bar', $app_bar); Test::LWP::UserAgent->register_psgi('bar', $app_bar2); $useragent2->register_psgi('baz', $app_baz); test_send_request('foo app (registered globally)', $useragent, GET('http://foo'), '200', [ 'Content-Type' => 'text/plain' ], 'this is the foo app'); $useragent->register_psgi('foo' , $app_foo2); test_send_request('foo app (registered on the object)', $useragent, GET('http://foo'), '200', [ 'Content-Type' => 'text/html' ], 'this is the alternative foo app'); # the object registration takes priority test_send_request('bar app (registered on the object)', $useragent, GET(URI->new('http://bar')), '200', [ 'Content-Type' => 'text/html' ], 'this is the bar app'); test_send_request('baz app (registered on the second object)', $useragent2, GET('http://baz'), '200', [ 'Content-Type' => 'image/jpeg' ], 'this is the baz app'); test_send_request('unmatched request', $useragent, GET('http://quux'), '404', [ ], ''); $useragent->unregister_psgi('bar', 'this_instance_only'); test_send_request('backup bar app is now available to this instance', $useragent, GET('http://bar'), '200', [ 'Content-Type' => 'text/plain' ], 'this is the alternative bar app'); $useragent->unregister_psgi('bar'); test_send_request('bar app (was registered on the instance, but now removed everywhere)', $useragent, GET('http://bar'), '404', [ ], ''); # mask a mapping from just this one instance $useragent->unregister_psgi('foo', 'instance_only'); test_send_request('foo app was registered on both, but now removed from the instance only', $useragent, GET('http://foo'), '200', [ 'Content-Type' => 'text/plain' ], 'this is the foo app'); test_send_request('foo app (registered globally; still available for other instances)', $useragent2, GET('http://foo'), '200', [ 'Content-Type' => 'text/plain' ], 'this is the foo app'); # mask the global mapping entirely $useragent->register_psgi('foo', undef); test_send_request('foo app was registered globally, but now removed from the instance only', $useragent, GET('http://foo'), '404', [ ], ''); $useragent->unmap_all('this_instance_only'); test_send_request('baz app is not available on this instance', $useragent, GET('http://baz'), '404', [ ], ''); test_send_request('baz app is still available on other instances', $useragent2, GET('http://baz'), '200', [ 'Content-Type' => 'image/jpeg' ], 'this is the baz app'); $useragent->unmap_all; test_send_request('bar app now removed', $useragent, GET('http://baz'), '404', [ ], ''); test_send_request('baz app now removed', $useragent, GET('http://baz'), '404', [ ], ''); } sub test_send_request { my ($name, $useragent, $request, $expected_code, $expected_headers, $expected_content) = @_; local $Test::Builder::Level = $Test::Builder::Level + 1; note "\n", $name; my $response = $useragent->request($request); # response is what we stored in the useragent isa_ok($response, 'HTTP::Response'); is( refaddr($useragent->last_http_response_received), refaddr($response), 'last_http_response_received', ); cmp_deeply( $useragent->last_http_request_sent, all( isa('HTTP::Request'), $request, ), "$name - request", ); my %header_spec = @$expected_headers; cmp_deeply( $response, methods( code => $expected_code, ( map { [ header => $_ ] => $header_spec{$_} } keys %header_spec ), content => $expected_content, request => $useragent->last_http_request_sent, ), "$name - response", ); ok( HTTP::Date::parse_date($response->header('Client-Date')), 'Client-Date is a timestamp', ); } done_testing; Test-LWP-UserAgent-0.022/t/05-exceptions.t000644 000767 000024 00000006452 12234623641 020310 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use Test::Warnings 0.005 ':no_end_test', ':all'; use Test::Deep; use Test::Fatal; use Scalar::Util 'refaddr'; use Test::LWP::UserAgent; use HTTP::Request::Common; { my $useragent = Test::LWP::UserAgent->new; $useragent->map_response(qr/generic_error/, sub { die 'network error!' }); my $line = __LINE__; $useragent->map_response(qr/http_response_error/, sub { die HTTP::Response->new('504') }); $useragent->map_response(qr/no_response_returned/, sub { return 'hi!' }); my $file = __FILE__; test_send_request( 'unexpected death', $useragent, GET('http://localhost/generic_error'), '500', [ 'Client-Warning' => 'Internal response', 'Content-Type' => 'text/plain', ], (LWP::UserAgent->VERSION < 6.00 ? "500 network error!\n" : re(qr/\Qnetwork error! at $file line $line.\E/) ), ); test_send_request( 'HTTP::Response death', $useragent, GET('http://localhost/http_response_error'), '504', [ ], '', undef, ); test_send_request( 'no death, but did not return HTTP::Response', $useragent, GET('http://localhost/no_response_returned'), '500', [], "500 Internal Server Error\n", qr/response from coderef is not a HTTP::Response, it's a non-object at /, ); } { note "\nNot capturing exceptions when processing the request, via use_eval => 0"; my $useragent = Test::LWP::UserAgent->new(use_eval => 0); $useragent->map_response(qr/generic_error/, sub { die 'network error!' }); my $line = __LINE__; my $file = __FILE__; like( exception { $useragent->request(GET 'http://localhost/generic_error') }, qr/\Qnetwork error! at $file line $line.\E/, 'exception was not caught when processing request', ); } sub test_send_request { my ($name, $useragent, $request, $expected_code, $expected_headers, $expected_content, $expected_warning) = @_; note "\n$name"; my $response; is( $expected_warning ? exception { like(warning { $response = $useragent->request($request) }, $expected_warning, 'expected warning') } : exception { $response = $useragent->request($request) }, undef, 'no exceptions when processing request', ); isa_ok($response, 'HTTP::Response'); is( refaddr($useragent->last_http_response_received), refaddr($response), 'last_http_response_received', ); my %header_spec = @$expected_headers; cmp_deeply( $response, methods( code => $expected_code, ( map { [ header => $_ ] => $header_spec{$_} } keys %header_spec ), content => $expected_content, request => $useragent->last_http_request_sent, ), 'response', ); is( refaddr($useragent->last_http_request_sent), refaddr($response->request), 'request was stored in response', ); ok( HTTP::Date::parse_date($response->header('Client-Date')), 'Client-Date is a timestamp', ); } had_no_warnings if $ENV{AUTHOR_TESTING}; done_testing; Test-LWP-UserAgent-0.022/t/06-network-fallback.t000644 000767 000024 00000010535 12234623641 021353 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; BEGIN { unless ($ENV{AUTHOR_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests use the network, and are for author testing'); } } use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::LWP::UserAgent; use HTTP::Request::Common; use URI; # I use POST rather than GET everywhere so as to not process the "302 # Redirect" response - there is no need, and the first response is much # shorter than the second. my $redirect_url = 'http://www.iana.org/domains/example/'; # allow LWP::UserAgent to carp about unknown constructor arguments $^W = 1; { my $useragent = Test::LWP::UserAgent->new; my $useragent2 = Test::LWP::UserAgent->new; ok(!Test::LWP::UserAgent->network_fallback, 'network_fallback not set globally'); ok(!$useragent->network_fallback, 'network_fallback not enabled for the instance'); ok(!$useragent2->network_fallback, 'network_fallback not enabled for the other instance'); test_send_request('no mappings', $useragent, POST($redirect_url), '404'); $useragent->network_fallback(1); ok($useragent->network_fallback, 'network_fallback enabled for the instance'); test_send_request('network_fallback on instance', $useragent, POST($redirect_url), '302'); test_send_request('no network_fallback on other instance', $useragent2, POST($redirect_url), '404'); $useragent->network_fallback(0); ok(!$useragent->network_fallback, 'network_fallback disnabled for the instance'); test_send_request('no network_fallback on instance', $useragent, POST($redirect_url), '404'); test_send_request('no network_fallback on other instance', $useragent2, POST($redirect_url), '404'); } { my $useragent = Test::LWP::UserAgent->new; my $useragent2 = Test::LWP::UserAgent->new; $useragent->network_fallback(1); ok($useragent->network_fallback, 'network_fallback enabled for the instance'); Test::LWP::UserAgent->network_fallback(1); ok(Test::LWP::UserAgent->network_fallback, 'network_fallback set globally'); ok($useragent->network_fallback, 'network_fallback enabled for the instance'); ok($useragent->network_fallback, 'network_fallback enabled for the other instance'); test_send_request('network_fallback on other instance', $useragent2, POST($redirect_url), '302'); test_send_request('network_fallback, with redirect', $useragent2, GET($redirect_url), '200'); Test::LWP::UserAgent->network_fallback(0); ok($useragent->network_fallback, 'network_fallback still enabled for the instance'); ok(!$useragent2->network_fallback, 'network_fallback not enabled for the other instance'); test_send_request('network_fallback instance flag still remains', $useragent, POST($redirect_url), '302'); test_send_request('global network_fallback clearable', $useragent2, POST($redirect_url), '404'); } { my $useragent = Test::LWP::UserAgent->new; my $useragent2 = Test::LWP::UserAgent->new; my $host = URI->new($redirect_url)->host; $useragent->map_network_response($host); ok(!$useragent->network_fallback, 'network_fallback not enabled for the instance'); test_send_request('network response mapped on instance', $useragent, POST($redirect_url), '302'); test_send_request('network response not mapped on other instance', $useragent2, POST($redirect_url), '404'); Test::LWP::UserAgent->map_network_response($host); test_send_request('network response mapped globally', $useragent2, POST($redirect_url), '302'); Test::LWP::UserAgent->unmap_all; } { my $useragent = Test::LWP::UserAgent->new(network_fallback => 1); my $useragent2 = Test::LWP::UserAgent->new; ok(!Test::LWP::UserAgent->network_fallback, 'network_fallback not set globally'); ok($useragent->network_fallback, 'network_fallback enabled for the instance'); ok(!$useragent2->network_fallback, 'network_fallback not enabled for the other instance'); test_send_request('network_fallback on instance', $useragent, POST($redirect_url), '302'); test_send_request('network_fallback on other instance', $useragent2, POST($redirect_url), '404'); } sub test_send_request { my ($name, $useragent, $request, $expected_code) = @_; note "\n$name"; local $Test::Builder::Level = $Test::Builder::Level + 1; is($useragent->request($request)->code, $expected_code, $name); } done_testing; Test-LWP-UserAgent-0.022/t/07-mask-response.t000644 000767 000024 00000004214 12234623641 020712 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::LWP::UserAgent; { my $useragent = Test::LWP::UserAgent->new; $useragent->map_response('bar.com', HTTP::Response->new('200')); Test::LWP::UserAgent->map_response('foo.com', HTTP::Response->new('201')); $useragent->map_response('foo.com', undef); my $response = $useragent->get('http://foo.com'); is($response->code, '404', 'global mapping is masked on the instance'); } { my $useragent = Test::LWP::UserAgent->new; $useragent->map_response('bar.com', HTTP::Response->new('200')); $useragent->map_response('foo.com', HTTP::Response->new('201')); $useragent->map_response('foo.com', undef); # send request - it should hit a 404. my $response = $useragent->get('http://foo.com'); is($response->code, '404', 'previous mapping is masked'); } { package MyHost; sub new { my ($class, $string) = @_; bless { _string => $string }, $class; } use overload '""' => sub { my $self = shift; $self->{_string}; }; use overload 'cmp' => sub { my ($self, $other, $swap) = @_; $self->{_string} cmp $other; }; } # same tests as above are repeated, but with overloaded string objects. { my $useragent = Test::LWP::UserAgent->new; $useragent->map_response(MyHost->new('bar.com'), HTTP::Response->new('200')); Test::LWP::UserAgent->map_response(MyHost->new('foo.com'), HTTP::Response->new('201')); $useragent->map_response(MyHost->new('foo.com'), undef); my $response = $useragent->get('http://foo.com'); is($response->code, '404', 'global mapping is masked on the instance'); } { my $useragent = Test::LWP::UserAgent->new; $useragent->map_response(MyHost->new('bar.com'), HTTP::Response->new('200')); $useragent->map_response(MyHost->new('foo.com'), HTTP::Response->new('201')); $useragent->map_response(MyHost->new('foo.com'), undef); # send request - it should hit a 404. my $response = $useragent->get('http://foo.com'); is($response->code, '404', 'previous mapping is masked'); } done_testing; Test-LWP-UserAgent-0.022/t/08-isa-coderef.t000644 000767 000024 00000002141 12234623641 020302 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::LWP::UserAgent; { package CodeRefOverload; use overload '&{}' => sub { sub { ::fail 'sub should not be called' } }; sub new { bless {}, 'CodeRefOverload' } } my $string = 'ohhai'; my $scalarref = \$string; my $coderef = sub { fail 'sub should not be called' }; my $nota_coderef = bless {}, 'NotaCodeRef'; my $isa_coderef = bless sub { fail 'sub should not be called' }, 'IsaCodeRef'; ok(!Test::LWP::UserAgent::__isa_coderef($string), 'string is not callable as a coderef'); ok(!Test::LWP::UserAgent::__isa_coderef($scalarref), 'scalarref is not callable as a coderef'); ok(Test::LWP::UserAgent::__isa_coderef($coderef), 'coderef is callable as a coderef'); ok(!Test::LWP::UserAgent::__isa_coderef($nota_coderef), 'blessed hash is not callable as a coderef'); ok(Test::LWP::UserAgent::__isa_coderef($isa_coderef), 'blessed coderef is callable as a coderef'); ok(Test::LWP::UserAgent::__isa_coderef(CodeRefOverload->new), 'object with code overload is callable as a coderef'); done_testing; Test-LWP-UserAgent-0.022/t/09-dispatch-to-request-method.t000644 000767 000024 00000003633 12234623641 023314 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use Test::Warnings 0.005 ':no_end_test', ':all'; use Test::Deep 0.110; use HTTP::Request::Common; use HTTP::Response; use Test::LWP::UserAgent; { package MyDispatcher; use strict; use warnings; sub new { my $class = shift; return bless {}, $class; } sub request { my ($self, $request) = @_; HTTP::Response->new('200', undef, [], 'response from ' . $request->uri); } } my $useragent = Test::LWP::UserAgent->new; $useragent->map_response('foo.com', 'MyDispatcher'); $useragent->map_response('bar.com', MyDispatcher->new); like( warning { $useragent->map_response('null.com', 'Foo') }, qr/^map_response: response is not a coderef or an HTTP::Response, it's a non-object/, 'appropriate warning when creating a bad mapping', ); cmp_deeply( $useragent->request(GET('http://foo.com')), all( isa('HTTP::Response'), methods( code => '200', content => 'response from http://foo.com', ), ), 'can dispatch to a class that implements request()', ); cmp_deeply( $useragent->request(GET('http://bar.com')), all( isa('HTTP::Response'), methods( code => '200', content => 'response from http://bar.com', ), ), 'can dispatch to an instance that implements request()', ); like( warning { cmp_deeply( $useragent->request(GET('http://null.com')), all( isa('HTTP::Response'), methods( code => '500', ), ), 'cannot dispatch to a bare string', ); }, qr/^response from coderef is not a HTTP::Response, it's a non-object/, 'appropriate warning when attempting to dispatch inappropriately', ); had_no_warnings if $ENV{AUTHOR_TESTING}; done_testing; Test-LWP-UserAgent-0.022/t/10-request-args-network.t000644 000767 000024 00000002467 12234623641 022236 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; BEGIN { unless ($ENV{AUTHOR_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests use the network, and are for author testing'); } } use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::Deep; use Test::TempDir; use Path::Tiny; use Test::LWP::UserAgent; # the root problem here was that we were not passing along additional # arguments to request() to the superclass, e.g. the option to save the # content to a file. my $useragent = Test::LWP::UserAgent->new(network_fallback => 1); my $response = $useragent->get('http://example.com/'); my $expected_content = $response->decoded_content; { # network_fallback case my (undef, $tmpfile) = tempfile; my $response = $useragent->get('http://example.com/', ':content_file' => $tmpfile); my $contents = path($tmpfile)->slurp; is($contents, $expected_content, 'response body is saved to file (network responses)'); is($response->content, '', 'response body is removed'); cmp_deeply( $response, methods( [ header => 'X-Died' ] => undef, [ header => 'Content-Type' ], => re(qr{^text/html}), [ header => 'Client-Date' ] => ignore, ), 'response headers look ok', ); } done_testing; Test-LWP-UserAgent-0.022/t/11-request-args-internal.t000644 000767 000024 00000004546 12234623641 022362 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use if $ENV{AUTHOR_TESTING}, 'Test::Warnings'; use Test::Deep; use Test::TempDir; use Path::Tiny; use Test::LWP::UserAgent; # the root problem here was that the real send_request calls LWP::Protocol::* # with all the arguments, some of which are then processed via the collect() # method -- including the option to save the content to a file. # I thought about creating a new LWP::Protocol subclass, which did the heavy # lifting that is in my send_request, but that's overboard, even for me... and # after looking at LWP::Protocol::http::request, all it does after handling # the networking itself is call $self->collect with all the args. { # internally-mapped responses my $useragent = Test::LWP::UserAgent->new; $useragent->map_response( qr/foo.com/, HTTP::Response->new( 200, 'OK', ['Content-Type' => 'text/plain'], 'all good!', ), ); my (undef, $tmpfile) = tempfile; my $response = $useragent->get( 'http://foo.com', ':content_file' => $tmpfile); my $contents = path($tmpfile)->slurp; is($contents, 'all good!', 'response body is saved to file (internal responses)'); is($response->content, '', 'response body is removed'); cmp_deeply( $response, methods( [ header => 'X-Died' ] => undef, [ header => 'Content-Type' ], => 'text/plain', [ header => 'Client-Date' ] => ignore, ), 'response headers look ok', ); } { # and another, using mirror() directly my $useragent = Test::LWP::UserAgent->new; $useragent->map_response( qr/foo.com/, HTTP::Response->new( 200, 'OK', ['Content-Type' => 'text/plain'], 'all good!', ), ); my (undef, $tmpfile) = tempfile; my $response = $useragent->mirror('http://foo.com', $tmpfile); my $contents = path($tmpfile)->slurp; is($contents, 'all good!', 'response body is saved to file (internal responses)'); is($response->content, '', 'response body is removed'); cmp_deeply( $response, methods( [ header => 'X-Died' ] => undef, [ header => 'Content-Type' ], => 'text/plain', [ header => 'Client-Date' ] => ignore, ), 'response headers look ok', ); } done_testing; Test-LWP-UserAgent-0.022/t/50-examples-application_client_test.t000644 000767 000024 00000000331 12234623641 024631 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::More; use Test::Requires qw(JSON Moose); plan skip_all => 'this example requires perl 5.10' if $^V < 5.010; do 'examples/application_client_test.t'; die $@ if $@; Test-LWP-UserAgent-0.022/t/51-call_psgi.t000644 000767 000024 00000000176 12234623641 020062 0ustar00etherstaff000000 000000 use strict; use warnings FATAL => 'all'; use Test::Requires 'HTTP::Message::PSGI'; do 'examples/call_psgi.t'; die $@ if $@; Test-LWP-UserAgent-0.022/lib/Test/000755 000767 000024 00000000000 12234623641 016733 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/lib/Test/LWP/000755 000767 000024 00000000000 12234623641 017375 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/lib/Test/LWP/UserAgent.pm000644 000767 000024 00000055141 12234623641 021636 0ustar00etherstaff000000 000000 use strict; use warnings; package Test::LWP::UserAgent; { $Test::LWP::UserAgent::VERSION = '0.022'; } # git description: v0.021-1-g86b1b81 BEGIN { $Test::LWP::UserAgent::AUTHORITY = 'cpan:ETHER'; } # ABSTRACT: a LWP::UserAgent suitable for simulating and testing network calls use parent 'LWP::UserAgent'; use Scalar::Util qw(blessed reftype); use Storable 'freeze'; use HTTP::Request; use HTTP::Response; use URI; use HTTP::Date; use HTTP::Status qw(:constants status_message); use Try::Tiny; use Safe::Isa; use Carp; use namespace::clean; my @response_map; my $network_fallback; my $last_useragent; sub new { my ($class, %options) = @_; my $_network_fallback = delete $options{network_fallback}; my $self = $class->SUPER::new(%options); $self->{__last_http_request_sent} = undef; $self->{__last_http_response_received} = undef; $self->{__response_map} = []; $self->{__network_fallback} = $_network_fallback; # strips default User-Agent header added by LWP::UserAgent, to make it # easier to define literal HTTP::Requests to match against $self->agent(undef) if defined $self->agent and $self->agent eq $self->_agent; return $self; } sub map_response { my ($self, $request_description, $response) = @_; if (not defined $response and blessed $self) { # mask a global domain mapping my $matched; foreach my $mapping (@{$self->{__response_map}}) { if ($mapping->[0] eq $request_description) { $matched = 1; undef $mapping->[1]; } } push @{$self->{__response_map}}, [ $request_description, undef ] if not $matched; return; } if (not $response->$_isa('HTTP::Response') and try { $response->can('request') }) { my $oldres = $response; $response = sub { $oldres->request($_[0]) }; } carp 'map_response: response is not a coderef or an HTTP::Response, it\'s a ', (blessed($response) || 'non-object') unless __isa_coderef($response) or $response->$_isa('HTTP::Response'); if (blessed $self) { push @{$self->{__response_map}}, [ $request_description, $response ]; } else { push @response_map, [ $request_description, $response ]; } } sub map_network_response { my ($self, $request_description) = @_; if (blessed $self) { # we cannot call ::request here, or we end up in an infinite loop push @{$self->{__response_map}}, [ $request_description, sub { $self->SUPER::send_request($_[0]) } ]; } else { push @response_map, [ $request_description, sub { LWP::UserAgent->new->send_request($_[0]) } ]; } } sub unmap_all { my ($self, $instance_only) = @_; if (blessed $self) { $self->{__response_map} = []; @response_map = () unless $instance_only; } else { carp 'instance-only unmap requests make no sense when called globally' if $instance_only; @response_map = (); } } sub register_psgi { my ($self, $domain, $app) = @_; return $self->map_response($domain, undef) if not defined $app; carp 'register_psgi: app is not a coderef, it\'s a ', ref($app) unless __isa_coderef($app); carp 'register_psgi: did you forget to load HTTP::Message::PSGI?' unless HTTP::Request->can('to_psgi') and HTTP::Response->can('from_psgi'); return $self->map_response( $domain, sub { HTTP::Response->from_psgi($app->($_[0]->to_psgi)) }, ); } sub unregister_psgi { my ($self, $domain, $instance_only) = @_; if (blessed $self) { @{$self->{__response_map}} = grep { $_->[0] ne $domain } @{$self->{__response_map}}; @response_map = grep { $_->[0] ne $domain } @response_map unless $instance_only; } else { @response_map = grep { $_->[0] ne $domain } @response_map; } } sub last_http_request_sent { my $self = shift; return blessed($self) ? $self->{__last_http_request_sent} : $last_useragent ? $last_useragent->last_http_request_sent : undef; } sub last_http_response_received { my $self = shift; return blessed($self) ? $self->{__last_http_response_received} : $last_useragent ? $last_useragent->last_http_response_received : undef; } sub last_useragent { return $last_useragent; } sub network_fallback { my ($self, $value) = @_; if (@_ == 1) { return blessed $self ? $self->{__network_fallback} : $network_fallback; } return $self->{__network_fallback} = $value if blessed $self; $network_fallback = $value; } sub send_request { my ($self, $request, $arg, $size) = @_; $self->progress('begin', $request); my $matched_response = $self->run_handlers('request_send', $request); my $uri = $request->uri; foreach my $entry (@{$self->{__response_map}}, @response_map) { last if $matched_response; next if not defined $entry; my ($request_desc, $response) = @$entry; if ($request_desc->$_isa('HTTP::Request')) { local $Storable::canonical = 1; $matched_response = $response, last if freeze($request) eq freeze($request_desc); } elsif (__is_regexp($request_desc)) { $matched_response = $response, last if $uri =~ $request_desc; } elsif (__isa_coderef($request_desc)) { $matched_response = $response, last if $request_desc->($request); } else { $uri = URI->new($uri) if not $uri->$_isa('URI'); $matched_response = $response, last if $uri->host eq $request_desc; } } $last_useragent = $self; $self->{__last_http_request_sent} = $request; if (not defined $matched_response and ($self->{__network_fallback} or $network_fallback)) { my $response = $self->SUPER::send_request($request, $arg, $size); $self->{__last_http_response_received} = $response; return $response; } my $response = defined $matched_response ? $matched_response : HTTP::Response->new('404'); if (__isa_coderef($response)) { # emulates handling in LWP::UserAgent::send_request if ($self->use_eval) { $response = try { $response->($request) } catch { my $exception = $_; if ($exception->$_isa('HTTP::Response')) { $response = $exception; } else { my $full = $exception; (my $status = $exception) =~ s/\n.*//s; $status =~ s/ at .* line \d+.*//s; # remove file/line number my $code = ($status =~ s/^(\d\d\d)\s+//) ? $1 : HTTP_INTERNAL_SERVER_ERROR; # note that _new_response did not always take a fourth # parameter - content used to always be "$code $message" $response = LWP::UserAgent::_new_response($request, $code, $status, $full); } } } else { $response = $response->($request); } } if (not $response->$_isa('HTTP::Response')) { carp 'response from coderef is not a HTTP::Response, it\'s a ', (blessed($response) || 'non-object'); $response = LWP::UserAgent::_new_response($request, HTTP_INTERNAL_SERVER_ERROR, status_message(HTTP_INTERNAL_SERVER_ERROR)); } else { $response->request($request); # record request for reference $response->header('Client-Date' => HTTP::Date::time2str(time)); } # handle any additional arguments that were provided, such as saving the # content to a file. this also runs additional handlers for us. my $protocol = LWP::Protocol->new('no-schemes-from-TLWPUA', $self); my $complete; $response = $protocol->collect($arg, $response, sub { # remove content from $response and stream it back return \'' if $complete; my $content = $response->content; $response->content(''); $complete++; \$content; }); $self->run_handlers('response_done', $response); $self->progress('end', $response); $self->{__last_http_response_received} = $response; return $response; } sub __isa_coderef { ref $_[0] eq 'CODE' or (reftype($_[0]) || '') eq 'CODE' or overload::Method($_[0], '&{}') } sub __is_regexp { re->can('is_regexp') ? re::is_regexp(shift) : ref(shift) eq 'Regexp'; } 1; __END__ =pod =encoding UTF-8 =for :stopwords Karen Etheridge useragent ORed WSDL irc AirG Yury Zavarin mst =head1 NAME Test::LWP::UserAgent - a LWP::UserAgent suitable for simulating and testing network calls =head1 VERSION version 0.022 =head1 SYNOPSIS In your application code: use URI; use HTTP::Request::Common; use LWP::UserAgent; my $useragent = $self->useragent || LWP::UserAgent->new; my $uri = URI->new('http://example.com'); $uri->port('3000'); $uri->path('success'); my $request = POST($uri, a => 1); my $response = $useragent->request($request); Then, in your tests: use Test::LWP::UserAgent; use Test::More; my $useragent = Test::LWP::UserAgent->new; $useragent->map_response( qr{example.com/success}, HTTP::Response->new('200', 'OK', ['Content-Type' => 'text/plain'], '')); $useragent->map_response( qr{example.com/fail}, HTTP::Response->new('500', 'ERROR', ['Content-Type' => 'text/plain'], '')); # now, do something that sends a request, and test how your application # responds to that response =head1 DESCRIPTION This module is a subclass of L which overrides a few key low-level methods that are concerned with actually sending your request over the network, allowing an interception of that request and simulating a particular response. This greatly facilitates testing of client networking code where the server follows a known protocol. The synopsis describes a classic case where you want to test how your application reacts to various responses from the server. This module will let you send back various responses depending on the request, without having to set up a real server to test against. This can be invaluable when you need to test edge cases or error conditions that do not normally arise from the server. There are a lot of different ways you can set up the response mappings, and hook into this module; see the documentation for the individual interface methods. You can use a L app to handle the requests - see F in this dist, and also L below. OR, you can route some or all requests through the network as normal, but still gain the hooks provided by this class to test what was sent and received: my $useragent = Test::LWP::UserAgent->new(network_fallback => 1); or: $useragent->map_network_response(qr/real.network.host/); # ... generate a request... # and then in your tests: is( $useragent->last_useragent->timeout, 180, 'timeout was overridden properly', ); is( $useragent->last_http_request_sent->uri, 'uri my code should have constructed', ); is( $useragent->last_http_response_received->code, '200', 'I should have gotten an OK response', ); =head2 Ensuring the right useragent is used Note that L itself is not monkey-patched - you must use this module (or a subclass) to send your request, or it cannot be caught and processed. One common mechanism to swap out the useragent implementation is via a lazily-built Moose attribute; if no override is provided at construction time, default to C<< LWP::UserAgent->new(%options) >>. Additionally, most methods can be called as class methods, which will store the settings globally, so that any instance of L can use them, which can simplify some of your application code. =head1 METHODS =over =item * C Accepts all options as in L, including C, an undocumented boolean which is enabled by default. When set, sending the HTTP request is wrapped in an C<< eval {} >>, allowing all exceptions to be caught and an appropriate error response (usually HTTP 500) to be returned. You may want to unset this if you really want to test extraordinary errors within your networking code. Normally, you should leave it alone, as L and this module are capable of handling normal errors. Plus, this option is added: =over =item * C<< network_fallback => >> If true, requests passing through this object that do not match a previously-configured mapping or registration will be directed to the network. (To only divert I requests rather than unmatched requests, use C, see below.) This option is also available as a read/write accessor via C<< $useragent->network_fallback() >>. =back B If called as on a blessed object, the action performed or data returned is limited to just that object; if called as a class method, the action or data is global. =item * C With this method, you set up what L should be returned for each request received. The request match specification can be described in multiple ways: =over =item * string The string is matched identically against the C field of the L in the request. $test_ua->map_response('example.com', HTTP::Response->new('500')); =item * regexp The regexp is matched against the URI in the request. $test_ua->map_response(qr{foo/bar}, HTTP::Response->new('200')); $test_ua->map_response(qr{baz/quux}, HTTP::Response->new('500')); =item * code The provided coderef is passed a single argument, the L, and returns a boolean indicating if there is a match. # matches all GET and POST requests $test_ua->map_response(sub { my $request = shift; return 1 if $request->method eq 'GET' || $request->method eq 'POST'; }, HTTP::Response->new('200'), ); =item * L object The L object is matched identically (including all query parameters, headers etc) against the provided object. =back The response can be represented either as a literal L object, or as a coderef that is run at the time of matching, with the request passed as the single argument: HTTP::Response->new(...); or sub { my $request = shift; HTTP::Response->new(...); } Instance mappings take priority over global (class method) mappings - if no matches are found from mappings added to the instance, the global mappings are then examined. When no matches have been found, a 404 response is returned. =item * C Same as C above, only requests that match this description will not use a response that you specify, but instead uses a real L to dispatch your request to the network. If called on an instance, all options passed to the constructor (e.g. timeout) are used for making the real network call. If called as a class method, a pristine L object with no customized options will be used instead. =item * C When called as a class method, removes all mappings set up globally (across all objects). Mappings set up on an individual object will still remain. When called as an object method, removes I mappings both globally and on this instance, unless a true value is passed as an argument, in which only mappings local to the object will be removed. (Any true value will do, so you can pass a meaningful string.) =item * C Register a particular L app (code reference) to be used when requests for a domain are received (matches are made exactly against C<< $request->uri->host >>). The request is passed to the C<$app> for processing, and the L response is converted back to an L (you must already have loaded L or equivalent, as this is not done for you). You can also use C with a regular expression as the first argument, or any of the other forms used by C, if you wish, as calling C<< $test_ua->register_psgi($domain, $app) >> is equivalent to: $test_ua->map_response( $domain, sub { HTTP::Response->from_psgi($app->($_[0]->to_psgi)) }, ); This feature is useful for testing your PSGI applications, or for simulating a server so as to test your client code. You might find using L or L easier for your needs, so check those out as well. =item * C When called as a class method, removes a domain->PSGI app entry that had been registered globally. Some mappings set up on an individual object may still remain. When called as an object method, removes a domain registration that was made both globally and locally, unless a true value was passed as the second argument, in which case only the registration local to the object will be removed. This allows a different mapping made globally to take over. If you want to mask a global registration on just one particular instance, then add C as a mapping on your instance: $useragent->map_response($domain, undef); =item * C The last L object that this object (if called on an object) or module (if called as a class method) processed, whether or not it matched a mapping you set up earlier. Note that this is also available via C<< last_http_response_received->request >>. =item * C The last L object that this module returned, as a result of a mapping you set up earlier with C. You shouldn't normally need to use this, as you know what you responded with - you should instead be testing how your code reacted to receiving this response. =item * C The last Test::LWP::UserAgent object that was used to send a request. Obviously this only provides new information if called as a class method; you can use this if you don't have direct control over the useragent itself, to get the object that was used, to verify options such as the network timeout. =item * C Getter/setter method for the network_fallback preference that will be used on this object (if called as an instance method), or globally, if called as a class method. Note that the actual behaviour used on an object is the ORed value of the instance setting and the global setting. =item * C This is the only method from L that has been overridden, which processes the L, sends to the network, then creates the L object from the reply received. Here, we loop through your local and global domain registrations, and local and global mappings (in this order) and returns the first match found; otherwise, a simple 404 response is returned (unless C was specified as a constructor option, in which case unmatched requests will be delivered to the network.) =back All other methods from L are available unchanged. =head1 Usage with SOAP requests =over =item * L To use this module when communicating via L with a SOAP server (either a real one, with live network requests, L or with one simulated with mapped responses), simply do this: use SOAP::Lite; use SOAP::Transport::HTTP; $SOAP::Transport::HTTP::Client::USERAGENT_CLASS = 'Test::LWP::UserAgent'; You must then make all your configuration changes and mappings globally. See also L. =item * L When using L with a compiled WSDL, you can change the useragent object via L: my $call = $wsdl->compileClient( $interface_name, transport => XML::Compile::Transport::SOAPHTTP->new( user_agent => $useragent, address => $wsdl->endPoint, ), ); See also L. =back =head1 MOTIVATION Most mock libraries on the CPAN use L, which is widely considered not good practice (among other things, C<@ISA> is violated, it requires knowing far too much about the module's internals, and is very clumsy to work with). (L is one of many that chronicles its issues.) This module is a direct descendant of L, exports nothing into your namespace, and all access is via method calls, so it is fully inheritable should you desire to add more features or override some bits of functionality. (Aside from the constructor), it only overrides the one method in L that issues calls to the network, so real L and L objects are used throughout. It provides a method (C) to access the last L, for testing things like the URI and headers that your code sent to L. =head1 SUPPORT Bugs may be submitted through L (or L). I am also usually active on irc, as 'ether' at C. =head1 ACKNOWLEDGEMENTS L, my former employer, and the first user of this distribution. mst - Matt S. Trout , for the better name of this distribution, and for the PSGI registration concept. Also Yury Zavarin, whose L inspired me to write this module, and from where I borrowed some aspects of the API. =head1 SEE ALSO =over 4 =item * L =item * L =item * L =item * L =item * L, L, L, =item * L, L =back =head1 AUTHOR Karen Etheridge =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2012 by Karen Etheridge. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Test-LWP-UserAgent-0.022/examples/advent_2012_1.pl000644 000767 000024 00000000552 12234623641 021570 0ustar00etherstaff000000 000000 use strict; use warnings; use feature 'say'; use Test::LWP::UserAgent; my $useragent = Test::LWP::UserAgent->new; $useragent->map_response(qr/example.com/, HTTP::Response->new('200')); my $response = $useragent->get('http://example.com'); # prints 200 say $response->code; $response = $useragent->get('http://google.com'); # prints 404 say $response->code; Test-LWP-UserAgent-0.022/examples/advent_2012_2.pl000644 000767 000024 00000000613 12234623641 021567 0ustar00etherstaff000000 000000 use strict; use warnings; use feature 'say'; use Test::LWP::UserAgent; my $useragent = Test::LWP::UserAgent->new; $useragent->network_fallback(1); $useragent->map_response(qr/example.com/, HTTP::Response->new('200')); my $response = $useragent->get('http://example.com'); # prints 200 say $response->code; $response = $useragent->get('http://google.com'); # prints 200 say $response->code; Test-LWP-UserAgent-0.022/examples/advent_2012_3.pl000644 000767 000024 00000000614 12234623641 021571 0ustar00etherstaff000000 000000 use strict; use warnings; use feature 'say'; use Test::LWP::UserAgent; use HTTP::Message::PSGI; use HTTP::Request::Common; use Plack::Util; my $useragent = Test::LWP::UserAgent->new; my $app = Plack::Util::load_psgi('examples/myapp.psgi'); $useragent->register_psgi('mytestdomain.com', $app); my $response = $useragent->request(GET 'http://mytestdomain.com/foo/bar'); say $response->content; Test-LWP-UserAgent-0.022/examples/application_client_test.t000644 000767 000024 00000003452 12234623641 024155 0ustar00etherstaff000000 000000 use strict; use warnings; # This example demonstrates how one might go about writing some tests for a # client library that interacts with a RESTful API. At a minimum, we want to # test when we receive a network timeout (which is quite common and all # clients must be able to handle), a failed query and a successful query. # Note that *we are not testing the network*, but testing the code that # interacts with the network. Therefore, we need to make the network behave in # different ways so we can test how we interact with it. use Test::More tests => 5; use Test::Warnings 0.005 ':all'; use Test::LWP::UserAgent; use lib 'examples'; use MyApp::Client; my $useragent = Test::LWP::UserAgent->new; # this is what LWP::UserAgent effectively does when it encounters a timeout $useragent->map_response(qr{user/timeout}, sub { die 'read timeout' }); $useragent->map_response( qr{user/fred}, HTTP::Response->new('404', HTTP::Status::status_message('404'), [ 'Content-Type' => 'text/plain' ], 'user fred does not exist', ), ); $useragent->map_response( qr{user/barney}, HTTP::Response->new('200', HTTP::Status::status_message('200'), [ 'Content-Type' => 'application/json' ], '{"user":"barney","userid":"50","blog_posts":"1","post_ids":["76"]}', ), ); my $client = MyApp::Client->new(useragent => $useragent); my @ids; like( warning { @ids = $client->get_indexes(user => 'timeout') }, qr{network timeout when fetching http://.*user/timeout}, 'warning issued on network timeout', ); is_deeply(\@ids, [], 'no ids returned on network timeout'); @ids = $client->get_indexes(user => 'fred'); is_deeply(\@ids, [], 'no ids returned for non-existent user'); @ids = $client->get_indexes(user => 'barney'); is_deeply(\@ids, [ 76 ], 'one id returned for regular user'); Test-LWP-UserAgent-0.022/examples/call_psgi.t000644 000767 000024 00000001472 12234623641 021212 0ustar00etherstaff000000 000000 use strict; use warnings; use Test::More 0.88; use Test::Warnings; use Test::LWP::UserAgent; use HTTP::Request::Common; use HTTP::Message::PSGI; my $useragent = Test::LWP::UserAgent->new; $useragent->register_psgi('example.com' => sub { my $env = shift; # logic here... [ '200', [ 'Content-Type' => 'text/plain' ], [ 'some body' ] ], } ); # my $response = $useragent->request(POST('http://example.com/success:3000', [ a => 1 ])); my $last_request = $useragent->last_http_request_sent; is($last_request->uri, 'http://example.com/success:3000', 'URI'); is($last_request->content, 'a=1', 'POST content'); # is($response->code, '200', 'response code is correct'); done_testing; Test-LWP-UserAgent-0.022/examples/MyApp/000755 000767 000024 00000000000 12234623641 020112 5ustar00etherstaff000000 000000 Test-LWP-UserAgent-0.022/examples/myapp.psgi000644 000767 000024 00000000305 12234623641 021074 0ustar00etherstaff000000 000000 use strict; use warnings; sub { my $env = shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'You sent me the path ' . $env->{PATH_INFO} . '.' ], ]; } Test-LWP-UserAgent-0.022/examples/MyApp/Client.pm000644 000767 000024 00000001471 12234623641 021671 0ustar00etherstaff000000 000000 package MyApp::Client; use strict; use warnings; use Moose; use LWP::UserAgent; use JSON; has useragent => ( is => 'ro', isa => 'LWP::UserAgent', lazy => 1, default => sub { LWP::UserAgent->new }, ); sub get_indexes { my ($self, %args) = @_; my $user = $args{user}; # call our server to get the data my $url = "http://myserver.com/user/$user"; my $response = $self->useragent->get($url); if ($response->code ne '200' or $response->headers->content_type ne 'application/json') { warn "network timeout when fetching $url" if $response->decoded_content =~ /time(?:d )?out/; return; } # parse JSON data from response my $data = decode_json($response->decoded_content); return @{$data->{post_ids} // []}; } __PACKAGE__->meta->make_immutable;