The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# -*-perl-*-

use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);

use MogileFS::Server;
use MogileFS::Util qw(error_code);
use MogileFS::Test;
use MogileFS::Factory;
use MogileFS::Factory::Host;
use MogileFS::Factory::Device;
use MogileFS::Host;
use MogileFS::Device;

use Data::Dumper qw/Dumper/;

my $sto = eval { temp_store(); };
if (!$sto) {
    plan skip_all => "Can't create temporary test database: $@";
    exit 0;
}

# Fetch the factories.
my $hostfac = MogileFS::Factory::Host->get_factory;
ok($hostfac, "got a host factory");
my $devfac = MogileFS::Factory::Device->get_factory;
ok($devfac, "got a device factory");

MogileFS::Config->set_config_no_broadcast("min_free_space", 100);

# Ensure the inherited singleton is good.
ok($hostfac != $devfac, "factories are not the same singleton");

{
    # Test host.
    my $host = $hostfac->set({ hostid => 1, hostname => 'foo', hostip =>
'127.0.0.5', status => 'alive', http_port => 7500, observed_state =>
'reachable'});
    ok($host, 'made a new host object');
    is($host->id, 1, 'host id is 1');
    is($host->name, 'foo', 'host name is foo');

    # Test device.
    my $dev = $devfac->set({ devid => 1, hostid => 1, status => 'alive',
weight => 100, mb_total => 5000, mb_used => 300, mb_asof => 1295217165,
observed_state => 'writeable'});
    ok($dev, 'made a new dev object');
    is($dev->id, 1, 'dev id is 1');
    is($dev->host->name, 'foo', 'name of devs host is foo');
    ok($dev->can_delete_from, 'can_delete_from works');
    ok($dev->can_read_from, 'can_read_from works');
    ok($dev->should_get_new_files, 'should_get_new_files works');

    # monitor needs to respect can_read_from,
    # everything else respects should_read_from
    {
        foreach my $s (qw/down dead/) {
            $host->{status} = $s;
            ok(!$host->alive, "host is not alive when $s");
            ok(!$dev->can_read_from, "can_read_from for device fails when host is $s");
            ok(!$dev->should_read_from, "device should not be readable when host is $s");
        }
        $host->{status} = "alive";
        ok($dev->can_read_from, "device is readable from again");
    }

    # first ensure device status is respected
    {
        foreach my $s (qw/down dead/) {
            $dev->{status} = $s;
            ok(!$dev->should_read_from, "device is NOT readable when $s");
        }
        foreach my $s (qw/readonly drain alive/) {
            $dev->{status} = $s;
            ok($dev->should_read_from, "device readable when $s");
        }
    }

    # take host observed states into account for should_read_from
    {
        $host->{observed_state} = "unreachable";
        ok($dev->can_read_from, "device can be read from by monitor of unreachable");
        ok(! $dev->should_read_from, "device should not be read from by non-monitor workers");
        ok(! $dev->observed_readable, "device not readable");
        ok(! $dev->observed_writeable, "device not writeable");
        ok($dev->observed_unreachable, "device is unreachable");

        $host->{observed_state} = "reachable";
        ok($dev->should_read_from, "device is readable again by non-monitor workers");
        ok($dev->observed_writeable, "device writable again");
        ok(! $dev->observed_unreachable, "device is reachable again");
    }

    # take device observed states into account for should_read_from
    {
        $dev->{observed_state} = "unreachable";
        ok(!$dev->should_read_from, "device should not be read from when observed unreachable");
        foreach my $s (qw/readable writeable/) {
            $dev->{observed_state} = $s;
            ok($dev->should_read_from, "device should be read from when observed $s");
        }
    }

    $hostfac->remove($host);
    $devfac->remove($dev);
}

# Might be able to skip the factory tests, as domain/class cover those.

{
    # Add a host and two devices to the DB.
    my $hostid = $sto->create_host('foo', '127.0.0.7');
    is($hostid, 1, 'new host got id 1');

    # returns 1 instead of the devid :(
    # since this it the only place which doesn't autogenerate its id.
    ok($sto->create_device(1, $hostid, 'alive'), 'created dev1');
    ok($sto->create_device(2, $hostid, 'down'), 'created dev2');

    # Update host details to DB and ensure they stick.
    ok($sto->update_host($hostid, { http_port => 6500, http_get_port => 6501 }),
        'updated host DB entry');
    # Update device details in DB and ensure they stick.
    ok($sto->update_device(1, { mb_total => 150, mb_used => 8 }),
        'updated dev1 DB entry');
    ok($sto->update_device(2, { mb_total => 100, mb_used => 3,
        status => 'dead' }), 'updated dev2 DB entry');

    # Test duplication errors.
}

{
    # Reload from DB and confirm they match what we had before.
    my @hosts = $sto->get_all_hosts;
    my @devs  = $sto->get_all_devices;

    is_deeply($hosts[0], {
            'http_get_port' => 6501,
            'status' => 'down',
            'http_port' => '6500',
            'hostip' => '127.0.0.7',
            'hostname' => 'foo',
            'hostid' => '1',
            'altip' => undef,
            'altmask' => undef
    }, 'host is as expected');

    is_deeply($devs[0], {
            'mb_total' => 150,
            'mb_used' => 8,
            'status' => 'alive',
            'devid' => '1',
            'weight' => '100',
            'mb_asof' => undef,
            'hostid' => '1'
    }, 'dev1 is as expected');
    is_deeply($devs[1], {
            'mb_total' => 100,
            'mb_used' => 3,
            'status' => 'dead',
            'devid' => '2',
            'weight' => '100',
            'mb_asof' => undef,
            'hostid' => '1'
    }, 'dev2 is as expected');
}

done_testing();