add database tests

This commit is contained in:
Bartosz Wieczorek 2018-02-01 08:02:23 +01:00
parent 34dadeaf54
commit a3f948ac04
14 changed files with 333 additions and 161 deletions

View File

@ -1,3 +1,7 @@
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
GRANT ALL ON SCHEMA public TO postgres;
GRANT ALL ON SCHEMA public TO public;
create EXTENSIon IF NOT EXISTS ltree;
@ -25,33 +29,6 @@ BEGIN
END; $$ -- $$
language 'plpgsql';
drop table if exists "user" cascade;
drop table if exists "auth_info" cascade;
drop table if exists "auth_token" cascade;
drop table if exists "user_audit_action" cascade;
drop table if exists "user_audit" cascade;
drop table if exists "stat" cascade;
drop table if exists "user_groups" cascade;
drop table if exists action cascade;
drop table if exists metric_systems cascade;
drop table if exists measurands cascade;
drop table if exists privilege cascade;
drop table if exists pointed_values cascade;
drop table if exists "auth_identity" cascade;
drop table if exists "group" cascade;
drop table if exists category_files;
drop table if exists units_conversions;
drop table if exists in_stock;
drop table if exists implemented_action;
drop table if exists inventory_history;
drop table if exists user_history;
drop table if exists item_files;
drop table if exists packages_files;
drop table if exists system_info;
drop table if exists user_inventory;
create table "system_info"(
"id" serial not null,
"name" text,

View File

@ -21,38 +21,14 @@ class Email {
using string = std::string;
public:
Email(string email) : _data(std::move(email)) {}
virtual ~Email() = default;
Email(string email) : _address(std::move(email)) {}
virtual bool isValid() const {
return true;
}
virtual bool isEmpty() const {
return _data.empty();
}
virtual operator string_view() const {
return _data;
string_view address() const {
return _address;
}
private:
string _data;
};
class UserPassword {
protected:
using string_view = std::string_view;
using string = std::string;
virtual void hash() = 0;
public:
virtual ~UserPassword() = default;
virtual string_view value() const = 0;
virtual string_view function() const = 0;
virtual string_view salt() const = 0;
string _address;
};
/// user data
@ -70,20 +46,59 @@ class UserName {
virtual operator string_view() const = 0;
};
class Password {
public:
Password(std::string function, std::string salt, std::string value)
: _function{std::move(function)}, _salt{std::move(salt)}, _value{std::move(value)} {}
/*! \brief Returns the function identifier.
*/
std::string function() const {
return _function;
}
/*! \brief Returns the salt.
*/
std::string salt() const {
return _salt;
}
/*! \brief Returns the hash value.
*/
std::string value() const {
return _value;
}
private:
std::string _function;
std::string _salt;
std::string _value;
};
class AuthInfo {
protected:
using string_view = std::string_view;
using string = std::string;
public:
virtual ~AuthInfo() = default;
AuthInfo(Password pass, Email email) : _password{std::move(pass)}, _email{std::move(email)} {}
virtual const Email & email() const = 0;
virtual void email(string email) = 0;
const Password & password() const {
return _password;
}
virtual bool emailVerified() const = 0;
virtual void verifyEmail() = 0;
virtual bool isUnique() const = 0;
const Email & email() const {
return _email;
};
bool emailVerified() const {}
bool isUnique() const {}
void verifyEmail() {}
private:
Password _password;
Email _email;
string _emailTomek;
};
class AuthIdentity {
@ -96,7 +111,27 @@ class AuthIdentity {
virtual string_view identity() const = 0;
virtual string_view provider() const = 0;
virtual AuthInfo * authInfo() const = 0;
private:
};
class ConstAuthIdentity final : public AuthIdentity {
protected:
using string_view = std::string_view;
using string = std::string;
public:
ConstAuthIdentity(string identity, string provider) : _identity{std::move(identity)}, _provider{std::move(provider)} {}
virtual string_view identity() const override {
return _identity;
}
virtual string_view provider() const override {
return _provider;
}
private:
string _identity;
string _provider;
};
class User {
@ -113,8 +148,8 @@ class User {
virtual UserConfig & config() = 0;
// user auth
virtual void changePassword(UserPassword & password) = 0;
virtual void login(UserPassword & password) = 0;
// virtual void changePassword(UserPassword & password) = 0;
// virtual void login(Pa & password) = 0;
virtual void logout() = 0;
};
} // namespace eedb

View File

@ -19,12 +19,11 @@ class Users {
public:
virtual ~Users() = default;
virtual shared_ptr< User > findWith(int) const = 0;
virtual shared_ptr< User > findWith(const AuthIdentity & name) const = 0;
virtual shared_ptr< User > findWith(const Email & email) const = 0;
virtual shared_ptr< User > findWith(const AuthToken & token) const = 0;
virtual unique_ptr< User > findWith(const AuthIdentity & name) const = 0;
virtual unique_ptr< User > findWith(const Email & email) const = 0;
virtual unique_ptr< User > findWith(const AuthToken & token) const = 0;
// adds new user into users
virtual shared_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) = 0;
virtual unique_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) = 0;
};
} // namespace eedb

