Moving qqq-backend-module-rdbms into its own subdir

This commit is contained in:
2022-07-28 12:02:55 -05:00
parent a149e4666a
commit 168fa1d85a
31 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,92 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
/*******************************************************************************
**
*******************************************************************************/
public class TestUtils
{
public static final String DEFAULT_BACKEND_NAME = "default";
/*******************************************************************************
**
*******************************************************************************/
public static QInstance defineInstance()
{
QInstance qInstance = new QInstance();
qInstance.addBackend(defineBackend());
qInstance.addTable(defineTablePerson());
return (qInstance);
}
/*******************************************************************************
**
*******************************************************************************/
public static RDBMSBackendMetaData defineBackend()
{
return (new RDBMSBackendMetaData()
.withName(DEFAULT_BACKEND_NAME)
.withVendor("h2")
.withHostName("mem")
.withDatabaseName("test_database")
.withUsername("sa"));
}
/*******************************************************************************
**
*******************************************************************************/
public static QTableMetaData defineTablePerson()
{
return new QTableMetaData()
.withName("a-person") // use this name, so it isn't the same as the actual database-table name (which must come from the backend details)
.withLabel("Person")
.withBackendName(defineBackend().getName())
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("create_date"))
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withBackendName("modify_date"))
.withField(new QFieldMetaData("firstName", QFieldType.STRING).withBackendName("first_name"))
.withField(new QFieldMetaData("lastName", QFieldType.STRING).withBackendName("last_name"))
.withField(new QFieldMetaData("birthDate", QFieldType.DATE).withBackendName("birth_date"))
.withField(new QFieldMetaData("email", QFieldType.STRING))
.withBackendDetails(new RDBMSTableBackendDetails()
.withTableName("person"));
}
}

View File

@ -0,0 +1,97 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.InputStream;
import java.sql.Connection;
import java.util.List;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.AfterEach;
import static junit.framework.Assert.assertNotNull;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@AfterEach
private void afterEachRDBMSActionTest()
{
QueryManager.resetPageSize();
QueryManager.resetStatistics();
QueryManager.setCollectStatistics(false);
}
/*******************************************************************************
**
*******************************************************************************/
protected void primeTestDatabase() throws Exception
{
primeTestDatabase("prime-test-database.sql");
}
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("unchecked")
protected void primeTestDatabase(String sqlFileName) throws Exception
{
ConnectionManager connectionManager = new ConnectionManager();
try(Connection connection = connectionManager.getConnection(TestUtils.defineBackend()))
{
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/" + sqlFileName);
assertNotNull(primeTestDatabaseSqlStream);
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
String joinedSQL = String.join("\n", lines);
for(String sql : joinedSQL.split(";"))
{
QueryManager.executeUpdate(connection, sql);
}
}
}
/*******************************************************************************
**
*******************************************************************************/
protected void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
{
ConnectionManager connectionManager = new ConnectionManager();
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
QueryManager.executeStatement(connection, sql, resultSetProcessor);
}
}

View File

@ -0,0 +1,121 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSCountActionTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUnfilteredCount() throws QException
{
CountInput countInput = initCountRequest();
CountOutput countOutput = new RDBMSCountAction().execute(countInput);
Assertions.assertEquals(5, countOutput.getCount(), "Unfiltered query should find all rows");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testEqualsQueryCount() throws QException
{
String email = "darin.kelkhoff@gmail.com";
CountInput countInput = initCountRequest();
countInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.EQUALS)
.withValues(List.of(email)))
);
CountOutput countOutput = new RDBMSCountAction().execute(countInput);
Assertions.assertEquals(1, countOutput.getCount(), "Expected # of rows");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotEqualsQuery() throws QException
{
String email = "darin.kelkhoff@gmail.com";
CountInput countInput = initCountRequest();
countInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_EQUALS)
.withValues(List.of(email)))
);
CountOutput countOutput = new RDBMSCountAction().execute(countInput);
Assertions.assertEquals(4, countOutput.getCount(), "Expected # of rows");
}
/*******************************************************************************
**
*******************************************************************************/
private CountInput initCountRequest()
{
CountInput countInput = new CountInput();
countInput.setInstance(TestUtils.defineInstance());
countInput.setTableName(TestUtils.defineTablePerson().getName());
return countInput;
}
}

View File

