How can I test a Dancer application with Test::WWW::Mechanize::PSGI? - perl

I'm not sure on the right way to set up the script app for www mechanize. I did try at least one alternate that works, however I'm trying to pass in configuration with the test so I can make logging quieter with the test suite.
#!/usr/bin/perl
use strict;
use warnings;
use Dancer qw(:syntax);
use MyApp;
use Test::More;
use Test::WWW::Mechanize::PSGI;
set apphandler => 'PSGI';
set log => 'warning';
set logger => 'note';
my $mech = Test::WWW::Mechanize::PSGI->new(
app => dance, # app => do('bin/app.pl'), #
);
$mech->get_ok('/login') or diag $mech->content;
done_testing;
running do on the script seems to allow the test to run, but logging variables aren't set right and at the same time seems like there'd be a better way to do that.
update
I think I might be getting closer to a solution...
#!/usr/bin/perl
use strict;
use warnings;
use FindBin;
use Cwd qw( realpath );
use Dancer qw(:syntax);
use MyApp;
use Test::More;
use Test::WWW::Mechanize::PSGI;
set apphandler => 'PSGI';
my $appdir = realpath( "$FindBin::Bin/.." );
my $mech = Test::WWW::Mechanize::PSGI->new(
app => sub {
my $env = shift;
setting(
appname => 'MyApp',
appdir => $appdir,
);
load_app 'MyApp';
config->{environment} = 'test'; # setting test env specific in test.yml detected ok
Dancer::Config->load;
my $request = Dancer::Request->new( env => $env );
Dancer->dance( $request );
}
);
$mech->get_ok('/login') or diag $mech->content;
done_testing;
I took this from the Dancer::Deployment documentation for Plack PSGI. However, I'm getting a 500 error from the test.
t/001-login.t .. Subroutine main::pass redefined at t/001-login.t line 8
Prototype mismatch: sub main::pass: none vs (;$) at t/001-login.t line 8
Use of uninitialized value $_[0] in join or string at /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/5.14.1/i686-linux/File/Spec/Unix.pm line 86.
# [2462] debug #0.004442> [hit #1]Adding mysql_enable_utf8 to DBI connection params to enable UTF-8 support in /home/ccushing/perl5/perlbrew/perls/perl- 5.14.1/lib/site_perl/5.14.1/Dancer/Plugin/Database.pm l. 148
# [2462] debug #0.117566> [hit #1]Adding mysql_enable_utf8 to DBI connection params to enable UTF-8 support in /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/site_perl/5.14.1/Dancer/Plugin/Database.pm l. 148
# [2462] error #0.148703> [hit #1]request to /login crashed: '/login/default.tt' doesn't exist or not a regular file at /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/site_perl/5.14.1/Dancer.pm line 161 in /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/site_perl/5.14.1/Dancer/Handler.pm l. 84
# <h2>runtime error</h2><pre class="error">'/login/default.tt' doesn't exist or not a regular file at /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/site_perl/5.14.1/Dancer.pm line 161
The DBI errors aren't relevant here, but their part of the error output I get. I can't figure out why it can't find /login/default.tt. I'm guessing that it's problem is that it has no idea where my views folder is as the template in question is at views/login/default.tt. This view works fine in the browser even when running on plackup. I'm stumped.

This works under the circumstance that I symlink t/views to views I currently believe this is probably the result of a bug, so I filed one here, and created this test case repository.
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
use Test::More;
use namespace::clean qw( pass );
}
use FindBin;
use Cwd qw( realpath );
use Dancer qw( :syntax );
#use MyApp;
use Test::WWW::Mechanize::PSGI;
set apphandler => 'PSGI';
my $appdir = realpath( "$FindBin::Bin/.." );
my $mech = Test::WWW::Mechanize::PSGI->new(
app => sub {
my $env = shift;
setting(
appname => 'MyApp',
appdir => $appdir,
);
load_app 'MyApp';
config->{environment} = 'test';
Dancer::Config->load;
my $request = Dancer::Request->new( env => $env );
Dancer->dance( $request );
}
);
$mech->get_ok('/') or diag $mech->content;
done_testing;
I set the logger and log in environments/test.yml.
I still get these errors, and I'd like to see them fixed but not sure what causes them.
Use of uninitialized value $_[0] in join or string at /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/5.14.1/i686-linux/File/Spec/Unix.pm line 86.
Use of uninitialized value $path in -e at /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/site_perl/5.14.1/Dancer/FileUtils.pm line 46.
Use of uninitialized value in index at /home/ccushing/perl5/perlbrew/perls/perl-5.14.1/lib/site_perl/5.14.1/Dancer/Renderer.pm line 160.
Hopefully someone can provide me with a better answer than I've been able to hammer through.

Related

Perl and Catalyst: accessing maketext from a model

