require 'test/unit'
require 'criteria/sql'
include Criteria

class TestSQL < Test::Unit::TestCase
  def test_create
    expected = sql = nil

    assert_nothing_raised {
      sql = SQLTable.create("test",
                            [:id,        Integer],
                            [:name,      String],
                            [:age,       Integer],
                            SQLFn.pk(:id))
    }

    expected = "CREATE TABLE test (id int, name text, age int, PRIMARY KEY(id))"
    assert_equal(expected, sql)


    assert_nothing_raised {
      sql = SQLTable.create("test2",
                            [:id,        Integer,     "int64 PRIMARY KEY"],
                            [:label,     String,      "VARCHAR(16)"],
                            [:partno,    Integer])
    }

    expected = "CREATE TABLE test2 (id int64 PRIMARY KEY, label VARCHAR(16), partno int)"
    assert_equal(expected, sql)
  end #m:test_create


  def test_insert
    idx = expected = sql = nil

    assert_nothing_raised {
      idx = SQLTable.new("test")
      sql = idx.insert(nil, "ABC", 123)
    }

    expected = "INSERT INTO test VALUES (NULL, 'ABC', 123)"
    assert_equal(expected, sql)

    assert_nothing_raised {
      sql = idx.insert(:name => "John Doe",
                       :age  => 32)
    }

    # FIXME: This test is based on the assumption the hash will
    # evaluate in the given order
    #expected = "INSERT INTO test (age, name) VALUES (32, 'John Doe')"
    #assert_equal(expected, sql)
  end #m:test_insert


  def test_update
    idx = expected = sql = nil

    assert_nothing_raised {
      idx = SQLTable.new("test")
      sql = (idx.age < 21).update(:minor => 1)
    }

    expected = "UPDATE test SET minor = 1 WHERE (test.age < 21)"
    assert_equal(expected, sql)

    assert_nothing_raised {
      sql = (idx.name.nil?).update(:name => "John Doe")
    }

    expected = "UPDATE test SET name = 'John Doe' WHERE (test.name IS NULL)"
    assert_equal(expected, sql)

    assert_nothing_raised {
      sql = idx.*.update(:checked => 0)
    }

    expected = "UPDATE test SET checked = 0"
    assert_equal(expected, sql)
  end #m:test_update


  def test_select
    idx = query = nil

    assert_nothing_raised {
      idx            = SQLTable.new("my_table")
      query          = (idx.price > idx.paid) &
                       (idx.duedate < 500) |
                       (idx.flagged == true)
      query.limit    = 5
      query.order    = :ASC
      query.order_by = idx.name, idx.age
    }

    sql = "SELECT * FROM my_table WHERE (((my_table.price > my_table.paid) AND (my_table.duedate < 500)) OR (my_table.flagged = 1)) LIMIT 5 ORDER BY my_table.name, my_table.age ASC"
    assert_equal(sql, query.select)
  end

  def test_join
    idx1 = idx2 = join = query = res = nil
    assert_nothing_raised {
      idx1           = SQLTable.new("my_table")
      idx2           = SQLTable.new("my_other_table")
      join           = (idx1.id == idx2.order_id)
      query          = (idx1.name == "Crono")
    }

    assert_nothing_raised {
      res = query.left_outer_join(join, idx2).select(idx1.id, idx2)
    }

    sql = "SELECT my_table.id, my_other_table.* FROM my_table, my_other_table LEFT OUTER JOIN my_other_table ON (my_table.id = my_other_table.order_id) WHERE (my_table.name = 'Crono')"
    assert_equal(sql, res)

    assert_nothing_raised {
      res   = query.left_join(idx2).select
    }

    sql = "SELECT * FROM my_table LEFT JOIN my_other_table WHERE (my_table.name = 'Crono')"
    assert_equal(sql, res)

    assert_nothing_raised {
      res = query.right_outer_join([:id], idx2).select
    }

    sql = "SELECT * FROM my_table RIGHT OUTER JOIN my_other_table USING(id) WHERE (my_table.name = 'Crono')"
    assert_equal(sql, res)
  end


  def test_delete
    idx=expected=sql=nil

    assert_nothing_raised {
      idx = SQLTable.new("test")
      sql = (idx.age < 21).delete
    }

    expected = "DELETE FROM test WHERE (test.age < 21)"
    assert_equal(expected, sql)

    assert_nothing_raised {
      sql = idx.*.delete
    }

    expected = "DELETE FROM test"
    assert_equal(expected, sql)
  end #m:test_delete


  def test_drop
    expected=sql=nil

    assert_nothing_raised {
      sql = SQLTable.drop("test")
    }

    expected = "DROP TABLE test"
    assert_equal(expected, sql)
  end #m:test_drop
end