@ -0,0 +1,226 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSDeleteActionTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testDeleteAll() throws Exception
{
DeleteInput deleteInput = initStandardPersonDeleteRequest();
deleteInput.setPrimaryKeys(List.of(1, 2, 3, 4, 5));
DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput);
assertEquals(5, deleteResult.getDeletedRecordCount(), "Unfiltered delete should return all rows");
assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors");
runTestSql("SELECT id FROM person", (rs -> assertFalse(rs.next())));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testDeleteOne() throws Exception
{
DeleteInput deleteInput = initStandardPersonDeleteRequest();
deleteInput.setPrimaryKeys(List.of(1));
DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput);
assertEquals(1, deleteResult.getDeletedRecordCount(), "Should delete one row");
assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors");
runTestSql("SELECT id FROM person WHERE id = 1", (rs -> assertFalse(rs.next())));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testDeleteSome() throws Exception
{
DeleteInput deleteInput = initStandardPersonDeleteRequest();
deleteInput.setPrimaryKeys(List.of(1, 3, 5));
DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput);
assertEquals(3, deleteResult.getDeletedRecordCount(), "Should delete one row");
assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors");
runTestSql("SELECT id FROM person", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertTrue(rs.getInt(1) == 2 || rs.getInt(1) == 4);
}
assertEquals(2, rowsFound);
}));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testDeleteSomeIdsThatExistAndSomeThatDoNot() throws Exception
{
DeleteInput deleteInput = initStandardPersonDeleteRequest();
deleteInput.setPrimaryKeys(List.of(1, -1));
DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput);
assertEquals(1, deleteResult.getDeletedRecordCount(), "Should delete one row");
assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors (the one not found is just noop)");
}
/*******************************************************************************
**
*******************************************************************************/
private DeleteInput initStandardPersonDeleteRequest()
{
DeleteInput deleteInput = new DeleteInput();
deleteInput.setInstance(TestUtils.defineInstance());
deleteInput.setTableName(TestUtils.defineTablePerson().getName());
return deleteInput;
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testDeleteWhereForeignKeyBlocksSome() throws Exception
{
//////////////////////////////////////////////////////////////////
// load the parent-child tables, with foreign keys and instance //
//////////////////////////////////////////////////////////////////
super.primeTestDatabase("prime-test-database-parent-child-tables.sql");
DeleteInput deleteInput = initChildTableInstanceAndDeleteRequest();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// try to delete all of the child records - 2 should fail, because they are referenced by parent_table.child_id //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
deleteInput.setPrimaryKeys(List.of(1, 2, 3, 4, 5));
QueryManager.setCollectStatistics(true);
QueryManager.resetStatistics();
DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput);
////////////////////////////////////////////////////////////////////////////////
// assert that 6 queries ran - the initial delete (which failed), then 6 more //
////////////////////////////////////////////////////////////////////////////////
QueryManager.setCollectStatistics(false);
Map<String, Integer> queryStats = QueryManager.getStatistics();
assertEquals(6, queryStats.get(QueryManager.STAT_QUERIES_RAN), "Number of queries ran");
assertEquals(2, deleteResult.getRecordsWithErrors().size(), "Should get back the 2 records with errors");
assertTrue(deleteResult.getRecordsWithErrors().stream().noneMatch(r -> r.getErrors().isEmpty()), "All we got back should have errors");
assertEquals(3, deleteResult.getDeletedRecordCount(), "Should get back that 3 were deleted");
runTestSql("SELECT id FROM child_table", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
///////////////////////////////////////////
// child_table rows 1 & 3 should survive //
///////////////////////////////////////////
assertTrue(rs.getInt(1) == 1 || rs.getInt(1) == 3);
}
assertEquals(2, rowsFound);
}));
}
/*******************************************************************************
**
*******************************************************************************/
private DeleteInput initChildTableInstanceAndDeleteRequest()
{
QInstance qInstance = TestUtils.defineInstance();
String childTableName = "childTable";
qInstance.addTable(new QTableMetaData()
.withName(childTableName)
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME)
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("name", QFieldType.STRING))
.withBackendDetails(new RDBMSTableBackendDetails()
.withTableName("child_table")));
qInstance.addTable(new QTableMetaData()
.withName("parentTable")
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME)
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("name", QFieldType.STRING))
.withField(new QFieldMetaData("childId", QFieldType.INTEGER).withBackendName("child_id"))
.withBackendDetails(new RDBMSTableBackendDetails()
.withTableName("parent_table")));
DeleteInput deleteInput = new DeleteInput();
deleteInput.setInstance(qInstance);
deleteInput.setTableName(childTableName);
return deleteInput;
}
}

View File

@ -0,0 +1,172 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.Collections;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSInsertActionTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testInsertNullList() throws QException
{
InsertInput insertInput = initInsertRequest();
insertInput.setRecords(null);
InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput);
assertEquals(0, insertOutput.getRecords().size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testInsertEmptyList() throws QException
{
InsertInput insertInput = initInsertRequest();
insertInput.setRecords(Collections.emptyList());
InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput);
assertEquals(0, insertOutput.getRecords().size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testInsertOne() throws Exception
{
InsertInput insertInput = initInsertRequest();
QRecord record = new QRecord().withTableName("person")
.withValue("firstName", "James")
.withValue("lastName", "Kirk")
.withValue("email", "jamestk@starfleet.net")
.withValue("birthDate", "2210-05-20");
insertInput.setRecords(List.of(record));
InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput);
assertEquals(1, insertOutput.getRecords().size(), "Should return 1 row");
assertNotNull(insertOutput.getRecords().get(0).getValue("id"), "Should have an id in the row");
// todo - add errors to QRecord? assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
assertAnInsertedPersonRecord("James", "Kirk", 6);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testInsertMany() throws Exception
{
QueryManager.setPageSize(2);
InsertInput insertInput = initInsertRequest();
QRecord record1 = new QRecord().withTableName("person")
.withValue("firstName", "Jean-Luc")
.withValue("lastName", "Picard")
.withValue("email", "jl@starfleet.net")
.withValue("birthDate", "2310-05-20");
QRecord record2 = new QRecord().withTableName("person")
.withValue("firstName", "William")
.withValue("lastName", "Riker")
.withValue("email", "notthomas@starfleet.net")
.withValue("birthDate", "2320-05-20");
QRecord record3 = new QRecord().withTableName("person")
.withValue("firstName", "Beverly")
.withValue("lastName", "Crusher")
.withValue("email", "doctor@starfleet.net")
.withValue("birthDate", "2320-06-26");
insertInput.setRecords(List.of(record1, record2, record3));
InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput);
assertEquals(3, insertOutput.getRecords().size(), "Should return right # of rows");
assertEquals(6, insertOutput.getRecords().get(0).getValue("id"), "Should have next id in the row");
assertEquals(7, insertOutput.getRecords().get(1).getValue("id"), "Should have next id in the row");
assertEquals(8, insertOutput.getRecords().get(2).getValue("id"), "Should have next id in the row");
assertAnInsertedPersonRecord("Jean-Luc", "Picard", 6);
assertAnInsertedPersonRecord("William", "Riker", 7);
assertAnInsertedPersonRecord("Beverly", "Crusher", 8);
}
private void assertAnInsertedPersonRecord(String firstName, String lastName, Integer id) throws Exception
{
runTestSql("SELECT * FROM person WHERE last_name = '" + lastName + "'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(id, rs.getInt("id"));
assertEquals(firstName, rs.getString("first_name"));
assertNotNull(rs.getString("create_date"));
assertNotNull(rs.getString("modify_date"));
}
assertEquals(1, rowsFound);
}));
}
/*******************************************************************************
**
*******************************************************************************/
private InsertInput initInsertRequest()
{
InsertInput insertInput = new InsertInput();
insertInput.setInstance(TestUtils.defineInstance());
insertInput.setTableName(TestUtils.defineTablePerson().getName());
return insertInput;
}
}

