1   package com.workingdogs.village;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.sql.Connection;
23  import java.sql.DriverManager;
24  import java.sql.PreparedStatement;
25  import java.sql.SQLException;
26  
27  import junit.framework.TestCase;
28  
29  /***
30   * This class is used for testing the functionality of this product. While
31   * creating this code, I have closed many potential bugs, but I'm sure that
32   * others still exist. Thus, if you find a bug in Village, please add to this
33   * test suite so that the bug will be sure to be fixed in future versions.
34   *
35   * <p>
36   * In order to do the testing, you will need to be able to connect via JDBC to
37   * your database. Since I use <a href="http://www.mysql.com/">MySQL</a>, this
38   * testing suite is best for that database. I also use the mm MySQL drivers
39   * <a href="http://mmmysql.sourceforge.net/">mm MySQL drivers</a> because it
40   * is the best driver that I have found for MySQL.
41   * </p>
42   *
43   * <P>
44   * Note that Village should work with <strong>any</strong> JDBC compliant driver.
45   * </p>
46   *
47   * <p>
48   * Here is the schema that this test expects (you should be able to copy and
49   * paste it into your MySQL database that you want to use):
50   * <pre>
51   *  CREATE TABLE test
52   *  (
53   *  a TINYINT null,
54   *  b SMALLINT null,
55   *  c MEDIUMINT null,
56   *  d INT null,
57   *  e INTEGER null,
58   *  f BIGINT null,
59   *  g REAL null,
60   *  h DOUBLE null,
61   *  i FLOAT null,
62   *  j DECIMAL(8,1) null,
63   *  k NUMERIC(8,1) null,
64   *  l CHAR(255) null,
65   *  m VARCHAR(255) null,
66   *  n DATE null,
67   *  o TIME null,
68   *  p TIMESTAMP null,
69   *  q DATETIME null,
70   *  r TINYBLOB null,
71   *  s BLOB null,
72   *  t MEDIUMBLOB null,
73   *  u LONGBLOB null,
74   *  v TINYTEXT null,
75   *  w TEXT null,
76   *  x MEDIUMTEXT null
77   *  );
78   *  </pre>
79   * </p>
80   *
81   * <p>
82   * Note that presently this class is hardcoded to use a MySQL database named
83   * "village" with a username of "village" and a password of "village".  It is
84   * a modified version of Jon's original Unit test that apparently pre-dated
85   * JUnit!
86   * </p>
87   *
88   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
89   * @author <a href="mailto:seade@backstagetech.com.au">Scott Eade</a>
90   * @version $Revision: 565 $
91   */
92  public class TestMySQL extends TestCase
93  {
94      /*** The database connection */
95      static Connection conn;
96  
97      /*** This is the name of the database. Created with mysqladmin create */
98      private static String DB_NAME = "village";
99  
100     /*** This is the name of the table in the DB_NAME */
101     private static String DB_TABLE = "test";
102 
103     /*** This is the name of the machine that is hosting the MySQL server */
104     private static String DB_HOST = "localhost";
105 
106     /***
107      * This is the user to log into the database as. For this test, the user
108      * must have insert/update/delete access to the database.
109      */
110     private static String DB_USER = "village";
111 
112     /*** the password for the user */
113     private static String DB_PASS = "village";
114 
115     /*** mm MySQL Driver setup */
116     private static String DB_DRIVER = "org.gjt.mm.mysql.Driver";
117     //private static String DB_DRIVER = "com.mysql.jdbc.Driver";
118 
119     /*** mm MySQL Driver setup */
120     private static String DB_CONNECTION = "jdbc:mysql://" + DB_HOST + "/"
121             + DB_NAME + "?user=" + DB_USER + "&password=" + DB_PASS;
122 
123     /*** used for debugging */
124     private static boolean debugging = true;
125 
126     /*** used for debugging */
127     private static int num = 1;
128 
129     /*** used for debugging */
130     private static int TDS = 1;
131 
132     /*** used for debugging */
133     private static int QDS = 2;
134 
135     /*** used for debugging */
136     private static int PASSED = 1;
137 
138     /*** used for debugging */
139     private static int FAILED = 2;
140 
141     /*** The number of times to hit the schema to try and reach a connection
142      *  limit. */
143     private static int SCHEMA_LOOPS = 2000;
144 
145     /***
146      * Creates a new instance.
147      *
148      * @param name the name of the test case to run
149      */
150     public TestMySQL(String name)
151     {
152         super(name);
153     }
154 
155 //    /***
156 //     * @TODO DOCUMENT ME!
157 //     *
158 //     * @param argv
159 //     */
160 //    public static void main(String [] argv)
161 //    {
162 //        if ((argv.length > 0) && (argv.length < 5))
163 //        {
164 //            System.out.println("Format: TestMySQL <DB_NAME> <DB_TABLE> <DB_HOST> <DB_USER> <DB_PASS>");
165 //
166 //            return;
167 //        }
168 //        else if (argv.length == 5)
169 //        {
170 //            DB_NAME = argv[0];
171 //            DB_TABLE = argv[1];
172 //            DB_HOST = argv[2];
173 //            DB_USER = argv[3];
174 //            DB_PASS = argv[4];
175 //            DB_CONNECTION = "jdbc:mysql://" + DB_HOST + "/" + DB_NAME + "?user=" + DB_USER + "&password=" + DB_PASS;
176 //        }
177 //
178 //        getConnection();
179 //
180 //        testDeleteSomeRecords();
181 //        testTableDataSet();
182 //        testQueryDataSet();
183 //        testSchemaResultSet();
184 //        testTableDataSet2();
185 //        testTableDataSet3();
186 //        testTableDataSet4();
187 //        testRemoveRecord();
188 //    }
189 
190     public void setUp()
191     {
192         try
193         {
194             getConnection();
195         }
196         catch (ClassNotFoundException e)
197         {
198             System.out.println("\n\nConnection failed : " + e.getMessage());
199         }
200         catch (SQLException e)
201         {
202             System.out.println("\n\nConnection failed : " + e.getMessage());
203         }
204     }
205 
206     protected void tearDown() throws Exception
207     {
208         // Empty the database.
209         PreparedStatement ps = conn.prepareStatement("delete from test");
210         ps.execute();
211 
212         conn.close();
213     }
214 
215     /***
216      * This test verifies that deleting multiple records actually works. after
217      * execution, there should be no more records in the database.
218      */
219     public static void testDeleteSomeRecords()
220             throws SQLException, DataSetException
221     {
222         KeyDef kd = new KeyDef().addAttrib("e");
223         TableDataSet tds = new TableDataSet(conn, DB_TABLE, kd);
224         tds.where("e > 100");
225 
226         // add some records
227         Record newRec = tds.addRecord();
228         newRec.setValue("e", "200");
229 
230         Record newRec2 = tds.addRecord();
231         newRec2.setValue("e", "300");
232         tds.save();
233 
234         // get those records
235         tds.fetchRecords();
236 
237         for (int i = 0; i < tds.size(); i++)
238         {
239             Record rec = tds.getRecord(i);
240 
241             // delete those records
242             rec.markToBeDeleted();
243             System.out.println("here " + i + ": " + rec.toString());
244         }
245 
246         tds.save();
247         tds.close();
248     }
249 
250     /***
251      * This test checks that a DataSetException is thrown when appropriate.
252      */
253     public static void testRemoveRecord()
254             throws SQLException, DataSetException
255     {
256         TableDataSet tds = new TableDataSet(conn, DB_TABLE);
257         tds.addRecord();
258 
259         Record rec = tds.getRecord(0);
260         tds.removeRecord(rec);
261 
262         try
263         {
264             Record foo = tds.getRecord(0);
265         }
266         catch (DataSetException e)
267         {
268             // expected
269         }
270         tds.close();
271     }
272 
273     public static void testTableDataSet2()
274             throws SQLException, DataSetException
275     {
276         TableDataSet tds = new TableDataSet(conn, DB_TABLE);
277         Record rec = tds.addRecord();
278         rec.setValue("b", 2);
279         tds.save();
280         tds.close();
281     }
282 
283     public static void testTableDataSet3()
284             throws SQLException, DataSetException
285     {
286         TableDataSet tds = new TableDataSet(conn, DB_TABLE);
287         Record rec = tds.addRecord();
288         rec.setValue("b", 2);
289         rec.save();
290         tds.close();
291     }
292 
293     public static void testTableDataSet4()
294             throws SQLException, DataSetException
295     {
296         KeyDef kd = new KeyDef().addAttrib("b");
297         TableDataSet tds = new TableDataSet(conn, DB_TABLE, kd);
298         Record rec = tds.addRecord();
299         rec.setValueNull("b");
300         System.out.println(rec.getSaveString());
301         rec.save();
302         rec.markToBeDeleted();
303         System.out.println(rec.getSaveString());
304         rec.save();
305         tds.close();
306     }
307 
308     public static void testTableDataSet()
309             throws SQLException, DataSetException
310     {
311         KeyDef kd = new KeyDef().addAttrib("a");
312         TableDataSet tds = new TableDataSet(conn, DB_TABLE, kd);
313         tds.order("a");
314         tds.fetchRecords();
315 
316         int size = tds.size();
317         assertEquals(0, size);
318 
319         debug(TDS, "size of fetchRecords", size);
320         debug(TDS, "getSelectString()", tds.getSelectString());
321         assertEquals("SELECT * FROM test ORDER BY a", tds.getSelectString());
322 
323         // add a new record
324         Record addRec = tds.addRecord();
325         addRec.setValue("a", 1);
326         addRec.setValue("b", 2);
327         addRec.setValue("c", 2343);
328         addRec.setValue("d", 33333);
329         addRec.setValue("e", 22222);
330         addRec.setValue("f", 234324);
331         addRec.setValue("g", 3434);
332         addRec.setValue("h", 2343.30);
333         addRec.setValue("i", 2343.22);
334         addRec.setValue("j", 333.3);
335         addRec.setValue("k", 333.3);
336         addRec.setValue("l", "lskdfsd");
337         addRec.setValue("m", "lksdflkjsldf");
338         addRec.setValue("n", new java.util.Date());
339         addRec.setValue("o", new java.util.Date());
340         addRec.setValue("p", new java.util.Date());
341         addRec.setValue("q", new java.util.Date());
342         addRec.setValue("r", "lksdflkjsldf");
343         addRec.setValue("s", "lksdflkjsldf");
344         addRec.setValue("t", "lksdflkjsldf");
345         addRec.setValue("u", "lksdflkjsldf");
346         addRec.setValue("v", "lksdflkjsldf");
347         addRec.setValue("w", "lksdflkjsldf");
348         addRec.setValue("x", "lksdflkjsldf");
349 
350         debug(TDS, "getSaveString() for insert", addRec.getSaveString());
351         assertEquals("INSERT INTO test ( a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )",
352                 addRec.getSaveString());
353 
354         // save it (causing an INSERT to happen)
355         addRec.save();
356 
357         debug(TDS, "size of TDS after save()", tds.size());
358         assertEquals(1, tds.size());
359 
360         Record updateRec = tds.getRecord(0);
361         updateRec.setValue("b", 234);
362         updateRec.setValue("c", 4);
363         updateRec.setValue("d", 4);
364         updateRec.setValue("e", 5);
365         updateRec.setValue("f", 6);
366         updateRec.setValue("g", 3);
367         updateRec.setValue("h", 3.4);
368         updateRec.setValue("i", 33.44);
369         updateRec.setValue("j", 33.55);
370         updateRec.setValue("k", 3333.7);
371         updateRec.setValue("l", "qweqwe");
372         updateRec.setValue("m", "qweqwe");
373         updateRec.setValue("n", new java.util.Date());
374         updateRec.setValue("o", new java.util.Date());
375         updateRec.setValue("p", new java.util.Date());
376         updateRec.setValue("q", new java.util.Date());
377         updateRec.setValue("r", "qweqwe");
378         updateRec.setValue("s", "qweqwe");
379         updateRec.setValue("t", "qweqwe");
380         updateRec.setValue("u", "qweqwe");
381         updateRec.setValue("v", "qweqwe");
382         updateRec.setValue("w", "qweqwe");
383         updateRec.setValue("x", "qweqwe");
384 
385         debug(TDS, "updateRec.getRefreshQueryString()", updateRec.getRefreshQueryString());
386 
387         debug(TDS, "updateRec.getSaveString() for update", updateRec.getSaveString());
388         assertEquals("UPDATE test SET b = ?, c = ?, d = ?, e = ?, f = ?, g = ?, h = ?, i = ?, j = ?, k = ?, l = ?, m = ?, n = ?, o = ?, p = ?, q = ?, r = ?, s = ?, t = ?, u = ?, v = ?, w = ?, x = ? WHERE a = ?",
389                 updateRec.getSaveString());
390 
391         updateRec.save();
392         assertEquals(1, tds.size());
393 
394         // mark it for deletion
395         addRec.markToBeDeleted();
396         assertEquals(1, addRec.getValue(1).asInt());
397 
398         debug(TDS, "addRec.getSaveString() for delete", addRec.getSaveString());
399         assertEquals("DELETE FROM test WHERE a = ?", addRec.getSaveString());
400 
401         // save it (causing a DELETE to happen and also remove the records from the TDS)
402         assertEquals(1, addRec.save());
403         // The save() no longer deletes the record, but rather marks it for deletion.
404         assertEquals(Enums.ZOMBIE, addRec.getSaveType());
405         assertEquals(1, tds.size());
406         // Remove the zombie records.
407         tds.save();
408         assertEquals(0, tds.size());
409 
410         tds.close();
411 
412         // Start a new TableDataSet, this is to test the Record.refresh() method
413         tds = new TableDataSet(conn, DB_TABLE, kd);
414         tds.fetchRecords();
415         addRec = tds.addRecord();
416         addRec.setValue("a", 1);
417         addRec.save();
418         assertEquals(1, tds.size());
419 
420         tds = new TableDataSet(conn, DB_TABLE, kd);
421         tds.fetchRecords();
422         assertEquals(1, tds.size());
423 
424         Record getRec = tds.getRecord(0);
425 
426         debug(TDS, "getRec.asString() 1a:", getRec.getValue("a").asString());
427         assertEquals("1", getRec.getValue("a").asString());
428         debug(TDS, "getRec.asString() 1b:", getRec.getValue("b").asString());
429         // TODO Check that smallint is supposed to return null now
430         assertNull(getRec.getValue("b").asString());
431 
432         // Since there is no getRec.save() before the .refresh() the value being
433         // set here will not end up in the database.
434         getRec.setValue("b", 5);
435 
436         debug(TDS, "getRec.asString() 2b:", getRec.getValue("b").asString());
437         assertEquals("5", getRec.getValue("b").asString());
438 
439         // While the JavaDoc for Record.refresh() indicates that it cannot be
440         // done for a modified record, it certainly allows it and in fact seems
441         // to be the intended purpose of the method.
442         getRec.refresh(conn);
443 
444         debug(TDS, "getRec.asString() 3b:", getRec.getValue("b").asString());
445         // TODO Check that smallint is supposed to return null now
446         assertNull(getRec.getValue("b").asString());
447         debug(TDS, "getRec.asString() 2a:", getRec.getValue("a").asString());
448         assertEquals("1", getRec.getValue("a").asString());
449 
450         getRec.markToBeDeleted();
451         getRec.save();
452 
453         System.out.println(tds.toString());
454         System.out.println(getRec.toString());
455         System.out.println(tds.schema().toString());
456 
457         tds.close();
458     }
459 
460     /***
461      * @throws DataSetException 
462      * @throws SQLException 
463      */
464     public static void testQueryDataSet()
465             throws SQLException, DataSetException
466     {
467         KeyDef kd = new KeyDef().addAttrib("a");
468         TableDataSet tds = new TableDataSet(conn, DB_TABLE, kd);
469         tds.fetchRecords();
470 
471         // add a new record
472         Record addRec = tds.addRecord();
473         addRec.setValue("a", 1);
474         addRec.setValue("b", 2);
475         debug(TDS, "addRec.getSaveString()", addRec.getSaveString());
476         assertEquals("INSERT INTO test ( a, b ) VALUES ( ?, ? )", addRec.getSaveString());
477 
478         // save it (causing an INSERT to happen)
479         addRec.save();
480         tds.close();
481 
482         // get a QDS
483         QueryDataSet qds = new QueryDataSet(conn, "SELECT * FROM " + DB_TABLE);
484         qds.fetchRecords();
485 
486         debug(QDS, "qds.getSelectString()", qds.getSelectString());
487         assertEquals("SELECT * FROM test", qds.getSelectString());
488 
489         debug(QDS, "qds.size()", qds.size()); // should be 1
490 
491         Record rec = qds.getRecord(0);
492         debug(QDS, "rec.size()", rec.size()); // should be 24
493 
494         debug(QDS, "rec.getValue(\"a\").asString()", rec.getValue("a").asString());
495         debug(QDS, "rec.getValue(\"b\").asString()", rec.getValue("b").asString());
496         debug(QDS, "rec.getValue(\"c\").asString()", rec.getValue("c").asString());
497         debug(QDS, "rec.getValue(\"d\").asString()", rec.getValue("d").asString());
498 
499         // this tests to make sure that "d" was assigned properly
500         // there was a bug where wasNull() was being checked and this wasn't
501         // being setup correctly.
502         // TODO Check that tinyint is supposed to return null now
503         //assertEquals("0", rec.getValue("d").asString());
504         assertNull(rec.getValue("d").asString());
505         qds.close();
506 
507         // delete the record
508         kd = new KeyDef().addAttrib("a");
509         tds = new TableDataSet(conn, DB_TABLE, kd);
510         tds.fetchRecords();
511 
512         Record getRec = tds.getRecord(0);
513         getRec.markToBeDeleted();
514         getRec.save();
515         tds.close();
516     }
517 
518     /***
519      * This is a test for TORQUE-8.
520      *
521      * @throws DataSetException 
522      * @throws SQLException 
523      */
524     public static void testSchemaResultSet()
525             throws SQLException, DataSetException
526     {
527         for (int i = 0; i < SCHEMA_LOOPS; i++)
528         {
529             System.out.println("testSchemaResultSet() run " + i + " of " + SCHEMA_LOOPS);
530             Schema schema = new Schema().schema(conn, "test", "a");
531             assertEquals(schema.getTableName(), "test");
532         }
533     }
534     
535     /***
536      * Get a connection. 
537      */
538     public static void getConnection()
539             throws ClassNotFoundException, SQLException
540     {
541         Class.forName(DB_DRIVER);
542         conn = DriverManager.getConnection(DB_CONNECTION);
543     }
544 
545     /***
546      * Print some debug info.
547      *
548      * @param type
549      * @param e
550      */
551     public static void debug(int type, Exception e)
552     {
553         debug(TDS, e.getMessage());
554         e.printStackTrace();
555         System.out.println("\n");
556     }
557 
558     /***
559      * Print some debug info.
560      *
561      * @param type
562      * @param method
563      */
564     public static void debug(int type, String method)
565     {
566         debug(type, method, null);
567     }
568 
569     /***
570      * Print some debug info.
571      *
572      * @param type
573      * @param method
574      * @param value
575      */
576     public static void debug(int type, String method, int value)
577     {
578         debug(type, method, String.valueOf(value));
579     }
580 
581     /***
582      * Print some debug info.
583      *
584      * @param type
585      * @param method
586      * @param value
587      */
588     public static void debug(int type, String method, String value)
589     {
590         if (debugging)
591         {
592             String name = "";
593 
594             if (type == TDS)
595             {
596                 name = "TableDataSet";
597             }
598             else
599             {
600                 name = "QueryDataSet";
601             }
602 
603             if (value != null)
604             {
605                 System.out.print("[" + num++ + "] " + name + " - " + method + " = " + value + "\n");
606             }
607             else
608             {
609                 System.out.print("[" + num++ + "] " + name + " - " + method + "\n");
610             }
611 
612             System.out.flush();
613         }
614     }
615 }