Commit b45da895 authored by Thomas Markwalder's avatar Thomas Markwalder

[4239] MySQL and Postgresql unit tests use production schema create scripts

src/lib/dhcpsrv/testutils/schema.h
src/lib/dhcpsrv/testutils/schema.cc
    New files that contain constants and functions common to RDBMS
    testing

src/lib/dhcpsrv/testutils/pgsql_schema.h
src/lib/dhcpsrv/testutils/pgsql_schema.cc
    New files that contain constants and functions needed for
    Posgresql testing

src/lib/dhcpsrv/testutils/mysql_schema.cc
src/lib/dhcpsrv/testutils/mysql_schema.h
    Removed common constants and functions
    Added MYSQL_VALID_TYPE
    runMySQLScript() - new function to submit a script to MySQL

src/lib/dhcpsrv/pgsql_lease_mgr.cc
    PgSqlLease6Exchange - replaced uint32_t iaid member with Uiaid union
    instance.  This permits the value to be safely stored in the database
    as an INT (signed 4-byte value)
    getColumnValue()- added int32_t variant of this method

src/lib/dhcpsrv/tests/Makefile.am
    Removed schema_pgsql_copy.h

src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
    Altered tests to use MYSQL_VALID_TYPE

src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc
    Moved constants and functions testutils/pgsl_schema.h & cc