View File

@ -0,0 +1,422 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSQueryActionTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUnfilteredQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testEqualsQuery() throws QException
{
String email = "darin.kelkhoff@gmail.com";
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.EQUALS)
.withValues(List.of(email)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertEquals(email, queryOutput.getRecords().get(0).getValueString("email"), "Should find expected email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotEqualsQuery() throws QException
{
String email = "darin.kelkhoff@gmail.com";
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_EQUALS)
.withValues(List.of(email)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").equals(email)), "Should NOT find expected email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testInQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.IN)
.withValues(List.of(2, 4)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(4)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotInQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.NOT_IN)
.withValues(List.of(2, 3, 4)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testStartsWith() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.STARTS_WITH)
.withValues(List.of("darin")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testContains() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.CONTAINS)
.withValues(List.of("kelkhoff")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testEndsWith() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.ENDS_WITH)
.withValues(List.of("gmail.com")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotStartsWith() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_STARTS_WITH)
.withValues(List.of("darin")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotContains() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_CONTAINS)
.withValues(List.of("kelkhoff")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotEndsWith() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_ENDS_WITH)
.withValues(List.of("gmail.com")))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testLessThanQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.LESS_THAN)
.withValues(List.of(3)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testLessThanOrEqualsQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.LESS_THAN_OR_EQUALS)
.withValues(List.of(2)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testGreaterThanQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.GREATER_THAN)
.withValues(List.of(3)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testGreaterThanOrEqualsQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.GREATER_THAN_OR_EQUALS)
.withValues(List.of(4)))
);
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testIsBlankQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("birthDate")
.withOperator(QCriteriaOperator.IS_BLANK)
));
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValue("birthDate") == null), "Should find expected row");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testBetweenQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.BETWEEN)
.withValues(List.of(2, 4))
));
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(3, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(3) || r.getValueInteger("id").equals(4)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotBetweenQuery() throws QException
{
QueryInput queryInput = initQueryRequest();
queryInput.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.NOT_BETWEEN)
.withValues(List.of(2, 4))
));
QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput);
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
private QueryInput initQueryRequest()
{
QueryInput queryInput = new QueryInput();
queryInput.setInstance(TestUtils.defineInstance());
queryInput.setTableName(TestUtils.defineTablePerson().getName());
return queryInput;
}
}

View File

