
use strict;
use warnings;

use Test::More tests => 71 ;
BEGIN { use_ok('Sleepycat::DbXml', 'simple') };
BEGIN { use_ok('File::Path') };

my $home = "tmp" ;


if (-d $home)
{
    rmtree($home);
}

mkdir $home, 0777 ;

my $env = new DbEnv ;

ok $env, "Created DbEnv";


is $env->open($home, Db::DB_CREATE|Db::DB_INIT_MPOOL|Db::DB_INIT_TXN|
                     Db::DB_INIT_LOCK, 0666), 0, "opened DbEnv" ;

ok my $txn = $env->txn_begin(), "Create Transaction" ;


my $doc ;

eval { $doc = new XmlDocument(); };

ok ! $@, "Creating document doesn't throw an exception" ;
ok defined $doc, "Document object created ok" ;

is $doc->getID(), 0, "Doc ID should be 0 before it has any content" ;

my $title = '<title>Knowledge Discovery in Databases.</title>';
my $content = "<book>$title</book>";
$doc->setContent($content);

ok $doc->getID() == 0, "Doc ID != 0 when doc has content, but not in container";

my $cont = $doc->getContentAsString();

ok $cont eq $content, "getContentAsString returned correct content" ;

$doc->setName("fred");

is $doc->getName(), 'fred', "setName/getName works ok" ; 


my $container_name = "fred" ;
my $x = new XmlContainer($env, $container_name); 
ok $x, "new XmlContainer returned object" ;


ok ! $x->isOpen(), "isOpen returns false on unopened container";

my $indexSpec = new XmlIndexSpecification();
my $Uri = "http://xyz.com/";
my $Name = "dummy";
my $Index = "node-attribute-presence-none";
$indexSpec->addIndex($Uri, $Name, $Index);

{
    # setIndexSpecification -- unopened container throws CONTAINER_CLOSED exception
    
    eval { $x->setIndexSpecification($txn, $indexSpec) ; } ;

    if (my $E = catch XmlException)
    {
        ok 1, "setIndexSpecification on unopened container throws an exception";
        is $E->getExceptionCode(), XmlException::CONTAINER_CLOSED, "exception is CONTAINER_CLOSED";

    }
    else
    {
	print $@;
        ok 0, "setIndexSpecification on unopened container throws an exception";
	ok 0;
    }

}

{
    # deleteDocument -- unopened container throws CONTAINER_CLOSED exception
    
    eval { $x->deleteDocument($txn, $doc) ; } ;

    if (my $E = catch XmlException)
    {
        ok 1, "deleteDocument on unopened container throws an exception";
        is $E->getExceptionCode(), XmlException::CONTAINER_CLOSED, "exception is CONTAINER_CLOSED";

    }
    else
    {
        ok 0, "deleteDocument on unopened container throws an exception";
	ok 0, "exception is CONTAINER_CLOSED";
    }

}


#print "opening\n" ;

eval { $x->open($txn, Db::DB_CREATE); } ;

if (my $E = XmlException::catch())
{
    ok 0, "open should not throw an exception";
    print "# " . $E->what() . "\n" ;
    exit;
}
else
{
    ok 1, "open should not throw an exception";
}

ok $x->isOpen(), "isOpen returns true on open container";

{
    # deleteDocument -- unknown id with open container throws exception
    
    eval { $x->deleteDocument($txn, 123) ; } ;

    if (my $E = catch XmlException)
    {
        ok 1, "deleteDocument opened container, unknown id throws an exception";
        is $E->getExceptionCode(), XmlException::DOCUMENT_NOT_FOUND, "exception is DOCUMENT_NOT_FOUND";

    }
    else
    {
        ok 0, "deleteDocument opened container, unknown id throws an exception";
	ok 0, "exception is DOCUMENT_NOT_FOUND";
    }

}

{
    # deleteDocument -- unknown doc with open container throws exception
    
    my $doc1 = new XmlDocument(); 

    ok defined $doc1, "Document object created ok" ;


    my $cont = '<book></book>';
    $doc1->setContent($cont);

    eval { $x->deleteDocument($txn, $doc1) ; } ;

    if (my $E = catch XmlException)
    {
        ok 1, "deleteDocument opened container, unknown doc throws an exception";
        is $E->getExceptionCode(), XmlException::DOCUMENT_NOT_FOUND, "exception is DOCUMENT_NOT_FOUND";

    }
    else
    {
        ok 0, "deleteDocument opened container, unknown doc throws an exception";
	ok 0, "exception is DOCUMENT_NOT_FOUND";
    }

}

my $name = $x->getName() ;
is $x->getName(), $container_name, "getName";

