#!/usr/bin/perl

# Tests for SMS::AQL using a mocked interface
#
# Thanks to Ton Voon @ Altinity (www.altinity.com) for providing this
# set of tests!
#
# $Id$

use strict;

use Test::More;
use LWP::UserAgent;


eval "use Test::MockObject::Extends";
plan skip_all => "Test::MockObject::Extends required for mock testing" 
    if $@;
    
# OK, we've got Test::MockObject::Extends, so we can go ahead:
plan tests => 81;


# NOTE - the test username and password is for testing SMS::AQL *only*,
# not to be used for any other purpose.  It is given a small amount of
# credit now and then, if you try to abuse it, it just won't get given
# any more credit.  So don't.
my $test_user = 'test_user';
my $test_pass = 'test_password';


use lib '../lib/';
use_ok('SMS::AQL');

my $warning;
my $sender;

# Catch warnings to test
local $SIG{__WARN__} = sub { $warning=shift };

$_ = SMS::AQL->new( { username => "this" } );
is($_, undef, "Fails to create new instance with only username");
like($warning, '/^Must supply username and password/', "Correct error message");

$_ = SMS::AQL->new( { password => "that" } );
is($_, undef, "Fails to create new instance with only password");
like($warning, '/^Must supply username and password/', "Correct error message");

$_ = SMS::AQL->new();
is($_, undef, "Fails to create new instance");
like($warning, '/^Must supply username and password/', "Correct error message");


ok($sender = new SMS::AQL({username => $test_user, password => $test_pass}), 
    'Create instance of SMS::AQL');

ok(ref $sender eq 'SMS::AQL', 
    '$sender is an instance of SMS::AQL');

# This wraps the ua so that methods can be overridden for testing purposes
my $mocked_ua = $sender->{ua} = Test::MockObject::Extends->new( $sender->{ua} );


$mocked_ua->mock("post", \&check_credit);
my $balance = $sender->credit();
is($balance, 501, "got account balance $balance");
is($sender->last_response, "AQSMS-CREDIT=501", "Got reply correctly");
is($sender->last_status, 1, "OK state");

sub check_credit {
	my ($self, $server, $postdata) = @_;
	my $expected = { username => "test_user", password => "test_password", cmd => "credit" };

	like( $server, '/^http:\/\/.*\/sms\/postmsg.php$/', "Server correct format: $server");
	is_deeply( $postdata, $expected, "Post data correct" );

	my $res = Test::MockObject->new();
	$res->set_true( "is_success" );
	$res->mock( "content", sub { "AQSMS-CREDIT=501" } );
	return $res;
}

$sender->{user} = "wrong_user";
$mocked_ua->mock( "post", \&check_credit_wrong_credentials );
$balance = $sender->credit;
is($balance, undef, "No balance received");
is($sender->last_response, "AQSMS-AUTHERROR", "Response gives AUTHERROR message");
is($sender->last_response_text, "The username and password supplied were incorrect", "Got nice text too");
is($sender->last_error, $sender->last_response_text, "And saved to last_error too");
is($sender->last_status, 0, "Error state");

sub check_credit_wrong_credentials {
	my ($self, $server, $postdata) = @_;
	my $expected = { username => "wrong_user", password => "test_password", cmd => "credit" };

	like( $server, '/^http:\/\/.*\/sms\/postmsg.php$/', "Server correct format: $server");
	is_deeply( $postdata, $expected, "Post data correct" );

	my $res = Test::MockObject->new();
	$res->set_true( "is_success" );
	$res->mock( "content", sub { "AQSMS-AUTHERROR" } );
	return $res;
}

$mocked_ua->mock( "post", sub { my $r = Test::MockObject->new(); $r->set_false( "is_success" ); return $r; } );
$balance = $sender->credit;
is($balance, undef, "No server available");
is($sender->last_error, "Could not get valid response from any server", "Correct error message");
is($sender->last_status, 0, "Error state");


my $rc = $sender->send_sms( "000", "Test text" );
is($rc, 0, "Sending failure due to no originator");
is($sender->last_error, "Cannot send message without sender specified", "And last_error set correctly");
is($sender->last_status, 0, "Error state");
like( $warning, '/^Cannot send message without sender specified/', "And right warning" );






#
# Sending tests
#

$sender->{user} = "test_user";
$mocked_ua->mock( "post", sub { my $r = Test::MockObject->new(); $r->set_false( "is_success" ); return $r; } );
$rc = $sender->send_sms( "000", "Test text", { sender => "Altinity" } );
is($rc, 0, "No server available");
is($sender->last_error, "Could not get valid response from any server", "Correct error message");
is($sender->last_status, 0, "Error state");


$mocked_ua->mock("post", \&send_text);
$rc = $sender->send_sms( "000", "Testing text", { sender => "Altinity" } );
is($rc, 1, "Successful send");
is($sender->last_response, "AQSMS-OK:1", "Got reply correctly");
is($sender->last_response_text, "OK", "Got text correctly");
is($sender->last_status, 1, "OK state");

