Add "Describe" command, with support for describing both (a) specific table(s) and...
[squelch.git] / src / test / java / net / jaekl / squelch / stmt / DescribeTest.java
1 package net.jaekl.squelch.stmt;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertFalse;
5 import static org.junit.Assert.assertTrue;
6
7 import java.io.ByteArrayOutputStream;
8 import java.io.IOException;
9 import java.io.OutputStreamWriter;
10 import java.io.PrintWriter;
11 import java.nio.charset.StandardCharsets;
12 import java.sql.DatabaseMetaData;
13 import java.sql.SQLException;
14
15 import net.jaekl.squelch.db.DbDriverMock;
16 import net.jaekl.squelch.sql.ConnectionMock;
17 import net.jaekl.squelch.sql.DatabaseMetaDataMock;
18 import net.jaekl.squelch.sql.ResultSetMock;
19 import net.jaekl.squelch.sql.Row;
20
21 import org.junit.Test;
22
23 public class DescribeTest {
24
25         @Test
26         public void testHandles() {
27                 final String[] AFFIRMATIVE = {
28                                 "\\d", "\\d ", " \\d", " \\d ", 
29                                 "describe", "describe ", " describe", " describe ",
30                                 "DESCRIBE", "DESCRIBE ", " DESCRIBE", " DESCRIBE ",
31                                 "descRIbe", "DEscribE ", " DEScribe", " DEscRIbE ",
32                                 "\\d tablename", "\\d tablename ", " \\d tablename", " \\d tableName ",
33                                 "describe tablename", "DESCRIBE tablename ", " DesCrIbE tablename", " DESCribE fred "
34                 };
35                 final String[] NEGATIVE = {
36                                 "\\d\\q", "", null, "    ", "select * from foo", "  select * from foo ",
37                                 "DESCRIBEQ", "describeFOO", " describeJackAndDianne "
38                 };
39
40                 Describe describe = new Describe();
41                 for (String s : AFFIRMATIVE) {
42                         assertTrue("handles " + s, describe.handles(s));
43                 }
44                 for (String s : NEGATIVE) {
45                         assertFalse("does not handle " + s, describe.handles(s));
46                 }
47         }
48
49         @Test
50         public void testDescribeAll_noTables() throws IOException, SQLException 
51         {
52                 DatabaseMetaDataMock dbmdm = new DatabaseMetaDataMock();
53                 Describe describe = new Describe();
54                 
55                 try (
56                                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
57                                 PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
58                         )
59                 {
60                         describe.describeAll(pw, dbmdm);
61                         pw.close();
62                         baos.close();
63                         String actual = baos.toString();
64                         final String EXPECTED = "???\n";
65                         assertEquals(EXPECTED, actual);
66                 }
67         }
68
69         @Test
70         public void testDescribeTable_noColumns()
71                 throws IOException, SQLException
72         {
73                 DatabaseMetaDataMock dbmdm = new DatabaseMetaDataMock();
74                 Describe describe = new Describe();
75                 
76                 try (
77                                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
78                                 PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
79                         )
80                 {
81                         describe.describeTable(pw, dbmdm, "%");
82                         pw.close();
83                         baos.close();
84                         String actual = baos.toString();
85                         final String EXPECTED = "??? %\n";
86                         assertEquals(EXPECTED, actual);
87                 }
88         }
89         
90         private DatabaseMetaDataMock construct_runs_dbmdm()
91         {
92                 ResultSetMock rsmCols = new ResultSetMock();
93                 ResultSetMock rsmTables = new ResultSetMock();
94
95                 DatabaseMetaDataMock dbmdm = new DatabaseMetaDataMock();
96                 Row[] rows = {
97                                 new Row(new Object[]{
98                                                 null,           // 1) table_cat
99                                                 "public",       // 2) table schema
100                                                 "runs",         // 3) table name
101                                                 "runid",        // 4) column name
102                                                 Integer.valueOf(4),             // 5) data type
103                                                 "int4",         // 6) type name
104                                                 Integer.valueOf(10),    // 7) column size
105                                                 null,           // 8) buffer length
106                                                 null,           // 9) decimal digits
107                                                 Integer.valueOf(10),    // 10) num_prec_radix
108                                                 Integer.valueOf(DatabaseMetaData.columnNoNulls),        // 11) nullable
109                                                 null,           // 12) remarks
110                                                 null,           // 13) default value for the column
111                                                 Integer.valueOf(0),     // 14) sql_data_type
112                                                 Integer.valueOf(0),     // 15) sql_datetime_sub
113                                                 null,           // 16) char_octet_length
114                                                 Integer.valueOf(1),     // 17) ordinal_position
115                                                 "NO",           // 18) is_nullable
116                                                 null,           // 19) scope_catalog
117                                                 null,           // 20) scope_schema
118                                                 null,           // 21) scope_table
119                                                 null,           // 22) source_data_type
120                                                 "NO",           // 23) is_autoincrement
121                                                 "NO"            // 24) is_generated_column
122                                 }),
123                                 new Row(new Object[]{
124                                                 null,           // 1) table_cat
125                                                 "public",       // 2) table schema
126                                                 "runs",         // 3) table name
127                                                 "projname",     // 4) column name
128                                                 Integer.valueOf(12),    // 5) data type
129                                                 "varchar",      // 6) type name
130                                                 Integer.valueOf(80),    // 7) column size
131                                                 null,           // 8) buffer length
132                                                 null,           // 9) decimal digits
133                                                 Integer.valueOf(10),    // 10) num_prec_radix
134                                                 Integer.valueOf(DatabaseMetaData.columnNoNulls),        // 11) nullable
135                                                 null,           // 12) remarks
136                                                 null,           // 13) default value for the column
137                                                 Integer.valueOf(0),     // 14) sql_data_type
138                                                 Integer.valueOf(0),     // 15) sql_datetime_sub
139                                                 null,           // 16) char_octet_length
140                                                 Integer.valueOf(2),     // 17) ordinal_position
141                                                 "NO",           // 18) is_nullable
142                                                 null,           // 19) scope_catalog
143                                                 null,           // 20) scope_schema
144                                                 null,           // 21) scope_table
145                                                 null,           // 22) source_data_type
146                                                 "NO",           // 23) is_autoincrement
147                                                 "NO"            // 24) is_generated_column
148                                 }),
149                                 new Row(new Object[]{
150                                                 null,           // 1) table_cat
151                                                 "public",       // 2) table schema
152                                                 "runs",         // 3) table name
153                                                 "version",      // 4) column name
154                                                 Integer.valueOf(12),    // 5) data type
155                                                 "varchar",      // 6) type name
156                                                 Integer.valueOf(10),    // 7) column size
157                                                 null,           // 8) buffer length
158                                                 null,           // 9) decimal digits
159                                                 Integer.valueOf(10),    // 10) num_prec_radix
160                                                 Integer.valueOf(DatabaseMetaData.columnNullable),       // 11) nullable
161                                                 null,           // 12) remarks
162                                                 null,           // 13) default value for the column
163                                                 Integer.valueOf(0),     // 14) sql_data_type
164                                                 Integer.valueOf(0),     // 15) sql_datetime_sub
165                                                 null,           // 16) char_octet_length
166                                                 Integer.valueOf(1),     // 17) ordinal_position
167                                                 "YES",          // 18) is_nullable
168                                                 null,           // 19) scope_catalog
169                                                 null,           // 20) scope_schema
170                                                 null,           // 21) scope_table
171                                                 null,           // 22) source_data_type
172                                                 "NO",           // 23) is_autoincrement
173                                                 "NO"            // 24) is_generated_column
174                                 })
175                 };
176                 rsmCols.mock_addRows(rows);
177                 
178                 rows = new Row[] {
179                         new Row(new Object[]{
180                                 null,    // TABLE_CAT String => table catalog (may be null)
181                                 null,    // TABLE_SCHEM String => table schema (may be null)
182                                 "runs",  // TABLE_NAME String => table name
183                                 "TABLE", // TABLE_TYPE String => table type. Typical types are "TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM".
184                                 "",     // REMARKS String => explanatory comment on the table
185                                 null,   // TYPE_CAT String => the types catalog (may be null)
186                                 null,   // TYPE_SCHEM String => the types schema (may be null)
187                                 null,   // TYPE_NAME String => type name (may be null)
188                                 null,   // SELF_REFERENCING_COL_NAME String => name of the designated "identifier" column of a typed table (may be null)
189                                 null    // REF_GENERATION String => specifies how values in SELF_REFERENCING_COL_NAME are created. Values are "SYSTEM", "USER", "DERIVED". (may be null) 
190                         })      
191                 };
192                 rsmTables.mock_addRows(rows);
193                 
194                 dbmdm.mock_setColRS(null, null, "runs", null, rsmCols);
195                 dbmdm.mock_setTableRS(null, null, "runs", null, rsmTables);
196                 
197                 return dbmdm;
198         }
199         
200         private String construct_runs_expected() {
201                 return   "TABLE runs\n"
202                    + "+--------+-----------+---------+\n"
203                    + "| Column |   Type    |Modifiers|\n"
204                    + "+--------+-----------+---------+\n"
205                    + "|runid   |int4(10)   |NOT NULL |\n"
206                    + "|projname|varchar(80)|NOT NULL |\n"
207                    + "|version |varchar(10)|NULL     |\n"
208                    + "+--------+-----------+---------+\n"
209                    + "3 row(s) returned.\n";
210         }
211
212         @Test
213         public void testDescribeTable_threeColumns()
214                 throws IOException, SQLException
215         {
216                 DatabaseMetaDataMock dbmdm = construct_runs_dbmdm();
217                 Describe describe = new Describe();
218                 
219                 try (
220                                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
221                                 PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
222                         )
223                 {
224                         describe.describeTable(pw, dbmdm, "runs");
225                         pw.close();
226                         baos.close();
227                         String actual = baos.toString();
228                         String expected = construct_runs_expected();
229                         
230                         assertEquals(expected, actual);
231                 }
232         }
233         
234         @Test
235         public void testExec() throws IOException, SQLException
236         {
237                 final String[] SUCCESSFUL = { 
238                                 "\\d runs", " \\d runs", "\\d runs ", " \\d runs ",
239                                 "DESCRIBE runs", " DescRIBe runs", "describe runs ", " describe runs "
240                 };
241                 String expected = construct_runs_expected();
242                 for (String stmt : SUCCESSFUL) {
243                         String actual = doExec(stmt);
244                         assertEquals("doExec():  " + stmt, expected, actual);
245                 }
246         }
247         
248         public String doExec(String stmt) throws IOException, SQLException
249         {
250                 DatabaseMetaDataMock dbmdm = construct_runs_dbmdm();
251                 DbDriverMock driver = new DbDriverMock();
252                 ConnectionMock cm = new ConnectionMock();
253                 cm.mock_setDatabaseMetaData(dbmdm);
254                 
255                 Describe describe = new Describe();
256
257                 try (
258                                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
259                                 PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
260                         )
261                 {
262                         describe.exec(driver, cm, pw, stmt);
263                         pw.close();
264                         baos.close();
265                         String actual = baos.toString();
266                         
267                         return actual;
268                 }
269         }
270 }