#!/usr/bin/perl

# Test all available XMLRPC-API-Calls

use strict;
use warnings;
use Data::Dumper;
use Frontier::Client;

my $server	= 'demo.haci.larsux.de';
my $user		= 'admin';
my $pass		= 'admin';
my $debug		= 1;
my $v6			= $ARGV[0] || 0;

my $api			= new Frontier::Client(url => "http://$server/RPC2");
my $session	= $api->call('login', [$user, $pass]);
die 'Login failed!' unless $session;

unless ($v6) {
	&test('addRoot', 
		['_apiTestRoot', 0], 
		0
	);

	&test('listRoots', 
		[], 
		{name => '_apiTestRoot', ipv6 => 0}, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('addNet', 
		['_apiTestRoot', '192.168.0.0/24', '1. test network', 'IN USE', 30], 
		0, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('addNet', 
		['_apiTestRoot', '192.168.0.8/29', '2. test network', 'IN USE', 32], 
		0, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('addNet', 
		['_apiTestRoot', '192.168.0.10/32', '3. test network', 'IN USE'], 
		0, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('addNet', 
		['_apiTestRoot', '192.168.0.100/32', '30. test network', 'IN USE'], 
		0, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('getFreeSubnets', 
		['_apiTestRoot', '192.168.0.0/24', 29, 2], 
		['192.168.0.0/29', '192.168.0.16/29'],
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('getFreeSubnetsFromSearch', 
		['1. test network', '', '', '', '', '_apiTestRoot', 28, 1], 
		{network => '192.168.0.16/28', rootName => '_apiTestRoot'},
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('getFreeSubnets', 
		['_apiTestRoot', '192.168.0.8/29', 32, 2], 
		['192.168.0.9/32', '192.168.0.11/32'],
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('search', 
		['192.168.0.0/24', '', 1, '', '', '_apiTestRoot'], 
		{network => '192.168.0.0/24', description => '1. test network', rootName => '_apiTestRoot', state => 'IN USE'}, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('search', 
		['1. test network', '', 1, '', '', '_apiTestRoot'], 
		{network => '192.168.0.0/24', description => '1. test network', rootName => '_apiTestRoot', state => 'IN USE'}, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('editNet', 
		['_apiTestRoot', '192.168.0.10/32', {description => '4. test network', state => 'FREE'}], 
		0, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('search', 
		['4. test network', 'FREE', 1, '', '', '_apiTestRoot'], 
		{network => '192.168.0.10/32', description => '4. test network', rootName => '_apiTestRoot', state => 'FREE'}, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('delNet', 
		['_apiTestRoot', '192.168.0.100/32'],
		0,
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('search', 
		['4. test network', 'FREE', 1, '', '', '_apiTestRoot'], 
		{network => '192.168.0.100/32', description => '30. test network', rootName => '_apiTestRoot', state => 'FREE'}, 
		[['delRoot', ['_apiTestRoot']]],
		1
	);

	&test('addNet', 
		['_apiTestRoot', '192.168.0.0/28', '6. test network', 'IN USE'], 
		0, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('delNet', 
		['_apiTestRoot', '192.168.0.0/28', 0, 1],
		0,
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('search', 
		['4. test network', 'FREE', 1, '', '', '_apiTestRoot'], 
		{network => '192.168.0.10/32', description => '4. test network', rootName => '_apiTestRoot', state => 'FREE'}, 
		[['delRoot', ['_apiTestRoot']]],
		1
	);

	&test('addNet', 
		['_apiTestRoot', '192.168.0.4/30', '7. test network'], 
		0, 
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('assignFreeSubnet', 
		['_apiTestRoot', '192.168.0.4/30', 32, 'assigned net'],
		{network => '192.168.0.5/32', description => 'assigned net', state => 'UNSPECIFIED'},
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('getFreeSubnets', 
		['_apiTestRoot', '192.168.0.4/30', 32, 2], 
		['192.168.0.6/32'],
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('getSubnets', 
		['_apiTestRoot', '192.168.0.4/30'], 
		{network => '192.168.0.5/32', description => 'assigned net', state => 'UNSPECIFIED'},
		[['delRoot', ['_apiTestRoot']]]
	);

	for (1..50) {
		&test('assignFreeSubnet', 
			['_apiTestRoot', '192.168.0.0/24', 32, 'assigned net ' . $_],
			{description => 'assigned net ' . $_, state => 'UNSPECIFIED'},
			[['delRoot', ['_apiTestRoot']]]
		);

		if ($_ == 25) {
			&test('delNet', 
				['_apiTestRoot', '192.168.0.25/32'],
				0,
				[['delRoot', ['_apiTestRoot']]]
			);
		}
	}

	&test('getSubnets', 
		['_apiTestRoot', '192.168.0.0/24'], 
		{network => '192.168.0.51/32', description => 'assigned net 50', state => 'UNSPECIFIED'},
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('getSubnets', 
		['_apiTestRoot', '192.168.0.0/24'], 
		{network => '192.168.0.25/32', description => 'assigned net 26', state => 'UNSPECIFIED'},
		[['delRoot', ['_apiTestRoot']]]
	);

	&test('delRoot', 
		['_apiTestRoot'], 
		0
	);
} else {

	&test('addRoot', 
		['_apiTestRootv6', 1, 1], 
		0
	);

	&test('listRoots', 
		[], 
		{name => '_apiTestRootv6', ipv6 => 1}, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('addNet', 
		['_apiTestRootv6', 'dead:beaf::/64', '1. test network', 'IN USE', 120], 
		0, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('addNet', 
		['_apiTestRootv6', 'dead:beaf::100/120', '2. test network', 'IN USE', 128], 
		0, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('addNet', 
		['_apiTestRootv6', 'dead:beaf::102/128', '3. test network', 'IN USE'], 
		0, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('addNet', 
		['_apiTestRootv6', 'dead:beaf::1000/128', '30. test network', 'IN USE'], 
		0, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('getFreeSubnets', 
		['_apiTestRootv6', 'dead:beaf::/64', 120, 2], 
		['dead:beaf:0000:0000:0000:0000:0000:0000/120', 'dead:beaf:0000:0000:0000:0000:0000:0200/120'],
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('getFreeSubnetsFromSearch', 
		['1. test network', '', '', '', '', '_apiTestRootv6', 124, 1], 
		{network => 'dead:beaf:0000:0000:0000:0000:0000:0000/124', rootName => '_apiTestRootv6'},
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('getFreeSubnets', 
		['_apiTestRootv6', 'dead:beaf::100/120', 128, 2], 
		['dead:beaf:0000:0000:0000:0000:0000:0100/128', 'dead:beaf:0000:0000:0000:0000:0000:0101/128'],
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('search', 
		['dead:beaf:0:0:0:0:0:0/64', '', 1, '', '', '_apiTestRootv6'], 
		{network => 'dead:beaf::/64', description => '1. test network', rootName => '_apiTestRootv6', state => 'IN USE'}, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('search', 
		['1. test network', '', 1, '', '', '_apiTestRootv6'], 
		{network => 'dead:beaf::/64', description => '1. test network', rootName => '_apiTestRootv6', state => 'IN USE'}, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('editNet', 
		['_apiTestRootv6', 'dead:beaf:0000:0000:0000:0000:0000:0102/128', {description => '4. test network', state => 'FREE'}], 
		0, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('search', 
		['4. test network', 'FREE', 1, '', '', '_apiTestRootv6'], 
		{network => 'dead:beaf::102/128', description => '4. test network', rootName => '_apiTestRootv6', state => 'FREE'}, 
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('delNet', 
		['_apiTestRootv6', 'dead:beaf:0000:0000:0000:0000:0000:0102/128'],
		0,
		[['delRoot', ['_apiTestRootv6']]]
	);

	&test('search', 
		['4. test network', 'FREE', 1, '', '', '_apiTestRootv6'], 
		{network => 'dead:beaf::102/128', description => '30. test network', rootName => '_apiTestRootv6', state => 'FREE'}, 
		[['delRoot', ['_apiTestRootv6']]],
		1
	);

	&test('delRoot', 
		['_apiTestRootv6'], 
		0
	);
}


print "Successfully passed all tests\n";
exit 0;
#------------------------------------
sub test {
	my $method					= shift;
	my $args						= shift;
	my $expectedResult	= shift;
	my $rollBackMethods	= shift;
	my $notExpect				= shift || 0;
	my $errorStr				= "Error while testing '$method': ";

	my $result	= undef;
	eval {
		$result	= $api->call($method, $session, @{$args});
	};
	&rollBack($errorStr . $@, $rollBackMethods) if $@;

	return unless defined $expectedResult;

	my $bOK			= 0;
	if (ref($expectedResult) eq 'HASH') {
		if (ref($result) eq 'ARRAY') {
			foreach (@{$result}) {
				my $resultItem	= $_;
				my $nrOfOK	= 0;
				if (ref($resultItem) eq 'HASH') {
					foreach (keys %{$expectedResult}) {
						my $key		= $_;
						my $value	= $expectedResult->{$key};
						$nrOfOK++ if exists $resultItem->{$key} && $value eq $resultItem->{$key};
						if ($nrOfOK == scalar keys %{$expectedResult}) {
							$bOK	= 1;
							last;
						}
					}
					last if $bOK;
				}
			}
			$bOK	= abs($bOK - 1) if $notExpect;
			unless ($bOK) {
				my $error	= $errorStr . (($notExpect) ? 'Not expected result found' : 'Expected result cannot be found');
				$error		.= Dumper($result) if $debug;
				&rollBack($error, $rollBackMethods);
			} else {
				print "Successfully tested '$method'\n";
			}
		}
		elsif (ref($result) eq 'HASH') {
			my $nrOfOK	= 0;
			foreach (keys %{$expectedResult}) {
				my $key		= $_;
				my $value	= $expectedResult->{$key};
				$nrOfOK++ if exists $result->{$key} && $value eq $result->{$key};
				if ($nrOfOK == scalar keys %{$expectedResult}) {
					$bOK	= 1;
					last;
				}
			}
			unless ($bOK) {
				my $error	= $errorStr . (($notExpect) ? 'Not expected result found' : 'Expected result cannot be found');
				$error		.= Dumper($result) if $debug;
				&rollBack($error, $rollBackMethods);
			} else {
				print "Successfully tested '$method'\n";
			}

		} else {
			&rollBack($errorStr . 'Result is not an array nor a hash: ' . $result, $rollBackMethods);
		}
	}
	elsif (ref($expectedResult) eq 'ARRAY') {
		if (ref($result) eq 'ARRAY') {
			my $nrOfOK	= 0;
			foreach (@{$expectedResult}) {
				my $expectedResultItem	= $_;
				foreach (@{$result}) {
					my $resultItem	= $_;
					$nrOfOK++ if $resultItem eq $expectedResultItem;
					if ($nrOfOK == scalar @{$expectedResult}) {
						$bOK	= 1;
						last;
					}
				}
				last if $bOK;
			}
		}
		unless ($bOK) {
			my $error	= $errorStr . 'Expected result cannot be found';
			$error		.= Dumper($result) if $debug;
			&rollBack($error, $rollBackMethods);
		} else {
			print "Successfully tested '$method'\n";
		}
	}
	elsif ($result ne $expectedResult) {
		&rollBack($errorStr . $result, $rollBackMethods);
	} else {
		print "Successfully tested '$method'\n";
	}
}

sub rollBack {
	my $result					= shift;
	my $rollBackMethods	= shift;

	warn Dumper($result);

	if (defined $rollBackMethods) {
		print "Rollbacking with " . join(', ', map {${$_}[0]} @{$rollBackMethods}) . "\n";
		foreach (@{$rollBackMethods}) {
			my $meth	= $$_[0];
			my $args	= $$_[1];
			&test($meth, $args);
		}
	}
	exit 1;
}

# vim:ts=2:sts=2:sw=2