my $message;
($rc, $message) = $sender->send_sms( "000", "Testing text", { sender => "Altinity" } );
is($rc, 1, "Successful send on an array interface");
is($message, "OK", "With right message");
is($sender->last_status, 1, "OK state");

sub send_text {
	my ($self, $server, $postdata) = @_;
	my $expected = { username => "test_user", password => "test_password", orig => "Altinity", to_num => "000", message=>"Testing text" };

	like( $server, '/^http:\/\/.*\/sms\/postmsg-concat.php$/', "Server correct format: $server");
	is_deeply( $postdata, $expected, "Post data correct" );

	my $res = Test::MockObject->new();
	$res->set_true( "is_success" );
	$res->mock( "content", sub { "AQSMS-OK:1" } );
	return $res;
}


# I could only get an "AQSMS-INVALID_DESTINATION if I set the to_num as "bob". Setting a mobile number with a digit short,
# or 000 would still go through as AQSMS-OK. However, SMS::AQL tries to cleanup the number, so using bob 
# fails because the postdata return "ob" instead. So for now, it makes sense to just put a dummy number in
# because this is really a test for AQL's server - we just need to make sure we process this reply correctly.

$mocked_ua->mock("post", \&send_text_invalid_destination);
$rc = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error");
is($sender->last_response, "AQSMS-INVALID_DESTINATION", "Got expected reply");
is($sender->last_response_text, "Invalid destination", "Got text correctly");
is($sender->last_status, 0, "Error state");

($rc, $message) = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error on an array interface");
is($message, "Invalid destination", "With right message");
is($sender->last_status, 0, "Error state");

sub send_text_invalid_destination {
	my ($self, $server, $postdata) = @_;
	my $expected = { username => "test_user", password => "test_password", orig => "Altinity", to_num => "000", message=>"Testing text to invalid dest" };

	like( $server, '/^http:\/\/.*\/sms\/postmsg-concat.php$/', "Server correct format: $server");
	is_deeply( $postdata, $expected, "Post data correct" );

	my $res = Test::MockObject->new();
	$res->set_true( "is_success" );
	$res->mock( "content", sub { "AQSMS-INVALID_DESTINATION" } );
	return $res;
}


$mocked_ua->mock("post", \&send_text_no_credits);
$rc = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error");
is($sender->last_response, "AQSMS-NOCREDIT", "Got expected reply");
is($sender->last_response_text, "Out of credits", "Got text correctly");
is($sender->last_status, 0, "Error state");

($rc, $message) = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error on an array interface");
is($message, "Out of credits", "With right message");
is($sender->last_status, 0, "Error state");

sub send_text_no_credits {
	my ($self, $server, $postdata) = @_;
	my $expected = { username => "test_user", password => "test_password", orig => "Altinity", to_num => "000", message=>"Testing text to invalid dest" };

	like( $server, '/^http:\/\/.*\/sms\/postmsg-concat.php$/', "Server correct format: $server");
	is_deeply( $postdata, $expected, "Post data correct" );

	my $res = Test::MockObject->new();
	$res->set_true( "is_success" );
	$res->mock( "content", sub { "AQSMS-NOCREDIT" } );
	return $res;
}


$mocked_ua->mock("post", \&send_text_unexpected_response);
$rc = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error");
is($sender->last_response, "AQSMS-NOTPROPER", "Got expected reply");
is($sender->last_response_text, "Unrecognised response from server: AQSMS-NOTPROPER", "Got text correctly");
is($sender->last_status, 0, "Error state");

($rc, $message) = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error on an array interface");
is($message, "Unrecognised response from server: AQSMS-NOTPROPER", "With right message");
is($sender->last_status, 0, "Error state");

sub send_text_unexpected_response {
	my ($self, $server, $postdata) = @_;
	my $expected = { username => "test_user", password => "test_password", orig => "Altinity", to_num => "000", message=>"Testing text to invalid dest" };

	like( $server, '/^http:\/\/.*\/sms\/postmsg-concat.php$/', "Server correct format: $server");
	is_deeply( $postdata, $expected, "Post data correct" );

	my $res = Test::MockObject->new();
	$res->set_true( "is_success" );
	$res->mock( "content", sub { "AQSMS-NOTPROPER" } );
	return $res;
}


$mocked_ua->mock( "post", sub { my $r = Test::MockObject->new(); $r->set_false( "is_success" ); return $r; } );
$rc = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error: No server available");
is($sender->last_error, "Could not get valid response from any server", "Correct error message");
is($sender->last_status, 0, "Error state");

($rc, $message) = $sender->send_sms( "000", "Testing text to invalid dest", { sender => "Altinity" } );
is($rc, 0, "Expected error: No server available");
is($message, "Could not get valid response from any server", "With right message");
is($sender->last_status, 0, "Error state");