View File

@ -4,6 +4,16 @@
#include <Wt/WServer.h>
namespace eedb::db {
PgConfig::PgConfig() {
host = "localhost";
user = "postgres";
password = "postgres";
dbname = "postgres";
port = 5432;
debug = true;
}
PgConfig::PgConfig(const Wt::WEnvironment & env) {
auto readString = [&env](auto && name, auto & val, auto def) {
if(!env.server()->readConfigurationProperty(name, val)) {

View File

@ -10,6 +10,7 @@ namespace eedb::db {
class PgConfig : public sqlpp::postgresql::connection_config {
public:
PgConfig();
explicit PgConfig(const Wt::WEnvironment & env);
};
} // namespace eedb::db

View File

@ -32,29 +32,47 @@ auto auth_token_info = user_auth_info.join(authToken).on(authToken.auth_info_
namespace eedb {
PgUserIdentity::PgUserIdentity(db::PgConnection & con, int uid, string provider) : _db(con), _provider(std::move(provider)), _email("") {
select(all_of(t_identity)) //
.from(auth_info_identity) //
.where(t_info.user_uid == uid);
struct PgUser::PgUserPriv{
int id;
};
PgUser::PgUser(int uid) : _priv{spimpl::make_unique_impl< PgUserPriv >()} {
_priv->id = uid;
}
AuthIdentity::string_view PgUserIdentity::identity() const {
const auto id_eq = t_info.user_uid == 0;
const auto provider_eq = t_identity.provider == _provider;
auto res = _db(select(t_identity.identity) //
.from(auth_info_identity) //
.where(id_eq and provider_eq));
if(res.empty())
return {};
// _name = res.front().identity;
return _name;
int PgUser::uid() const
{
return _priv->id;
}
AuthIdentity::string_view PgUserIdentity::provider() const {}
const AuthInfo & PgUser::authInfo() const
{
}
//PgUserIdentity::PgUserIdentity(db::PgConnection & con, int uid, string provider) : _db(con), _provider(std::move(provider)), _email("") {
// select(all_of(t_identity)) //
// .from(auth_info_identity) //
// .where(t_info.user_uid == uid);
//}
//AuthIdentity::string_view PgUserIdentity::identity() const {
// const auto id_eq = t_info.user_uid == 0;
// const auto provider_eq = t_identity.provider == _provider;
// auto res = _db(select(t_identity.identity) //
// .from(auth_info_identity) //
// .where(id_eq and provider_eq));
// if(res.empty())
// return {};
// // _name = res.front().identity;
// return _name;
//}
//AuthIdentity::string_view PgUserIdentity::provider() const {}
const AuthIdentity & PgUser::identity() const {}
@ -62,9 +80,10 @@ const UserConfig & PgUser::config() const {}
UserConfig & PgUser::config() {}
void PgUser::changePassword(UserPassword & password) {}
//void PgUser::changePassword(UserPassword & password) {}
void PgUser::login(UserPassword & password) {}
//void PgUser::login(UserPassword & password) {}
void PgUser::logout() {}
} // namespace eedb

View File

@ -2,37 +2,29 @@
#include <eedb/data/User.hpp>
#include <utils/spimpl.hpp>
namespace eedb::db {
class PgConnection;
}
namespace eedb {
class PgUserIdentity : public AuthIdentity {
// UserIdentity interface
public:
PgUserIdentity(db::PgConnection & con, int uid, string provider);
string_view identity() const override;
string_view provider() const override;
private:
eedb::db::PgConnection & _db;
string _name;
string _provider;
Email _email;
};
class PgUser : public User {
// User interface
public:
PgUser(int uid);
int uid() const override;
const AuthInfo &authInfo() const override;
const AuthIdentity & identity() const override;
const UserConfig & config() const override;
UserConfig & config() override;
void changePassword(UserPassword & password) override;
void login(UserPassword & password) override;
void logout() override;
private:
struct PgUserPriv;
spimpl::unique_impl_ptr< PgUserPriv > _priv;
};
} // namespace eedb

View File

@ -1,23 +1,69 @@
#include <eedb/db/data/PgUser.hpp>
#include <eedb/db/data/PgUsers.hpp>
#include <eedb/db/data/PgUser.hpp>
#include <eedb/db/connection.hpp>
#include <eedb/db/model/auth_identity.h>
#include <eedb/db/model/auth_info.h>
#include <eedb/db/model/auth_token.h>
#include <eedb/db/model/user.h>
constexpr eedb::user t_user;
constexpr eedb::auth_identity t_auth_identity;
constexpr eedb::auth_info t_auth_info;
constexpr eedb::auth_token t_auth_token;
namespace eedb {
template < typename T >
using shared_ptr = PgUsers::shared_ptr< T >;
using unique_ptr = PgUsers::unique_ptr< T >;
shared_ptr< User > PgUsers::findWith(int) const {}
struct PgUsers::PgUsersPriv {
PgUsersPriv(eedb::db::PgConnection & db) : _db{db} {}
shared_ptr< User > PgUsers::findWith(const AuthIdentity & identity) const {}
eedb::db::PgConnection & _db;
};
shared_ptr< User > PgUsers::findWith(const Email & email) const {}
PgUsers::PgUsers(eedb::db::PgConnection & db) : _priv{spimpl::make_unique_impl< PgUsersPriv >(db)} {}
shared_ptr< User > PgUsers::findWith(const Users::AuthToken & token) const {}
unique_ptr< User > PgUsers::findWith(const AuthIdentity & identity) const {
return {};
}
shared_ptr< User > PgUsers::addUser(unique_ptr< AuthIdentity >) {}
unique_ptr< User > PgUsers::findWith(const Email & email) const {
return {};
}
unique_ptr< User > PgUsers::findWith(const Users::AuthToken & token) const {
return {};
}
unique_ptr< User > PgUsers::addUser(unique_ptr< AuthInfo > authInfo, unique_ptr< AuthIdentity > authIdentity) {
assert(authInfo);
assert(authIdentity);
auto & db = *(_priv->_db.native());
auto user_id = db(sqlpp::postgresql::insert_into(t_user) //
.set( //
t_user.full_name = "none", //
t_user.status = 0)
.returning(t_user.uid))
.front()
.uid;
const auto & pass = authInfo->password();
const auto & email = authInfo->email();
db(insert_into(t_auth_info)
.set( //
t_auth_info.password_hash = pass.value(), //
t_auth_info.password_method = pass.function(), //
t_auth_info.password_salt = pass.salt(), //
t_auth_info.user_uid = user_id, //
t_auth_info.email = std::string{email.address()}));
return std::make_unique< eedb::PgUser >(user_id);
}
} // namespace eedb

View File

@ -2,15 +2,26 @@
#include <eedb/data/Users.hpp>
#include <utils/spimpl.hpp>
namespace eedb::db {
class PgConnection;
}
namespace eedb {
class PgUsers : public Users {
// Users interface
public:
shared_ptr< User > findWith(int) const override;
shared_ptr< User > findWith(const AuthIdentity & name) const override;
shared_ptr< User > findWith(const Email & email) const override;
shared_ptr< User > findWith(const AuthToken & token) const override;
PgUsers(eedb::db::PgConnection & db);
shared_ptr< User > addUser(unique_ptr< AuthIdentity >) override;
unique_ptr< User > findWith(const AuthIdentity & name) const override;
unique_ptr< User > findWith(const Email & email) const override;
unique_ptr< User > findWith(const AuthToken & token) const override;
unique_ptr< User > addUser(unique_ptr< AuthInfo >, unique_ptr< AuthIdentity >) override;
private:
struct PgUsersPriv;
spimpl::unique_impl_ptr< PgUsersPriv > _priv;
};
} // namespace eedb

View File

@ -113,21 +113,6 @@ namespace eedb {
using _traits = ::sqlpp::make_traits<::sqlpp::text>;
};
struct Auth_info_id {
struct _alias_t {
static constexpr const char _literal[] = R"("auth_info_id")";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T>
struct _member_t {
T auth_info_id;
T &operator()() { return auth_info_id; }
const T &operator()() const { return auth_info_id; }
};
};
using _traits = ::sqlpp::make_traits<::sqlpp::bigint, sqlpp::tag::can_be_null>;
};
}
struct user : sqlpp::table_t<user,
@ -137,8 +122,7 @@ namespace eedb {
user_::Full_name,
user_::Created,
user_::Updated,
user_::Config,
user_::Auth_info_id> {
user_::Config> {
using _value_type = sqlpp::no_value_t;
struct _alias_t {
static constexpr const char _literal[] = R"("user")";

View File

@ -0,0 +1,19 @@
#include <eedb/db/data/PgCategory.hpp>
#include <utils/DbTestBase.hpp>
class PgCategoryTest : public DbTestBase< PgCategoryTest > {
public:
PgCategoryTest() {
db().native()->start_transaction();
// db().native()->execute(R"sql( INSERT INTO "category" ("owner", "name", "parent_id", "parent_path") VALUES ('root', NULL, 'root'); )sql");
}
~PgCategoryTest() {
db().native()->rollback_transaction(false);
}
};
template<> std::unique_ptr< PgTestDatabasePrepare > DbTestBase<PgCategoryTest>::_test_db = {};
TEST_F(PgCategoryTest, rootCategoryHasNoParent) {}

View File

@ -0,0 +1,96 @@
#include <eedb/db/data/PgUsers.hpp>
#include <eedb/db/model/auth_identity.h>
#include <eedb/db/model/auth_info.h>
#include <eedb/db/model/auth_token.h>
#include <eedb/db/model/user.h>
#include <eedb/db/model/user_action.h>
#include <eedb/db/model/user_audit.h>
#include <sqlpp11/sqlpp11.h>
#include <utils/DbTestBase.hpp>
constexpr eedb::user t_user;
constexpr eedb::user_audit t_user_audit;
constexpr eedb::user_action t_user_action;
constexpr eedb::auth_identity t_auth_identity;
constexpr eedb::auth_info t_auth_info;
constexpr eedb::auth_token t_auth_token;
class PgUsersTest : public DbTestBase< PgUsersTest > {
public:
PgUsersTest() {
sut = std::make_unique< eedb::PgUsers >(db());
db().native()->start_transaction();
}
~PgUsersTest() {
db().native()->rollback_transaction(false);
}
auto createUser(std::string name, std::string email) {
auto info_ = std::make_unique< eedb::AuthInfo >(
eedb::Password{"bcrypt", "OM/Z1c4WBFXvwkxh", "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q"}, eedb::Email{email});
auto identity_ = std::make_unique< eedb::ConstAuthIdentity >(name, "loginname");
return sut->addUser(std::move(info_), std::move(identity_));
}
auto createTestUsers(){
createUser("test_user_1", "test_user_1@eedb.pl");
createUser("test_user_2", "test_user_2@eedb.pl");
createUser("test_user_3", "test_user_3@eedb.pl");
createUser("test_user_4", "test_user_4@eedb.pl");
createUser("test_user_5", "test_user_5@eedb.pl");
createUser("test_user_6", "test_user_6@eedb.pl");
}
protected:
std::unique_ptr< eedb::PgUsers > sut;
};
template <>
std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgUsersTest >::_test_db = {};
using namespace sqlpp;
TEST_F(PgUsersTest, createValidUser) {
auto info_ = std::make_unique< eedb::AuthInfo >(
eedb::Password{"bcrypt", "OM/Z1c4WBFXvwkxh", "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q"},
eedb::Email{"none@eedb.pl"});
auto identity_ = std::make_unique< eedb::ConstAuthIdentity >("username", "loginname");
auto new_user = sut->addUser(std::move(info_), std::move(identity_));
ASSERT_TRUE(new_user);
auto info = db()(select(sqlpp::all_of(t_auth_info)).from(t_auth_info).where(t_auth_info.user_uid == new_user->uid()));
EXPECT_FALSE(info.empty());
EXPECT_EQ(info.front().password_hash, "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q");
EXPECT_EQ(info.front().password_method, "bcrypt");
EXPECT_EQ(info.front().password_salt, "OM/Z1c4WBFXvwkxh");
EXPECT_EQ(info.front().email, "none@eedb.pl");
EXPECT_EQ(info.front().email_verified, false);
EXPECT_EQ(info.front().user_uid.value(), new_user->uid());
auto user = db()(select(sqlpp::all_of(t_user)).from(t_user).where(t_user.uid == new_user->uid()));
/// TODO check username structure
// auto identity = db()(select(sqlpp::all_of(t_auth_identity)).from(t_auth_identity).where(t_auth_identity.auth_info_id ==
// info.front().id)); EXPECT_EQ(identity.front().provider, "loginname"); EXPECT_EQ(identity.front().identity, "username");
}
TEST_F(PgUsersTest, createSameUserMustFail) {
createUser("_test_user_", "test_email@eedb.pl");
EXPECT_THROW(createUser("_test_user_", "test_email@eedb.pl"), sqlpp::postgresql::integrity_constraint_violation);
}
TEST_F(PgUsersTest, createWithSameEmailMustFail) {
createUser("_test_user_1", "test_email@eedb.pl");
EXPECT_THROW(createUser("_test_user_2", "test_email@eedb.pl"), sqlpp::postgresql::integrity_constraint_violation);
}
TEST_F(PgUsersTest, findByName) {
createTestUsers();
EXPECT_TRUE(sut->findWith(eedb::ConstAuthIdentity{"test_user_1", "loginname"}));
}

View File

@ -1,15 +1,10 @@
#include <gtest/gtest.h>
#include <eedb/db/data/PgCategory.hpp>
#include <eedb/db/RawSql.hpp>
#include <eedb/db/config.hpp>
#include <eedb/db/connection.hpp>
#include <sqlpp11/postgresql/exception.h>
#include <Wt/Test/WTestEnvironment.h>
#include <Wt/WServer.h>
#include <boost/process.hpp>
#include <thread>
@ -44,11 +39,9 @@ class DockerRunner {
class PgTestDatabasePrepare {
public:
PgTestDatabasePrepare() {
Wt::Test::WTestEnvironment env;
_docker = std::make_unique< DockerRunner >("eedb_test_database");
auto dbConfig = std::make_shared< eedb::db::PgConfig >(env);
auto dbConfig = std::make_shared< eedb::db::PgConfig >();
dbConfig->host = "localhost";
dbConfig->port = 5432;
dbConfig->password = "postgres";
@ -82,33 +75,23 @@ class PgTestDatabasePrepare {
std::unique_ptr< eedb::db::PgConnection > _db;
};
class PgCategoryTest : public testing::Test {
template<typename T>
class DbTestBase : public testing::Test {
public:
static void SetUpTestCase() {
_test_db = std::make_unique< PgTestDatabasePrepare >();
eedb::db::exec_file(_test_db->db(), "/home/bwieczor/src/eedb/sql/schema.sql");
eedb::db::exec_file(_test_db->db(), "");
}
PgCategoryTest() {
_test_db->db().native()->start_transaction();
_test_db->db().native()->execute(R"sql( INSERT INTO "category" ("owner", "name", "parent_id", "parent_path") VALUES ('root', NULL, 'root'); )sql");
}
~PgCategoryTest() {
_test_db->db().native()->rollback_transaction(false);
}
static void TearDownTestCase() {
_test_db.reset();
}
eedb::db::PgConnection & db(){
return _test_db->db();
}
protected:
static std::unique_ptr< PgTestDatabasePrepare > _test_db;
std::unique_ptr< eedb::PgCategory > sut;
};
std::unique_ptr< PgTestDatabasePrepare > PgCategoryTest::_test_db = {};
TEST_F(PgCategoryTest, rootCategoryHasNoParent) {}