@ -0,0 +1,358 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSUpdateActionTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
QueryManager.setCollectStatistics(true);
QueryManager.resetStatistics();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateNullList() throws QException
{
UpdateInput updateInput = initUpdateRequest();
updateInput.setRecords(null);
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
assertEquals(0, updateResult.getRecords().size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateEmptyList() throws QException
{
UpdateInput updateInput = initUpdateRequest();
updateInput.setRecords(Collections.emptyList());
new RDBMSUpdateAction().execute(updateInput);
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
assertEquals(0, updateResult.getRecords().size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateOne() throws Exception
{
UpdateInput updateInput = initUpdateRequest();
QRecord record = new QRecord().withTableName("person")
.withValue("id", 2)
.withValue("firstName", "James")
.withValue("lastName", "Kirk")
.withValue("email", "jamestk@starfleet.net")
.withValue("birthDate", "2210-05-20");
updateInput.setRecords(List.of(record));
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
Map<String, Integer> statistics = QueryManager.getStatistics();
assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN));
assertEquals(1, updateResult.getRecords().size(), "Should return 1 row");
assertEquals(2, updateResult.getRecords().get(0).getValue("id"), "Should have id=2 in the row");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(2, rs.getInt("id"));
assertEquals("James", rs.getString("first_name"));
assertEquals("2210-05-20", rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Maes'", (rs -> {
if(rs.next())
{
fail("Should not have found Maes any more.");
}
}));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateManyWithDifferentColumnsAndValues() throws Exception
{
UpdateInput updateInput = initUpdateRequest();
QRecord record1 = new QRecord().withTableName("person")
.withValue("id", 1)
.withValue("firstName", "Darren")
.withValue("lastName", "From Bewitched")
.withValue("birthDate", "1900-01-01");
QRecord record2 = new QRecord().withTableName("person")
.withValue("id", 3)
.withValue("firstName", "Wilt")
.withValue("birthDate", null);
QRecord record3 = new QRecord().withTableName("person")
.withValue("id", 5)
.withValue("firstName", "Richard")
.withValue("birthDate", null);
updateInput.setRecords(List.of(record1, record2, record3));
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
// this test runs one batch and one regular query
Map<String, Integer> statistics = QueryManager.getStatistics();
assertEquals(1, statistics.get(QueryManager.STAT_BATCHES_RAN));
assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN));
assertEquals(3, updateResult.getRecords().size(), "Should return 3 rows");
assertEquals(1, updateResult.getRecords().get(0).getValue("id"), "Should have expected ids in the row");
assertEquals(3, updateResult.getRecords().get(1).getValue("id"), "Should have expected ids in the row");
assertEquals(5, updateResult.getRecords().get(2).getValue("id"), "Should have expected ids in the row");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(1, rs.getInt("id"));
assertEquals("Darren", rs.getString("first_name"));
assertEquals("From Bewitched", rs.getString("last_name"));
assertEquals("1900-01-01", rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Chamberlain'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(3, rs.getInt("id"));
assertEquals("Wilt", rs.getString("first_name"));
assertNull(rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Richardson'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(5, rs.getInt("id"));
assertEquals("Richard", rs.getString("first_name"));
assertNull(rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateManyWithSameColumnsDifferentValues() throws Exception
{
UpdateInput updateInput = initUpdateRequest();
QRecord record1 = new QRecord().withTableName("person")
.withValue("id", 1)
.withValue("firstName", "Darren")
.withValue("lastName", "From Bewitched")
.withValue("birthDate", "1900-01-01");
QRecord record2 = new QRecord().withTableName("person")
.withValue("id", 3)
.withValue("firstName", "Wilt")
.withValue("lastName", "Tim's Uncle")
.withValue("birthDate", null);
updateInput.setRecords(List.of(record1, record2));
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
Map<String, Integer> statistics = QueryManager.getStatistics();
assertEquals(1, statistics.get(QueryManager.STAT_BATCHES_RAN));
assertEquals(2, updateResult.getRecords().size(), "Should return 2 rows");
assertEquals(1, updateResult.getRecords().get(0).getValue("id"), "Should have expected ids in the row");
assertEquals(3, updateResult.getRecords().get(1).getValue("id"), "Should have expected ids in the row");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(1, rs.getInt("id"));
assertEquals("Darren", rs.getString("first_name"));
assertEquals("From Bewitched", rs.getString("last_name"));
assertEquals("1900-01-01", rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Tim''s Uncle'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(3, rs.getInt("id"));
assertEquals("Wilt", rs.getString("first_name"));
assertEquals("Tim's Uncle", rs.getString("last_name"));
assertNull(rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateManyWithSameColumnsSameValues() throws Exception
{
UpdateInput updateInput = initUpdateRequest();
List<QRecord> records = new ArrayList<>();
for(int i = 1; i <= 5; i++)
{
records.add(new QRecord().withTableName("person")
.withValue("id", i)
.withValue("birthDate", "1999-09-09"));
}
updateInput.setRecords(records);
UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput);
Map<String, Integer> statistics = QueryManager.getStatistics();
assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN));
assertEquals(5, updateResult.getRecords().size(), "Should return 5 rows");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE id <= 5", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals("1999-09-09", rs.getString("birth_date"));
}
assertEquals(5, rowsFound);
}));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testModifyDateGetsUpdated() throws Exception
{
String originalModifyDate = selectModifyDate(1);
UpdateInput updateInput = initUpdateRequest();
List<QRecord> records = new ArrayList<>();
records.add(new QRecord().withTableName("person")
.withValue("id", 1)
.withValue("firstName", "Johnny Updated"));
updateInput.setRecords(records);
new RDBMSUpdateAction().execute(updateInput);
String updatedModifyDate = selectModifyDate(1);
assertTrue(StringUtils.hasContent(originalModifyDate));
assertTrue(StringUtils.hasContent(updatedModifyDate));
assertNotEquals(originalModifyDate, updatedModifyDate);
}
/*******************************************************************************
**
*******************************************************************************/
private String selectModifyDate(Integer id) throws Exception
{
StringBuilder modifyDate = new StringBuilder();
runTestSql("SELECT modify_date FROM person WHERE id = " + id, (rs -> {
if(rs.next())
{
modifyDate.append(rs.getString("modify_date"));
}
}));
return (modifyDate.toString());
}
/*******************************************************************************
**
*******************************************************************************/
private UpdateInput initUpdateRequest()
{
UpdateInput updateInput = new UpdateInput();
updateInput.setInstance(TestUtils.defineInstance());
updateInput.setTableName(TestUtils.defineTablePerson().getName());
return updateInput;
}
}

View File

