mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Adding comments and more tests
This commit is contained in:
@ -5,7 +5,6 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.Serializable;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -41,8 +40,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
import picocli.CommandLine.Model.CommandSpec;
|
import picocli.CommandLine.Model.CommandSpec;
|
||||||
import picocli.CommandLine.Model.OptionSpec;
|
import picocli.CommandLine.Model.OptionSpec;
|
||||||
@ -52,14 +49,15 @@ import picocli.CommandLine.UnmatchedArgumentException;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** QQQ PicoCLI implementation. Given a QInstance, produces an entire CLI
|
||||||
|
** for working with all tables in that instance.
|
||||||
|
**
|
||||||
** Note: Please do not use System.out or .err here -- rather, use the CommandLine
|
** Note: Please do not use System.out or .err here -- rather, use the CommandLine
|
||||||
** object's out & err members - so the unit test can see the output!
|
** object's out & err members - so the unit test can see the output!
|
||||||
*
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QPicoCliImplementation
|
public class QPicoCliImplementation
|
||||||
{
|
{
|
||||||
private static final Logger LOG = LogManager.getLogger(QPicoCliImplementation.class);
|
|
||||||
|
|
||||||
public static final int DEFAULT_LIMIT = 20;
|
public static final int DEFAULT_LIMIT = 20;
|
||||||
|
|
||||||
private static QInstance qInstance;
|
private static QInstance qInstance;
|
||||||
@ -274,7 +272,6 @@ public class QPicoCliImplementation
|
|||||||
{
|
{
|
||||||
CommandSpec deleteCommand = CommandSpec.create();
|
CommandSpec deleteCommand = CommandSpec.create();
|
||||||
|
|
||||||
QFieldMetaData primaryKeyField = table.getField(table.getPrimaryKeyField());
|
|
||||||
deleteCommand.addOption(OptionSpec.builder("--primaryKey")
|
deleteCommand.addOption(OptionSpec.builder("--primaryKey")
|
||||||
.type(String.class) // todo - mmm, better as picocli's "compound" thing, w/ the actual pkey's type?
|
.type(String.class) // todo - mmm, better as picocli's "compound" thing, w/ the actual pkey's type?
|
||||||
.build());
|
.build());
|
||||||
@ -287,19 +284,19 @@ public class QPicoCliImplementation
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("checkstyle:Indentation")
|
|
||||||
private Class<?> getClassForField(QFieldMetaData field)
|
private Class<?> getClassForField(QFieldMetaData field)
|
||||||
{
|
{
|
||||||
|
// @formatter:off // IJ can't do new-style switch correctly yet...
|
||||||
return switch(field.getType())
|
return switch(field.getType())
|
||||||
{
|
{
|
||||||
case STRING, TEXT, HTML, PASSWORD -> String.class;
|
case STRING, TEXT, HTML, PASSWORD -> String.class;
|
||||||
case INTEGER -> Integer.class;
|
case INTEGER -> Integer.class;
|
||||||
case DECIMAL -> BigDecimal.class;
|
case DECIMAL -> BigDecimal.class;
|
||||||
case DATE -> LocalDate.class;
|
case DATE -> LocalDate.class;
|
||||||
// case TIME -> LocalTime.class;
|
// case TIME -> LocalTime.class;
|
||||||
case DATE_TIME -> LocalDateTime.class;
|
case DATE_TIME -> LocalDateTime.class;
|
||||||
default -> throw new IllegalStateException("Unsupported field type: " + field.getType());
|
};
|
||||||
};
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -436,7 +433,7 @@ public class QPicoCliImplementation
|
|||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// get the records that the user specified //
|
// get the records that the user specified //
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
List<QRecord> recordList = null;
|
List<QRecord> recordList;
|
||||||
if(subParseResult.hasMatchedOption("--jsonBody"))
|
if(subParseResult.hasMatchedOption("--jsonBody"))
|
||||||
{
|
{
|
||||||
String json = subParseResult.matchedOptionValue("--jsonBody", "");
|
String json = subParseResult.matchedOptionValue("--jsonBody", "");
|
||||||
@ -502,6 +499,7 @@ public class QPicoCliImplementation
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -509,13 +507,10 @@ public class QPicoCliImplementation
|
|||||||
{
|
{
|
||||||
DeleteRequest deleteRequest = new DeleteRequest(qInstance);
|
DeleteRequest deleteRequest = new DeleteRequest(qInstance);
|
||||||
deleteRequest.setTableName(tableName);
|
deleteRequest.setTableName(tableName);
|
||||||
QTableMetaData table = qInstance.getTable(tableName);
|
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// get the pKeys that the user specified //
|
// get the pKeys that the user specified //
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
List<Serializable> primaryKeyList = null;
|
|
||||||
|
|
||||||
String primaryKeyOption = subParseResult.matchedOptionValue("--primaryKey", "");
|
String primaryKeyOption = subParseResult.matchedOptionValue("--primaryKey", "");
|
||||||
String[] primaryKeyValues = primaryKeyOption.split(",");
|
String[] primaryKeyValues = primaryKeyOption.split(",");
|
||||||
deleteRequest.setPrimaryKeys(Arrays.asList(primaryKeyValues));
|
deleteRequest.setPrimaryKeys(Arrays.asList(primaryKeyValues));
|
||||||
|
@ -2,12 +2,17 @@ package com.kingsrook.qqq.frontend.picocli;
|
|||||||
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.UUID;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -17,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Unit test for the QPicoCliImplementation.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
class QPicoCliImplementationTest
|
class QPicoCliImplementationTest
|
||||||
@ -27,6 +33,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Fully rebuild the test-database before each test runs, for completely known state.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@ -38,6 +45,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test that w/ no arguments you just get usage.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -50,6 +58,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test that --help gives you usage.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -63,6 +72,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test the --verion argument
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -75,6 +85,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Test that an unrecognized opttion gives an error
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -89,6 +100,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test the top-level --meta-data option
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -108,6 +120,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test giving a table-name, gives usage for that table
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -121,6 +134,22 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test unknown command under table, prints error and usage.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_tableUnknownCommand()
|
||||||
|
{
|
||||||
|
String badCommand = "qwuijibo";
|
||||||
|
TestOutput testOutput = testCli("person", badCommand);
|
||||||
|
assertTrue(testOutput.getError().contains("Unmatched argument at index 1: '" + badCommand + "'"));
|
||||||
|
assertTrue(testOutput.getError().contains("Usage: " + CLI_NAME + " person [COMMAND]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** test requesting table meta-data
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -144,6 +173,7 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test running a query on a table
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -161,6 +191,134 @@ class QPicoCliImplementationTest
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** test running an insert w/o specifying any fields, prints usage
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_tableInsertNoFieldsPrintsUsage()
|
||||||
|
{
|
||||||
|
TestOutput testOutput = testCli("person", "insert");
|
||||||
|
assertTrue(testOutput.getOutput().contains("Usage: " + CLI_NAME + " person insert"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** test running an insert w/ fields as arguments
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_tableInsertFieldArguments()
|
||||||
|
{
|
||||||
|
TestOutput testOutput = testCli("person", "insert",
|
||||||
|
"--field-firstName=Lucy",
|
||||||
|
"--field-lastName=Lu");
|
||||||
|
JSONObject insertResult = JsonUtils.toJSONObject(testOutput.getOutput());
|
||||||
|
assertNotNull(insertResult);
|
||||||
|
assertEquals(1, insertResult.getJSONArray("records").length());
|
||||||
|
assertEquals(6, insertResult.getJSONArray("records").getJSONObject(0).getInt("primaryKey"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** test running an insert w/ a mapping and json as an argument
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_tableInsertJsonObjectArgumentWithMapping()
|
||||||
|
{
|
||||||
|
String mapping = """
|
||||||
|
--mapping={"firstName":"first","lastName":"ln"}
|
||||||
|
""";
|
||||||
|
|
||||||
|
String jsonBody = """
|
||||||
|
--jsonBody={"first":"Chester","ln":"Cheese"}
|
||||||
|
""";
|
||||||
|
|
||||||
|
TestOutput testOutput = testCli("person", "insert", mapping, jsonBody);
|
||||||
|
JSONObject insertResult = JsonUtils.toJSONObject(testOutput.getOutput());
|
||||||
|
assertNotNull(insertResult);
|
||||||
|
assertEquals(1, insertResult.getJSONArray("records").length());
|
||||||
|
assertEquals(6, insertResult.getJSONArray("records").getJSONObject(0).getInt("primaryKey"));
|
||||||
|
assertEquals("Chester", insertResult.getJSONArray("records").getJSONObject(0).getJSONObject("values").getString("firstName"));
|
||||||
|
assertEquals("Cheese", insertResult.getJSONArray("records").getJSONObject(0).getJSONObject("values").getString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** test running an insert w/ a mapping and json as a multi-record file
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_tableInsertJsonArrayFileWithMapping() throws IOException
|
||||||
|
{
|
||||||
|
String mapping = """
|
||||||
|
--mapping={"firstName":"first","lastName":"ln"}
|
||||||
|
""";
|
||||||
|
|
||||||
|
String jsonContents = """
|
||||||
|
[{"first":"Charlie","ln":"Bear"},{"first":"Coco","ln":"Bean"}]
|
||||||
|
""";
|
||||||
|
|
||||||
|
File file = new File("/tmp/" + UUID.randomUUID() + ".json");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileUtils.writeStringToFile(file, jsonContents);
|
||||||
|
|
||||||
|
TestOutput testOutput = testCli("person", "insert", mapping, "--jsonFile=" + file.getAbsolutePath());
|
||||||
|
JSONObject insertResult = JsonUtils.toJSONObject(testOutput.getOutput());
|
||||||
|
assertNotNull(insertResult);
|
||||||
|
JSONArray records = insertResult.getJSONArray("records");
|
||||||
|
assertEquals(2, records.length());
|
||||||
|
assertEquals(6, records.getJSONObject(0).getInt("primaryKey"));
|
||||||
|
assertEquals(7, records.getJSONObject(1).getInt("primaryKey"));
|
||||||
|
assertEquals("Charlie", records.getJSONObject(0).getJSONObject("values").getString("firstName"));
|
||||||
|
assertEquals("Bear", records.getJSONObject(0).getJSONObject("values").getString("lastName"));
|
||||||
|
assertEquals("Coco", records.getJSONObject(1).getJSONObject("values").getString("firstName"));
|
||||||
|
assertEquals("Bean", records.getJSONObject(1).getJSONObject("values").getString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** test running an insert w/ an index-based mapping and csv file
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_tableInsertCsvFileWithIndexMapping() throws IOException
|
||||||
|
{
|
||||||
|
String mapping = """
|
||||||
|
--mapping={"firstName":1,"lastName":3}
|
||||||
|
""";
|
||||||
|
|
||||||
|
String csvContents = """
|
||||||
|
"Louis","P","Willikers",1024,
|
||||||
|
"Nestle","G","Crunch",1701,
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
File file = new File("/tmp/" + UUID.randomUUID() + ".csv");
|
||||||
|
file.deleteOnExit();
|
||||||
|
FileUtils.writeStringToFile(file, csvContents);
|
||||||
|
|
||||||
|
TestOutput testOutput = testCli("person", "insert", mapping, "--csvFile=" + file.getAbsolutePath());
|
||||||
|
JSONObject insertResult = JsonUtils.toJSONObject(testOutput.getOutput());
|
||||||
|
assertNotNull(insertResult);
|
||||||
|
JSONArray records = insertResult.getJSONArray("records");
|
||||||
|
assertEquals(2, records.length());
|
||||||
|
assertEquals(6, records.getJSONObject(0).getInt("primaryKey"));
|
||||||
|
assertEquals(7, records.getJSONObject(1).getInt("primaryKey"));
|
||||||
|
assertEquals("Louis", records.getJSONObject(0).getJSONObject("values").getString("firstName"));
|
||||||
|
assertEquals("Willikers", records.getJSONObject(0).getJSONObject("values").getString("lastName"));
|
||||||
|
assertEquals("Nestle", records.getJSONObject(1).getJSONObject("values").getString("firstName"));
|
||||||
|
assertEquals("Crunch", records.getJSONObject(1).getJSONObject("values").getString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** test running a delete against a table
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
@ -330,4 +488,4 @@ class QPicoCliImplementationTest
|
|||||||
this.errorLines = errorLines;
|
this.errorLines = errorLines;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,14 @@ import static junit.framework.Assert.assertNotNull;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Utility methods for unit tests.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class TestUtils
|
public class TestUtils
|
||||||
{
|
{
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Prime a test database (e.g., h2, in-memory)
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -43,6 +45,7 @@ public class TestUtils
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Run an SQL Query in the test database
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
public static void runTestSql(String sql, QueryManager.ResultSetProcessor resultSetProcessor) throws Exception
|
||||||
@ -55,6 +58,7 @@ public class TestUtils
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Define the q-instance for testing (h2 rdbms and 'person' table)
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static QInstance defineInstance()
|
public static QInstance defineInstance()
|
||||||
@ -68,6 +72,7 @@ public class TestUtils
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Define the h2 rdbms backend
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static QBackendMetaData defineBackend()
|
public static QBackendMetaData defineBackend()
|
||||||
@ -85,6 +90,7 @@ public class TestUtils
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Define the person table
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static QTableMetaData defineTablePerson()
|
public static QTableMetaData defineTablePerson()
|
||||||
|
Reference in New Issue
Block a user