Commit f3890b72 authored by Matthieu Dorier's avatar Matthieu Dorier
Browse files

added 3 more tests

parent 0a80390a
......@@ -3,10 +3,14 @@
#include <json/json.h>
#include <thallium.hpp>
#include <string>
#include <memory>
#include <sonata/Exception.hpp>
namespace sonata {
namespace tl = thallium;
class AdminImpl;
/**
......@@ -26,7 +30,7 @@ class Admin {
* @param engine Thallium engine.
* @param token Security token setup by providers.
*/
Admin(thallium::engine& engine, const std::string& token="");
Admin(tl::engine& engine, const std::string& token="");
/**
* @brief Copy constructor.
......@@ -91,7 +95,7 @@ class Admin {
const std::string& name,
const std::string& type,
const char* config) const {
return createDatabase(address, provider_id, name, type, std::string(config));
createDatabase(address, provider_id, name, type, std::string(config));
}
/**
......
......@@ -22,7 +22,13 @@ class Collection {
friend class Database;
public:
/**
* @brief Default constructor. The resulting collection
* instance will be invalid.
*/
Collection();
/**
* @brief Copy constructor.
*/
......
......@@ -24,6 +24,11 @@ class Database {
public:
/**
* @brief Constructor. The resulting Database handle will be invalid.
*/
Database();
/**
* @brief Copy-constructor.
*/
......
......@@ -11,6 +11,8 @@
namespace sonata {
Collection::Collection() = default;
Collection::Collection(const std::shared_ptr<CollectionImpl>& impl)
: self(impl) {}
......
......@@ -10,6 +10,8 @@
namespace sonata {
Database::Database() = default;
Database::Database(const std::shared_ptr<DatabaseImpl>& impl)
: self(impl) {}
......
......@@ -195,12 +195,18 @@ class UnQLiteBackend : public Backend {
"$ret = false;"
"$err = \"Collection does not exist\";"
"} else {"
"$ret = db_fetch_all($collection, $filter_cb);"
"if($ret == FALSE) {"
"$err = db_errlog();"
"} else {"
"$data = $ret;"
"if(0 == db_total_records($collection)) {"
"$ret = TRUE;"
"$data = [];"
"} else {"
"$ret = db_fetch_all($collection, $filter_cb);"
"if($ret == FALSE) {"
"$ret = TRUE;"
"$data = [];"
"} else {"
"$data = $ret;"
"$ret = TRUE;"
"}"
"}"
"}";
RequestResult<std::vector<std::string>> result;
......@@ -268,12 +274,17 @@ class UnQLiteBackend : public Backend {
"$ret = false;"
"$err = \"Collection does not exist\";"
"} else {"
"$ret = db_fetch_all($collection);"
"if($ret == FALSE) {"
"$err = db_errlog();"
"} else {"
"$data = $ret;"
"if(0 == db_total_records($collection)) {"
"$ret = TRUE;"
"$data = [];"
"} else {"
"$ret = db_fetch_all($collection);"
"if($ret == FALSE) {"
"$err = db_errlog();"
"} else {"
"$data = $ret;"
"$ret = TRUE;"
"}"
"}"
"}";
RequestResult<std::vector<std::string>> result;
......
#include <sonata/Admin.hpp>
#include <cppunit/extensions/HelperMacros.h>
extern thallium::engine* engine;
class AdminTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE( AdminTest );
CPPUNIT_TEST( testAdminCreateDatabase );
CPPUNIT_TEST_SUITE_END();
static constexpr const char* db_config = "{ \"path\" : \"mydb\" }";
public:
void setUp() {}
void tearDown() {}
void testAdminCreateDatabase() {
sonata::Admin admin(*engine);
std::string addr = engine->self();
// Create a valid Database
CPPUNIT_ASSERT_NO_THROW_MESSAGE("admin.createDatabase should return a valid Database",
admin.createDatabase(addr, 0, "db1", "unqlite", db_config));
// Create a Database with a name already taken
CPPUNIT_ASSERT_THROW_MESSAGE("admin.createDatabase should throw an exception (wrong name)",
admin.createDatabase(addr, 0, "db1", "unqlite", db_config),
sonata::Exception);
// Create a Database with a wrong backend type
CPPUNIT_ASSERT_THROW_MESSAGE("admin.createDatabase should throw an exception (wrong backend)",
admin.createDatabase(addr, 0, "db_blabla", "blabla", db_config),
sonata::Exception);
// Create a Database with a wrong configuration
CPPUNIT_ASSERT_THROW_MESSAGE("admin.createDatabase should throw an exception (wrong config)",
admin.createDatabase(addr, 0, "db_no_config", "unqlite", ""),
sonata::Exception);
// Destroy the Database
admin.destroyDatabase(addr, 0, "db1");
}
};
CPPUNIT_TEST_SUITE_REGISTRATION( AdminTest );
......@@ -5,7 +5,19 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test.json ${CMAKE_CURRENT_BINARY_DIR}
add_library(sonata-test Main.cpp)
target_link_libraries(sonata-test cppunit sonata-server sonata-admin sonata-client)
add_executable(AdminTest AdminTest.cpp)
target_link_libraries(AdminTest sonata-test)
add_executable(ClientTest AdminTest.cpp)
target_link_libraries(ClientTest sonata-test)
add_executable(DatabaseTest DatabaseTest.cpp)
target_link_libraries(DatabaseTest sonata-test)
add_test(NAME DatabaseTest COMMAND ./DatabaseTest)
add_executable(CollectionTest CollectionTest.cpp)
target_link_libraries(CollectionTest sonata-test)
add_test(NAME AdminTest COMMAND ./AdminTest AdminTest.xml)
add_test(NAME ClientTest COMMAND ./ClientTest ClientTest.xml)
add_test(NAME DatabaseTest COMMAND ./DatabaseTest DatabaseText.xml)
add_test(NAME CollectionTest COMMAND ./CollectionTest CollectionTest.xml)
#include <cppunit/extensions/HelperMacros.h>
#include <sonata/Client.hpp>
#include <sonata/Admin.hpp>
extern thallium::engine* engine;
class ClientTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE( ClientTest );
CPPUNIT_TEST( testOpenDatabase );
CPPUNIT_TEST_SUITE_END();
static constexpr const char* db_config = "{ \"path\" : \"mydb\" }";
public:
void setUp() {
sonata::Admin admin(*engine);
std::string addr = engine->self();
admin.createDatabase(addr, 0, "mydb", "unqlite", db_config);
}
void tearDown() {
sonata::Admin admin(*engine);
std::string addr = engine->self();
admin.destroyDatabase(addr, 0, "mydb");
}
void testOpenDatabase() {
sonata::Client client(*engine);
std::string addr = engine->self();
Database mydb = client.open(addr, 0, "mydb");
CPPUNIT_ASSERT_MESSAGE(
"Database mydb should be valid",
static_cast<bool>(mydb));
CPPUNIT_ASSERT_THROW_MESSAGE(
"client.open should fail on non-existing database",
client.open(addr, 0, "blabla");
}
};
CPPUNIT_TEST_SUITE_REGISTRATION( ClientTest );
#include <cppunit/extensions/HelperMacros.h>
#include <sonata/Client.hpp>
#include <sonata/Admin.hpp>
#include "CollectionTestBase.hpp"
extern thallium::engine* engine;
class CollectionTest : public CppUnit::TestFixture,
public CollectionTestBase
{
CPPUNIT_TEST_SUITE( CollectionTest );
CPPUNIT_TEST( testStore );
CPPUNIT_TEST( testFetch );
CPPUNIT_TEST( testFilter );
CPPUNIT_TEST( testUpdate );
CPPUNIT_TEST( testAll );
CPPUNIT_TEST( testLastRecordID );
CPPUNIT_TEST( testSize );
CPPUNIT_TEST( testErase );
CPPUNIT_TEST_SUITE_END();
static constexpr const char* db_config = "{ \"path\" : \"mydb\" }";
public:
void setUp() {
sonata::Admin admin(*engine);
std::string addr = engine->self();
admin.createDatabase(addr, 0, "mydb", "unqlite", db_config);
sonata::Client client(*engine);
auto db = client.open(addr, 0, "mydb");
db.create("mycollection");
}
void tearDown() {
sonata::Admin admin(*engine);
std::string addr = engine->self();
admin.destroyDatabase(addr, 0, "mydb");
}
void testStore() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
uint64_t ref_record_id = 0;
// Strings can be stored
for(const auto& r : records_str) {
uint64_t record_id;
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.store should not throw.",
record_id = coll.store(r));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"record id should be correct.",
ref_record_id, record_id);
ref_record_id += 1;
}
// JSON objects can be stored
for(const auto& r : records_json) {
uint64_t record_id;
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.store should not throw.",
record_id = coll.store(r));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"record id should be correct.",
ref_record_id, record_id);
ref_record_id += 1;
}
}
void testFetch() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
for(const auto& r : records_str) {
coll.store(r);
}
// Fetch a record that should exist
Json::Value result;
uint64_t id = records_str.size()/2;
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.fetch should not throw.",
coll.fetch(id, &result));
// Fetched record should be as expected
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"Fetched record should contain correct data.",
records_json[id]["name"].asString(),
result["name"].asString());
// Fetch a record that does not exist
uint64_t bad_id = records_str.size();
CPPUNIT_ASSERT_THROW_MESSAGE(
"coll.fetch should throw.",
coll.fetch(bad_id, &result),
sonata::Exception);
}
void testFilter() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
std::string code = "function($rec) { return $rec.papers > 35; }";
// Try to filter from an empty collection
std::vector<std::string> results;
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"it should be possible to filter from an empty collection.",
coll.filter(code, &results));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"result should have no records.",
0, (int)results.size());
for(const auto& r : records_str) {
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.store should not throw.",
coll.store(r));
}
// Filter that returns half of the records
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"it should be possible to filter from an empty collection.",
coll.filter(code, &results));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"result should have 2 records.",
2, (int)results.size());
// Filter that returns no record
code = "function($rec) { return $rec.papers > 1000; }";
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"it should be possible to return no result.",
coll.filter(code, &results));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"result should have 0 records.",
0, (int)results.size());
}
void testUpdate() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
for(const auto& r : records_str) {
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.store should not throw.",
coll.store(r));
}
// Update record 0 with new content
std::string new_content = "{ \"name\" : \"Georges\", \"city\" : \"Lyon\", \"papers\" : 89 }";
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"update should not throw.",
coll.update(0, new_content));
// Check the new content
Json::Value val;
coll.fetch(0, &val);
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"fetch should give the updated value.",
val["name"].asString(), std::string("Georges"));
// Update a record that does not exist
CPPUNIT_ASSERT_THROW_MESSAGE(
"update should throw for a record that does not exist.",
coll.update(records_str.size(), new_content),
sonata::Exception);
}
void testAll() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
// Get all items from an empty collection, as JSON objects
Json::Value result_json;
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.all should not throw on empty collection.",
coll.all(&result_json));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"resulting vector should have 0 element.",
0, (int)result_json.size());
// Get all items from an empty collection, as strings
std::vector<std::string> result_str;
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.all should not throw on empty collection.",
coll.all(&result_str));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"resulting vector should have 0 element.",
0, (int)result_str.size());
// Store records into the collection
for(const auto& r : records_str) {
coll.store(r);
}
// Get all items as JSON objects
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.all should not throw on empty collection.",
coll.all(&result_json));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"resulting vector should have the correct number of elements.",
(int)records_json.size(), (int)result_json.size());
// Check content of the items
for(unsigned i=0; i < result_json.size(); i++) {
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"items should have the same name as stored.",
records_json[i]["name"].asString(),
result_json[i]["name"].asString());
}
// Get all items as strings
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.all should not throw on empty collection.",
coll.all(&result_str));
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"resulting vector should have the correct number of element.",
records_str.size(), result_str.size());
}
void testLastRecordID() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
for(const auto& r : records_str) {
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.store should not throw.",
coll.store(r));
}
// Check that the last record id of the collection is correct
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"coll.size should be correct.",
(int)records_str.size()-1, (int)coll.last_record_id());
}
void testSize() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
for(const auto& r : records_str) {
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.store should not throw.",
coll.store(r));
}
// Check that the size of the collection is correct
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"coll.size should be correct.",
(int)records_str.size(), (int)coll.size());
}
void testErase() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");
sonata::Collection coll = mydb.open("mycollection");
for(const auto& r : records_str) {
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"coll.store should not throw.",
coll.store(r));
}
// Erase the first record
CPPUNIT_ASSERT_NO_THROW_MESSAGE(
"erasing should work.",
coll.erase(0));
// Check that we can't access it anymore
std::string tmp;
CPPUNIT_ASSERT_THROW_MESSAGE(
"record 0 should be inaccessible.",
coll.fetch(0, &tmp),
sonata::Exception);
// Check that the size is appropriate
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"size of collection should be correct.",
(int)records_str.size()-1, (int)coll.size());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION( CollectionTest );
#ifndef __COLLECTION_TEST_BASE
#define __COLLECTION_TEST_BASE
#include <string>
#include <vector>
#include <fstream>
#include <streambuf>
#include <json/json.h>
class CollectionTestBase {
public:
CollectionTestBase() {
std::ifstream t("test.json");
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
Json::CharReaderBuilder builder;
Json::CharReader* reader = builder.newCharReader();
Json::Value json;
std::string errors;
bool parsingSuccessful = reader->parse(
str.c_str(),
str.c_str() + str.size(),
&json,
&errors
);
delete reader;
if (!parsingSuccessful) {
throw std::runtime_error("Failed to parse test JSON file");
}
for(unsigned i = 0; i < json.size(); i++) {
records_json.push_back(json[i]);
records_str.push_back(json[i].toStyledString());
}
}
std::vector<std::string> records_str;
std::vector<Json::Value> records_json;
};
#endif
......@@ -7,41 +7,122 @@ extern thallium::engine* engine;
class DatabaseTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE( DatabaseTest );
CPPUNIT_TEST( testAdminCreateDatabase );
CPPUNIT_TEST( testCreateCollection );
CPPUNIT_TEST( testOpenCollection );
CPPUNIT_TEST( testCollectionExists );
CPPUNIT_TEST( testDropCollection );
CPPUNIT_TEST_SUITE_END();
static constexpr const char* db_config = "{ \"path\" : \"mydb\" }";
public:
void setUp() {}
void tearDown() {}
void setUp() {
sonata::Admin admin(*engine);
std::string addr = engine->self();
admin.createDatabase(addr, 0, "mydb", "unqlite", db_config);
}
void testAdminCreateDatabase() {
void tearDown() {
sonata::Admin admin(*engine);
std::string addr = engine->self();
admin.destroyDatabase(addr, 0, "mydb");
}
// Create a valid Database
CPPUNIT_ASSERT_NO_THROW_MESSAGE("admin.createDatabase should return a valid Database",
admin.createDatabase(addr, 0, "db1", "unqlite", db_config));
void testCreateCollection() {
sonata::Client client(*engine);
std::string addr = engine->self();
sonata::Database mydb = client.open(addr, 0, "mydb");