/* 
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "tut_stdafx.h"

#ifndef _WIN32
#include <sstream>
#include <fstream>
#endif

#ifndef NL
#if !defined(__WIN__) && !defined(_WIN32) && !defined(_WIN64)
#define NL "\n"
#define NLNL "\n\n"
#else
#define NL "\r\n"
#define NLNL "\r\n\r\n"
#endif
#endif

#include "testgrt.h"
#include "grt_test_utility.h"
#include "grts/structs.db.mgmt.h"
#include "grts/structs.db.mysql.h"
#include "grts/structs.workbench.h"
#include "grts/structs.workbench.physical.h"
#include "grt/grt_manager.h"
#include "wb_module_validation_mysql.h"
#include "grtdb/db_object_helpers.h"
#include "wb_helpers.h"

using namespace std;

BEGIN_TEST_DATA_CLASS(test_WbModuleValidationMySQL)
public:
  GRTManagerTest grtm;
  WbModuleValidationMySQLImpl *module;
  db_mgmt_RdbmsRef rdbms;
  GRT *grt;

TEST_DATA_CONSTRUCTOR(test_WbModuleValidationMySQL)
  : grtm(false)
{
  grt= grtm.get_grt();
  grt->scan_metaclasses_in("../../res/grt/");
  grt->end_loading_metaclasses();
  rdbms = db_mgmt_RdbmsRef(grtm.get_grt());
}

END_TEST_DATA_CLASS

TEST_MODULE(test_WbModuleValidationMySQL, "WB Module MySQL-specific Validation");

TEST_FUNCTION(1)
{
  module= grt->get_native_module<WbModuleValidationMySQLImpl>();

  ensure("physicalView must be defined",false);

  ensure("WbValidationMySQL module initialization", NULL != module);

  db_mgmt_ManagementRef mgmt(grtm.get_grt()); 

  ListRef<db_DatatypeGroup> grouplist= 
    ListRef<db_DatatypeGroup>::cast_from(grtm.get_grt()->unserialize("../../bin/debug/data/db_datatype_groups.xml"));

  // unfortunately we have to do these lines to make sure that references (links) will be resolved during import
  ValueRef root= grt->root();
  assure(root.type() == DictType);
  DictRef::cast_from(root).set("grouplist", grouplist);

  rdbms= db_mgmt_RdbmsRef::cast_from(grt->unserialize("../../modules/db.mysql/res/mysql_rdbms_info.xml"));
  ensure("db_mgmt_RdbmsRef initialization", rdbms.is_valid());

  db_DatatypeGroupRef group= rdbms->simpleDatatypes().get(0)->group();
}
/*
struct TestMySQLModel
{
  workbench_physical_ModelRef model;
  workbench_physical_ViewRef physicalView;

  db_mysql_CatalogRef catalog;
  db_mysql_SchemaRef schema;
  db_mysql_TableRef table;
  db_mysql_TableRef table2;
  db_mysql_TableRef table3;
  db_mysql_TableRef table4;
  db_mysql_ColumnRef column;
  db_mysql_ColumnRef column2;
  db_mysql_ColumnRef columnText;
  db_mysql_ColumnRef columnDate;
  db_mysql_ColumnRef columnDouble;
  db_mysql_IndexRef primaryKey;
  db_mysql_IndexColumnRef indexColumn;
  db_mysql_ForeignKeyRef foreignKey;
  db_mysql_ForeignKeyRef foreignKey2;
  db_mysql_TriggerRef trigger;

  db_mysql_ViewRef view;
  db_mysql_RoutineRef routine;
  db_mysql_RoutineGroupRef routineGroup;
  db_UserRef user;
  db_RoleRef role;

  db_RolePrivilegeRef tablePrivilege;
  db_RolePrivilegeRef viewPrivilege;
  db_RolePrivilegeRef routinePrivilege;

  workbench_physical_TableFigureRef tableFigure;
  workbench_physical_ViewFigureRef viewFigure;
  workbench_physical_RoutineGroupFigureRef routineGroupFigure;

  TestMySQLModel(db_mgmt_RdbmsRef &rdbms)
    : model(rdbms.get_grt()),
    catalog(rdbms.get_grt()),
    physicalView(rdbms.get_grt()),
    schema(rdbms.get_grt()),
    table(rdbms.get_grt()),
    table2(rdbms.get_grt()),
    table3(rdbms.get_grt()),
    table4(rdbms.get_grt()),
    column(rdbms.get_grt()),
    column2(rdbms.get_grt()),
    columnText(rdbms.get_grt()),
    columnDate(rdbms.get_grt()),
    columnDouble(rdbms.get_grt()),
    primaryKey(rdbms.get_grt()),
    indexColumn(rdbms.get_grt()),
    foreignKey(rdbms.get_grt()),
    foreignKey2(rdbms.get_grt()),
    trigger(rdbms.get_grt()),
    view(rdbms.get_grt()),
    routine(rdbms.get_grt()),
    routineGroup(rdbms.get_grt()),
    user(rdbms.get_grt()),
    role(rdbms.get_grt()),
    tablePrivilege(rdbms.get_grt()),
    viewPrivilege(rdbms.get_grt()),
    routinePrivilege(rdbms.get_grt()),
    tableFigure(rdbms.get_grt()),
    viewFigure(rdbms.get_grt()),
    routineGroupFigure(rdbms.get_grt())
  {
    model->catalog(catalog);             catalog->owner(model);
    model->views().insert(physicalView); physicalView->owner(model);

    catalog->name("ValidateEmptyContentCatalog");
    catalog->schemata().insert(schema); schema->owner(catalog);
    catalog->users().insert(user);      user->owner(catalog);
    catalog->roles().insert(role);      role->owner(catalog);

    schema->name("ValidateEmptyContentDb");
    schema->tables().insert(table);     table->owner(schema);
    schema->tables().insert(table2);     table2->owner(schema);
    schema->tables().insert(table3);     table3->owner(schema);
    schema->tables().insert(table4);     table4->owner(schema);
    schema->views().insert(view);       view->owner(schema);
    schema->routines().insert(routine); routine->owner(schema);
    schema->routineGroups().insert(routineGroup);  routineGroup->owner(schema);

    table->name("t1");
    table->columns().insert(column);    column->owner(table);
    table->columns().insert(column2);    column2->owner(table);
    table->columns().insert(columnText);    columnText->owner(table);
    table->columns().insert(columnDate);    columnDate->owner(table);
    table->columns().insert(columnDouble);  columnDouble->owner(table);
    table->indices().insert(primaryKey); primaryKey->owner(table);
    table->foreignKeys().insert(foreignKey);  foreignKey->owner(table);
    table->primaryKey(primaryKey);
    table->triggers().insert(trigger);   trigger->owner(table);

    table->tableEngine("InnoDB");

    table2->name("t2");

    table3->name("table1");
    table4->name("table4");

    view->name("v1");
    view->sqlDefinition("create view v1 as select id from t1");

    routine->name("r1");
    routine->sqlDefinition("CREATE FUNCTION get_count(less_than INT, greather_than INT) RETURNS INT"NL
"    DETERMINISTIC"NL
"    READS SQL DATA"NL
"BEGIN"NL
"       #OK, here some comment"NL
"  DECLARE res INTEGER; #FEES PAID TO RENT THE VIDEOS INITIALLY"NL
"  SELECT count(*) INTO res"NL
"    FROM t1"NL
"    WHERE id > less_tnan AND id < greather_than;"NL
"  RETURN res;"NL
"END");

    routineGroup->name("group1");
    routineGroup->routines().insert(routine);

    column->name("id");
    column->simpleType(rdbms->simpleDatatypes().get(0));
    bec::ColumnHelper::set_default_value(column, "");

    column2->name("parent_id");
    column2->simpleType(rdbms->simpleDatatypes().get(0));
    bec::ColumnHelper::set_default_value(column2, "");

    columnText->name("desc");
    columnText->simpleType(rdbms->simpleDatatypes().get(8));
    bec::ColumnHelper::set_default_value(columnText, "''");

    columnDate->name("datetimefield");
    columnDate->simpleType(rdbms->simpleDatatypes().get(20));
    bec::ColumnHelper::set_default_value(columnDate, "'03-03-2008'");

    columnDouble->name("doublefield");
    columnDouble->simpleType(rdbms->simpleDatatypes().get(6));
    bec::ColumnHelper::set_default_value(columnDouble, "");

    primaryKey->name("PK");
    primaryKey->columns().insert(indexColumn);
    primaryKey->isPrimary(1);

    indexColumn->owner(primaryKey);
    indexColumn->referencedColumn(column);

    foreignKey->name("FK");
    foreignKey->columns().insert(column2);
    foreignKey->referencedColumns().insert(column);
    foreignKey->referencedTable(table);

    trigger->name("trig");
    trigger->sqlDefinition("CREATE "
"TRIGGER trig BEFORE INSERT "
"ON t1 FOR EACH ROW SET id = new.id");

    user->name("User1");
    user->roles().insert(role);

    role->name("Role1");
    role->privileges().insert(tablePrivilege);  tablePrivilege->owner(role);
    role->privileges().insert(viewPrivilege);   viewPrivilege->owner(role);
    role->privileges().insert(routinePrivilege);routinePrivilege->owner(role);

    tablePrivilege->databaseObject(table);
    tablePrivilege->privileges().insert("ALL");
    viewPrivilege->databaseObject(view);
    viewPrivilege->privileges().insert("ALL");
    routinePrivilege->databaseObject(routine);
    routinePrivilege->privileges().insert("ALL");

    tableFigure->table(table);
    tableFigure->owner(physicalView);
    physicalView->figures().insert(tableFigure);

    viewFigure->view(view);
    viewFigure->owner(physicalView);
    physicalView->figures().insert(viewFigure);
    
    routineGroupFigure->routineGroup(routineGroup);
    routineGroupFigure->owner(physicalView);
    physicalView->figures().insert(routineGroupFigure);
  }
};

TEST_FUNCTION(2)
{
  TestMySQLModel model(rdbms);
  ensure("Integrity check", module->validateIntegrity(model.model) == 0);
}

TEST_FUNCTION(3)
{
  TestMySQLModel model(rdbms);
  ensure("Syntax check", module->validateSyntax(model.model) == 0);
}


TEST_FUNCTION(9)
{// test validateAll
  TestMySQLModel model(rdbms);
  ensure("Validate All", module->validateAll(model.model) == 0);
}



TEST_FUNCTION(10)
{// test validateTableNameLength
  TestMySQLModel model(rdbms);
  ensure("validateTableNameLength", 0 == module->validateIntegrity(model.model));
  model.table->name(std::string(65, 'a'));
  ensure("validateTableNameLength2", 1 == module->validateIntegrity(model.model));
  model.table->name(std::string(12, 'a'));
  model.table->comment(std::string(10000, 'a'));
  ensure("validateTableNameLength3", 1 == module->validateIntegrity(model.model));
  model.table->name(grt::StringRef());
  ensure("validateTableNameLength2", 2 == module->validateIntegrity(model.model));
}


TEST_FUNCTION(11)
{// test validateViewNameLength
  TestMySQLModel model(rdbms);
  ensure("validateViewNameLength ", module->validateAll(model.model) == 0);
  model.view->name(std::string(65, 'a'));
  ensure("validateViewNameLength ", module->validateAll(model.model) == 1);
}

TEST_FUNCTION(12)
{// test validateRoutineNameLength
  TestMySQLModel model(rdbms);
  ensure("validateRoutineNameLength", 0 == module->validateAll(model.model));
  model.routine->name(std::string(100, 'a'));
  ensure("Check for too long routine name", 1 == module->validateAll(model.model));
}

TEST_FUNCTION(13)
{// test validateUserNameLength
  TestMySQLModel model(rdbms);
  ensure("validateUserNameLength", 0 == module->validateIntegrity(model.model));
  model.user->name(std::string(65, 'a'));
  ensure("Check for too long User name", 1 == module->validateIntegrity(model.model));
}


TEST_FUNCTION(14)
{// test validateForeignKeyWithEngineType
  TestMySQLModel model(rdbms);
  ensure("validateForeignKeyWithEngineType", 0 == module->validateIntegrity(model.model));
  model.table->tableEngine("myisaM");
  ensure("validateForeignKeyWithEngineType2", 1 == module->validateIntegrity(model.model));
}


TEST_FUNCTION(15)
{// test validateTriggerSyntax
  TestMySQLModel model(rdbms);
  ensure("validateTriggerSyntax", 0 == module->validateSyntax(model.model));
// is commented because it is no easy way to determine that function failed now
  //return;
  model.trigger->sqlDefinition("CREATE "
"TRIGGER trig BEFORE INSERT XXX "
"ON t1 FOR EACH ROW SET id = new.id;");
  ensure("validateTriggerSyntax", 1 == module->validateSyntax(model.model));

  model.trigger->sqlDefinition("UCREATE "
"TRIGGER trig BEFORE INSERT "
"ON t1 FOR EACH ROW SET id = new.id;");
  ensure("validateTriggerSyntax", 1 == module->validateSyntax(model.model));

  model.trigger->sqlDefinition("CREATE "
"TRIGGER trig AFTER UPDATE "
"ON t1 FOR EACH ROW BEGIN IF (old.id=2) THEN UPDATE t2 set id=2 where 1=2; END IF");
  ensure("validateTriggerSyntax", 1 == module->validateSyntax(model.model));
}


TEST_FUNCTION(16)
{// test validateViewSyntax
  TestMySQLModel model(rdbms);
  ensure("validateViewSyntax", 0 == module->validateSyntax(model.model));
// is commented because it is no easy way to determine that function failed now
  //return;
  model.view->sqlDefinition("create view v1 as select id from t1 group by;");
  ensure("validateViewSyntax", 1 == module->validateSyntax(model.model));
  model.view->sqlDefinition("some dummy strings");
  ensure("validateViewSyntax", 1 == module->validateSyntax(model.model));
  model.view->sqlDefinition("");
  ensure("validateViewSyntax", 1 == module->validateSyntax(model.model));
}


TEST_FUNCTION(17)
{// test validateRoutineSyntax
  TestMySQLModel model(rdbms);
  ensure("validateRoutineSyntax", 0 == module->validateSyntax(model.model));
// is commented because it is no easy way to determine that function failed now
  //return;
  model.routine->sqlDefinition("pupper code");
  ensure("validateRoutineSyntax", 1 == module->validateSyntax(model.model));
}


TEST_FUNCTION(18)
{// test validateReservedKeywords
  TestMySQLModel model(rdbms);
  ensure("validateReservedKeywords", 0 == module->validateSyntax(model.model));
  model.routine->name("UPDATE");
  ensure("validateReservedKeywords", 1 == module->validateSyntax(model.model));
}


TEST_FUNCTION(19)
{// test validateCharacters
  TestMySQLModel model(rdbms);
  ensure("validateCharacters", 0 == module->validateSyntax(model.model));
  model.table->name("Tabëe");
  ensure("validateCharacters", 1 == module->validateSyntax(model.model));
}


TEST_FUNCTION(20)
{// test validateDefaultValueType int
  TestMySQLModel model(rdbms);
  model.column->defaultValue("55");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.column->defaultValue("'string'");
  ensure("validateDefaultValueType", 1 == module->validateIntegrity(model.model));
}


TEST_FUNCTION(21)
{// test validateDefaultValueType string
  TestMySQLModel model(rdbms);
  model.columnText->defaultValue("'desc'");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnText->defaultValue("desc");
  ensure("validateDefaultValueType", 1 == module->validateIntegrity(model.model));
}


TEST_FUNCTION(22)
{// test validateDefaultValueType date
  TestMySQLModel model(rdbms);
  model.columnDate->defaultValue("071104050505");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDate->defaultValue("20071104050505");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDate->defaultValue("071104");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDate->defaultValue("20071104");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDate->defaultValue("'2007-10-30'");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDate->defaultValue("'2007@10@30 11:11:11'");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDate->defaultValue("now()");
  ensure("validateDefaultValueType", 1 == module->validateIntegrity(model.model));
}


TEST_FUNCTION(23)
{// test validateDefaultValueType double
  TestMySQLModel model(rdbms);
  model.columnDouble->scale(2);
  model.columnDouble->defaultValue(".9");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDouble->defaultValue("1");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDouble->defaultValue("1.2");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));
  model.columnDouble->defaultValue("1.2e-05");
  ensure("validateDefaultValueType", 0 == module->validateIntegrity(model.model));

  model.columnDouble->defaultValue("e1.2e-05");
  ensure("validateDefaultValueType", 1 == module->validateIntegrity(model.model));
}

TEST_FUNCTION(24)
{// test validateDuplicatesAdditions
  TestMySQLModel model(rdbms);
  ensure("DupAdditions", 0 != module->validateDuplicatesAdditions(model.schema));
  model.view->name(model.table->name());
  ensure("DupAdditions", 0 == module->validateDuplicatesAdditions(model.schema));
}

TEST_FUNCTION(26)
{
  TestMySQLModel model(rdbms);
  model.view->name(model.table->name());
  
  const int count = module->validateAll(model.model);
  ensure("Errors count", count == 1 );
}

TEST_FUNCTION(27)
{
  TestMySQLModel model(rdbms);
  model.view->name(model.table->name());
 
  model.table2->name("t1sdfgkjslgkjdfhjsdlkfjslfkjsdlkfjlsdkfjsdfhsdkjhgdkjfhgkdjfhgkj876w34*&%&^$&^^*&#I#$*&^(*&#&($MJHKJHKH");

  model.table3->name("table");
 
  //Verify wrong default values
  model.columnDate->defaultValue("now()");
  model.columnDouble->defaultValue("e1.2e-05");
  model.columnText->defaultValue("desc");
  model.column->defaultValue("'string'");
  
  //Validate wrong syntax
  model.routine->sqlDefinition("pupper code");
  model.view->sqlDefinition("some dummy strings");
  model.trigger->sqlDefinition("UCREATE "
                              "TRIGGER trig BEFORE INSERT " 
                              "ON t1 FOR EACH ROW SET id = new.id;");

  const int count = module->validateAll(model.model);
  ensure("Errors count", count == 11 );
}

TEST_FUNCTION(28)
{
  TestMySQLModel model(rdbms);
  model.table->foreignKeys().insert(model.foreignKey2);  
  model.foreignKey2->owner(model.table);

  model.foreignKey2->referencedColumns().insert(model.column);

  model.foreignKey2->name("FK");
  model.foreignKey2->columns().insert(model.column2);
  model.foreignKey2->referencedColumns().insert(model.column);
  model.foreignKey2->referencedTable(model.table);

  const int count = module->validateAll(model.model);
  ensure("Errors count", count == 2 );
}

TEST_FUNCTION(29)
{
  TestMySQLModel model(rdbms);
  
  model.column->autoIncrement(1);
  model.column2->autoIncrement(1);
  model.column->defaultValue("123");

  const int count = module->validateAll(model.model);
  ensure("Errors count", count == 3 );
}

TEST_FUNCTION(30)
{
  TestMySQLModel model(rdbms);
  
  model.column->simpleType(db_SimpleDatatypeRef());

  const int count = module->validateAll(model.model);
  ensure("Errors count", count == 3 );
}

TEST_FUNCTION(31)
{
  TestMySQLModel model(rdbms);
  model.table->foreignKeys().insert(model.foreignKey2);  
  model.foreignKey2->owner(model.table);

  model.foreignKey2->referencedColumns().insert(model.column);
  model.foreignKey2->referencedColumns().insert(db_mysql_ColumnRef());

  model.foreignKey2->name("FK");
  model.foreignKey2->columns().insert(model.column2);
  model.foreignKey2->referencedColumns().insert(model.column);
  model.foreignKey2->referencedTable(model.table);

  const int count = module->validateAll(model.model);
  ensure("Errors count", count == 3 );
}

TEST_FUNCTION(32)
{
  TestMySQLModel model(rdbms);
  db_mysql_ColumnRef col2(rdbms.get_grt());
  col2->name("col2");
  col2->owner(model.table);
  
  model.table->foreignKeys().insert(model.foreignKey2);  

  model.foreignKey2->name("FK");
  model.foreignKey2->owner(model.table);

  model.foreignKey2->referencedColumns().insert(model.column);
  model.foreignKey2->referencedColumns().insert(db_mysql_ColumnRef());

  model.foreignKey2->columns().insert(model.column2);
  model.foreignKey2->columns().insert(col2);
  model.foreignKey2->referencedColumns().insert(model.column);
  model.foreignKey2->referencedTable(model.table);

  const int count = module->validateAll(model.model);
  ensure_equals("Errors count", count, 4);
}

TEST_FUNCTION(33)
{
  TestMySQLModel model(rdbms);

  model.table->foreignKeys().insert(model.foreignKey2);  

  model.foreignKey2->name("FK");
  model.foreignKey2->owner(model.table);

  model.foreignKey2->columns().insert(model.column2);
  model.foreignKey2->columns().insert(model.column2);
  model.foreignKey2->columns().insert(model.column2);

  ensure_equals("Errors count", module->validateAll(model.model), 2);
}

TEST_FUNCTION(34)
{
  TestMySQLModel model(rdbms);

  model.table->foreignKeys().insert(model.foreignKey2);  
  model.foreignKey2->owner(model.table);
  model.foreignKey2->name("FK2");

  model.foreignKey2->referencedTable(model.table);

  model.column->simpleType(rdbms->simpleDatatypes().get(3));
  model.column2->simpleType(rdbms->simpleDatatypes().get(4));

  model.foreignKey2->referencedColumns().insert(model.column);
  model.foreignKey2->columns().insert(model.column2);

  const int count = module->validateAll(model.model);
  ensure_equals("Errors count", count, 2);
}

TEST_FUNCTION(35)
{
  TestMySQLModel model(rdbms);

  model.table->foreignKeys().insert(model.foreignKey2);  
  model.foreignKey2->owner(db_mysql_TableRef());
  model.foreignKey2->name("FK2");

  model.column2->owner(db_mysql_TableRef());
  model.foreignKey2->referencedTable(db_mysql_TableRef());

  model.column->simpleType(rdbms->simpleDatatypes().get(3));
  model.column2->simpleType(rdbms->simpleDatatypes().get(4));

  model.foreignKey2->referencedColumns().insert(model.column);
  model.foreignKey2->columns().insert(model.column2);

  const int count = module->validateAll(model.model);
  ensure_equals("Errors count", count, 5);
}

TEST_FUNCTION(37)
{
  TestMySQLModel model(rdbms);
  
  model.columnText->isNotNull(0);
  model.columnText->defaultValue("NULL");
  ensure("Errors count", 0 == module->validateAll(model.model) );

  model.columnText->defaultValue("'NULL '");
  ensure("Errors count", 0 == module->validateAll(model.model) );

  model.columnText->defaultValue("");
  ensure("Errors count", 0 == module->validateAll(model.model) );

  model.columnText->isNotNull(1);
  model.columnText->defaultValue("NULL");
  ensure("Errors count", 1 == module->validateAll(model.model) );

  model.columnText->defaultValue("'NULL '");
  ensure("Errors count", 0 == module->validateAll(model.model) );

  model.columnText->defaultValue("");
  ensure("Errors count", 0 == module->validateAll(model.model) );
}
*/
END_TESTS