{
    # setIndexSpecification -- opened container without docs is ok
    
    my $uri ;
    my $name ;
    my $index ;

    my $ix = $x->getIndexSpecification($txn); 

    ok !$ix->next($uri, $name, $index), 
        "next returns false";

    eval { $x->setIndexSpecification($txn, $indexSpec) ; } ;

    if (my $E = catch XmlException)
    {
        ok 0, "setIndexSpecification on opened container is ok";
	warn "# exception says: " . $E->what() ;

    }
    else
    {
        ok 1, "setIndexSpecification on opened container is ok";
    }

    $ix = $x->getIndexSpecification($txn); 

    ok $ix->next($uri, $name, $index), 
        "next returns true";

    is $index, $Index, "next index 1 is '$Index'";
    is $name, $Name, "next name is '$Name'";
    is $uri, $Uri, "next uri is '$Uri'";

    ok ! $ix->next($uri, $name, $index), 
        "next 1 returns false";

}

my $id = $x->putDocument($txn, $doc);

ok $id > 0, "Document ID > 0, when in container" ;

is $id, $doc->getID(), "DOC ID's should be equal";

$doc = $x->getDocument($txn, $id) ;
$cont = "xxx";
$cont = $doc->getContentAsString();

is $cont, $content, "getContentAsString ok" ;

{
    # setIndexSpecification -- container with doc throws CONTAINER_NOT_EMPTY exception
    
    my $uri = "http://xyz.com/";
    my $name = "dummy";
    my $index = "node-attribute-presence";

    eval { $x->setIndexSpecification($txn, $indexSpec) ; } ;

    if (my $E = catch XmlException)
    {
        ok 1, "setIndexSpecification on un-empty container does not throw an exception";

    }
    else
    {
        ok 1, "setIndexSpecification on un-empty container does not throw an exception";
    }

}

{
    # remove on opened container throw exception

    eval { $x->remove($txn) ; } ;

    if (my $E = catch XmlException)
    {
        ok 1, "remove on open container throws exception" ;
        is $E->getExceptionCode(), XmlException::CONTAINER_OPEN, "exception is CONTAINER_OPEN";
    }
    else {
    
        ok 0 ;
        ok 0 ;
    }

}


{
    # parseXPathExpression - ok

    my $context = new XmlQueryContext(XmlQueryContext::ResultValues) ;
    my $path = '/book/title';
    my $expression;
    eval { $expression = $x->parseXPathExpression($txn, $path, $context) } ;
    ok !$@, "parseXPathExpression ok" ;

    is $expression->getXPathQuery(), $path, "getXPathQuery ok";
    my $results;
    eval { $results = $x->queryWithXPath($txn, $expression) } ;
    ok !$@, "quertyWithXPath ok" ;

    my $value = new XmlValue ;
    $results->next($txn, $value); 
    ok !$value->isNull(), "results->next not null";
    ok !$value->isDocument(), "results->next is document";
    ok !$value->isString(), "results->next is string";
    ok $value->isNode(), "results->next is node";
    is $value->asString(), $title, "nodelist contents ok";

    $results->next($txn, $value); 
    ok $value->isNull(), "results->next is null";
}

{
    # queryWithXPath - ok

    my $context = new XmlQueryContext() ;
    my $path = '/book';
    my $results;

    eval { $results = $x->queryWithXPath($txn, $path, $context) } ;
    ok !$@, "quertyWithXPath ok" ;

    my $value = new XmlValue ;
    $results->next($txn, $value); 
    ok !$value->isNull(), "results->next not null";
    ok $value->isDocument(), "results->next is document";
    is $value->asString(), $content, "document contents ok";

    $results->next($txn, $value); 
    ok $value->isNull(), "results->next is null";

}

{
    # deleteDocument -- known doc with open container is ok
    
    my $doc1 = new XmlDocument(); 

    ok defined $doc1, "Document object created ok" ;

    my $cont = '<book></book>';
    $doc1->setContent($cont);

    my $id = $x->putDocument($txn, $doc1);

    eval { $x->deleteDocument($txn, $doc1) ; } ;

    if (my $E = catch XmlException)
    {
        print "# deleteDocument failed " . $E->what() . "\n" ;
        ok 0, "deleteDocument opened container, known doc is ok";

    }
    else
    {
        ok 1, "deleteDocument opened container, known doc is ok";
    }

    eval { $x->getDocument($txn, $id) };
    is $@ && $@->getExceptionCode(), XmlException::DOCUMENT_NOT_FOUND, 
        "getDocument throws :DOCUMENT_NOT_FOUND";

}

