//
// Copyright (c) 2010 Ronald Kluth
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

/**
 * @file TestOutputDatabaseAccessor.cpp
 * @author Ronald Kluth
 * @date created at 2010/04/28
 * @brief Unit tests for class DatabaseAccessor
 * @since 0.1
 */

#define CPPLOG_USE_DATABASE

#include "../../../setup.h"

#ifndef CPPLOG_USE_ODBC
#define CPPLOG_USE_SQLITE

#include "global.h"
#include <iostream>

CPPLOG_DECLARE_INFO_TYPE( TestDbInfo1, "test_db_info1", std::string )
CPPLOG_DECLARE_INFO_TYPE( TestDbInfo2, "test_db_info2", std::size_t )
CPPLOG_DECLARE_INFO_TYPE_LIST( TestDbInfos, TestDbInfo1, TestDbInfo2 );
CPPLOG_DECLARE_SQL_COLUMN_TYPES( TestDbInfos, testDbSqlTypes, "VARCHAR", "INTEGER" );

CPPLOG_DECLARE_INFO_TYPE( TestDbInfoKey, "id", std::size_t )
CPPLOG_DECLARE_INFO_TYPE_LIST( TestStoreDataInfos, TestDbInfoKey, TestDbInfo1, TestDbInfo2 );

SUITE( CppLogUnitTest )
{
	struct TestOutputDatabaseAccessorFixture
	{
		Log::DatabaseAccessor db;
		std::string tableName;
		TestOutputDatabaseAccessorFixture()
		:	db( "TestOutputDatabaseAccessor.db" )
		,	tableName( "TestOutputDatabaseAccessor" )
		{}

		void insertRecord( Log::Record const& rec )
		{
			db.insertIntoTable( tableName )
					.columnValues< TestStoreDataInfos >( rec );
		}
	};

	TEST_FIXTURE( TestOutputDatabaseAccessorFixture, CreateTable )
	{
		CHECK_THROW( db.createTable( "InvalidTableColumn" )
				.column( "column with spaces", "VARCHAR" ), std::runtime_error );

		db.createTable( tableName )
				.column( "id", "INTEGER NOT NULL" )
				.columns< TestDbInfos >( testDbSqlTypes )
				.primaryKey( "id" );

		CHECK( db.tableExists( tableName ) );
	}

	TEST_FIXTURE( TestOutputDatabaseAccessorFixture, InsertIntoTable )
	{
		bool tableExists = db.tableExists( tableName );
		CHECK( tableExists );

		if( tableExists )
		{
			Log::Record rec;
			rec + TestDbInfo1( "Info1" ) + TestDbInfo2( 42 );

			std::size_t key;
			db.getMaxColumnValue( tableName, "id", key );
			db.insertIntoTable( tableName )
					.columnValue( "id", ++key )
					.columnValues< TestDbInfos >( rec );

			CHECK_EQUAL( 1, db.getRowCount( tableName ) );

			std::size_t keyCheck = 0;
			db.getMaxColumnValue( tableName, "id", keyCheck );
			CHECK_EQUAL( key, keyCheck );

			std::string info1Check;
			db.getMaxColumnValue( tableName, TestDbInfo1NameHolder::getName().c_str(), info1Check );
			CHECK_EQUAL( "Info1", info1Check );

			std::size_t info2Check = 0;
			db.getMaxColumnValue( tableName, TestDbInfo2NameHolder::getName().c_str(), info2Check );
			CHECK_EQUAL( 42, info2Check );
		}
	}

	TEST_FIXTURE( TestOutputDatabaseAccessorFixture, CreateInserter )
	{
		Log::Record rec;
		rec + TestDbInfo1( "Info2" ) + TestDbInfo2( 43 );
		std::size_t key = 0;

		std::auto_ptr< Log::DatabaseAccessor::SqlInserter > inserter =
				db.createInserter();

		CHECK( inserter.get() != 0 );

		inserter->table( tableName ).column( "id" ).columns< TestDbInfos >();

		std::ostringstream os;
		os
		<< "INSERT INTO "
		<< tableName
		<< "(id,"
		<< TestDbInfo1::getName().c_str() << ","
		<< TestDbInfo2::getName().c_str()
		<< ") VALUES(?,?,?)";

		CHECK_EQUAL( os.str(), inserter->getSql() );

		db.getMaxColumnValue( tableName, "id", key );
		inserter->value( ++key ).values< TestDbInfos >( rec );

		CHECK_EQUAL( 3, inserter->getBindingCount() );

		inserter->execute();
		CHECK( inserter->isExecuted() );
	}

	TEST_FIXTURE( TestOutputDatabaseAccessorFixture, CurrentDbTime )
	{
		std::string currentTime = db.getCurrentDatabaseTime();
		CHECK( ! currentTime.empty() );
	}

	TEST_FIXTURE( TestOutputDatabaseAccessorFixture, StoreData )
	{
		std::size_t key;
		db.getMaxColumnValue( tableName, "id", key );

		std::vector< Log::Record > buffer;
		buffer.push_back( Log::Record() + TestDbInfoKey( ++key )
				+ TestDbInfo1( "test store data 1" ) + TestDbInfo2( 99 ) );
		buffer.push_back( Log::Record()	+ TestDbInfoKey( ++key )
				+ TestDbInfo1( "test store data 2" ) + TestDbInfo2( 100 ) );

		std::size_t rowCount = db.getRowCount( tableName );
		db.storeData( buffer,
				std::tr1::bind( &TestOutputDatabaseAccessorFixture::insertRecord,
						this, std::tr1::placeholders::_1 ) );

		CHECK_EQUAL( rowCount + 2, db.getRowCount( tableName ) );
	}

	TEST_FIXTURE( TestOutputDatabaseAccessorFixture, LastInsertId )
	{
		db.createTable( "test_auto_inc_last_insert" )
				.column( "id", "INTEGER NOT NULL" )
				.column( "text", "VARCHAR" )
				.primaryKey( "id" );

		db.insertIntoTable( "test_auto_inc_last_insert" )
				.columnValue( "text", "some test string" );

		std::size_t generatedKey1 = db.getLastInsertId();

		db.insertIntoTable( "test_auto_inc_last_insert" )
				.columnValue( "text", "some test string" );

		std::size_t generatedKey2 = db.getLastInsertId();

		CHECK( generatedKey1 < generatedKey2 );
	}
}

#endif /* ! defined CPPLOG_USE_ODBC */