@ -0,0 +1,113 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
** Unit test for ConnectionManager
*******************************************************************************/
@Disabled("This was okay for POC, but shouldn't run in CI")
class ConnectionManagerTest
{
@Test
public void test() throws SQLException
{
Connection connection = new ConnectionManager().getConnection(getAuroraBacked());
assertNotNull(connection);
String sql = """
insert into raw_parcel_invoice_line_ups (id, create_date, modify_date, parcel_invoice_line_id, account_country, account_number, account_split_payment_indicator, account_tax_id, alternate_invoice_amount, alternate_invoice_number, alternate_invoicing_currency_code, bol__number_1, bol__number_2, bol__number_3, bol__number_4, bol__number_5, basis_currency_code, basis_value, bill_option_code,
billed_weight, billed_weight_type, billed_weight_unit_of_measure, cccd_number, cpc_code, carrier_name_clinical_trial_identification_number__sds_id, charge_category_code, charge_category_detail_code, charge_classification_code, charge_description, charge_description_code, charge_source, charged_unit_quantity, class_number, contact_name, container_type,
corrected_zone, currency_variance_amount, customer_reference_number, customs_number, customs_office_name, cycle_date, cycle_number, declaration_number, declared_freight_class, detail_class, detail_keyed_billed_dimension, detail_keyed_billed_unit_of_measure, detail_keyed_dim, detail_keyed_unit_of_measure, direct_shipment_date, document_number, document_type,
duty_amount, duty_rate, duty_value, eft_date, eori_number, epu, entered_currency_code, entered_value, entered_weight, entered_weight_unit_of_measure, entry_date, entry_number, entry_port, entry_type, exchange_rate, excise_tax_amount, excise_tax_rate, export_place, foreign_trade_reference_number, freight_sequence_number, gst_amount, gst_rate,
goods_description, import_tax_id, incentive_amount, invoice_amount, invoice_currency_code, invoice_date, invoice_due_date, invoice_exchange_rate, invoice_level_charge, invoice_number, invoice_remit_amount, invoice_type_code, invoice_type_detail_code, item_quantity, item_quantity_unit_of_measure, job_number, lead_shipment_number, line_item_number,
master_air_waybill_number, miscellaneous_address_1_address_line_1, miscellaneous_address_1_address_line_2, miscellaneous_address_1_city, miscellaneous_address_1_company_name, miscellaneous_address_1_country, miscellaneous_address_1_name, miscellaneous_address_1_postal, miscellaneous_address_1_state, miscellaneous_address_2_address_line_1,
miscellaneous_address_2_address_line_2, miscellaneous_address_2_city, miscellaneous_address_2_company_name, miscellaneous_address_2_country, miscellaneous_address_2_name, miscellaneous_address_2_postal, miscellaneous_address_2_state, miscellaneous_address_qual_1, miscellaneous_address_qual_2, miscellaneous_currency_code, miscellaneous_incentive_amount,
miscellaneous_line_1, miscellaneous_line_10, miscellaneous_line_11, miscellaneous_line_2, miscellaneous_line_3, miscellaneous_line_4, miscellaneous_line_5, miscellaneous_line_7, miscellaneous_line_8, miscellaneous_line_9, miscellaneous_net_amount, nmfc, net_amount, office_number, order_in_council, origin_country, original_service_description,
original_shipment_package_quantity, original_tracking_number, other_amount, other_basis_amount, other_customs_number, other_customs_number_indicator, other_rate, oversize_quantity, po__number_1, po__number_10, po__number_2, po__number_3, po__number_4, po__number_5, po__number_6, po__number_7, po__number_8, po__number_9, package_dimension_unit_of_measure,
package_dimensions, package_quantity, package_reference_number_1, package_reference_number_2, package_reference_number_3, package_reference_number_4, package_reference_number_5, payer_role_code, pickup_record_number, place_holder_46, place_holder_47, place_holder_48, place_holder_52, place_holder_53, place_holder_54, place_holder_55, place_holder_56,
place_holder_57, place_holder_58, place_holder_59, promo_discount_alias, promo_discount_applied_indicator, raw_dimension_unit_of_measure, raw_dimensions, receiver_address_line_1, receiver_address_line_2, receiver_city, receiver_company_name, receiver_country, receiver_name, receiver_postal, receiver_state, recipient_number, scc_scale_weight,
sds_delivery_date, sds_error_code, sds_match_level_cd, sds_rnr_date, sima_access, scale_weight_unit_of_measure, scale_weight_quantity, sender_address_line_1, sender_address_line_2, sender_city, sender_company_name, sender_country, sender_name, sender_postal, sender_state, shipment_date, shipment_delivery_date, shipment_description, shipment_export_date,
shipment_import_date, shipment_reference_number_1, shipment_reference_number_2, shipment_release_date, shipment_value_amount, sold_to_address_line_1, sold_to_address_line_2, sold_to_city, sold_to_company_name, sold_to_country, sold_to_name, sold_to_postal, sold_to_state, store_number, tariff_code, tariff_rate, tariff_treatment_number, tax_indicator,
tax_type, tax_value, tax_variance_amount, tax_law_article_basis_amount, tax_law_article_number, third_party_address_line_1, third_party_address_line_2, third_party_city, third_party_company_name, third_party_country, third_party_name, third_party_postal, third_party_state, total_customs_amount, total_value_for_duty, tracking_number,
transaction_currency_code, transaction_date, transport_mode, type_code_1, type_code_2, type_detail_code_1, type_detail_code_2, type_detail_value_1, type_detail_value_2, unit_of_measure, vat_amount, vat_basis_amount, vat_rate, validation_date, version, weight, world_ease_number, zone, data_lake_id)
values
""";
String values = """
(0, '2022-06-13 13:07:52.0', '2022-06-13 13:07:52.0', null, 'US', '00000F2098', null, null, 0, null, null, null, null, null, null, null, null, 0.00, null, 0, null, null, null, null,
null, 'MIS', 'SVCH', 'FRT', 'Service Charge', null, null, 0, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, 0, 0, null, null, null, null, 0,
0, null, null, null, null, null, 0, 0, 0, null, null, 0, 0, 0, null, null, 0.00, 36, 'USD', '2022-01-01', '2022-01-10', 0, 0, '0000000F2098012', null, 'E', null, 0, null, null, null,
0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null, 0, null, 36.00, null, null, null, null, 0, null, 0,
0, null, null, 0, 0, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, '00000F2098', 0, null, null, null, null, 0, null, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null,
0, null, null, null, 0, 0, 0, null, null, null, null, null, null, null, null, null, 0, 0, null, 'USD', '2022-01-01', null, null, null, null, null, null, null, null, 0, 0, 0, null, 2, null, null, null, 'XXXXXX')
""";
sql += String.join(",", Collections.nCopies(1000, values));
for(int i = 0; i < 10; i++)
{
System.out.println("== Cycle " + i);
QueryManager.executeUpdate(connection, "BEGIN WORK");
System.out.println("Begin work...");
Integer insertCount = QueryManager.executeUpdateForRowCount(connection, sql);
System.out.println("Inserted: " + insertCount);
Integer deleteCount = QueryManager.executeUpdateForRowCount(connection, "DELETE from raw_parcel_invoice_line_ups WHERE data_lake_id='XXXXXX'");
System.out.println("Deleted: " + deleteCount);
boolean commit = true;
if(commit)
{
QueryManager.executeUpdate(connection, "COMMIT WORK");
System.out.println("Commit.");
}
else
{
QueryManager.executeUpdate(connection, "ROLLBACK WORK");
System.out.println("Rollback.");
}
}
}
private RDBMSBackendMetaData getAuroraBacked()
{
return new RDBMSBackendMetaData()
.withName("aurora-test")
.withVendor("aurora")
.withHostName("nf-one-development-aurora.cwuhqcx1inwx.us-east-2.rds.amazonaws.com")
.withPort(3306)
.withDatabaseName("nutrifresh_one")
.withUsername("nf_admin")
.withPassword("%!2rwcH+fb#WgPg");
}
}

