
use strict;
use warnings;

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


my $tmp_dir = "tmp" ;

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

mkdir $tmp_dir, 0777 ;

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 = "$tmp_dir/fred" ;
my $x = new XmlContainer($container_name); 
ok $x, "new XmlContainer returned object" ;


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

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($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($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";
    }

}

{
    # setPageSize with size < 512 throws exception
    eval { $x->setPageSize(100) } ;

    if (my $E = catch XmlException)
    {
        ok 1, "setPageSize with size < 512 throws exception";

        is $E->getExceptionCode(), XmlException::INVALID_VALUE, "exception is DATABASE_ERROR";

    }
    else
    {
        ok 0, "setPageSize with size < 512 throws exception";
        ok 0;
    }
}

{
    # setPageSize before container opened is ok
    eval { $x->setPageSize(512) } ;

    if (my $E = catch XmlException)
    {
        ok 0, "setPageSize before container opened is ok";
	warn "# " . $E->what . "\n" ;

    }
    else
    {
        ok 1, "setPageSize before container opened is ok";
    }
}


#print "opening\n" ;

eval { $x->open(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";
ok $x->exists(), "exists should be true";

{
    # deleteDocument -- unknown id with open container throws exception
    
    eval { $x->deleteDocument(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($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(); 

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

    eval { $x->setIndexSpecification($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(); 

    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";

}

{
    # setPageSize after container opened throws exception
    eval { $x->setPageSize(1000) } ;

    if (my $E = catch XmlException)
    {
        ok 1, "setPageSize after container opened throws exception";
        is $E->getExceptionCode(), XmlException::CONTAINER_OPEN, "exception is CONTAINER_OPEN";

    }
    else
    {
        ok 0, "setPageSize after container opened throws exception";
        ok 0;
    }
}

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

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

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

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

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

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

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

    if (my $E = catch XmlException)
    {
        ok 0, "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() ; } ;

    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($path, $context) } ;
    ok !$@, "parseXPathExpression ok" ;

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

    my $value = new XmlValue ;
    $results->next($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($value); 
    ok $value->isNull(), "results->next is null";
}

{
    # queryWithXPath - ok

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

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

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

    $results->next($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($doc1);

    eval { $x->deleteDocument($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($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($doc1);

    eval { $x->deleteDocument($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($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($doc1);

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

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

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

}

$x->close();

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

{
    # dump and load 

    $x->dump("$tmp_dir/cont");

    my $new_container_name = "$tmp_dir/new";
    my $y = new XmlContainer($new_container_name); 

    $y->load("$tmp_dir/cont");
    $y->open(); 

    my $doc = $y->getDocument($id) ;
    my $cont = "xxx";
    $cont = $doc->getContentAsString();
    is $cont, $content, "getContentAsString ok" ;
    unlink "$tmp_dir/cont";
    $y->close(); 
    $y->remove(); 
}

{
    # verify and load

    $x->verify("$tmp_dir/verify");

    my $new_container_name = "$tmp_dir/new1";
    my $y = new XmlContainer($new_container_name); 

    $y->load("$tmp_dir/verify");
    $y->open(); 

    my $doc = $y->getDocument($id) ;
    my $cont = "xxx";
    $cont = $doc->getContentAsString();
    is $cont, $content, "getContentAsString ok" ;
    unlink "$tmp_dir/verify";
    $y->close(); 
    $y->remove(); 
}

{
    # rename

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

}


{
    # remove 
    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" ;
    }
    ok ! $x->exists(), "exists should be false";
}

{
    # quertyWithXPath - fails with closed container

    my $context = new XmlQueryContext() ;
    my $path = '/book';
    my $results;
    eval { $results = $x->queryWithXPath($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;
    }
}

{
    # parseXPathExpression - fails with closed container

    my $context = new XmlQueryContext() ;
    my $path = '/book';
    my $expression;
    eval { $expression = $x->parseXPathExpression($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;
    }
}


eval {

{
    # XmlContainer::queryWithXPath -- ResultDocumentsAndValues

    my $container_name = "$tmp_dir/fred" ;
    my $x = new XmlContainer($container_name); 
    ok $x, "new XmlContainer returned object" ;
    $x->open(Db::DB_CREATE); 

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

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

    my $context = new XmlQueryContext() ;
    $context->setReturnType(XmlQueryContext::ResultDocumentsAndValues);

    my $results ;
    $results = $x->queryWithXPath('/book/title', $context) ;
    ok $results, "queryWithXPath returned a result";

    my $doc1 = new XmlDocument ;
    my $value = new XmlValue ;
    ok $results->next($doc1, $value), "results.next returns true" ;

    is $doc->getID(), $doc1->getID(), "returned document matches";

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

    my $doc2 = new XmlDocument ;
    ok ! $results->next($doc2, $value), "results.next returns false";

    $x->close() ;
    $x->remove()

}

{
    # XmlContainer::queryWithXPath -- ResultDocuments, use next(document)

    my $container_name = "$tmp_dir/fred" ;
    my $x = new XmlContainer($container_name); 
    ok $x, "new XmlContainer returned object" ;
    $x->open(Db::DB_CREATE); 

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

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

    my $context = new XmlQueryContext() ;
    $context->setReturnType(XmlQueryContext::ResultDocuments);

    my $results ;
    $results = $x->queryWithXPath('/book/title', $context) ;
    ok $results, "queryWithXPath returned a result";

    my $doc1 = new XmlDocument ;
    ok $results->next($doc1), "results.next returns true" ;

    is $doc->getID(), $doc1->getID(), "returned document matches";

    my $doc2 = new XmlDocument ;
    ok ! $results->next($doc2), "results.next returns false";

    $x->close() ;
    $x->remove()

}

{
    # XmlContainer::queryWithXPath -- ResultDocuments, but use next(value)

    my $container_name = "$tmp_dir/fred" ;
    my $x = new XmlContainer($container_name); 
    ok $x, "new XmlContainer returned object" ;
    $x->open(Db::DB_CREATE); 

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

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

    my $context = new XmlQueryContext() ;
    $context->setReturnType(XmlQueryContext::ResultDocuments);

    my $results ;
    $results = $x->queryWithXPath('/book/title', $context) ;
    ok $results, "queryWithXPath returned a result";

    my $value = new XmlValue ;
    ok $results->next($value), "results.next returns true" ;

    ok !$value->isNull(), "results->next not null";
    ok  $value->isDocument(), "results->next is a document";
    ok !$value->isString(), "results->next is not string";
    ok !$value->isNode(), "results->next is not node";
    is $value->asString(), $content, " contents ok";

    ok ! $results->next($value), "results.next returns false";

    $x->close() ;
    $x->remove()

}


{
    # XmlContainer::queryWithXPath -- ResultValues, but use next XMLDocument
    # This should throw an exception

    my $container_name = "$tmp_dir/fred" ;
    my $x = new XmlContainer($container_name); 
    ok $x, "new XmlContainer returned object" ;
    $x->open(Db::DB_CREATE); 

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

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

    my $context = new XmlQueryContext() ;
    $context->setReturnType(XmlQueryContext::ResultValues);

    my $results ;
    $results = $x->queryWithXPath('/book/title', $context) ;
    ok $results, "queryWithXPath returned a result";

    my $doc1 = new XmlDocument ;
    eval { $results->next($doc1) } ;
    ok $@, "next failed";
    is $@->getExceptionCode(), XmlException::INVALID_VALUE, "exception is INVALID_VALUE";

    $x->close() ;
    $x->remove()

}

{
    # XmlContainer::queryWithXPath -- ResultValues, 
    # but use next XMLDocument, XmlValue

    my $container_name = "$tmp_dir/fred" ;
    my $x = new XmlContainer($container_name); 
    ok $x, "new XmlContainer returned object" ;
    $x->open(Db::DB_CREATE); 

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

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

    my $context = new XmlQueryContext() ;
    $context->setReturnType(XmlQueryContext::ResultValues);

    my $results ;
    $results = $x->queryWithXPath('/book/title', $context) ;
    ok $results, "queryWithXPath returned a result";

    my $doc1 = new XmlDocument ;
    my $value = new XmlValue ;

    eval { $results->next($doc1, $value) } ;
    ok $@, "next failed";
    is $@->getExceptionCode(), XmlException::INVALID_VALUE, "exception is INVALID_VALUE";

    $x->close() ;
    $x->remove()

}

{
    # XmlContainer::queryWithXPath -- ResultDocuments
    # but use next XMLDocument, XmlValue

    my $container_name = "$tmp_dir/fred" ;
    my $x = new XmlContainer($container_name); 
    ok $x, "new XmlContainer returned object" ;
    $x->open(Db::DB_CREATE); 

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

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

    my $context = new XmlQueryContext() ;
    $context->setReturnType(XmlQueryContext::ResultDocuments);

    my $results ;
    $results = $x->queryWithXPath('/book/title', $context) ;
    ok $results, "queryWithXPath returned a result";

    my $doc1 = new XmlDocument ;
    my $value = new XmlValue ;
    ok $results->next($doc1, $value), "results.next returns true" ;

    is $doc->getID(), $doc1->getID(), "returned document matches";

    ok !$value->isNull(), "results->next not null";
    ok  $value->isDocument(), "results->next is a document";
    ok !$value->isString(), "results->next is not string";
    ok !$value->isNode(), "results->next is not node";
    is $value->asString(), $content, "contents ok";

    my $doc2 = new XmlDocument ;
    ok ! $results->next($doc2, $value), "results.next returns false";

    $x->close() ;
    $x->remove()

}


} ;

my $e;
    
if ($e = catch std::exception)
{
    warn $e->what() . "\n";
}
elsif ($@)
{
    warn $@;
}

__END__


