The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl
use warnings;
use strict;
use Test::More tests => 89;
use Test::Bot::BasicBot::Pluggable;

use FindBin qw( $Bin );
use lib $Bin;

my $bot = Test::Bot::BasicBot::Pluggable->new();

ok( my $ib = $bot->load("Infobot"), "Loaded infobot module" );

ok( my $uur = $ib->get("user_unknown_responses"),
    "got list of unknown responses" )
  or die;
my $no_regex = qr/($uur)/;

# ok, the intent here is to test / document the infobot grammar, because
# every time I mess with it I get annoying regressions. In general, B::B::P
# wasn't built with Test-Driven techniques, and this is hurting me recently,
# it's way too hard to write tests retroactively..

ok( $ib->help, "module has help text" );

# by default, the infobot doesn't learn things that it merely overhears
ok( !$bot->tell_indirect("foo is red"), "passive learning off by default" );
ok( !$bot->tell_indirect("foo?"),       "no answer to passive learn" );
like( $bot->tell_direct("foo?"), $no_regex, "no info on foo" );

# ..but it will learn things it's told $bot->tell_directly.
like( $bot->tell_direct("foo?"), $no_regex, "no info on foo" );
is( $bot->tell_direct("foo is red"), "Okay.", "active learning works" );
is( $bot->tell_direct("foo?"), "foo is red", "correct answer to active learn" );

like( $bot->tell_direct("quux?"), $no_regex, "no info on quux" );
is( $bot->tell_direct("quux are blue"), "Okay.", "active learning works" );
is(
    $bot->tell_direct("quux?"),
    "quux are blue",
    "correct answer to active learn"
);

# you can tell someone about foo
is(
    $bot->tell_direct("tell testbot about foo"),
    "Told testbot about foo.",
    "tell someone about foo"
);

ok( !$bot->tell_indirect("foo?"), "passive questioning off by default" );

# you can turn on the ability to ask questions without addressing the bot
ok( $ib->set( "user_passive_answer", 1 ), "activate passive ask" );
is( $bot->tell_indirect("foo?"), "foo is red", "passive questioning now on" );

# and the ability to add factoids without addressing the bot
ok( $ib->set( "user_passive_learn", 1 ), "activate passive learn" );
is( $bot->tell_direct("bar is green"), "Okay.", "passive learning now works" );
is( $bot->tell_indirect("bar?"), "bar is green", "passive questioning works" );

# you can search factoids, but not in public
is(
    $bot->tell_direct("search for foo"),
    "privmsg only, please",
    "not searched in public"
);
$ib->set( "user_allow_searching", 0 );
is(
    $bot->tell_private("search for foo"),
    "searching disabled",
    "searched for 'foo' disabled"
);
$ib->set( "user_allow_searching", 1 );
is(
    $bot->tell_private("search for foo"),
    "I know about: 'foo'.",
    "searched for 'foo'"
);
is(
    $bot->tell_private("search for foobar"),
    "I don't know anything about foobar.",
    "searched for 'foobar' (which we know nothing about)"
);
is(
    $bot->tell_private("search for foo bar"),
    "I know about: 'foo', 'bar'.",
    "searched for 'foo' and 'bar'"
);
$ib->set( 'user_num_results' => 1 );
is(
    $bot->tell_private("search for foo bar"),
    "I know about: 'foo'.",
    "searched for 'foo' and 'bar' with user_num_results set to 1"
);

# you can append strings to factoids
is( $bot->tell_direct("foo is also blue"), "Okay.", "can append to faactoids" );
is( $bot->tell_direct("foo?"), "foo is red or blue", "works" );
is( $bot->tell_direct("foo is also pink"), "Okay.", "can append to faactoids" );
is( $bot->tell_direct("foo?"), "foo is red or blue or pink", "works" );

# factoids can be forgotten
is( $bot->tell_direct("forget foo"), "I forgot about foo.", "forgotten foo" );
like( $bot->tell_direct("foo?"), $no_regex, "no info on foo" );
is(
    $bot->tell_direct("forget foo"),
    "I don't know anything about foo.",
    "can't forget something i don't know"
);

# factoids can be replaced
my $but_reply =
  '... but bar is green ...';    # ok, why does this get interpreted as '1'
is( $bot->tell_direct("bar is yellow"),
    $but_reply, "Can't just redefine factoids" );
is( $bot->tell_indirect("bar is yellow"), '', "Can't just redefine factoids" );
is( $bot->tell_indirect("bar?"), "bar is green", "not changed" );
is( $bot->tell_direct("no, bar is yellow"),
    "Okay.", "Can explicitly redefine factoids" );
is( $bot->tell_indirect("bar?"), "bar is yellow", "changed" );