View File

@ -0,0 +1,271 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.jdbc;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.util.GregorianCalendar;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*******************************************************************************
**
*******************************************************************************/
class QueryManagerTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
void beforeEach() throws SQLException
{
Connection connection = getConnection();
QueryManager.executeUpdate(connection, "CREATE TABLE t (i INTEGER, dt DATETIME, c CHAR(1), d DATE)");
}
/*******************************************************************************
**
*******************************************************************************/
@AfterEach
void afterEach() throws SQLException
{
Connection connection = getConnection();
QueryManager.executeUpdate(connection, "DROP TABLE t");
}
/*******************************************************************************
**
*******************************************************************************/
private Connection getConnection() throws SQLException
{
return new ConnectionManager().getConnection(TestUtils.defineBackend());
}
/*******************************************************************************
** Test the various overloads that bind params.
** Note, we're just confirming that these methods don't throw...
*******************************************************************************/
@Test
void testBindParams() throws SQLException
{
long ctMillis = System.currentTimeMillis();
Connection connection = getConnection();
PreparedStatement ps = connection.prepareStatement("UPDATE t SET i = ? WHERE i > 0");
///////////////////////////////////////////////////////////////////////////////
// these calls - we just want to assert that they don't throw any exceptions //
///////////////////////////////////////////////////////////////////////////////
QueryManager.bindParamObject(ps, 1, (short) 1);
QueryManager.bindParamObject(ps, 1, (long) 1);
QueryManager.bindParamObject(ps, 1, true);
QueryManager.bindParamObject(ps, 1, BigDecimal.ONE);
QueryManager.bindParamObject(ps, 1, "hello".getBytes(StandardCharsets.UTF_8));
QueryManager.bindParamObject(ps, 1, new Timestamp(ctMillis));
QueryManager.bindParamObject(ps, 1, new Date(ctMillis));
QueryManager.bindParamObject(ps, 1, new GregorianCalendar());
QueryManager.bindParamObject(ps, 1, LocalDate.now());
QueryManager.bindParamObject(ps, 1, OffsetDateTime.now());
QueryManager.bindParamObject(ps, 1, LocalDateTime.now());
assertThrows(SQLException.class, () ->
{
QueryManager.bindParamObject(ps, 1, new Object());
});
QueryManager.bindParam(ps, 1, (Integer) null);
QueryManager.bindParam(ps, 1, (Boolean) null);
QueryManager.bindParam(ps, 1, (BigDecimal) null);
QueryManager.bindParam(ps, 1, (byte[]) null);
QueryManager.bindParam(ps, 1, (Timestamp) null);
QueryManager.bindParam(ps, 1, (String) null);
QueryManager.bindParam(ps, 1, (Date) null);
QueryManager.bindParam(ps, 1, (GregorianCalendar) null);
QueryManager.bindParam(ps, 1, (LocalDate) null);
QueryManager.bindParam(ps, 1, (LocalDateTime) null);
QueryManager.bindParam(ps, 1, 1);
QueryManager.bindParam(ps, 1, true);
QueryManager.bindParam(ps, 1, BigDecimal.ONE);
QueryManager.bindParam(ps, 1, "hello".getBytes(StandardCharsets.UTF_8));
QueryManager.bindParam(ps, 1, new Timestamp(ctMillis));
QueryManager.bindParam(ps, 1, "hello");
QueryManager.bindParam(ps, 1, new Date(ctMillis));
QueryManager.bindParam(ps, 1, new GregorianCalendar());
QueryManager.bindParam(ps, 1, LocalDate.now());
QueryManager.bindParam(ps, 1, LocalDateTime.now());
}
/*******************************************************************************
** Test the various getXXX methods from result sets
*******************************************************************************/
@Test
void testGetValueMethods() throws SQLException
{
Connection connection = getConnection();
QueryManager.executeUpdate(connection, "INSERT INTO t (i, dt, c) VALUES (1, now(), 'A')");
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from t");
preparedStatement.execute();
ResultSet rs = preparedStatement.getResultSet();
rs.next();
assertEquals(1, QueryManager.getInteger(rs, "i"));
assertEquals(1, QueryManager.getInteger(rs, 1));
assertEquals(1L, QueryManager.getLong(rs, "i"));
assertEquals(1L, QueryManager.getLong(rs, 1));
assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, "i"));
assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1));
assertEquals(1, QueryManager.getObject(rs, "i"));
assertEquals(1, QueryManager.getObject(rs, 1));
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "i"));
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1));
assertEquals(true, QueryManager.getBoolean(rs, "i"));
assertEquals(true, QueryManager.getBoolean(rs, 1));
assertNotNull(QueryManager.getDate(rs, "dt"));
assertNotNull(QueryManager.getDate(rs, 2));
assertNotNull(QueryManager.getCalendar(rs, "dt"));
assertNotNull(QueryManager.getCalendar(rs, 2));
assertNotNull(QueryManager.getLocalDate(rs, "dt"));
assertNotNull(QueryManager.getLocalDate(rs, 2));
assertNotNull(QueryManager.getLocalDateTime(rs, "dt"));
assertNotNull(QueryManager.getLocalDateTime(rs, 2));
assertNotNull(QueryManager.getOffsetDateTime(rs, "dt"));
assertNotNull(QueryManager.getOffsetDateTime(rs, 2));
assertNotNull(QueryManager.getTimestamp(rs, "dt"));
assertNotNull(QueryManager.getTimestamp(rs, 2));
assertEquals("A", QueryManager.getObject(rs, "c"));
assertEquals("A", QueryManager.getObject(rs, 3));
}
/*******************************************************************************
** Test the various getXXX methods from result sets, when they return null
*******************************************************************************/
@Test
void testGetValueMethodsReturningNull() throws SQLException
{
Connection connection = getConnection();
QueryManager.executeUpdate(connection, "INSERT INTO t (i, dt, c) VALUES (null, null, null)");
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from t");
preparedStatement.execute();
ResultSet rs = preparedStatement.getResultSet();
rs.next();
assertNull(QueryManager.getInteger(rs, "i"));
assertNull(QueryManager.getInteger(rs, 1));
assertNull(QueryManager.getLong(rs, "i"));
assertNull(QueryManager.getLong(rs, 1));
assertNull(QueryManager.getByteArray(rs, "i"));
assertNull(QueryManager.getByteArray(rs, 1));
assertNull(QueryManager.getObject(rs, "i"));
assertNull(QueryManager.getObject(rs, 1));
assertNull(QueryManager.getBigDecimal(rs, "i"));
assertNull(QueryManager.getBigDecimal(rs, 1));
assertNull(QueryManager.getBoolean(rs, "i"));
assertNull(QueryManager.getBoolean(rs, 1));
assertNull(QueryManager.getDate(rs, "dt"));
assertNull(QueryManager.getDate(rs, 2));
assertNull(QueryManager.getCalendar(rs, "dt"));
assertNull(QueryManager.getCalendar(rs, 2));
assertNull(QueryManager.getLocalDate(rs, "dt"));
assertNull(QueryManager.getLocalDate(rs, 2));
assertNull(QueryManager.getLocalDateTime(rs, "dt"));
assertNull(QueryManager.getLocalDateTime(rs, 2));
assertNull(QueryManager.getOffsetDateTime(rs, "dt"));
assertNull(QueryManager.getOffsetDateTime(rs, 2));
assertNull(QueryManager.getTimestamp(rs, "dt"));
assertNull(QueryManager.getTimestamp(rs, 2));
assertNull(QueryManager.getObject(rs, "c"));
assertNull(QueryManager.getObject(rs, 3));
}
/*******************************************************************************
** We had a bug where LocalDates weren't being properly bound. This test
** confirms (more?) correct behavior
*******************************************************************************/
@Test
void testLocalDate() throws SQLException
{
Connection connection = getConnection();
QueryManager.executeUpdate(connection, "INSERT INTO t (d) VALUES (?)", LocalDate.of(2013, Month.OCTOBER, 1));
PreparedStatement preparedStatement = connection.prepareStatement("SELECT d from t");
preparedStatement.execute();
ResultSet rs = preparedStatement.getResultSet();
rs.next();
Date date = QueryManager.getDate(rs, 1);
assertEquals(1, date.getDate(), "Date value");
assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value");
assertEquals(2013, date.getYear() + 1900, "Year value");
LocalDate localDate = QueryManager.getLocalDate(rs, 1);
assertEquals(1, localDate.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value");
assertEquals(2013, localDate.getYear(), "Year value");
LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1);
assertEquals(1, localDateTime.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value");
assertEquals(2013, localDateTime.getYear(), "Year value");
assertEquals(0, localDateTime.getHour(), "Hour value");
assertEquals(0, localDateTime.getMinute(), "Minute value");
OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1);
assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value");
assertEquals(2013, offsetDateTime.getYear(), "Year value");
assertEquals(0, offsetDateTime.getHour(), "Hour value");
assertEquals(0, offsetDateTime.getMinute(), "Minute value");
}
}