Edited to clarify / reflect what I've been trying:
I'm using CatalystX::I18N::* in order to be able to internationalise my site. I have that working nicely, and my site text is coming from $c->maketext().
However, I've been trying to access these codes from my database model (in order to generate, e.g., success or failure messages when checking input before creating / updating) and am struggling.
According to the CatalystX::I18N docs, CatalystX::I18N::Maketext is a 'Helpful wrapper around Locale::Maketext. Can also be used outside of Catalyst'.
I have MyApp::Maketext setup as directed:
package MyApp::Maketext;
use strict;
use warnings;
use parent qw(CatalystX::I18N::Maketext);
1;
I have a little test script running, the setup for which is this:
#!/usr/bin/perl
use strict;
use warnings;
use FindBin qw( $Bin );
use lib "$Bin/../lib";
use TopTable::Maketext;
use Path::Class::Dir;
my $dir = Path::Class::Dir->new( "$Bin/..", "root", "locale" );
TopTable::Maketext->load_lexicon(
locales => ["en_GB"], # Required
directories => [$dir], # Required
gettext_style => 0, # Optional, Default 1
);
I am then trying two different ways to get a handle to the maketext() method:
my $lang = TopTable::Maketext->get_handle;
printf "%s\n", $lang->maketext( "menu.title.news" );
Gives the following result:
Can't call method "maketext" on an undefined value at bin\maketext-demo.pl line 23.
If I swap ->get_handle to ->new:
my $lang = TopTable::Maketext->new;
printf "%s\n", $lang->maketext( "menu.title.news" );
I get the following:
maketext doesn't know how to say:
menu.title.news
as needed at bin\maketext-demo.pl line 23.
I'm at a bit of a loss as to what to try next! Thank you so much in advance for any pointers anyone can give.
Chris
I have finally got my head around this - this is the code that eventually worked:
#!/usr/bin/perl
use strict;
use warnings;
use FindBin qw( $Bin );
use lib "$Bin/../lib";
use Data::Dumper::Concise;
use TopTable::Maketext;
use Config::ZOMG;
use Path::Class::Dir;
my $tt_config = Config::ZOMG->new( name => 'TopTable' );
my $config_hash = $tt_config->load;
my (#locales, %inhertiance, $config);
$config = $config_hash->{I18N}{locales};
foreach my $locale (keys %$config) {
push(#locales, $locale);
$inhertiance{$locale} = $config->{$locale}{inherits} if defined $con
+fig->{$locale}{inherits};
}
my $dir = Path::Class::Dir->new( "$Bin/..", "root", "locale" );
TopTable::Maketext->load_lexicon(
locales => \#locales,
directories => [$dir],
gettext_style => 1,
inheritance => \%inhertiance,
);
my $lang = TopTable::Maketext->get_handle( "en_GB" );
printf "%s\n", $lang->maketext( "menu.title.league-tables", "Division Three" );
1;
This gives the correct value of:
League Tables for Division Three
Thanks for putting up with my spam!

How to pass a command line option in Perl Dancer App executed by plackup

If I want to start a Perl Dancer app, I have to run the following command:
perl app.psgi
If I want to pass an option to the application and access it inside the script from #ARGV, I can do it like this:
perl app.psgi --option1 --option2
I can run this app using also "plackup", however I cannot pass the options like when I am running the script using Perl. The #ARGV parameters array is empty.
What can I do?
How can I pass command line options to the "app.psgi" script started from "plackup"?
Below is the file of how the script approximately looks like:
#!/usr/bin/env perl
use Dancer2;
use Data::Dumper;
use MIME::Base64 qw( encode_base64 );
use POSIX;
my $system = shift #ARGV || 'default_system';
print "SYSTEM: $system\n";
my $host = '127.0.0.1';
my $port = 5000;
set host => $host;
set port => $port;
get '/expenses' => sub {
my %params = params;
return to_json {status => 'OK'};
};
post '/expenses' => sub {
my %params = params;
return to_json {status => 'OK'};
};
dance;
It seem like plackup is running the app in a sandbox environment where #ARGV is being erased.
You can still try use environment variables instead of arguments on the command line. For example, using MY_SYSTEM as an example:
#!/usr/bin/env perl
use Dancer2;
use Data::Dumper;
use MIME::Base64 qw( encode_base64 );
use POSIX;
print "SYSTEM: $ENV{MY_SYSTEM}\n";
# [...]
and then run the app using:
$ MY_SYSTEM=Foo plackup app.psgi

WWW:Facebook::API used in perl

I am getting www:Facebook:api in perl and CPAN
error while using the Use of uninitialized value within %field in hash element at /usr/share/perl5/WWW/Facebook/API/Auth.pm line 62.
i defined all keys
#!/usr/bin/perl -w
use strict;
use warnings;
use CGI;
use WWW::Facebook::API;
use WWW::Facebook::API::Auth;
use HTTP::Request;
use LWP;
my $TMP = $ENV{HOME}.'/tmp';
my $facebook_api = '--------';
my $facebook_secret = '-------';
my $facebook_clientid = '--------';
my $gmail_user = '-------';
my $gmail_password = '--------';
my $client = WWW::Facebook::API->new(
desktop => 1,
api_version => '1.0',
api_key => $facebook_api,
secret => $facebook_secret,
throw_errors => 1,
);
$client->app_id($facebook_clientid);
local $SIG{INT} = sub {
print "Logging out of Facebookn";
my $r = $client->auth->logout;
exit(1);
};
my $token = $client->auth->create_token;
print "$token \n";
$client->auth->get_session($token);
print "$client \n";
WWW::Facebook::API doesn't look like it's been updated for a while. Line 62 of that file is:
$self->base->{ $field{$key} } = $resp->{$key};
The undefined value is the $field{$key} part. The %fieldhash is a hard-coded mapping between the names of Facebook API's known fields (i.e. the fields in the data Facebook returns to you) and the names which the module wants them to be called. It seems that Facebook has added some additional fields to its data, and the module has not been updated to deal with them.
Ultimately, this is just a warning; you can just ignore it if you like. If you want your script's output to be a bit tidier, you could change that line to:
$self->base->{ $field{$key} } = $resp->{$key} if defined $field{$key};

Is it possible to read __DATA__ with Config::General in Perl?

I'd like to setup Config::General to read from the __DATA__ section of a script instead of an external file. (I realize that's not normally how it works, but I'd like to see if I can get it going. A specific use case is so I can send a script example to another developer without having to send a separate config file.)
According to the perldoc perldata, $main::DATA should act as a valid filehandle. I think Config::General should then be able to use -ConfigFile => \$FileHandle to read it, but it's not working for me. For example, this script will execute without crashing, but the __DATA__ isn't read in.
#!/usr/bin/perl -w
use strict;
use Config::General;
use YAML::XS;
my $configObj = new Config::General(-ConfigFile => $main::DATA);
my %config_hash = $configObj->getall;
print Dump \%config_hash;
__DATA__
testKey = testValue
I also tried:
my $configObj = new Config::General(-ConfigFile => \$main::DATA);
and
my $configObj = new Config::General(-ConfigFile => *main::DATA);
and a few other variations, but couldn't get anything to work.
Is it possible to use Config::General to read config key/values from __DATA__?
-ConfigFile requires a reference to a handle. This works:
my $configObj = Config::General->new(
-ConfigFile => \*main::DATA
);
The DATA handle is a glob, not a scalar.
Try *main::DATA instead of $main::DATA.
(and maybe try \*main::DATA. From the Config::General docs it looks like you are supposed to pass a filehandle argument as a reference.)
If the -ConfigGeneral => filehandle argument to the constructor doesn't do what you mean, an alternative is
new Config::General( -String => join ("", <main::DATA>) );
This works for me:
#!/usr/bin/perl
use strict;
use warnings;
use Config::General;
use YAML::XS;
my $string;
{
local $/;
$string = <main::DATA>;
};
my $configObj = new Config::General(-String => $string);
my %config_hash = $configObj->getall;
use Data::Dumper;
warn Dumper(\%config_hash);
__DATA__
testKey = testValue

How can I make these tests more DRY?

I currently have the following at the beginning of several test files, but it's very not DRY. But I'm not really sure what the best way to move this into its own file is. Any suggestions?
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
use Test::More;
use namespace::clean qw( pass );
}
use FindBin;
use Cwd qw( realpath );
use Dancer qw( :syntax );
use Test::WWW::Mechanize::PSGI;
set apphandler => 'PSGI';
my $appdir = realpath( "$FindBin::Bin/.." );
my $t = Test::WWW::Mechanize::PSGI->new(
app => sub {
my $env = shift;
setting(
appname => 'MyApp',
appdir => $appdir,
);
load_app 'MyApp';
config->{environment} = 'test';
Dancer::Config->load;
my $request = Dancer::Request->new( env => $env );
Dancer->dance( $request );
}
);
$t->agent('test');
$t->get_ok('/login') or diag $t->content;
$t->submit_form_ok({
form_name =>'loginform',
fields => {
username => 'myuser',
password => 'foo',
},
}, 'login ok' );
### END BOILERPLATE ###
update
unfortunately part of my problem with moving this off into a library is that as soon as I've done that the code stops working. I tried encapsulating it into a subroutine and returning $t but that doesn't appear to work. I'm trying to figure out what exactly needs to go into the library and what exactly needs to go into the test.
Make it a module (say t::MyApp), change my $t to our $t, and have the module export $t. (You could also write a custom import method to turn on strict & warnings in your test script.)
You could create a .pm module that includes these lines, with some object-oriented code to obtain the $t and other information from the boilerplate code, and then use it from your tests.