view release on metacpan or search on metacpan
NAME
Finance::Bank::Cahoot - Check your Cahoot bank accounts from Perl
DESCRIPTION
This module provides a rudimentary interface to the Cahoot online
banking system at "https://www.cahoot.com/". You will need either
"Crypt::SSLeay" or "IO::Socket::SSL" installed for HTTPS support to work
with WWW::Mechanize.
SYNOPSIS
my $cahoot = Finance::Bank::Cahoot->new(credentials => 'Constant',
credentials_options => {
account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' } );
my $accounts = $cahoot->accounts;
$cahoot->set_account($accounts->[0]->{account});
my $snapshot = $cahoot->snapshot;
foreach my $row (@$snapshot) {
print join ',', @$row; print "\n";
}
METHODS
new Create a new instance of a connection to the Cahoot server.
"new" can be called in two different ways. It can take a single
parameter, "credentials", which will accept an already created
credentials object, of type
"Finance::Bank::Cahoot::CredentialsProvider::*". Alternatively, it
can take two parameters, "credentials" and "credentials_options". In
this case "credentials" is the name of a credentials class to create
an instance of, and "credentials_options" is a hash of the options
to pass-through to the constructor of the chosen class.
If the second form of "new" is being used, and the chosen class is
*not* one of the ones supplied as standard then it will need to be
"required" first.
If any errors occur then "new" will "croak".
my $cahoot = Finance::Bank::Cahoot->new(credentials => 'Constant',
credentials_options => {
account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' } );
# Or create the credentials object ourselves
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(
account => '12345678', password => 'verysecret', place => 'London',
date => '01/01/1906', username => 'dummy', maiden => 'Smith' } );
my $cahoot = Finance::Bank::Cahoot->new(credentials => $credentials);
login
Login to the Cahoot server using the credentials supplied to "new".
This method is implicit for all data access methods, so typically
does not need to be called explicitly. The method takes no arguments
and will only call one of memorable place, date or mother's maiden
name as expected by the Cahoot portal.
accounts
Returns a list reference containing a summary of any accounts
available from the supplied credentials. If a login has yet to occur
"accounts" will automatically do this.
my $accounts = $cahoot->accounts;
Each item in the list is a hash reference that holds summary
information for a single account, and contains this data:
name - the text name of the account
account - the account number
balance - the current balance of the account
examples/debits view on Meta::CPAN
#! /usr/bin/perl
use strict;
use warnings;
use Finance::Bank::Cahoot;
my $cahoot = Finance::Bank::Cahoot->new(
credentials => 'ReadLine',
credentials_options => { account => '12345678',
username => 'acmeuser' });
$cahoot->login;
my $accounts = $cahoot->accounts;
foreach my $account (@$accounts) {
next unless $account->{name} =~ /current/;
$cahoot->set_account($account->{account});
}
my $debits = $cahoot->debits;
examples/snapshot view on Meta::CPAN
#! /usr/bin/perl
use strict;
use warnings;
use Finance::Bank::Cahoot;
my $cahoot = Finance::Bank::Cahoot->new(
credentials => 'ReadLine',
credentials_options => { account => '12345678',
username => 'acmeuser' });
my $accounts = $cahoot->accounts;
foreach my $account (@$accounts) {
next unless $account->{name} =~ /current/;
$cahoot->set_account($account->{account});
my $snapshot = $cahoot->snapshot;
foreach my $transaction (@$snapshot) {
print $transaction->date, q{,},
$transaction->details, q{,},
$transaction->credit || 0, q{,},
examples/statement view on Meta::CPAN
#! /usr/bin/perl
use strict;
use warnings;
use Finance::Bank::Cahoot;
my $cahoot = Finance::Bank::Cahoot->new(
credentials => 'ReadLine',
credentials_options => { account => '12345678',
username => 'acmeuser' });
$cahoot->login;
my $accounts = $cahoot->accounts;
foreach my $account (@$accounts) {
next unless $account->{name} =~ /current/;
$cahoot->set_account($account->{account});
}
my $statements = $cahoot->statements;
lib/Finance/Bank/Cahoot.pm view on Meta::CPAN
use HTML::TableExtract;
use WWW::Mechanize;
use Finance::Bank::Cahoot::Statement;
use Finance::Bank::Cahoot::DirectDebit;
sub new
{
my ($class, %opts) = @_;
croak 'Must provide a credentials handler' if not exists $opts{credentials};
my $self = { _mech => new WWW::Mechanize(autocheck => 1),
_credentials => $opts{credentials},
_connected => 0,
};
$self->{_mech}->agent_alias('Mac Safari');
bless $self, $class;
$self->_set_credentials(%opts);
return $self;
}
sub _set_credentials
{
my ($self, %opts) = @_;
croak 'Must provide either a premade credentials object or a class name together with options'
if not exists $opts{credentials};
if (ref $opts{credentials}) {
croak 'Not a valid credentials object'
if not $self->_isa_credentials($opts{credentials});
croak 'Can\'t accept credential options if supplying a premade credentials object'
if exists $opts{credentials_options};
$self->{_credentials} = $opts{credentials};
} else {
croak 'Must provide credential options unless suppying a premade credentials object'
if not exists $opts{credentials_options};
$self->{_credentials} =
$self->_new_credentials($opts{credentials}, $opts{credentials_options});
}
return $self;
}
sub _new_credentials
{
my ($self, $class, $options) = @_;
croak 'Invalid class name'
if $class !~ /^(?:\w|::)+$/;
my $full_class = 'Finance::Bank::Cahoot::CredentialsProvider::'.$class;
eval "local \$SIG{'__DIE__'}; local \$SIG{'__WARN__'}; require $full_class;"; ## no critic
croak 'Not a valid credentials class - not found' if $EVAL_ERROR;
my $credentials;
{
local $Carp::CarpLevel = $Carp::CarpLevel + 1; ## no critic
$credentials = $full_class->new(credentials => [@REQUIRED_SUBS],
options => $options);
}
croak 'Not a valid credentials class - incomplete' if not $self->_isa_credentials($credentials);
return $credentials;
}
sub _isa_credentials
{
my ($self, $credentials) = @_;
foreach my $sub (@REQUIRED_SUBS) {
return unless defined eval {
local $SIG{'__DIE__'}; ## no critic
local $SIG{'__WARN__'}; ## no critic
$credentials->can($sub);
};
}
return 1;
}
sub login
{
my ($self) = @_;
return if $self->{_connected};
$self->_get('https://securebank.cahoot.com/AquariusSecurity/bks/web/en/core_banking/log_in/frameset_top_log_in.jsp');
my %fields = (inputuserid => $self->{_credentials}->username());
my $content = $self->{_mech}->content;
foreach my $input ($self->{_mech}->find_all_inputs()) {
my $name = $input->name();
next if not defined $name;
next if defined $fields{$name};
$fields{$name} = $self->{_credentials}->place() if $content =~ /address or place:/i;
$fields{$name} = $self->{_credentials}->date() if $content =~ /memorable year:/i;
$fields{$name} = $self->{_credentials}->maiden() if $content =~ /maiden name:/i;
}
$self->_submit_form(fields => \%fields);
my %chars;
my $label;
# Expect:
# <label for="passwordChar1">... select character #d ...</label>
HTML::Parser->new(unbroken_text => 1,
report_tags => [qw(label)],
start_h => [ sub {
lib/Finance/Bank/Cahoot.pm view on Meta::CPAN
text_h => [ sub {
return if not defined $label;
$_[0] =~ /select character.*?(\d+)/;
$chars{$label} = $1 if defined $1;
}, 'dtext' ])->parse($self->{_mech}->content());
{
# We submit a form, modifying hidden fields - WWW::Mechanize does not like
# this (see FAQ).
local $^W = 0; ## no critic
$self->_submit_form(fields => { passwordChar1Hidden => $self->{_credentials}->password($chars{1}),
passwordChar2Hidden => $self->{_credentials}->password($chars{2}),
passwordChar1 => q{*},
passwordChar2 => q{*} });
}
$self->{_connected} = 1;
return $self;
}
sub _test_content_for_error
{
my ($self) = @_;
lib/Finance/Bank/Cahoot.pm view on Meta::CPAN
=head1 DESCRIPTION
This module provides a rudimentary interface to the Cahoot online
banking system at C<https://www.cahoot.com/>. You will need
either C<Crypt::SSLeay> or C<IO::Socket::SSL> installed for HTTPS
support to work with WWW::Mechanize.
=head1 SYNOPSIS
my $cahoot = Finance::Bank::Cahoot->new(credentials => 'Constant',
credentials_options => {
account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' } );
my $accounts = $cahoot->accounts;
$cahoot->set_account($accounts->[0]->{account});
my $snapshot = $cahoot->snapshot;
lib/Finance/Bank/Cahoot.pm view on Meta::CPAN
=head1 METHODS
=over 4
=item B<new>
Create a new instance of a connection to the Cahoot server.
C<new> can be called in two different ways. It can take a single parameter,
C<credentials>, which will accept an already created credentials object, of type
C<Finance::Bank::Cahoot::CredentialsProvider::*>. Alternatively, it can take two
parameters, C<credentials> and C<credentials_options>. In this case
C<credentials> is the name of a credentials class to create an instance of, and
C<credentials_options> is a hash of the options to pass-through to the
constructor of the chosen class.
If the second form of C<new> is being used, and the chosen class is I<not> one
of the ones supplied as standard then it will need to be C<required> first.
If any errors occur then C<new> will C<croak>.
my $cahoot = Finance::Bank::Cahoot->new(credentials => 'Constant',
credentials_options => {
account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' } );
# Or create the credentials object ourselves
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(
account => '12345678', password => 'verysecret', place => 'London',
date => '01/01/1906', username => 'dummy', maiden => 'Smith' } );
my $cahoot = Finance::Bank::Cahoot->new(credentials => $credentials);
=item B<login>
Login to the Cahoot server using the credentials supplied to C<new>. This method
is implicit for all data access methods, so typically does not need to be called
explicitly. The method takes no arguments and will only call one of memorable
place, date or mother's maiden name as expected by the Cahoot portal.
=item B<accounts>
Returns a list reference containing a summary of any accounts available from
the supplied credentials. If a login has yet to occur C<accounts> will
automatically do this.
my $accounts = $cahoot->accounts;
Each item in the list is a hash reference that holds summary information for a
single account, and contains this data:
=over 4
=item B<name> - the text name of the account
lib/Finance/Bank/Cahoot/CredentialsProvider.pm view on Meta::CPAN
use Carp qw(croak);
use English '-no_match_vars';
sub new
{
my ($class, %args) = @_;
croak 'Calling abstract base class constructor for '.__PACKAGE__.' is forbidden'
if $class eq __PACKAGE__;
croak 'Must provide a list of credentials'
if not exists $args{credentials};
croak 'credentials is not an array ref'
if ref $args{credentials} ne 'ARRAY';
if (exists $args{options}) {
croak 'options must be a hash ref'
if ref $args{options} ne 'HASH';
my @o = keys %{$args{options}};
croak 'Empty list of options'
if $#o < 0;
}
my $self = { };
bless $self, $class;
$self->{_credentials} = $args{credentials};
foreach my $credential (@{$args{credentials}}) {
no strict 'refs'; ## no critic
*{"${class}::$credential"} = sub { my ($self, $offset) = @_;
return $self->get($credential, $offset);
};
}
$self->_init($args{options});
return $self;
}
sub get
lib/Finance/Bank/Cahoot/CredentialsProvider.pm view on Meta::CPAN
}
1;
__END__
=for stopwords Connell Belka
=head1 NAME
Finance::Bank::Cahoot::CredentialsProvider - Abstract base class for credentials providers
=head1 SYNOPSIS
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::Acme->new(
credentials => [qw(account password)],
options => {account => 'acmeuser'});
=head1 DESCRIPTION
Provides an abstract base class for deriving new credentials providers with a
defined interface. Derived classes B<MUST> implement C<_init> and C<get>
methods.
=head1 METHODS
=over 4
=item B<new>
Create a new instance of a credentials provider. Each credential is available
with its own access method of the same name. All methods may be optionally supplied a
character offset in the credentials value (first character is 0).
=item B<credentials> is an array ref of all the credentials types available via the
credentials provider.
=item B<options> is a hash ref of options for each credential. These are
used by the credentials provider in an implementation-defined manner.
=back
=head1 ABSTRACT METHODS
=over 4
=item B<_init>
Initialization routine for the derived class to implement. Called by C<new>
with the credentials options as a hash reference. In the following example,
taken from the the C<Constant> credentials provider, C<_init> simply stores
each option value in the class:
sub _init
{
my ($self, $options) = @_;
while (my ($credential, $value) = each %{$options}) {
croak 'Invalid credential '.$credential.' supplied with callback'
if not $self->can($credential);
$self->{$credential} = $value;
}
}
=item B<get>
Public access method for the derived class to implement. Called with the
name of the credential to supply and an optional character offset (0 is
the first character). In the following example, taken from the the
C<Constant> credentials provider, the credential is simply returned from
class members created by the previous C<_init> method:
sub get
{
my ($self, $credential, $offset) = @_;
return substr ($self->{$credential}, $offset, 1)
if defined $offset;
return $self->{$credential};
}
lib/Finance/Bank/Cahoot/CredentialsProvider/Callback.pm view on Meta::CPAN
__END__
=for stopwords Connell Belka
=head1 NAME
Finance::Bank::Cahoot::CredentialsProvider::Callback - Credentials provider that uses callbacks
=head1 SYNOPSIS
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::Callback->new(
credentials => [qw(account password)],
options => { account => sub { return '12345678' },
username => sub { return 'username' },
password => sub { return substr('verysecret', $_[0]-1, 1) } });
=head1 DESCRIPTION
This module provides an implementation of a credentials provider where each of
the credentials is provided by a user-supplied callback. All callbacks return a
text string. Any callback may be optionally supplied a character offset in the
credentials value (first character is 0).
=head1 METHODS
=over 4
=item B<new>
Create a new instance of the credentials provider. All parameters are mandatory.
=item B<credentials> is an array ref of all the credentials types available via the
credentials provider.
=item B<options> is a hash ref of callback routines for each credential.
=item B<get>
Returns a credential value whose name is passed as the first parameter. An
optional character offset (0 is the first character) may also be provided.
my $password_char = $provider->password(5);
lib/Finance/Bank/Cahoot/CredentialsProvider/Constant.pm view on Meta::CPAN
__END__
=for stopwords Connell Belka
=head1 NAME
Finance::Bank::Cahoot::CredentialsProvider::Constant - Credentials provider for static data
=head1 SYNOPSIS
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(
credentials => [qw(account password)],
options => {account => 'acmeuser'});
=head1 DESCRIPTION
Provides a credentials provider that returns static data. Each credential is available
with its own access method of the same name. All methods may be optionally supplied a
character offset in the credentials value (first character is 1).
=head1 METHODS
=over 4
=item B<new>
Create a new instance of a static data credentials provider.
=item B<credentials> is an array ref of all the credentials types available via the
credentials provider.
=item B<options> is a hash ref of constant return values of credentials.
=item B<get>
Returns a credential value whose name is passed as the first parameter. An
optional character offset (1 is the first character) may also be provided.
my $password_char = $provider->password(5);
=back
lib/Finance/Bank/Cahoot/CredentialsProvider/CryptFile.pm view on Meta::CPAN
$self->{$k} = $v;
}
$fh->close;
}
if (defined $options->{fallback}) {
my $fallback_class = 'Finance::Bank::Cahoot::CredentialsProvider::'.$options->{fallback};
eval "use $fallback_class"; ## no critic
croak 'Invalid fallback provider '.$options->{fallback} if $EVAL_ERROR;
my $fallback_args = { credentials => $self->{_credentials},
options => $options->{fallback_options} };
eval "\$self->{_fallback} = $fallback_class->new(\%{\$fallback_args})"; ## no critic
croak 'Fallback provider '.$options->{fallback}.' failed to initialise' if $EVAL_ERROR;
}
my $do_update = 0;
foreach my $credential (@{$self->{_credentials}}) {
if (not defined $self->{$credential}) {
croak 'No fallback provider given and '.$credential.' is not in keyfile'
if not defined $self->{_fallback};
$self->{$credential} = $self->{_fallback}->$credential;
$do_update = 1;
}
}
if ($do_update) {
my $fh = new IO::File $keyfile, 'w'
or croak "Can't open $keyfile for writing: $OS_ERROR";
my @ciphers;
foreach my $credential (@{$self->{_credentials}}) {
push @ciphers, $credential."\t".$self->{$credential};
}
my $ciphertext = $cipher->encrypt(join "\n", @ciphers);
$fh->print($ciphertext);
$fh->close;
}
return $self;
}
sub get
lib/Finance/Bank/Cahoot/CredentialsProvider/CryptFile.pm view on Meta::CPAN
__END__
=for stopwords Connell Belka Gariv passphrase keyfile crypto
=head1 NAME
Finance::Bank::Cahoot::CredentialsProvider::CryptFile - Credentials provider for encrypted stored data
=head1 SYNOPSIS
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::CrpytFile->new(
credentials => [qw(account password)],
options => {key => 'verysecret', keyfile => '/etc/cahoot'});
=head1 DESCRIPTION
Provides a credentials provider that uses credentials stored in an encrypted file.
Each credential is available with its own access method of the same name. All methods
may be optionally supplied a character offset in the credentials value (first
character is 0).
=head1 METHODS
=over 4
=item B<new>
Create a new instance of a static data credentials provider.
=item B<credentials> is an array ref of all the credentials types available via the
credentials provider.
=item B<options> a hash ref of options for the credentials provider.
=over 4
=item B<key> Is the text passphrase for encrypting/decrypting the credentials store.
=item B<keyfile> is an optional path to the credentials store. The default store
is C<$HOME/.cahoot>.
=item B<fallback> is the name of a C<Finance::Bank::Cahoot::CredentialsProvider>
credentials provider to use for any credentials that are not present in
the encrypted store. Newly discovered credentials and encrypted and written
back to the store.
=item B<fallback_options> is a hash ref that is passed to the fallback credentials
provider's constructor as C<options>.
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::CryptFile->new(
credentials => [qw(account username password)],
options => { key => 'verysecret',
keyfile => '/etc/cahoot,
fallback => 'Constant',
fallback_options => { account => '12345678',
username => 'acmeuser',
password => 'secret' } });
=back
=item B<get>
lib/Finance/Bank/Cahoot/CredentialsProvider/ReadLine.pm view on Meta::CPAN
}
1;
__END__
=for stopwords Connell Belka username online
=head1 NAME
Finance::Bank::Cahoot::CredentialsProvider::ReadLine - Console-based credentials provider
=head1 SYNOPSIS
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::ReadLine->new(
account => '12345678', username => 'acmeuser',
password_prompt => 'Enter character %d of your password: '
);
=head1 DESCRIPTION
This module provides a C<Term::ReadLine> implementation of a credentials provider
for console entry of credentials. Each credentials method can be overridden by
a constant parameter to reduce the amount of console interaction with the user in
the case of less security sensitive data such as a username. In addition to the
value overrides, the text prompt for each readline method can also be overridden.
=head1 METHODS
=over 4
=item B<new>
Create a new instance of the credentials provider.
=item B<credentials> is an array ref of all the credentials types available via the
credentials provider.
=item B<options> is a hash ref of optional default values and prompts for each
credential. Prompts are provided in C<options> using keys of the form
C<credential_prompt>.
my $credentials = Finance::Bank::Cahoot::CredentialsProvider::ReadLine->new(
account => '12345678', username => 'acmeuser',
password_prompt => 'Enter character %d of your password: '
);
=item B<get>
Returns a credential value whose name is passed as the first parameter. An
optional character offset (1 is the first character) may also be provided.
my $password_char = $provider->password(5);
lib/Finance/Bank/Cahoot/DirectDebit.pm view on Meta::CPAN
Finance::Bank::Cahoot::DirectDebit - Cahoot direct debit record
=head1 DESCRIPTION
This module describes describes the object that holds the information
contained in a single statement transaction.
=head1 SYNOPSIS
my $cahoot = Finance::Bank::Cahoot->new(credentials => 'ReadLine');
my @accounts = $cahoot->accounts;
$cahoot->set_account($accounts->[0]->{account});
my $debits = $cahoot->debits;
foreach my $debit (@$debits) {
print $debit->payee, q{,},
$debit->reference || 0, qq{\n};
}
=head1 METHODS
lib/Finance/Bank/Cahoot/Statement.pm view on Meta::CPAN
Finance::Bank::Cahoot::Statement - Cahoot statement object
=head1 DESCRIPTION
This module describes describes the object that holds the information
contained in a single statement returned by the C<Finance::Bank::Cahoot>
C<statement> and C<snapshot> methods.
=head1 SYNOPSIS
my $cahoot = Finance::Bank::Cahoot->new(credentials => 'ReadLine');
my @accounts = $cahoot->accounts;
$cahoot->set_account($accounts->[0]->{account});
my $snapshot = $cahoot->snapshot;
foreach my $transaction (@$snapshot) {
print $transaction->date, q{,},
$transaction->details, q{,},
$transaction->credit || 0, q{,},
$transaction->debit || 0, qq{\n};
}
lib/Finance/Bank/Cahoot/Statement/Entry.pm view on Meta::CPAN
Finance::Bank::Cahoot::Statement::Entry - Cahoot statement transaction object
=head1 DESCRIPTION
This module describes describes the object that holds the information
contained in a single statement transaction.
=head1 SYNOPSIS
my $cahoot = Finance::Bank::Cahoot->new(credentials => 'ReadLine');
my @accounts = $cahoot->accounts;
$cahoot->set_account($accounts->[0]->{account});
my $snapshot = $cahoot->snapshot;
foreach my $transaction (@$snapshot) {
print $transaction->date, q{,},
$transaction->details, q{,},
$transaction->credit || 0, q{,},
$transaction->debit || 0, qq{\n};
}
t/01-connection.t view on Meta::CPAN
use Test::More tests => 31;
use Test::Exception;
use Mock::WWW::Mechanize;
my $cs = Mock::WWW::Mechanize->new('t/pages');
use_ok( 'Finance::Bank::Cahoot' );
dies_ok {
my $c = Finance::Bank::Cahoot->new()
} 'no credentials supplied: expected to fail';
my %invalid_details = ('Must provide a credentials handler'
=> {},
'Must provide credential options unless suppying a premade credentials object (1)'
=> { credentials => 'Constant' },
'Must provide credential options unless suppying a premade credentials object (2)'
=> { credentials => 'UnknownCP' },
'Not a valid credentials class - not found (1)'
=> { credentials => 'UnknownCP', credentials_options => {} },
'Not a valid credentials object (1)'
=> { credentials => bless {}, 'YetAnotherUnknownCP' },
'Not a valid credentials object (2)'
=> { credentials => {} },
'Not a valid credentials object (3)'
=> { credentials => {}, credentials_options => {} },
'Invalid class name'
=> { credentials => ':bogus:', credentials_options =>{} },
'Must provide a credentials handler'
=> { credentials_options => {} }
);
while (my ($message, $options) = each %invalid_details) {
dies_ok {
my $c = Finance::Bank::Cahoot->new(%{$options});
} 'invalid credential parameters: expected to fail';
my $re = $message;
$re =~ s/\s*\(\d+\)$//;
like($@, qr/$re at /, 'exception: '.$message);
}
dies_ok {
my $provider = {};
$INC{'Finance/Bank/Cahoot/CredentialsProvider/Bogus.pm'} = 1;
bless $provider, 'Finance::Bank::Cahoot::CredentialsProvider::Bogus';
package Finance::Bank::Cahoot::CredentialsProvider::Bogus;
sub new {};
package main;
my $c = Finance::Bank::Cahoot->new(credentials => 'Bogus', credentials_options => {});
} 'incomplete pre-made credentials class: expected to fail';
like($@, qr/Not a valid credentials class - incomplete at /, 'exception: invalid credentials');
{
my $cahoot;
ok($cahoot = Finance::Bank::Cahoot->new(credentials => 'Constant',
credentials_options => { account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith'
},
),
'valid credentials - getting ::Cahoot to create credentials object'
);
isa_ok($cahoot, 'Finance::Bank::Cahoot' );
foreach my $method (qw(login set_account statement statements
set_statement snapshot accounts)) {
can_ok($cahoot, $method);
}
foreach my $method (qw(password place date username maiden)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::Constant::$method"};
}
}
{
my $creds = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(credentials => [qw(password place date username maiden)],
options => { account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' });
ok(my $cahoot = Finance::Bank::Cahoot->new(credentials => $creds),
'valid credentials - providing premade credentials object');
$cahoot->login();
ok($cahoot->{_connected}, 'Logged in successfully');
}
t/02-constant.t view on Meta::CPAN
dies_ok {
my $provider = Finance::Bank::Cahoot::CredentialsProviderProvider->new;
} 'invalid base constructor: expected to fail';
{
package Finance::Bank::Cahoot::CredentialsProvider::Broken;
use base qw(Finance::Bank::Cahoot::CredentialsProvider);
sub _init {};
package main;
my $provider = Finance::Bank::Cahoot::CredentialsProvider::Broken->new(credentials => []);
dies_ok {
$provider->get;
} 'get method not overridden: expected to fail';
like($@, qr/Calling abstract base class get method for Finance::Bank::Cahoot::CredentialsProvider::Broken is forbidden at/,
'exception: Calling abstract base class get method');
}
use_ok('Finance::Bank::Cahoot::CredentialsProvider::Constant');
dies_ok {
my $provider = new Finance::Bank::Cahoot::CredentialsProvider;
} 'construct abstract base: expected to fail';
my %invalid_details = ('Must provide a list of credentials'
=> { },
'credentials is not an array ref'
=> { credentials => { } },
'Empty list of options'
=> { credentials => [qw(account password username)],
options => { } },
'options must be a hash ref'
=> { credentials => [qw(account password username)],
options => '' },
'Invalid credential bogus supplied with callback'
=> { credentials => [qw(account password username)],
options => { bogus => '' } },
);
while (my ($message, $credentials) = each %invalid_details) {
dies_ok {
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::Constant->new(%{$credentials});
} 'invalid credentials: expected to fail';
like($@, qr/$message at/, 'exception: '.$message);
foreach (qw(account password username)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::Constant::$_"};
}
}
{
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::Constant->new(credentials => [qw(account username password maiden)],
options => { account => '12345678',
username => 'acmeuser',
password => 'secret',
maiden => 'Smith' });
isa_ok($provider, 'Finance::Bank::Cahoot::CredentialsProvider::Constant');
is($provider->account, '12345678', 'account name');
is($provider->username, 'acmeuser', 'user name');
is($provider->password(1), 's', 'password character 1');
t/03-callback.t view on Meta::CPAN
#!/usr/bin/perl -w
use strict;
use Test::More tests => 20;
use Test::Exception;
use Carp;
use_ok('Finance::Bank::Cahoot::CredentialsProvider::Callback');
my %invalid_details = ('Must provide a list of credentials'
=> { },
'credentials is not an array ref'
=> { credentials => { } },
'Empty list of options'
=> { credentials => [qw(account password username)],
options => { } },
'options must be a hash ref'
=> { credentials => [qw(account password username)],
options => '' },
'Invalid credential bogus supplied with callback'
=> { credentials => [qw(account password username)],
options => { bogus => sub {} } },
'Callback for account is not a code ref'
=> { credentials => [qw(account password username)],
options => { account => '', 'password' => sub {}, username => sub {} } }
);
while (my ($message, $credentials) = each %invalid_details) {
dies_ok {
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::Callback->new(%{$credentials});
} 'invalid credentials: expected to fail';
like($@, qr/$message at/, 'exception: '.$message);
foreach (qw(account password username)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::Callback::$_"};
}
}
{
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::Callback->new(
credentials => [qw(account username password maiden)],
options => { account => sub { return '12345678' },
username => sub { return 'username' },
password => sub { return defined $_[0]
? substr('secret', $_[0]-1, 1) : 'secret' },
maiden => sub { return 'Smith' } });
is($provider->account, '12345678', 'account name');
is($provider->username, 'username', 'user name');
is($provider->password(1), 's', 'password character 1');
is($provider->password(2), 'e', 'password character 2');
t/04-readline.t view on Meta::CPAN
use Test::MockObject;
use Carp;
my $term = new Test::MockObject;
$term->fake_module('Term::ReadLine');
$term->fake_new('Term::ReadLine');
$term->mock('readline', sub { return $_[1].'bogus data' });
use_ok('Finance::Bank::Cahoot::CredentialsProvider::ReadLine');
my %invalid_details = ('Must provide a list of credentials'
=> { },
'credentials is not an array ref'
=> { credentials => { } },
'Empty list of options'
=> { credentials => [qw(account password username)],
options => { } },
'options must be a hash ref'
=> { credentials => [qw(account password username)],
options => '' },
'Invalid credential bogus supplied with callback'
=> { credentials => [qw(account password username)],
options => { bogus => '' } },
'Prompt for unknown credential bogus'
=> { credentials => [qw(account password username)],
options => { bogus_prompt => '' } }
);
while (my ($message, $credentials) = each %invalid_details) {
dies_ok {
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::ReadLine->new(%{$credentials});
} 'invalid credentials: expected to fail';
like($@, qr/$message at/, 'exception: '.$message);
foreach (qw(account password username)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::ReadLine::$_"};
}
}
{
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::ReadLine->new(
credentials => [qw(account username password maiden date)],
options => { date => '10/01/70',
account_prompt => '::account::',
password_prompt => '::password::',
maiden_prompt => '::maiden::' });
is($provider->date, '10/01/70', 'constant value');
is($provider->account, '::account::bogus data', 'account method');
is($provider->username, 'Enter username: bogus data', 'username method');
is($provider->password, '::password::bogus data', 'password method');
is($provider->maiden, '::maiden::bogus data', 'maiden method');
t/05-cryptfile.t view on Meta::CPAN
use strict;
use warnings;
use Test::More tests => 32;
use Test::Exception;
use Test::MockObject;
use Carp;
use_ok('Finance::Bank::Cahoot::CredentialsProvider::CryptFile');
my %invalid_details = ('Must provide a list of credentials'
=> { },
'credentials is not an array ref'
=> { credentials => { } },
'Empty list of options'
=> { credentials => [qw(account password username)],
options => { } },
'options must be a hash ref'
=> { credentials => [qw(account password username)],
options => '' },
'No key provided'
=> { credentials => [qw(account password username)],
options => { bogus => '' } },
'Can\'t open .* for writing: .*'
=> { credentials => [qw(account password username)],
options => { key => 'test', keyfile => '/W^%$#/@%W$S', fallback => 'Constant',
fallback_options => { account => '12345678',
username => 'acmeuser',
password => 'secret' } } },
'Can\'t open .* for reading: .*'
=> { credentials => [qw(account password username)],
options => { key => 'test', keyfile => 'temp_keyfile' } },
'Invalid fallback provider bogus (1)'
=> { credentials => [qw(account password username)],
options => { key => 'test', fallback => 'bogus' } },
'Invalid fallback provider bogus (2)'
=> { credentials => [qw(account password username)],
options => { key => 'test', keyfile => 'temp_keyfile', fallback => 'bogus' } },
'No fallback provider given and account is not in keyfile'
=> { credentials => [qw(account password username)],
options => { key => 'test', keyfile => 'temp_keyfile'} },
'Fallback provider Constant failed to initialise (1)'
=> { credentials => [qw(account password username)],
options => { key => 'test', keyfile => 'temp_keyfile', fallback => 'Constant',
fallback_options => { bogus => 1 } } }
);
while (my ($message, $credentials) = each %invalid_details) {
if ($message =~ /open.*reading/) {
new IO::File $credentials->{options}->{keyfile}, 'w';
chmod 000, $credentials->{options}->{keyfile};
} else {
unlink 'temp_keyfile';
}
dies_ok {
local $^W = 0; ## supress UNIVERSAL::can warning from Crypt::CBC
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::CryptFile->new(%{$credentials});
} $message.': expected to fail';
my $re = $message;
$re =~ s/\s*\(\d+\)$//;
like($@, qr/$re at/, 'exception: '.$message);
foreach (qw(account password username)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::CryptFile::$_"};
undef *{"Finance::Bank::Cahoot::CredentialsProvider::Constant::$_"};
}
}
{
unlink 'temp_keyfile';
my $provider =
Finance::Bank::Cahoot::CredentialsProvider::CryptFile->new(
credentials => [qw(account username password)],
options => { key => 'verysecret',
keyfile => 'temp_keyfile',
fallback => 'Constant',
fallback_options => { account => '12345678',
username => 'acmeuser',
password => 'secret' } });
is($provider->account, '12345678', 'account method via constant fallback');
is($provider->username, 'acmeuser', 'username method via constant fallback');
is($provider->password, 'secret', 'password method via constant fallback');
foreach my $method (qw(account username password)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::CryptFile::$method"};
undef *{"Finance::Bank::Cahoot::CredentialsProvider::Constant::$method"};
}
undef $provider;
my $provider2 =
Finance::Bank::Cahoot::CredentialsProvider::CryptFile->new(
credentials => [qw(account username password)],
options => { key => 'verysecret', keyfile => 'temp_keyfile' });
is($provider2->account, '12345678', 'account method via autosaved cryptfile');
is($provider2->username, 'acmeuser', 'username method via autosaved cryptfile');
is($provider2->password(0), 's', 'password character 0 method via autosaved cryptfile');
is($provider2->password(1), 'e', 'password character 1 method via autosaved cryptfile');
is($provider2->password(2), 'c', 'password character 2 method via autosaved cryptfile');
is($provider2->password(3), 'r', 'password character 3 method via autosaved cryptfile');
}
unlink 'temp_keyfile';
t/06-snapshot.t view on Meta::CPAN
use Test::Deep;
use Mock::WWW::Mechanize;
my $cs = Mock::WWW::Mechanize->new('t/pages');
use_ok('Finance::Bank::Cahoot');
use_ok('Finance::Bank::Cahoot::CredentialsProvider::Constant');
{
my $creds = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(
credentials => [qw(account password place date username maiden)],
options => { account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' });
ok(my $c = Finance::Bank::Cahoot->new(credentials => $creds),
'valid credentials - providing premade credentials object');
$c->login();
my $accounts = $c->accounts();
is_deeply($accounts,
[ { name => 'current account', account => '12345678',
account_index => 0,
balance => '847.83', available => '1847.83' },
{ name => 'flexible loan', account => '87654321',
account_index => 1,
balance => '0.00', available => '1000.00' },
t/07-statement.t view on Meta::CPAN
dies_ok {
my $row = Finance::Bank::Cahoot::Statement->new('bogus')
} 'invalid data row to constructor: expected to fail';
like($@, qr/statement is not an array ref at /,
'exception: invalid statement to constructor');
}
{
my $creds = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(
credentials => [qw(account password place date username maiden)],
options => { account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' });
ok(my $c = Finance::Bank::Cahoot->new(credentials => $creds),
'valid credentials - providing premade credentials object');
$c->login();
my $accounts = $c->accounts();
is_deeply($accounts,
[ { name => 'current account', account => '12345678',
account_index => 0, balance => '847.83', available => '1847.83' },
{ name => 'flexible loan', account => '87654321',
account_index => 1, balance => '0.00', available => '1000.00' },
],
'got expected account summary (list)' );
t/07-statement.t view on Meta::CPAN
],
'got correct statement');
}
foreach my $method (qw(account password place date username maiden)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::Constant::$method"};
}
}
{
my $c = Finance::Bank::Cahoot->new(credentials => 'Constant',
credentials_options => { account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' });
dies_ok {
$c->set_statement()
} 'select undef statement: expected to fail';
like($@, qr/No statement selected for set_statement/, 'exception: no statement selected');
dies_ok {
t/07-statement.t view on Meta::CPAN
} 'get statement with no account: expected to fail';
like($@, qr/No account currently selected/, 'exception: no account selected');
foreach my $method (qw(account password place date username maiden)) {
no strict 'refs';
undef *{"Finance::Bank::Cahoot::CredentialsProvider::Constant::$method"};
}
}
{
my $creds = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(
credentials => [qw(account password place date username maiden)],
options => { account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' });
my $c = Finance::Bank::Cahoot->new(credentials => $creds);
my $accounts = $c->accounts();
$c->set_account($accounts->[0]->{account});
my $statements = $c->statements;
is_deeply($statements,
[ { 'description' => '16/01/08 - 15/02/08',
'end' => 1203033600,
'start' => 1200441600 },
{ 'description' => '16/12/07 - 15/01/08',
'end' => 1200355200,
'start' => 1197763200 },
t/12-directdebit.t view on Meta::CPAN
dies_ok {
my $row = Finance::Bank::Cahoot::DirectDebit->new('bogus')
} 'invalid data row to constructor: expected to fail';
like($@, qr/row data is not an array ref at /,
'exception: invalid data row to constructor');
}
{
my $creds = Finance::Bank::Cahoot::CredentialsProvider::Constant->new(
credentials => [qw(account password place date username maiden)],
options => { account => '12345678',
password => 'verysecret',
place => 'London',
date => '01/01/1906',
username => 'dummy',
maiden => 'Smith' });
ok(my $c = Finance::Bank::Cahoot->new(credentials => $creds),
'valid credentials - providing premade credentials object');
$c->login();
my $accounts = $c->accounts();
$c->set_account($accounts->[0]->{account});
my $debits = $c->debits();
cmp_deeply($debits,
array_each(isa('Finance::Bank::Cahoot::DirectDebit')),
'got an array of direct debits');
cmp_deeply($debits,
[ methods(payee => 'ACME WATER CO',