View File

@ -0,0 +1,73 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.model.metadata;
import java.io.IOException;
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for RDBMSBackendMetaData
*******************************************************************************/
class RDBMSBackendMetaDataTest
{
/*******************************************************************************
** Test that an instance can be serialized as expected
*******************************************************************************/
@Test
public void testSerializingToJson()
{
QInstance qInstance = TestUtils.defineInstance();
String json = new QInstanceAdapter().qInstanceToJsonIncludingBackend(qInstance);
System.out.println(JsonUtils.prettyPrint(json));
System.out.println(json);
String expectToContain = """
"backends":{"default":{"hostName":"mem","databaseName":"test_database\"""";
assertTrue(json.contains(expectToContain));
}
/*******************************************************************************
** Test that an instance can be deserialized as expected
*******************************************************************************/
@Test
public void testDeserializingFromJson() throws IOException
{
QInstanceAdapter qInstanceAdapter = new QInstanceAdapter();
QInstance qInstance = TestUtils.defineInstance();
String json = qInstanceAdapter.qInstanceToJsonIncludingBackend(qInstance);
QInstance deserialized = qInstanceAdapter.jsonToQInstanceIncludingBackends(json);
assertThat(deserialized).usingRecursiveComparison().isEqualTo(qInstance);
}
}