{
    # deleteDocument -- known doc id with open container is ok
    
    my $doc1 = new XmlDocument(); 

    ok defined $doc1, "Document object created ok" ;

    my $cont = '<book></book>';
    $doc1->setContent($cont);

    my $id = $x->putDocument($txn, $doc1);

    eval { $x->deleteDocument($txn, $id) ; } ;

    if (my $E = catch XmlException)
    {
        print "# deleteDocument failed " . $E->what() . "\n" ;
        ok 0, "deleteDocument opened container, known id is ok";

    }
    else
    {
        ok 1, "deleteDocument opened container, known id is ok";
    }

    eval { $x->getDocument($txn, $id) };
    is $@ && $@->getExceptionCode(), XmlException::DOCUMENT_NOT_FOUND, 
        "getDocument throws :DOCUMENT_NOT_FOUND";
}

{
    # updateDocument
    
    my $doc1 = new XmlDocument(); 

    ok defined $doc1, "Document object created ok" ;

    my $cont1 = '<book1></book1>';
    my $cont2 = '<book2></book2>';
    $doc1->setContent($cont1);

    my $id = $x->putDocument($txn, $doc1);

    my $d = $x->getDocument($txn, $id);
    is $d->getContentAsString(), $cont1, "document content is $cont1";

    $doc1->setContent($cont2);
    $x->updateDocument($txn, $doc1) ;  

    # todo - need to check that cont2 is now in the container.
    #$d = $x->getDocument($id)}; 
    #is $d->getContentAsString(), $cont2, "document content is $cont2";

}

$txn->commit();
$x->close();

ok ! $x->isOpen(), "isOpen returns false on closed container";

{
    # dump and load 

    $x->dump("cont");

    my $new_container_name = "new";
    my $y = new XmlContainer($env, $new_container_name); 
    my $txn = $env->txn_begin();


    $y->load("cont");
    $y->open($txn); 

    my $doc = $y->getDocument($txn, $id) ;
    my $cont = "xxx";
    $cont = $doc->getContentAsString();
    is $cont, $content, "getContentAsString ok" ;
    unlink "cont";
    $txn->commit();
    $y->close(); 

    $txn = $env->txn_begin();
    $y->remove($txn); 
    $txn->commit();
}

{
    # verify and load

    $x->verify("verify");

    my $new_container_name = "new1";
    my $y = new XmlContainer($env, $new_container_name); 
    my $txn = $env->txn_begin();

    $y->load("verify");
    $y->open($txn); 

    my $doc = $y->getDocument($txn, $id) ;
    my $cont = "xxx";
    $cont = $doc->getContentAsString();
    is $cont, $content, "getContentAsString ok" ;
    unlink "verify";
    $txn->commit();
    $y->close(); 

    $txn = $env->txn_begin();
    $y->remove($txn); 
    $txn->commit();
}

{
    # rename

    $container_name = "joe" ;
    my $txn = $env->txn_begin();
    eval { $x->rename($txn, $container_name); };
    if (my $E = catch XmlException)
    {
        print "# rename failed " . $E->what() . "\n" ;
        ok 0, "rename worked";
    }
    else
    {
        ok 1, "rename worked";
    }
    $txn->commit();
    
    is $x->getName(), $name, "getName";
    $x->setName($container_name);
    is $x->getName(), $container_name, "getName";
    $name = $x->getName() ;

}


{
    # remove 
    my $txn = $env->txn_begin();
    eval { $x->remove() ; } ;

    if (my $E = catch XmlException)
    {
        ok 0, "remove on closed container does not throw exception" ;
        is $E->getExceptionCode(), XmlException::CONTAINER_OPEN, "exception is CONTAINER_OPEN";
        print "# remove" . $E->what() . "\n" ;
        exit ;
    }
    else {
    
        ok 1, "remove on closed container does not throw exception" ;
    }
    $txn->commit();
}

{
    # quertyWithXPath - fails with closed container

    my $txn = $env->txn_begin();
    my $context = new XmlQueryContext() ;
    my $path = '/book';
    my $results;
    eval { $results = $x->queryWithXPath($txn, $path, $context) } ;
    if (my $E = catch XmlException)
    {
        ok 1, "queryWithXPath fails on closed container";
        is $E->getExceptionCode(), XmlException::CONTAINER_CLOSED, "exception is CONTAINER_CLOSED";

    }
    else
    {
        ok 0, "queryWithXPath fails on closed container";
	ok 0;
    }
    $txn->commit();
}

{
    # parseXPathExpression - fails with closed container

    my $txn = $env->txn_begin();
    my $context = new XmlQueryContext() ;
    my $path = '/book';
    my $expression;
    eval { $expression = $x->parseXPathExpression($txn, $path, $context) } ;
    if (my $E = catch XmlException)
    {
        ok 1, "parseXPathExpression fails on closed container";
        is $E->getExceptionCode(), XmlException::CONTAINER_CLOSED, "exception is CONTAINER_CLOSED";

    }
    else
    {
        ok 0, "parseXPathExpression fails on closed container";
	ok 0;
    }
    $txn->commit();
}