parent 395104bb
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -461,6 +461,27 @@ public:
}
}
/// @brief Converts a column in a row in a result set to a int32_t.
///
/// @param r the result set containing the query results
/// @param row the row number within the result set
/// @param col the column number within the row
/// @param[out] value parameter to receive the converted value
///
/// @throw DbOperationError if the value cannot be fetched or is
/// invalid.
void getColumnValue(PGresult*& r, const int row, const size_t col,
int32_t &value) const {
const char* data = getRawColumnValue(r, row, col);
try {
value = boost::lexical_cast<int32_t>(data);
} catch (const std::exception& ex) {
isc_throw(DbOperationError, "Invalid int32_t data: " << data
<< " for: " << getColumnLabel(col) << " row:" << row
<< " : " << ex.what());
}
}
/// @brief Converts a column in a row in a result set to a uint8_t.
///
/// @param r the result set containing the query results
......@@ -807,7 +828,7 @@ private:
public:
PgSqlLease6Exchange()
: lease_(), duid_length_(0), duid_(), iaid_(0), iaid_str_(""),
: lease_(), duid_length_(0), duid_(), iaid_u_(0), iaid_str_(""),
lease_type_(Lease6::TYPE_NA), lease_type_str_(""), prefix_len_(0),
prefix_len_str_(""), pref_lifetime_(0), preferred_lft_str_("") {
......@@ -878,7 +899,11 @@ public:
lease_type_str_ = boost::lexical_cast<std::string>(lease_->type_);
bind_array.add(lease_type_str_);
iaid_str_ = boost::lexical_cast<std::string>(lease_->iaid_);
// The iaid is stored as an INT in lease6 table, so we must
// lexically cast from an integer version to avoid out of range
// exception failure upon insert.
iaid_u_.uval_ = lease_->iaid_;
iaid_str_ = boost::lexical_cast<std::string>(iaid_u_.ival_);
bind_array.add(iaid_str_);
prefix_len_str_ = boost::lexical_cast<std::string>
......@@ -930,7 +955,7 @@ public:
getColumnValue(r, row, LEASE_TYPE_COL, lease_type_);
getColumnValue(r, row , IAID_COL, iaid_);
getColumnValue(r, row , IAID_COL, iaid_u_.ival_);
getColumnValue(r, row , PREFIX_LEN_COL, prefix_len_);
......@@ -942,8 +967,9 @@ public:
/// @todo: implement this in #3557.
HWAddrPtr hwaddr;
Lease6Ptr result(new Lease6(lease_type_, addr, duid_ptr, iaid_,
pref_lifetime_, valid_lifetime_, 0, 0,
Lease6Ptr result(new Lease6(lease_type_, addr, duid_ptr,
iaid_u_.uval_, pref_lifetime_,
valid_lifetime_, 0, 0,
subnet_id_, fqdn_fwd_, fqdn_rev_,
hostname_, hwaddr, prefix_len_));
result->cltt_ = cltt_;
......@@ -987,7 +1013,14 @@ private:
size_t duid_length_;
vector<uint8_t> duid_;
uint8_t duid_buffer_[DUID::MAX_DUID_LEN];
uint32_t iaid_;
union Uiaid {
Uiaid(uint32_t val) : uval_(val){};
Uiaid(int32_t val) : ival_(val){};
uint32_t uval_;
int32_t ival_;
} iaid_u_;
std::string iaid_str_;
Lease6::Type lease_type_;
std::string lease_type_str_;
......
......@@ -115,7 +115,6 @@ if HAVE_PGSQL
libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc
endif
libdhcpsrv_unittests_SOURCES += pool_unittest.cc
libdhcpsrv_unittests_SOURCES += schema_pgsql_copy.h
libdhcpsrv_unittests_SOURCES += srv_config_unittest.cc
libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
......
......@@ -125,21 +125,21 @@ TEST(MySqlHostDataSource, OpenDatabase) {
// Check that invalid login data causes an exception.
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
DbOpenError);
// Check for missing parameters
EXPECT_THROW(HostDataSourceFactory::create(connectionString(
VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
NoDatabaseName);
// Tidy up after the test
......
......@@ -129,21 +129,21 @@ TEST(MySqlOpenTest, OpenDatabase) {
// Check that invalid login data causes an exception.
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
DbOpenError);
// Check for missing parameters
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
MYSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
NoDatabaseName);
// Tidy up after the test
......
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -11,9 +11,9 @@
#include <dhcpsrv/pgsql_lease_mgr.h>
#include <dhcpsrv/tests/test_utils.h>
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
#include <dhcpsrv/testutils/pgsql_schema.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
#include <algorithm>
......@@ -22,6 +22,8 @@
#include <string>
#include <utility>
#include <stdlib.h>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
......@@ -30,112 +32,6 @@ using namespace std;
namespace {
// This holds statements to create and destroy the schema.
#include "schema_pgsql_copy.h"
// Connection strings.
// Database: keatest
// Host: localhost
// Username: keatest
// Password: keatest
const char* VALID_TYPE = "type=postgresql";
const char* INVALID_TYPE = "type=unknown";
const char* VALID_NAME = "name=keatest";
const char* INVALID_NAME = "name=invalidname";
const char* VALID_HOST = "host=localhost";
const char* INVALID_HOST = "host=invalidhost";
const char* VALID_USER = "user=keatest";
const char* INVALID_USER = "user=invaliduser";
const char* VALID_PASSWORD = "password=keatest";
const char* INVALID_PASSWORD = "password=invalid";
// Given a combination of strings above, produce a connection string.
string connectionString(const char* type, const char* name, const char* host,
const char* user, const char* password) {
const string space = " ";
string result = "";
if (type != NULL) {
result += string(type);
}
if (name != NULL) {
if (! result.empty()) {
result += space;
}
result += string(name);
}
if (host != NULL) {
if (! result.empty()) {
result += space;
}
result += string(host);
}
if (user != NULL) {
if (! result.empty()) {
result += space;
}
result += string(user);
}
if (password != NULL) {
if (! result.empty()) {
result += space;
}
result += string(password);
}
return (result);
}
// Return valid connection string
string
validConnectionString() {
return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
VALID_USER, VALID_PASSWORD));
}
// @brief Clear everything from the database
//
// There is no error checking in this code: if something fails, one of the
// tests will (should) fall over.
void destroySchema() {
// Open database
PGconn* conn = 0;
conn = PQconnectdb("host = 'localhost' user = 'keatest'"
" password = 'keatest' dbname = 'keatest'");
// Get rid of everything in it.
for (int i = 0; destroy_statement[i] != NULL; ++i) {
PGresult* r = PQexec(conn, destroy_statement[i]);
PQclear(r);
}
PQfinish(conn);
}
// @brief Create the Schema
//
// Creates all the tables in what is assumed to be an empty database.
//
// There is no error checking in this code: if it fails, one of the tests
// will fall over.
void createSchema() {
// Open database
PGconn* conn = 0;
conn = PQconnectdb("host = 'localhost' user = 'keatest'"
" password = 'keatest' dbname = 'keatest'");
// Get rid of everything in it.
for (int i = 0; create_statement[i] != NULL; ++i) {
PGresult* r = PQexec(conn, create_statement[i]);
PQclear(r);
}
PQfinish(conn);
}
/// @brief Test fixture class for testing PostgreSQL Lease Manager
///
/// Opens the database prior to each test and closes it afterwards.
......@@ -149,12 +45,12 @@ public:
PgSqlLeaseMgrTest() {
// Ensure schema is the correct one.
destroySchema();
createSchema();
destroyPgSQLSchema();
createPgSQLSchema();
// Connect to the database
try {
LeaseMgrFactory::create(validConnectionString());
LeaseMgrFactory::create(validPgSQLConnectionString());
} catch (...) {
std::cerr << "*** ERROR: unable to open database. The test\n"
"*** environment is broken and must be fixed before\n"
......@@ -173,7 +69,7 @@ public:
virtual ~PgSqlLeaseMgrTest() {
lmptr_->rollback();
LeaseMgrFactory::destroy();
destroySchema();
destroyPgSQLSchema();
}
/// @brief Reopen the database
......@@ -185,7 +81,7 @@ public:
/// the same database.
void reopen(Universe) {
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(validConnectionString());
LeaseMgrFactory::create(validPgSQLConnectionString());
lmptr_ = &(LeaseMgrFactory::instance());
}
......@@ -201,13 +97,13 @@ public:
TEST(PgSqlOpenTest, OpenDatabase) {
// Schema needs to be created for the test to work.
destroySchema();
createSchema();
destroyPgSQLSchema();
createPgSQLSchema();
// Check that lease manager open the database opens correctly and tidy up.
// If it fails, print the error message.
try {
LeaseMgrFactory::create(validConnectionString());
LeaseMgrFactory::create(validPgSQLConnectionString());
EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
LeaseMgrFactory::destroy();
} catch (const isc::Exception& ex) {
......@@ -233,31 +129,31 @@ TEST(PgSqlOpenTest, OpenDatabase) {
// Check that invalid login data causes an exception.
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
PGSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
PGSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
DbOpenError);
// This test might fail if 'auth-method' in PostgresSQL host-based authentication
// file (/var/lib/pgsql/9.4/data/pg_hba.conf) is set to 'trust',
// which allows logging without password. 'Auth-method' should be changed to 'password'.
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
DbOpenError);
// Check for missing parameters
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
PGSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
NoDatabaseName);
// Tidy up after the test
destroySchema();
destroyPgSQLSchema();
}
/// @brief Check the getType() method
......@@ -490,9 +386,4 @@ TEST_F(PgSqlLeaseMgrTest, getExpiredLeases6) {
testGetExpiredLeases6();
}
/// @brief Check that expired reclaimed DHCPv6 leases are removed.
TEST_F(PgSqlLeaseMgrTest, deleteExpiredReclaimedLeases6) {
testDeleteExpiredReclaimedLeases6();
}
};
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef SCHEMA_COPY_H
#define SCHEMA_COPY_H
namespace {
// What follows is a set of statements that creates a copy of the schema
// in the test database. It is used by the PostgreSQL unit test prior to each
// test.
//
// Each SQL statement is a single string. The statements are not terminated
// by semicolons, and the strings must end with a comma. The final line
// statement must be NULL (not in quotes)
// NOTE: This file mirrors the schema in src/lib/dhcpsrv/dhcpdb_create.pgsql.
// If this file is altered, please ensure that any change is compatible
// with the schema in dhcpdb_create.pgsql.
// Deletion of existing tables.
const char* destroy_statement[] = {
"DROP TABLE lease4",
"DROP TABLE lease6",
"DROP TABLE lease6_types",
"DROP TABLE schema_version",
NULL
};
// Creation of the new tables.
const char* create_statement[] = {
"START TRANSACTION",
"CREATE TABLE lease4 ("
"address BIGINT PRIMARY KEY NOT NULL,"
"hwaddr BYTEA,"
"client_id BYTEA,"
"valid_lifetime BIGINT,"
"expire TIMESTAMP WITH TIME ZONE,"
"subnet_id BIGINT,"
"fqdn_fwd BOOLEAN,"
"fqdn_rev BOOLEAN,"
"hostname VARCHAR(255)"
")",
"CREATE TABLE lease6 ("
"address VARCHAR(39) PRIMARY KEY NOT NULL,"
"duid BYTEA,"
"valid_lifetime BIGINT,"
"expire TIMESTAMP WITH TIME ZONE,"
"subnet_id BIGINT,"
"pref_lifetime BIGINT,"
"lease_type SMALLINT,"
"iaid BIGINT,"
"prefix_len SMALLINT,"
"fqdn_fwd BOOLEAN,"
"fqdn_rev BOOLEAN,"
"hostname VARCHAR(255)"
")",
"CREATE TABLE lease6_types ("
"lease_type SMALLINT PRIMARY KEY NOT NULL,"
"name VARCHAR(5)"
")",
"INSERT INTO lease6_types VALUES (0, 'IA_NA')",
"INSERT INTO lease6_types VALUES (1, 'IA_TA')",
"INSERT INTO lease6_types VALUES (2, 'IA_PD')",
"CREATE TABLE schema_version ("
"version INT PRIMARY KEY NOT NULL,"
"minor INT"
")",
"INSERT INTO schema_version VALUES (1, 0)",
"COMMIT",
// This line concludes creation of database version 1.0.
// Schema upgrade to 2.0 starts here.
"ALTER TABLE lease4 "
"ADD COLUMN state INT8 DEFAULT 0",
"ALTER TABLE lease6 "
"ADD COLUMN state INT8 DEFAULT 0",
"CREATE INDEX lease4_by_state_expire ON lease4 (state ASC, expire ASC)",
"CREATE INDEX lease6_by_state_expire ON lease6 (state ASC, expire ASC)",
// Production schema includes the lease_state table which maps
// the lease states to their names. This is not used in the unit tests
// so it is commented out.
/*"CREATE TABLE lease_state (",
"state INT8 PRIMARY KEY NOT NULL,"
"name VARCHAR(64) NOT NULL);",
"ALTER TABLE lease4 "
"ADD CONSTRAINT fk_lease4_state FOREIGN KEY (state) "
"REFERENCES lease_state (state)",
"ALTER TABLE lease6 "
"ADD CONSTRAINT fk_lease6_state FOREIGN KEY (state) "
"REFERENCES lease_state (state)",
"ALTER TABLE lease6 "
"ADD CONSTRAINT fk_lease6_type FOREIGN KEY (lease_type) "
"REFERENCES lease6_types (lease_type)",
"INSERT INTO lease_state VALUES (0, \"default\");",
"INSERT INTO lease_state VALUES (1, \"declined\");",
"INSERT INTO lease_state VALUES (2, \"expired-reclaimed\");",*/
"UPDATE schema_version SET version = '2', minor = '0';",
"COMMIT",
// Schema upgrade to 2.0 ends here.
NULL
};
}; // Anonymous namespace
#endif // SCHEMA_COPY_H
......@@ -12,20 +12,40 @@ if HAVE_GTEST
noinst_LTLIBRARIES = libdhcpsrvtest.la
libdhcpsrvtest_la_SOURCES = config_result_check.cc config_result_check.h
libdhcpsrvtest_la_SOURCES = config_result_check.cc config_result_check.h
if HAVE_MYSQL
libdhcpsrvtest_la_SOURCES += schema.cc schema.h
else
if HAVE_PGSQL
libdhcpsrvtest_la_SOURCES += schema.cc schema.h
endif
endif
if HAVE_MYSQL
libdhcpsrvtest_la_SOURCES += mysql_schema.cc mysql_schema.h
endif
if HAVE_PGSQL
libdhcpsrvtest_la_SOURCES += pgsql_schema.cc pgsql_schema.h
endif
libdhcpsrvtest_la_CXXFLAGS = $(AM_CXXFLAGS)
libdhcpsrvtest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
if HAVE_MYSQL
libdhcpsrvtest_la_CPPFLAGS += $(MYSQL_CPPFLAGS)
endif
if HAVE_PGSQL
libdhcpsrvtest_la_CPPFLAGS += $(PGSQL_CPPFLAGS)
endif
libdhcpsrvtest_la_LDFLAGS = $(AM_LDFLAGS)
if HAVE_MYSQL
libdhcpsrvtest_la_LDFLAGS += $(MYSQL_LIBS)
endif
if HAVE_PGSQL
libdhcpsrvtest_la_LDFLAGS += $(PGSQL_LIBS)
endif
libdhcpsrvtest_la_LIBADD = $(top_builddir)/src/lib/cc/libkea-cc.la
libdhcpsrvtest_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
......
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -8,6 +8,7 @@
#include <string>
#include <mysql.h>
#include <dhcpsrv/mysql_connection.h>
#include <dhcpsrv/testutils/mysql_schema.h>
#include <gtest/gtest.h>
#include <fstream>
......@@ -20,75 +21,17 @@ namespace isc {
namespace dhcp {
namespace test {
// Connection strings.
// Database: keatest
// Host: localhost
// Username: keatest
// Password: keatest
const char* VALID_TYPE = "type=mysql";
const char* INVALID_TYPE = "type=unknown";
const char* VALID_NAME = "name=keatest";
const char* INVALID_NAME = "name=invalidname";
const char* VALID_HOST = "host=localhost";
const char* INVALID_HOST = "host=invalidhost";
const char* VALID_USER = "user=keatest";
const char* INVALID_USER = "user=invaliduser";
const char* VALID_PASSWORD = "password=keatest";
const char* INVALID_PASSWORD = "password=invalid";
string connectionString(const char* type, const char* name, const char* host,
const char* user, const char* password) {
const string space = " ";
string result = "";
if (type != NULL) {
result += string(type);
}
if (name != NULL) {
if (! result.empty()) {
result += space;
}
result += string(name);
}
if (host != NULL) {
if (! result.empty()) {
result += space;
}
result += string(host);