fixed bodged svk push
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Test.pm
1 package Catalyst::Test;
2
3 use strict;
4 use warnings;
5
6 use Catalyst::Exception;
7 use Catalyst::Utils;
8 use Class::Inspector;
9
10 =head1 NAME
11
12 Catalyst::Test - Test Catalyst Applications
13
14 =head1 SYNOPSIS
15
16     # Helper
17     script/test.pl
18
19     # Tests
20     use Catalyst::Test 'TestApp';
21     request('index.html');
22     get('index.html');
23
24     # Run tests against a remote server
25     CATALYST_SERVER='http://localhost:3000/' prove -r -l lib/ t/
26
27     # Tests with inline apps need to use Catalyst::Engine::Test
28     package TestApp;
29
30     use Catalyst;
31
32     sub foo : Global {
33             my ( $self, $c ) = @_;
34             $c->res->output('bar');
35     }
36
37     __PACKAGE__->setup();
38
39     package main;
40
41     use Test::More tests => 1;
42     use Catalyst::Test 'TestApp';
43
44     ok( get('/foo') =~ /bar/ );
45
46 =head1 DESCRIPTION
47
48 Test Catalyst Applications.
49
50 =head2 METHODS
51
52 =head2 get
53
54 Returns the content.
55
56     my $content = get('foo/bar?test=1');
57
58 Note that this method doesn't follow redirects, so to test for a
59 correctly redirecting page you'll need to use a combination of this
60 method and the L<request> method below:
61
62     my $res = request('/'); # redirects to /y
63     warn $res->header('location');
64     use URI;
65     my $uri = URI->new($res->header('location'));
66     is ( $uri->path , '/y');
67     my $content = get($uri->path);
68
69 =head2 request
70
71 Returns a C<HTTP::Response> object.
72
73     my $res = request('foo/bar?test=1');
74
75 =cut
76
77 sub import {
78     my $self  = shift;
79     my $class = shift;
80
81     my ( $get, $request );
82
83     if ( $ENV{CATALYST_SERVER} ) {
84         $request = sub { remote_request(@_) };
85         $get     = sub { remote_request(@_)->content };
86     } elsif (! $class) {
87         $request = sub { Catalyst::Exception->throw("Must specify a test app: use Catalyst::Test 'TestApp'") };
88         $get     = $request;
89     } else {
90         unless( Class::Inspector->loaded( $class ) ) {
91             require Class::Inspector->filename( $class );
92         }
93         $class->import;
94
95         $request = sub { local_request( $class, @_ ) };
96         $get     = sub { local_request( $class, @_ )->content };
97     }
98
99     no strict 'refs';
100     my $caller = caller(0);
101     *{"$caller\::request"} = $request;
102     *{"$caller\::get"}     = $get;
103 }
104
105 =head2 local_request
106
107 =cut
108
109 sub local_request {
110     my $class = shift;
111
112     require HTTP::Request::AsCGI;
113
114     my $request = Catalyst::Utils::request( shift(@_) );
115     my $cgi     = HTTP::Request::AsCGI->new( $request, %ENV )->setup;
116
117     $class->handle_request;
118
119     return $cgi->restore->response;
120 }
121
122 my $agent;
123
124 =head2 remote_request
125
126 Do an actual remote request using LWP.
127
128 =cut
129
130 sub remote_request {
131
132     require LWP::UserAgent;
133
134     my $request = Catalyst::Utils::request( shift(@_) );
135     my $server  = URI->new( $ENV{CATALYST_SERVER} );
136
137     if ( $server->path =~ m|^(.+)?/$| ) {
138         $server->path("$1");    # need to be quoted
139         }
140
141     # the request path needs to be sanitised if $server is using a
142     # non-root path due to potential overlap between request path and
143     # response path.
144     if ($server->path) {
145         my @sp = split '/', $server->path;
146         my @rp = split '/', $request->uri->path;
147         shift @sp;shift @rp; # leading /
148         if (@rp) {
149             foreach my $sp (@sp) {
150                 shift @rp if $sp eq $rp[0];
151             }
152         }
153         $request->uri->path(join '/', @rp);
154     }
155
156     # the request path needs to be sanitised if $server is using a
157     # non-root path due to potential overlap between request path and
158     # response path.
159     if ($server->path) {
160         # If request path is '/', we have to add a trailing slash to the
161         # final request URI
162         my $add_trailing = $request->uri->path eq '/';
163         
164         my @sp = split '/', $server->path;
165         my @rp = split '/', $request->uri->path;
166         shift @sp;shift @rp; # leading /
167         if (@rp) {
168             foreach my $sp (@sp) {
169                 $sp eq $rp[0] ? shift @rp : last
170             }
171         }
172         $request->uri->path(join '/', @rp);
173         
174         if ( $add_trailing ) {
175             $request->uri->path( $request->uri->path . '/' );
176         }
177     }
178
179     $request->uri->scheme( $server->scheme );
180     $request->uri->host( $server->host );
181     $request->uri->port( $server->port );
182     $request->uri->path( $server->path . $request->uri->path );
183
184     unless ($agent) {
185
186         $agent = LWP::UserAgent->new(
187             keep_alive   => 1,
188             max_redirect => 0,
189             timeout      => 60,
190         );
191
192         $agent->env_proxy;
193     }
194
195     return $agent->request($request);
196 }
197
198 =head1 SEE ALSO
199
200 L<Catalyst>.
201
202 =head1 AUTHOR
203
204 Sebastian Riedel, C<sri@cpan.org>
205
206 =head1 COPYRIGHT
207
208 This program is free software, you can redistribute it and/or modify it under
209 the same terms as Perl itself.
210
211 =cut
212
213 1;