Add "Describe" command, with support for describing both (a) specific table(s) and...
[squelch.git] / src / test / java / net / jaekl / squelch / stmt / DescribeTest.java
diff --git a/src/test/java/net/jaekl/squelch/stmt/DescribeTest.java b/src/test/java/net/jaekl/squelch/stmt/DescribeTest.java
new file mode 100644 (file)
index 0000000..f211105
--- /dev/null
@@ -0,0 +1,270 @@
+package net.jaekl.squelch.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+
+import net.jaekl.squelch.db.DbDriverMock;
+import net.jaekl.squelch.sql.ConnectionMock;
+import net.jaekl.squelch.sql.DatabaseMetaDataMock;
+import net.jaekl.squelch.sql.ResultSetMock;
+import net.jaekl.squelch.sql.Row;
+
+import org.junit.Test;
+
+public class DescribeTest {
+
+       @Test
+       public void testHandles() {
+               final String[] AFFIRMATIVE = {
+                               "\\d", "\\d ", " \\d", " \\d ", 
+                               "describe", "describe ", " describe", " describe ",
+                               "DESCRIBE", "DESCRIBE ", " DESCRIBE", " DESCRIBE ",
+                               "descRIbe", "DEscribE ", " DEScribe", " DEscRIbE ",
+                               "\\d tablename", "\\d tablename ", " \\d tablename", " \\d tableName ",
+                               "describe tablename", "DESCRIBE tablename ", " DesCrIbE tablename", " DESCribE fred "
+               };
+               final String[] NEGATIVE = {
+                               "\\d\\q", "", null, "    ", "select * from foo", "  select * from foo ",
+                               "DESCRIBEQ", "describeFOO", " describeJackAndDianne "
+               };
+
+               Describe describe = new Describe();
+               for (String s : AFFIRMATIVE) {
+                       assertTrue("handles " + s, describe.handles(s));
+               }
+               for (String s : NEGATIVE) {
+                       assertFalse("does not handle " + s, describe.handles(s));
+               }
+       }
+
+       @Test
+       public void testDescribeAll_noTables() throws IOException, SQLException 
+       {
+               DatabaseMetaDataMock dbmdm = new DatabaseMetaDataMock();
+               Describe describe = new Describe();
+               
+               try (
+                               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                               PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
+                       )
+               {
+                       describe.describeAll(pw, dbmdm);
+                       pw.close();
+                       baos.close();
+                       String actual = baos.toString();
+                       final String EXPECTED = "???\n";
+                       assertEquals(EXPECTED, actual);
+               }
+       }
+
+       @Test
+       public void testDescribeTable_noColumns()
+               throws IOException, SQLException
+       {
+               DatabaseMetaDataMock dbmdm = new DatabaseMetaDataMock();
+               Describe describe = new Describe();
+               
+               try (
+                               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                               PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
+                       )
+               {
+                       describe.describeTable(pw, dbmdm, "%");
+                       pw.close();
+                       baos.close();
+                       String actual = baos.toString();
+                       final String EXPECTED = "??? %\n";
+                       assertEquals(EXPECTED, actual);
+               }
+       }
+       
+       private DatabaseMetaDataMock construct_runs_dbmdm()
+       {
+               ResultSetMock rsmCols = new ResultSetMock();
+               ResultSetMock rsmTables = new ResultSetMock();
+
+               DatabaseMetaDataMock dbmdm = new DatabaseMetaDataMock();
+               Row[] rows = {
+                               new Row(new Object[]{
+                                               null,           // 1) table_cat
+                                               "public",       // 2) table schema
+                                               "runs",         // 3) table name
+                                               "runid",        // 4) column name
+                                               Integer.valueOf(4),             // 5) data type
+                                               "int4",         // 6) type name
+                                               Integer.valueOf(10),    // 7) column size
+                                               null,           // 8) buffer length
+                                               null,           // 9) decimal digits
+                                               Integer.valueOf(10),    // 10) num_prec_radix
+                                               Integer.valueOf(DatabaseMetaData.columnNoNulls),        // 11) nullable
+                                               null,           // 12) remarks
+                                               null,           // 13) default value for the column
+                                               Integer.valueOf(0),     // 14) sql_data_type
+                                               Integer.valueOf(0),     // 15) sql_datetime_sub
+                                               null,           // 16) char_octet_length
+                                               Integer.valueOf(1),     // 17) ordinal_position
+                                               "NO",           // 18) is_nullable
+                                               null,           // 19) scope_catalog
+                                               null,           // 20) scope_schema
+                                               null,           // 21) scope_table
+                                               null,           // 22) source_data_type
+                                               "NO",           // 23) is_autoincrement
+                                               "NO"            // 24) is_generated_column
+                               }),
+                               new Row(new Object[]{
+                                               null,           // 1) table_cat
+                                               "public",       // 2) table schema
+                                               "runs",         // 3) table name
+                                               "projname",     // 4) column name
+                                               Integer.valueOf(12),    // 5) data type
+                                               "varchar",      // 6) type name
+                                               Integer.valueOf(80),    // 7) column size
+                                               null,           // 8) buffer length
+                                               null,           // 9) decimal digits
+                                               Integer.valueOf(10),    // 10) num_prec_radix
+                                               Integer.valueOf(DatabaseMetaData.columnNoNulls),        // 11) nullable
+                                               null,           // 12) remarks
+                                               null,           // 13) default value for the column
+                                               Integer.valueOf(0),     // 14) sql_data_type
+                                               Integer.valueOf(0),     // 15) sql_datetime_sub
+                                               null,           // 16) char_octet_length
+                                               Integer.valueOf(2),     // 17) ordinal_position
+                                               "NO",           // 18) is_nullable
+                                               null,           // 19) scope_catalog
+                                               null,           // 20) scope_schema
+                                               null,           // 21) scope_table
+                                               null,           // 22) source_data_type
+                                               "NO",           // 23) is_autoincrement
+                                               "NO"            // 24) is_generated_column
+                               }),
+                               new Row(new Object[]{
+                                               null,           // 1) table_cat
+                                               "public",       // 2) table schema
+                                               "runs",         // 3) table name
+                                               "version",      // 4) column name
+                                               Integer.valueOf(12),    // 5) data type
+                                               "varchar",      // 6) type name
+                                               Integer.valueOf(10),    // 7) column size
+                                               null,           // 8) buffer length
+                                               null,           // 9) decimal digits
+                                               Integer.valueOf(10),    // 10) num_prec_radix
+                                               Integer.valueOf(DatabaseMetaData.columnNullable),       // 11) nullable
+                                               null,           // 12) remarks
+                                               null,           // 13) default value for the column
+                                               Integer.valueOf(0),     // 14) sql_data_type
+                                               Integer.valueOf(0),     // 15) sql_datetime_sub
+                                               null,           // 16) char_octet_length
+                                               Integer.valueOf(1),     // 17) ordinal_position
+                                               "YES",          // 18) is_nullable
+                                               null,           // 19) scope_catalog
+                                               null,           // 20) scope_schema
+                                               null,           // 21) scope_table
+                                               null,           // 22) source_data_type
+                                               "NO",           // 23) is_autoincrement
+                                               "NO"            // 24) is_generated_column
+                               })
+               };
+               rsmCols.mock_addRows(rows);
+               
+               rows = new Row[] {
+                       new Row(new Object[]{
+                               null,    // TABLE_CAT String => table catalog (may be null)
+                               null,    // TABLE_SCHEM String => table schema (may be null)
+                               "runs",  // TABLE_NAME String => table name
+                               "TABLE", // TABLE_TYPE String => table type. Typical types are "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
+                               "",     // REMARKS String => explanatory comment on the table
+                               null,   // TYPE_CAT String => the types catalog (may be null)
+                               null,   // TYPE_SCHEM String => the types schema (may be null)
+                               null,   // TYPE_NAME String => type name (may be null)
+                               null,   // SELF_REFERENCING_COL_NAME String => name of the designated "identifier" column of a typed table (may be null)
+                               null    // REF_GENERATION String => specifies how values in SELF_REFERENCING_COL_NAME are created. Values are "SYSTEM", "USER", "DERIVED". (may be null) 
+                       })      
+               };
+               rsmTables.mock_addRows(rows);
+               
+               dbmdm.mock_setColRS(null, null, "runs", null, rsmCols);
+               dbmdm.mock_setTableRS(null, null, "runs", null, rsmTables);
+               
+               return dbmdm;
+       }
+       
+       private String construct_runs_expected() {
+               return   "TABLE runs\n"
+                  + "+--------+-----------+---------+\n"
+                  + "| Column |   Type    |Modifiers|\n"
+                  + "+--------+-----------+---------+\n"
+                  + "|runid   |int4(10)   |NOT NULL |\n"
+                  + "|projname|varchar(80)|NOT NULL |\n"
+                  + "|version |varchar(10)|NULL     |\n"
+                  + "+--------+-----------+---------+\n"
+                  + "3 row(s) returned.\n";
+       }
+
+       @Test
+       public void testDescribeTable_threeColumns()
+               throws IOException, SQLException
+       {
+               DatabaseMetaDataMock dbmdm = construct_runs_dbmdm();
+               Describe describe = new Describe();
+               
+               try (
+                               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                               PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
+                       )
+               {
+                       describe.describeTable(pw, dbmdm, "runs");
+                       pw.close();
+                       baos.close();
+                       String actual = baos.toString();
+                       String expected = construct_runs_expected();
+                       
+                       assertEquals(expected, actual);
+               }
+       }
+       
+       @Test
+       public void testExec() throws IOException, SQLException
+       {
+               final String[] SUCCESSFUL = { 
+                               "\\d runs", " \\d runs", "\\d runs ", " \\d runs ",
+                               "DESCRIBE runs", " DescRIBe runs", "describe runs ", " describe runs "
+               };
+               String expected = construct_runs_expected();
+               for (String stmt : SUCCESSFUL) {
+                       String actual = doExec(stmt);
+                       assertEquals("doExec():  " + stmt, expected, actual);
+               }
+       }
+       
+       public String doExec(String stmt) throws IOException, SQLException
+       {
+               DatabaseMetaDataMock dbmdm = construct_runs_dbmdm();
+               DbDriverMock driver = new DbDriverMock();
+               ConnectionMock cm = new ConnectionMock();
+               cm.mock_setDatabaseMetaData(dbmdm);
+               
+               Describe describe = new Describe();
+
+               try (
+                               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                               PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
+                       )
+               {
+                       describe.exec(driver, cm, pw, stmt);
+                       pw.close();
+                       baos.close();
+                       String actual = baos.toString();
+                       
+                       return actual;
+               }
+       }
+}