View File

@ -0,0 +1,48 @@
--
-- QQQ - Low-code Application Framework for Engineers.
-- Copyright (C) 2021-2022. Kingsrook, LLC
-- 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
-- contact@kingsrook.com
-- https://github.com/Kingsrook/
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
--
-- 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 Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
--
DROP TABLE IF EXISTS child_table;
CREATE TABLE child_table
(
id INT AUTO_INCREMENT primary key,
name VARCHAR(80) NOT NULL
);
INSERT INTO child_table (id, name) VALUES (1, 'Timmy');
INSERT INTO child_table (id, name) VALUES (2, 'Jimmy');
INSERT INTO child_table (id, name) VALUES (3, 'Johnny');
INSERT INTO child_table (id, name) VALUES (4, 'Gracie');
INSERT INTO child_table (id, name) VALUES (5, 'Suzie');
DROP TABLE IF EXISTS parent_table;
CREATE TABLE parent_table
(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(80) NOT NULL,
child_id INT,
foreign key (child_id) references child_table(id)
);
INSERT INTO parent_table (id, name, child_id) VALUES (1, 'Tim''s Dad', 1);
INSERT INTO parent_table (id, name, child_id) VALUES (2, 'Tim''s Mom', 1);
INSERT INTO parent_table (id, name, child_id) VALUES (3, 'Childless Man', null);
INSERT INTO parent_table (id, name, child_id) VALUES (4, 'Childless Woman', null);
INSERT INTO parent_table (id, name, child_id) VALUES (5, 'Johny''s Single Dad', 3);

View File

@ -0,0 +1,60 @@
--
-- QQQ - Low-code Application Framework for Engineers.
-- Copyright (C) 2021-2022. Kingsrook, LLC
-- 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
-- contact@kingsrook.com
-- https://github.com/Kingsrook/
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
--
-- 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 Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
--
DROP TABLE IF EXISTS person;
CREATE TABLE person
(
id INT AUTO_INCREMENT primary key ,
create_date TIMESTAMP DEFAULT now(),
modify_date TIMESTAMP DEFAULT now(),
first_name VARCHAR(80) NOT NULL,
last_name VARCHAR(80) NOT NULL,
birth_date DATE,
email VARCHAR(250) NOT NULL
);
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (1, 'Darin', 'Kelkhoff', '1980-05-31', 'darin.kelkhoff@gmail.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (2, 'James', 'Maes', '1980-05-15', 'jmaes@mmltholdings.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (3, 'Tim', 'Chamberlain', '1976-05-28', 'tchamberlain@mmltholdings.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (4, 'Tyler', 'Samples', NULL, 'tsamples@mmltholdings.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (5, 'Garret', 'Richardson', '1981-01-01', 'grichardson@mmltholdings.com');
DROP TABLE IF EXISTS carrier;
CREATE TABLE carrier
(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(80) NOT NULL,
company_code VARCHAR(80) NOT NULL,
service_level VARCHAR(80) NOT NULL
);
INSERT INTO carrier (id, name, company_code, service_level) VALUES (1, 'UPS Ground', 'UPS', 'G');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (2, 'UPS 2Day', 'UPS', '2');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (3, 'UPS International', 'UPS', 'I');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (4, 'Fedex Ground', 'FEDEX', 'G');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (5, 'Fedex Next Day', 'UPS', '1');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (6, 'Will Call', 'WILL_CALL', 'W');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (7, 'USPS Priority', 'USPS', '1');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (8, 'USPS Super Slow', 'USPS', '4');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (9, 'USPS Super Fast', 'USPS', '0');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (10, 'DHL International', 'DHL', 'I');
INSERT INTO carrier (id, name, company_code, service_level) VALUES (11, 'GSO', 'GSO', 'G');