# factoids can contain RSS
SKIP: {
    eval "use XML::Feed";
    skip 'XML::Feed not installed', 4 if $@;

    is( $bot->tell_direct("rsstest is <rss=\"file:///$Bin/test.rss\">"),
        "Okay.", "set RSS" );
    is( $bot->tell_indirect("rsstest?"), "title", "can read rss" );
    $bot->tell_direct("rsstest2 is <rss=\"file:///$Bin/05infobot.t\">");
    like(
        $bot->tell_indirect("rsstest2?"),
qr{rsstest2 is << Error parsing RSS from file:///.*/05infobot.t: Cannot detect feed type >>},
        "can't read rss"
    );
    is(
        $bot->tell_direct("literal rsstest?"),
        "rsstest =is= <rss=\"file:///$Bin/test.rss\">",
        "literal of rsstest"
    );
}

my $old_stopwords = $ib->get("user_stopwords");

# certain things can't be factoid keys.
ok( $ib->set( "user_stopwords", "and" ), "set stopword 'and'" );
ok( !$bot->tell_direct("and is mumu"), "can't set 'and' as factoid" );
ok( !$bot->tell_direct("dkjsdlfkdsjfglkdsfjglfkdjgldksfjglkdfjglds is mumu"),
    "can't set very long factoid" );

$ib->set( "user_stopwords", $old_stopwords );

# literal syntax
ok( $bot->tell_direct("bar is also fum"), "bar also fum" );
is( $bot->tell_direct("literal bar?"), "bar =is= yellow =or= fum", "bar" );

# alternate factoids ('|')
is( $bot->tell_direct("foo is one"),         "Okay.", "foo is one" );
is( $bot->tell_direct("foo is also two"),    "Okay.", "foo is also two" );
is( $bot->tell_direct("foo is also |maybe"), "Okay.", "foo is also maybe" );

ok( my $reply = $bot->tell_direct("foo?"), "got one of the foos" );
ok( ( $reply eq 'foo is maybe' or $reply eq 'foo is one or two' ),
    "it's one of the two" );

# blech's torture test, all three in one
# notes on dipsy differences:
# * 'ok' is 'okay.' in a true infobot
# * literal doesn't highlight =or= like it does =is=
# * infobots attempt to parse english
# * there's a difference between 'is' and 'are'
# * doesn't respond to a passive attempt to reset an item

is( $bot->tell_direct("forget foo"), "I forgot about foo.", "forgotten foo" );

is( $bot->tell_direct("foo is foo"),   "Okay.",      "simple set" );
is( $bot->tell_direct("foo?"),         "foo is foo", "simple get" );
is( $bot->tell_direct("what is foo?"), "foo is foo", "English-language get" )
  ;    # fails
is( $bot->tell_direct("where is foo?"), "foo is foo", "Another English get" );
is( $bot->tell_direct("who is foo?"), "foo is foo", "Yet another English get" );

is( $bot->tell_direct("hoogas are things"), "Okay.", "simple 'are' set" )
  ;    # fails
is(
    $bot->tell_direct("what are hoogas?"),
    "hoogas are things",
    "English-language 'are' get"
);

is(
    $bot->tell_direct("foo is a silly thing"),
    "... but foo is foo ...",
    "warning about overwriting"
);
is( $bot->tell_indirect("foo is a silly thing"), "", "shouldn't get a reply" );

is( $bot->tell_direct("foo is also bar"), "Okay.", "simple append" );
is( $bot->tell_direct("foo?"), "foo is foo or bar", "appended ok" );
is( $bot->tell_direct("foo is also baz or quux"), "Okay.", "complex append" );
is( $bot->tell_direct("foo?"), "foo is foo or bar or baz or quux", "also ok" );
is( $bot->tell_direct("foo is also | a silly thing"),
    "Okay.", "alternate appended" );

is(
    $bot->tell_direct("literal foo?"),
    "foo =is= foo =or= bar =or= baz =or= quux =or= |a silly thing",
    "entire factoid looks right"
);
is( $bot->tell_direct("foo is also |<reply>this is a very silly thing"),
    "Okay.", "and a reply" );
is(
    $bot->tell_direct("literal foo?"),
"foo =is= foo =or= bar =or= baz =or= quux =or= |a silly thing =or= |<reply>this is a very silly thing",
    "entire entry looks fine to me"
);

# run through a few times, and see what we get out
foreach my $i ( 0 .. 9 ) {
    ok( $reply = $bot->tell_direct("foo?"), "got one of the foos" );
    ok(
        (
                 $reply eq 'foo is foo or bar or baz or quux'
              or $reply eq 'foo is a silly thing'
              or $reply eq 'this is a very silly thing'
        ),
        "it's '$reply'"
    );
}