diff options
Diffstat (limited to 'test')
25 files changed, 2474 insertions, 0 deletions
diff --git a/test/attributes/test_attribute.rb b/test/attributes/test_attribute.rb new file mode 100644 index 0000000000..f0341f4070 --- /dev/null +++ b/test/attributes/test_attribute.rb @@ -0,0 +1,648 @@ +require 'spec_helper' + +module Arel + module Attributes + describe 'attribute' do + describe '#not_eq' do + it 'should create a NotEqual node' do + relation = Table.new(:users) + relation[:id].not_eq(10).must_be_kind_of Nodes::NotEqual + end + + it 'should generate != in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].not_eq(10) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" != 10 + } + end + + it 'should handle nil' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].not_eq(nil) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" IS NOT NULL + } + end + end + + describe '#not_eq_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].not_eq_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].not_eq_any([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" != 1 OR "users"."id" != 2) + } + end + end + + describe '#not_eq_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].not_eq_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].not_eq_all([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" != 1 AND "users"."id" != 2) + } + end + end + + describe '#gt' do + it 'should create a GreaterThan node' do + relation = Table.new(:users) + relation[:id].gt(10).must_be_kind_of Nodes::GreaterThan + end + + it 'should generate >= in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].gt(10) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" > 10 + } + end + end + + describe '#gt_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].gt_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].gt_any([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" > 1 OR "users"."id" > 2) + } + end + end + + describe '#gt_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].gt_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].gt_all([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" > 1 AND "users"."id" > 2) + } + end + end + + describe '#gteq' do + it 'should create a GreaterThanOrEqual node' do + relation = Table.new(:users) + relation[:id].gteq(10).must_be_kind_of Nodes::GreaterThanOrEqual + end + + it 'should generate >= in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].gteq(10) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" >= 10 + } + end + end + + describe '#gteq_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].gteq_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].gteq_any([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" >= 1 OR "users"."id" >= 2) + } + end + end + + describe '#gteq_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].gteq_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].gteq_all([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" >= 1 AND "users"."id" >= 2) + } + end + end + + describe '#lt' do + it 'should create a LessThan node' do + relation = Table.new(:users) + relation[:id].lt(10).must_be_kind_of Nodes::LessThan + end + + it 'should generate < in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].lt(10) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" < 10 + } + end + end + + describe '#lt_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].lt_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].lt_any([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" < 1 OR "users"."id" < 2) + } + end + end + + describe '#lt_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].lt_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].lt_all([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" < 1 AND "users"."id" < 2) + } + end + end + + describe '#lteq' do + it 'should create a LessThanOrEqual node' do + relation = Table.new(:users) + relation[:id].lteq(10).must_be_kind_of Nodes::LessThanOrEqual + end + + it 'should generate <= in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].lteq(10) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" <= 10 + } + end + end + + describe '#lteq_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].lteq_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].lteq_any([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" <= 1 OR "users"."id" <= 2) + } + end + end + + describe '#lteq_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].lteq_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].lteq_all([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" <= 1 AND "users"."id" <= 2) + } + end + end + + describe '#average' do + it 'should create a AVG node' do + relation = Table.new(:users) + relation[:id].average.must_be_kind_of Nodes::Avg + end + + # FIXME: backwards compat. Is this really necessary? + it 'should set the alias to "avg_id"' do + relation = Table.new(:users) + mgr = relation.project relation[:id].average + mgr.to_sql.must_be_like %{ + SELECT AVG("users"."id") AS avg_id + FROM "users" + } + end + end + + describe '#maximum' do + it 'should create a MAX node' do + relation = Table.new(:users) + relation[:id].maximum.must_be_kind_of Nodes::Max + end + + # FIXME: backwards compat. Is this really necessary? + it 'should set the alias to "max_id"' do + relation = Table.new(:users) + mgr = relation.project relation[:id].maximum + mgr.to_sql.must_be_like %{ + SELECT MAX("users"."id") AS max_id + FROM "users" + } + end + end + + describe '#minimum' do + it 'should create a Min node' do + relation = Table.new(:users) + relation[:id].minimum.must_be_kind_of Nodes::Min + end + end + + describe '#sum' do + it 'should create a SUM node' do + relation = Table.new(:users) + relation[:id].sum.must_be_kind_of Nodes::Sum + end + + # FIXME: backwards compat. Is this really necessary? + it 'should set the alias to "sum_id"' do + relation = Table.new(:users) + mgr = relation.project relation[:id].sum + mgr.to_sql.must_be_like %{ + SELECT SUM("users"."id") AS sum_id + FROM "users" + } + end + end + + describe '#count' do + it 'should return a count node' do + relation = Table.new(:users) + relation[:id].count.must_be_kind_of Nodes::Count + end + + it 'should take a distinct param' do + relation = Table.new(:users) + count = relation[:id].count(nil) + count.must_be_kind_of Nodes::Count + count.distinct.must_be_nil + end + end + + describe '#eq' do + it 'should return an equality node' do + attribute = Attribute.new nil, nil, nil + equality = attribute.eq 1 + check equality.left.must_equal attribute + check equality.right.must_equal 1 + equality.must_be_kind_of Nodes::Equality + end + + it 'should generate = in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].eq(10) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" = 10 + } + end + + it 'should handle nil' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].eq(nil) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" IS NULL + } + end + end + + describe '#eq_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].eq_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].eq_any([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" = 1 OR "users"."id" = 2) + } + end + end + + describe '#eq_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].eq_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].eq_all([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" = 1 AND "users"."id" = 2) + } + end + end + + describe '#matches' do + it 'should create a Matches node' do + relation = Table.new(:users) + relation[:name].matches('%bacon%').must_be_kind_of Nodes::Matches + end + + it 'should generate LIKE in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:name].matches('%bacon%') + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."name" LIKE '%bacon%' + } + end + end + + describe '#matches_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:name].matches_any(['%chunky%','%bacon%']).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:name].matches_any(['%chunky%','%bacon%']) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."name" LIKE '%chunky%' OR "users"."name" LIKE '%bacon%') + } + end + end + + describe '#matches_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:name].matches_all(['%chunky%','%bacon%']).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:name].matches_all(['%chunky%','%bacon%']) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."name" LIKE '%chunky%' AND "users"."name" LIKE '%bacon%') + } + end + end + + describe '#does_not_match' do + it 'should create a DoesNotMatch node' do + relation = Table.new(:users) + relation[:name].does_not_match('%bacon%').must_be_kind_of Nodes::DoesNotMatch + end + + it 'should generate NOT LIKE in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:name].does_not_match('%bacon%') + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."name" NOT LIKE '%bacon%' + } + end + end + + describe '#does_not_match_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:name].does_not_match_any(['%chunky%','%bacon%']).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:name].does_not_match_any(['%chunky%','%bacon%']) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."name" NOT LIKE '%chunky%' OR "users"."name" NOT LIKE '%bacon%') + } + end + end + + describe '#does_not_match_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:name].does_not_match_all(['%chunky%','%bacon%']).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:name].does_not_match_all(['%chunky%','%bacon%']) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."name" NOT LIKE '%chunky%' AND "users"."name" NOT LIKE '%bacon%') + } + end + end + + describe '#in' do + it 'can be constructed with a list' do + end + + it 'should return an in node' do + attribute = Attribute.new nil, nil, nil + node = Nodes::In.new attribute, [1,2,3] + check node.left.must_equal attribute + check node.right.must_equal [1, 2, 3] + end + + it 'should generate IN in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].in([1,2,3]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" IN (1, 2, 3) + } + end + end + + describe '#in_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].in_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].in_any([[1,2], [3,4]]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" IN (1, 2) OR "users"."id" IN (3, 4)) + } + end + end + + describe '#in_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].in_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].in_all([[1,2], [3,4]]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" IN (1, 2) AND "users"."id" IN (3, 4)) + } + end + end + + describe '#not_in' do + it 'can be constructed with a list' do + end + + it 'should return a NotIn node' do + attribute = Attribute.new nil, nil, nil + node = Nodes::NotIn.new attribute, [1,2,3] + check node.left.must_equal attribute + check node.right.must_equal [1, 2, 3] + end + + it 'should generate NOT IN in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].not_in([1,2,3]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE "users"."id" NOT IN (1, 2, 3) + } + end + end + + describe '#not_in_any' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].not_in_any([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ORs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].not_in_any([[1,2], [3,4]]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" NOT IN (1, 2) OR "users"."id" NOT IN (3, 4)) + } + end + end + + describe '#not_in_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].not_in_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].not_in_all([[1,2], [3,4]]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" NOT IN (1, 2) AND "users"."id" NOT IN (3, 4)) + } + end + end + + describe '#eq_all' do + it 'should create a Grouping node' do + relation = Table.new(:users) + relation[:id].eq_all([1,2]).must_be_kind_of Nodes::Grouping + end + + it 'should generate ANDs in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.where relation[:id].eq_all([1,2]) + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" WHERE ("users"."id" = 1 AND "users"."id" = 2) + } + end + end + + describe '#asc' do + it 'should create an Ordering node' do + relation = Table.new(:users) + relation[:id].asc.must_be_kind_of Nodes::Ordering + end + + it 'should generate ASC in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.order relation[:id].asc + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" ORDER BY "users"."id" ASC + } + end + end + + describe '#desc' do + it 'should create an Ordering node' do + relation = Table.new(:users) + relation[:id].desc.must_be_kind_of Nodes::Ordering + end + + it 'should generate DESC in sql' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + mgr.order relation[:id].desc + mgr.to_sql.must_be_like %{ + SELECT "users"."id" FROM "users" ORDER BY "users"."id" DESC + } + end + end + end + + describe 'equality' do + describe '#to_sql' do + it 'should produce sql' do + table = Table.new :users + condition = table['id'].eq 1 + condition.to_sql.must_equal '"users"."id" = 1' + end + end + end + end +end diff --git a/test/nodes/test_count.rb b/test/nodes/test_count.rb new file mode 100644 index 0000000000..d65df03313 --- /dev/null +++ b/test/nodes/test_count.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Arel::Nodes::Count do + describe 'backwards compatibility' do + it 'must be an expression' do + Arel::Nodes::Count.new('foo').must_be_kind_of Arel::Expression + end + end + + describe "as" do + it 'should alias the count' do + table = Arel::Table.new :users + table[:id].count.as('foo').to_sql.must_be_like %{ + COUNT("users"."id") AS foo + } + end + end +end diff --git a/test/nodes/test_delete_statement.rb b/test/nodes/test_delete_statement.rb new file mode 100644 index 0000000000..a71da7adae --- /dev/null +++ b/test/nodes/test_delete_statement.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe Arel::Nodes::DeleteStatement do + describe "#clone" do + it "clones wheres" do + statement = Arel::Nodes::DeleteStatement.new + statement.wheres = %w[a b c] + + dolly = statement.clone + dolly.wheres.must_equal statement.wheres + dolly.wheres.wont_be_same_as statement.wheres + end + end +end diff --git a/test/nodes/test_equality.rb b/test/nodes/test_equality.rb new file mode 100644 index 0000000000..513cafd87f --- /dev/null +++ b/test/nodes/test_equality.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +module Arel + module Nodes + describe 'equality' do + # FIXME: backwards compat + describe 'backwards compat' do + describe 'operator' do + it 'returns :==' do + attr = Table.new(:users)[:id] + left = attr.eq(10) + check left.operator.must_equal :== + end + end + + describe 'operand1' do + it "should equal left" do + attr = Table.new(:users)[:id] + left = attr.eq(10) + check left.left.must_equal left.operand1 + end + end + + describe 'operand2' do + it "should equal right" do + attr = Table.new(:users)[:id] + left = attr.eq(10) + check left.right.must_equal left.operand2 + end + end + + describe 'to_sql' do + it 'takes an engine' do + engine = FakeRecord::Base.new + engine.connection.extend Module.new { + attr_accessor :quote_count + def quote(*args) @quote_count += 1; super; end + def quote_column_name(*args) @quote_count += 1; super; end + def quote_table_name(*args) @quote_count += 1; super; end + } + engine.connection.quote_count = 0 + + attr = Table.new(:users)[:id] + test = attr.eq(10) + test.to_sql engine + check engine.connection.quote_count.must_equal 2 + end + end + end + + describe 'or' do + it 'makes an OR node' do + attr = Table.new(:users)[:id] + left = attr.eq(10) + right = attr.eq(11) + node = left.or right + check node.expr.left.must_equal left + check node.expr.right.must_equal right + end + end + + describe 'and' do + it 'makes and AND node' do + attr = Table.new(:users)[:id] + left = attr.eq(10) + right = attr.eq(11) + node = left.and right + check node.left.must_equal left + check node.right.must_equal right + end + end + end + end +end diff --git a/test/nodes/test_insert_statement.rb b/test/nodes/test_insert_statement.rb new file mode 100644 index 0000000000..47f3c27dee --- /dev/null +++ b/test/nodes/test_insert_statement.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Arel::Nodes::InsertStatement do + describe "#clone" do + it "clones columns and values" do + statement = Arel::Nodes::InsertStatement.new + statement.columns = %w[a b c] + statement.values = %w[x y z] + + dolly = statement.clone + dolly.columns.must_equal statement.columns + dolly.values.must_equal statement.values + + dolly.columns.wont_be_same_as statement.columns + dolly.values.wont_be_same_as statement.values + end + end +end diff --git a/test/nodes/test_or.rb b/test/nodes/test_or.rb new file mode 100644 index 0000000000..354d803110 --- /dev/null +++ b/test/nodes/test_or.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +module Arel + module Nodes + describe 'or' do + describe '#or' do + it 'makes an OR node' do + attr = Table.new(:users)[:id] + left = attr.eq(10) + right = attr.eq(11) + node = left.or right + check node.expr.left.must_equal left + check node.expr.right.must_equal right + + oror = node.or(right) + check oror.expr.left.must_equal node + check oror.expr.right.must_equal right + end + end + end + end +end diff --git a/test/nodes/test_select_core.rb b/test/nodes/test_select_core.rb new file mode 100644 index 0000000000..0aacf41720 --- /dev/null +++ b/test/nodes/test_select_core.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Arel::Nodes::SelectCore do + describe "#clone" do + it "clones froms, projections and wheres" do + core = Arel::Nodes::SelectCore.new + core.froms = %w[a b c] + core.projections = %w[d e f] + core.wheres = %w[g h i] + + dolly = core.clone + + dolly.froms.must_equal core.froms + dolly.projections.must_equal core.projections + dolly.wheres.must_equal core.wheres + + dolly.froms.wont_be_same_as core.froms + dolly.projections.wont_be_same_as core.projections + dolly.wheres.wont_be_same_as core.wheres + end + end +end diff --git a/test/nodes/test_select_statement.rb b/test/nodes/test_select_statement.rb new file mode 100644 index 0000000000..45613bfa4d --- /dev/null +++ b/test/nodes/test_select_statement.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe Arel::Nodes::SelectStatement do + describe "#clone" do + it "clones cores" do + statement = Arel::Nodes::SelectStatement.new %w[a b c] + + dolly = statement.clone + dolly.cores.must_equal statement.cores + dolly.cores.wont_be_same_as statement.cores + end + end +end diff --git a/test/nodes/test_sql_literal.rb b/test/nodes/test_sql_literal.rb new file mode 100644 index 0000000000..3aeab41f0c --- /dev/null +++ b/test/nodes/test_sql_literal.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +module Arel + module Nodes + describe 'sql literal' do + describe 'sql' do + it 'makes a sql literal node' do + sql = Arel.sql 'foo' + sql.must_be_kind_of Arel::Nodes::SqlLiteral + end + end + + describe 'count' do + it 'makes a count node' do + node = SqlLiteral.new('*').count + viz = Visitors::ToSql.new Table.engine + viz.accept(node).must_be_like %{ COUNT(*) } + end + + it 'makes a distinct node' do + node = SqlLiteral.new('*').count true + viz = Visitors::ToSql.new Table.engine + viz.accept(node).must_be_like %{ COUNT(DISTINCT *) } + end + end + end + end +end diff --git a/test/nodes/test_sum.rb b/test/nodes/test_sum.rb new file mode 100644 index 0000000000..e6a57e4dd6 --- /dev/null +++ b/test/nodes/test_sum.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe Arel::Nodes::Sum do + describe "as" do + it 'should alias the sum' do + table = Arel::Table.new :users + table[:id].sum.as('foo').to_sql.must_be_like %{ + SUM("users"."id") AS foo + } + end + end +end diff --git a/test/nodes/test_update_statement.rb b/test/nodes/test_update_statement.rb new file mode 100644 index 0000000000..88c147b268 --- /dev/null +++ b/test/nodes/test_update_statement.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Arel::Nodes::UpdateStatement do + describe "#clone" do + it "clones wheres and values" do + statement = Arel::Nodes::UpdateStatement.new + statement.wheres = %w[a b c] + statement.values = %w[x y z] + + dolly = statement.clone + dolly.wheres.must_equal statement.wheres + dolly.wheres.wont_be_same_as statement.wheres + + dolly.values.must_equal statement.values + dolly.values.wont_be_same_as statement.values + end + end +end diff --git a/test/spec_helper.rb b/test/spec_helper.rb new file mode 100644 index 0000000000..dd288e0d0d --- /dev/null +++ b/test/spec_helper.rb @@ -0,0 +1,21 @@ +require 'rubygems' +require 'minitest/autorun' +require 'fileutils' +require 'arel' + +require 'support/fake_record' +Arel::Table.engine = Arel::Sql::Engine.new(FakeRecord::Base.new) + +# HACK require 'support/shared/tree_manager_shared' + +class Object + def must_be_like other + self.gsub(/\s+/, ' ').strip.must_equal other.gsub(/\s+/, ' ').strip + end + + # TODO: remove + def check truthiness + raise "not truthy" unless truthiness + end +end + diff --git a/test/support/fake_record.rb b/test/support/fake_record.rb new file mode 100644 index 0000000000..ef3cc6a291 --- /dev/null +++ b/test/support/fake_record.rb @@ -0,0 +1,89 @@ +module FakeRecord + class Column < Struct.new(:name, :type) + end + + class Connection + attr_reader :tables + + def initialize + @tables = %w{ users photos developers } + @columns = { + 'users' => [ + Column.new('id', :integer), + Column.new('name', :string) + ] + } + @primary_keys = { + 'users' => 'id' + } + end + + def primary_key name + @primary_keys[name.to_s] + end + + def table_exists? name + @tables.include? name.to_s + end + + def columns name, message = nil + @columns[name.to_s] + end + + def quote_table_name name + "\"#{name.to_s}\"" + end + + def quote_column_name name + "\"#{name.to_s}\"" + end + + def quote thing, column = nil + if column && column.type == :integer + return 'NULL' if thing.nil? + return thing.to_i + end + + case thing + when true + "'t'" + when false + "'f'" + when nil + 'NULL' + when Numeric + thing + else + "'#{thing}'" + end + end + end + + class ConnectionPool + class Spec < Struct.new(:config) + end + + attr_reader :spec, :connection + + def initialize + @spec = Spec.new(:adapter => 'sqlite3') + @connection = Connection.new + end + + def with_connection + yield connection + end + end + + class Base + attr_accessor :connection_pool + + def initialize + @connection_pool = ConnectionPool.new + end + + def connection + connection_pool.connection + end + end +end diff --git a/test/test_activerecord_compat.rb b/test/test_activerecord_compat.rb new file mode 100644 index 0000000000..2f1d7cff12 --- /dev/null +++ b/test/test_activerecord_compat.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +module Arel + describe 'activerecord compatibility' do + describe 'select manager' do + it 'provides wheres' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.where table[:id].eq 1 + manager.where table[:name].eq 'Aaron' + + check manager.wheres.map { |x| + x.value + }.join(', ').must_equal "\"users\".\"id\" = 1, \"users\".\"name\" = 'Aaron'" + end + end + end +end diff --git a/test/test_attributes.rb b/test/test_attributes.rb new file mode 100644 index 0000000000..4cdb625b17 --- /dev/null +++ b/test/test_attributes.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +module Arel + describe 'Attributes' do + describe 'for' do + it 'returns the correct constant for strings' do + [:string, :text, :binary].each do |type| + column = Struct.new(:type).new type + Attributes.for(column).must_equal Attributes::String + end + end + + it 'returns the correct constant for ints' do + column = Struct.new(:type).new :integer + Attributes.for(column).must_equal Attributes::Integer + end + + it 'returns the correct constant for floats' do + column = Struct.new(:type).new :float + Attributes.for(column).must_equal Attributes::Float + end + + it 'returns the correct constant for decimals' do + column = Struct.new(:type).new :decimal + Attributes.for(column).must_equal Attributes::Decimal + end + + it 'returns the correct constant for boolean' do + column = Struct.new(:type).new :boolean + Attributes.for(column).must_equal Attributes::Boolean + end + + it 'returns the correct constant for time' do + [:date, :datetime, :timestamp, :time].each do |type| + column = Struct.new(:type).new type + Attributes.for(column).must_equal Attributes::Time + end + end + end + end +end diff --git a/test/test_crud.rb b/test/test_crud.rb new file mode 100644 index 0000000000..2a0ff651e9 --- /dev/null +++ b/test/test_crud.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +module Arel + class FakeCrudder < SelectManager + class FakeEngine + attr_reader :calls, :connection_pool, :spec, :config + + def initialize + @calls = [] + @connection_pool = self + @spec = self + @config = { :adapter => 'sqlite3' } + end + + def connection; self end + + def method_missing name, *args + @calls << [name, args] + end + end + + include Crud + + attr_reader :engine + attr_accessor :ctx + + def initialize engine = FakeEngine.new + super + end + end + + describe 'crud' do + describe 'insert' do + it 'should call insert on the connection' do + table = Table.new :users + fc = FakeCrudder.new + fc.from table + fc.insert [[table[:id], 'foo']] + fc.engine.calls.find { |method, _| + method == :insert + }.wont_be_nil + end + end + + describe 'update' do + it 'should call update on the connection' do + table = Table.new :users + fc = FakeCrudder.new + fc.from table + fc.update [[table[:id], 'foo']] + fc.engine.calls.find { |method, _| + method == :update + }.wont_be_nil + end + end + + describe 'delete' do + it 'should call delete on the connection' do + table = Table.new :users + fc = FakeCrudder.new + fc.from table + fc.delete + fc.engine.calls.find { |method, _| + method == :delete + }.wont_be_nil + end + end + end +end diff --git a/test/test_delete_manager.rb b/test/test_delete_manager.rb new file mode 100644 index 0000000000..0a41c4d3fc --- /dev/null +++ b/test/test_delete_manager.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +module Arel + describe 'delete manager' do + describe 'new' do + it 'takes an engine' do + Arel::DeleteManager.new Table.engine + end + end + + describe 'from' do + it 'uses from' do + table = Table.new(:users) + dm = Arel::DeleteManager.new Table.engine + dm.from table + dm.to_sql.must_be_like %{ DELETE FROM "users" } + end + + it 'chains' do + table = Table.new(:users) + dm = Arel::DeleteManager.new Table.engine + check dm.from(table).must_equal dm + end + end + + describe 'where' do + it 'uses where values' do + table = Table.new(:users) + dm = Arel::DeleteManager.new Table.engine + dm.from table + dm.where table[:id].eq(10) + dm.to_sql.must_be_like %{ DELETE FROM "users" WHERE "users"."id" = 10} + end + + it 'chains' do + table = Table.new(:users) + dm = Arel::DeleteManager.new Table.engine + check dm.where(table[:id].eq(10)).must_equal dm + end + end + + # HACK + # describe "TreeManager" do + # before do + # table = Table.new :users + # Arel::DeleteManager.new(Table.engine).tap do |manager| + # manager.where(table[:id].eq(10)) + # end + # end + # + # it_should_behave_like "TreeManager" + # end + end +end diff --git a/test/test_insert_manager.rb b/test/test_insert_manager.rb new file mode 100644 index 0000000000..97ba4d7f76 --- /dev/null +++ b/test/test_insert_manager.rb @@ -0,0 +1,140 @@ +require 'spec_helper' + +module Arel + describe 'insert manager' do + describe 'new' do + it 'takes an engine' do + Arel::InsertManager.new Table.engine + end + end + + describe 'insert' do + it "inserts false" do + table = Table.new(:users) + manager = Arel::InsertManager.new Table.engine + + table[:id].column.extend(Module.new { def type; :boolean; end }) + + manager.insert [[table[:id], false]] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id") VALUES ('f') + } + end + + it "inserts null" do + table = Table.new(:users) + manager = Arel::InsertManager.new Table.engine + manager.insert [[table[:id], nil]] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id") VALUES (NULL) + } + end + + it "inserts time" do + table = Table.new(:users) + manager = Arel::InsertManager.new Table.engine + + time = Time.now + attribute = table[:id] + attribute.column.type = :date + + manager.insert [[attribute, time]] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id") VALUES (#{Table.engine.connection.quote time}) + } + end + + it 'takes a list of lists' do + table = Table.new(:users) + manager = Arel::InsertManager.new Table.engine + manager.into table + manager.insert [[table[:id], 1], [table[:name], 'aaron']] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id", "name") VALUES (1, 'aaron') + } + end + + it 'defaults the table' do + table = Table.new(:users) + manager = Arel::InsertManager.new Table.engine + manager.insert [[table[:id], 1], [table[:name], 'aaron']] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id", "name") VALUES (1, 'aaron') + } + end + + it 'takes an empty list' do + manager = Arel::InsertManager.new Table.engine + manager.insert [] + end + end + + describe 'into' do + it 'takes an engine' do + manager = Arel::InsertManager.new Table.engine + manager.into(Table.new(:users)).must_equal manager + end + + it 'converts to sql' do + table = Table.new :users + manager = Arel::InsertManager.new Table.engine + manager.into table + manager.to_sql.must_be_like %{ + INSERT INTO "users" + } + end + end + + describe 'columns' do + it "converts to sql" do + table = Table.new :users + manager = Arel::InsertManager.new Table.engine + manager.into table + manager.columns << table[:id] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id") + } + end + end + + describe "values" do + it "converts to sql" do + table = Table.new :users + manager = Arel::InsertManager.new Table.engine + manager.into table + + manager.values = Nodes::Values.new [1] + manager.to_sql.must_be_like %{ + INSERT INTO "users" VALUES (1) + } + end + end + + describe "combo" do + it "puts shit together" do + table = Table.new :users + manager = Arel::InsertManager.new Table.engine + manager.into table + + manager.values = Nodes::Values.new [1, 'aaron'] + manager.columns << table[:id] + manager.columns << table[:name] + manager.to_sql.must_be_like %{ + INSERT INTO "users" ("id", "name") VALUES (1, 'aaron') + } + end + end + + # HACK + # describe "TreeManager" do + # subject do + # table = Table.new(:users) + # Arel::InsertManager.new(Table.engine).tap do |manager| + # manager.insert [[table[:id], nil]] + # end + # end + # + # it_should_behave_like "TreeManager" + # end + end +end diff --git a/test/test_select_manager.rb b/test/test_select_manager.rb new file mode 100644 index 0000000000..75d06438c2 --- /dev/null +++ b/test/test_select_manager.rb @@ -0,0 +1,593 @@ +require 'spec_helper' + +module Arel + class EngineProxy + attr_reader :executed + attr_reader :connection_pool + attr_reader :spec + attr_reader :config + + def initialize engine + @engine = engine + @executed = [] + @connection_pool = self + @spec = self + @config = { :adapter => 'sqlite3' } + end + + def with_connection + yield self + end + + def connection + self + end + + def quote_table_name thing; @engine.connection.quote_table_name thing end + def quote_column_name thing; @engine.connection.quote_column_name thing end + def quote thing, column; @engine.connection.quote thing, column end + + def execute sql, name = nil, *args + @executed << sql + end + alias :update :execute + alias :delete :execute + alias :insert :execute + end + + describe 'select manager' do + describe 'backwards compatibility' do + describe 'project' do + it 'accepts symbols as sql literals' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.project :id + manager.from table + manager.to_sql.must_be_like %{ + SELECT id FROM "users" + } + end + end + + describe 'order' do + it 'accepts symbols' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.project SqlLiteral.new '*' + manager.from table + manager.order :foo + manager.to_sql.must_be_like %{ + SELECT * FROM "users" ORDER BY foo + } + end + end + + describe 'group' do + it 'takes a symbol' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.group :foo + manager.to_sql.must_be_like %{ + SELECT FROM "users" GROUP BY foo + } + end + end + + describe 'from' do + it 'ignores strings when table of same name exists' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + + manager.from table + manager.from 'users' + manager.project table['id'] + manager.to_sql.must_be_like 'SELECT "users"."id" FROM users' + end + end + + describe '#having' do + it 'converts strings to SQLLiterals' do + table = Table.new :users + mgr = table.from table + mgr.having 'foo' + mgr.to_sql.must_be_like %{ SELECT FROM "users" HAVING foo } + end + end + end + + describe 'clone' do + it 'creates new cores' do + table = Table.new :users, :engine => Table.engine, :as => 'foo' + mgr = table.from table + m2 = mgr.clone + m2.project "foo" + mgr.to_sql.wont_equal m2.to_sql + end + end + + describe 'initialize' do + it 'uses alias in sql' do + table = Table.new :users, :engine => Table.engine, :as => 'foo' + mgr = table.from table + mgr.skip 10 + mgr.to_sql.must_be_like %{ SELECT FROM "users" "foo" OFFSET 10 } + end + end + + describe 'skip' do + it 'should add an offset' do + table = Table.new :users + mgr = table.from table + mgr.skip 10 + mgr.to_sql.must_be_like %{ SELECT FROM "users" OFFSET 10 } + end + + it 'should chain' do + table = Table.new :users + mgr = table.from table + mgr.skip(10).to_sql.must_be_like %{ SELECT FROM "users" OFFSET 10 } + end + end + + describe 'taken' do + it 'should return limit' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.take 10 + check manager.taken.must_equal 10 + end + end + + describe 'insert' do + it 'uses the select FROM' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.insert 'VALUES(NULL)' + + engine.executed.last.must_be_like %{ + INSERT INTO "users" VALUES(NULL) + } + end + end + + describe 'lock' do + # This should fail on other databases + it 'adds a lock node' do + table = Table.new :users + mgr = table.from table + mgr.lock.to_sql.must_be_like %{ SELECT FROM "users" } + end + end + + describe 'orders' do + it 'returns order clauses' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + order = table[:id] + manager.order table[:id] + check manager.orders.must_equal [order] + end + end + + describe 'order' do + it 'generates order clauses' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.project SqlLiteral.new '*' + manager.from table + manager.order table[:id] + manager.to_sql.must_be_like %{ + SELECT * FROM "users" ORDER BY "users"."id" + } + end + + # FIXME: I would like to deprecate this + it 'takes *args' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.project SqlLiteral.new '*' + manager.from table + manager.order table[:id], table[:name] + manager.to_sql.must_be_like %{ + SELECT * FROM "users" ORDER BY "users"."id", "users"."name" + } + end + + it 'chains' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + check manager.order(table[:id]).must_equal manager + end + end + + describe 'on' do + it 'takes two params' do + left = Table.new :users + right = left.alias + predicate = left[:id].eq(right[:id]) + manager = Arel::SelectManager.new Table.engine + + manager.from left + manager.join(right).on(predicate, predicate) + manager.to_sql.must_be_like %{ + SELECT FROM "users" + INNER JOIN "users" "users_2" + ON "users"."id" = "users_2"."id" AND + "users"."id" = "users_2"."id" + } + end + + it 'takes three params' do + left = Table.new :users + right = left.alias + predicate = left[:id].eq(right[:id]) + manager = Arel::SelectManager.new Table.engine + + manager.from left + manager.join(right).on( + predicate, + predicate, + left[:name].eq(right[:name]) + ) + manager.to_sql.must_be_like %{ + SELECT FROM "users" + INNER JOIN "users" "users_2" + ON "users"."id" = "users_2"."id" AND + "users"."id" = "users_2"."id" AND + "users"."name" = "users_2"."name" + } + end + end + + describe 'join' do + it 'responds to join' do + left = Table.new :users + right = left.alias + predicate = left[:id].eq(right[:id]) + manager = Arel::SelectManager.new Table.engine + + manager.from left + manager.join(right).on(predicate) + manager.to_sql.must_be_like %{ + SELECT FROM "users" + INNER JOIN "users" "users_2" + ON "users"."id" = "users_2"."id" + } + end + + it 'takes a class' do + left = Table.new :users + right = left.alias + predicate = left[:id].eq(right[:id]) + manager = Arel::SelectManager.new Table.engine + + manager.from left + manager.join(right, Nodes::OuterJoin).on(predicate) + manager.to_sql.must_be_like %{ + SELECT FROM "users" + LEFT OUTER JOIN "users" "users_2" + ON "users"."id" = "users_2"."id" + } + end + + it 'noops on nil' do + manager = Arel::SelectManager.new Table.engine + check manager.join(nil).must_equal manager + end + end + + describe 'joins' do + it 'returns join sql' do + table = Table.new :users + aliaz = table.alias + manager = Arel::SelectManager.new Table.engine + manager.from Nodes::InnerJoin.new(table, aliaz, table[:id].eq(aliaz[:id])) + manager.join_sql.must_be_like %{ + INNER JOIN "users" "users_2" "users"."id" = "users_2"."id" + } + check manager.joins(manager).must_equal manager.join_sql + end + + it 'returns outer join sql' do + table = Table.new :users + aliaz = table.alias + manager = Arel::SelectManager.new Table.engine + manager.from Nodes::OuterJoin.new(table, aliaz, table[:id].eq(aliaz[:id])) + manager.join_sql.must_be_like %{ + LEFT OUTER JOIN "users" "users_2" "users"."id" = "users_2"."id" + } + check manager.joins(manager).must_equal manager.join_sql + end + + it 'returns string join sql' do + table = Table.new :users + aliaz = table.alias + manager = Arel::SelectManager.new Table.engine + manager.from Nodes::StringJoin.new(table, 'hello') + manager.join_sql.must_be_like %{ 'hello' } + check manager.joins(manager).must_equal manager.join_sql + end + + it 'returns nil join sql' do + manager = Arel::SelectManager.new Table.engine + manager.join_sql.must_be_nil + end + end + + describe 'order_clauses' do + it 'returns order clauses as a list' do + table = Table.new :users + aliaz = table.alias + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.order table[:id] + manager.order_clauses.first.must_be_like %{ "users"."id" } + end + end + + describe 'group' do + it 'takes an attribute' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.group table[:id] + manager.to_sql.must_be_like %{ + SELECT FROM "users" GROUP BY "users"."id" + } + end + + it 'chains' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + check manager.group(table[:id]).must_equal manager + end + + it 'takes multiple args' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.group table[:id], table[:name] + manager.to_sql.must_be_like %{ + SELECT FROM "users" GROUP BY "users"."id", "users"."name" + } + end + + # FIXME: backwards compat + it 'makes strings literals' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.group 'foo' + manager.to_sql.must_be_like %{ SELECT FROM "users" GROUP BY foo } + end + end + + describe 'delete' do + it "copies from" do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.delete + + engine.executed.last.must_be_like %{ DELETE FROM "users" } + end + + it "copies where" do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.where table[:id].eq 10 + manager.delete + + engine.executed.last.must_be_like %{ + DELETE FROM "users" WHERE "users"."id" = 10 + } + end + end + + describe 'where_sql' do + it 'gives me back the where sql' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.where table[:id].eq 10 + manager.where_sql.must_be_like %{ WHERE "users"."id" = 10 } + end + + it 'returns nil when there are no wheres' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from table + manager.where_sql.must_be_nil + end + end + + describe 'update' do + it 'copies limits' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.take 1 + manager.update(SqlLiteral.new('foo = bar')) + + engine.executed.last.must_be_like %{ + UPDATE "users" SET foo = bar + WHERE "users"."id" IN (SELECT "users"."id" FROM "users" LIMIT 1) + } + end + + it 'copies order' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.order :foo + manager.update(SqlLiteral.new('foo = bar')) + + engine.executed.last.must_be_like %{ + UPDATE "users" SET foo = bar + WHERE "users"."id" IN (SELECT "users"."id" FROM "users" ORDER BY foo) + } + end + + it 'takes a string' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.update(SqlLiteral.new('foo = bar')) + + engine.executed.last.must_be_like %{ UPDATE "users" SET foo = bar } + end + + it 'copies where clauses' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.where table[:id].eq 10 + manager.from table + manager.update(table[:id] => 1) + + engine.executed.last.must_be_like %{ + UPDATE "users" SET "id" = 1 WHERE "users"."id" = 10 + } + end + + it 'executes an update statement' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.update(table[:id] => 1) + + engine.executed.last.must_be_like %{ + UPDATE "users" SET "id" = 1 + } + end + end + + describe 'project' do + it 'takes multiple args' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.project Nodes::SqlLiteral.new('foo'), + Nodes::SqlLiteral.new('bar') + manager.to_sql.must_be_like %{ SELECT foo, bar } + end + + it 'takes strings' do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.project Nodes::SqlLiteral.new('*') + manager.to_sql.must_be_like %{ SELECT * } + end + + it "takes sql literals" do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.project Nodes::SqlLiteral.new '*' + manager.to_sql.must_be_like %{ + SELECT * + } + end + end + + describe 'take' do + it "knows take" do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from(table).project(table['id']) + manager.where(table['id'].eq(1)) + manager.take 1 + + manager.to_sql.must_be_like %{ + SELECT "users"."id" + FROM "users" + WHERE "users"."id" = 1 + LIMIT 1 + } + end + + it "chains" do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.take(1).must_equal manager + end + end + + describe 'where' do + it "knows where" do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from(table).project(table['id']) + manager.where(table['id'].eq(1)) + manager.to_sql.must_be_like %{ + SELECT "users"."id" + FROM "users" + WHERE "users"."id" = 1 + } + end + + it "chains" do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + manager.from(table) + manager.project(table['id']).where(table['id'].eq 1).must_equal manager + end + end + + describe "join" do + it "joins itself" do + left = Table.new :users + right = left.alias + predicate = left[:id].eq(right[:id]) + + mgr = left.join(right) + mgr.project Nodes::SqlLiteral.new('*') + check mgr.on(predicate).must_equal mgr + + mgr.to_sql.must_be_like %{ + SELECT * FROM "users" + INNER JOIN "users" "users_2" + ON "users"."id" = "users_2"."id" + } + end + end + + describe 'from' do + it "makes sql" do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + + manager.from table + manager.project table['id'] + manager.to_sql.must_be_like 'SELECT "users"."id" FROM "users"' + end + + it "chains" do + table = Table.new :users + manager = Arel::SelectManager.new Table.engine + check manager.from(table).project(table['id']).must_equal manager + manager.to_sql.must_be_like 'SELECT "users"."id" FROM "users"' + end + end + + # HACK + # describe "TreeManager" do + # subject do + # table = Table.new :users + # Arel::SelectManager.new(Table.engine).tap do |manager| + # manager.from(table).project(table['id']) + # end + # end + # + # it_should_behave_like "TreeManager" + # end + end +end diff --git a/test/test_table.rb b/test/test_table.rb new file mode 100644 index 0000000000..ecb5c1eb79 --- /dev/null +++ b/test/test_table.rb @@ -0,0 +1,175 @@ +require 'spec_helper' + +module Arel + describe Table do + before do + @relation = Table.new(:users) + end + + describe 'primary_key' do + it 'should return an attribute' do + check @relation.primary_key.name.must_equal :id + end + end + + describe 'select_manager' do + it 'should return an empty select manager' do + sm = @relation.select_manager + sm.to_sql.must_be_like 'SELECT' + end + end + + describe 'having' do + it 'adds a having clause' do + mgr = @relation.having @relation[:id].eq(10) + mgr.to_sql.must_be_like %{ + SELECT FROM "users" HAVING "users"."id" = 10 + } + end + end + + describe 'backwards compat' do + describe 'joins' do + it 'returns nil' do + check @relation.joins(nil).must_equal nil + end + end + + describe 'join' do + it 'noops on nil' do + mgr = @relation.join nil + + mgr.to_sql.must_be_like %{ SELECT FROM "users" } + end + + it 'takes a second argument for join type' do + right = @relation.alias + predicate = @relation[:id].eq(right[:id]) + mgr = @relation.join(right, Nodes::OuterJoin).on(predicate) + + mgr.to_sql.must_be_like %{ + SELECT FROM "users" + LEFT OUTER JOIN "users" "users_2" + ON "users"."id" = "users_2"."id" + } + end + end + end + + describe 'group' do + it 'should create a group' do + manager = @relation.group @relation[:id] + manager.to_sql.must_be_like %{ + SELECT FROM "users" GROUP BY "users"."id" + } + end + end + + describe 'alias' do + it 'should create a node that proxies to a table' do + check @relation.aliases.must_equal [] + + node = @relation.alias + check @relation.aliases.must_equal [node] + check node.name.must_equal 'users_2' + check node[:id].relation.must_equal node + end + end + + describe 'new' do + it 'takes :columns' do + columns = Table.engine.connection.columns("users") + @relation = Table.new(:users, :columns => columns) + check @relation.columns.first.name.must_equal :id + check @relation.engine.must_equal Table.engine + end + + it 'should accept an engine' do + rel = Table.new :users, 'foo' + check rel.engine.must_equal 'foo' + end + + it 'should accept a hash' do + rel = Table.new :users, :engine => 'foo' + check rel.engine.must_equal 'foo' + end + + it 'ignores as if it equals name' do + rel = Table.new :users, :as => 'users' + rel.table_alias.must_be_nil + end + end + + describe 'order' do + it "should take an order" do + manager = @relation.order "foo" + manager.to_sql.must_be_like %{ SELECT FROM "users" ORDER BY foo } + end + end + + describe 'take' do + it "should add a limit" do + manager = @relation.take 1 + manager.project SqlLiteral.new '*' + manager.to_sql.must_be_like %{ SELECT * FROM "users" LIMIT 1 } + end + end + + describe 'project' do + it 'can project' do + manager = @relation.project SqlLiteral.new '*' + manager.to_sql.must_be_like %{ SELECT * FROM "users" } + end + + it 'takes multiple parameters' do + manager = @relation.project SqlLiteral.new('*'), SqlLiteral.new('*') + manager.to_sql.must_be_like %{ SELECT *, * FROM "users" } + end + end + + describe 'where' do + it "returns a tree manager" do + manager = @relation.where @relation[:id].eq 1 + manager.project @relation[:id] + manager.must_be_kind_of TreeManager + manager.to_sql.must_be_like %{ + SELECT "users"."id" + FROM "users" + WHERE "users"."id" = 1 + } + end + end + + describe 'columns' do + it 'returns a list of columns' do + columns = @relation.columns + check columns.length.must_equal 2 + columns.map { |x| x.name.to_s }.sort.must_equal %w{ name id }.sort + end + end + + it "should have a name" do + @relation.name.must_equal :users + end + + it "should have an engine" do + @relation.engine.must_equal Table.engine + end + + describe '[]' do + describe 'when given a Symbol' do + it "manufactures an attribute if the symbol names an attribute within the relation" do + column = @relation[:id] + check column.name.must_equal :id + column.must_be_kind_of Attributes::Integer + end + end + + describe 'when table does not exist' do + it 'returns nil' do + @relation[:foooo].must_be_nil + end + end + end + end +end diff --git a/test/test_update_manager.rb b/test/test_update_manager.rb new file mode 100644 index 0000000000..670f19d621 --- /dev/null +++ b/test/test_update_manager.rb @@ -0,0 +1,90 @@ +require 'spec_helper' + +module Arel + describe 'update manager' do + describe 'new' do + it 'takes an engine' do + Arel::UpdateManager.new Table.engine + end + end + + describe 'set' do + it "updates with null" do + table = Table.new(:users) + um = Arel::UpdateManager.new Table.engine + um.table table + um.set [[table[:name], nil]] + um.to_sql.must_be_like %{ UPDATE "users" SET "name" = NULL } + end + + it 'takes a string' do + table = Table.new(:users) + um = Arel::UpdateManager.new Table.engine + um.table table + um.set Nodes::SqlLiteral.new "foo = bar" + um.to_sql.must_be_like %{ UPDATE "users" SET foo = bar } + end + + it 'takes a list of lists' do + table = Table.new(:users) + um = Arel::UpdateManager.new Table.engine + um.table table + um.set [[table[:id], 1], [table[:name], 'hello']] + um.to_sql.must_be_like %{ + UPDATE "users" SET "id" = 1, "name" = 'hello' + } + end + + it 'chains' do + table = Table.new(:users) + um = Arel::UpdateManager.new Table.engine + um.set([[table[:id], 1], [table[:name], 'hello']]).must_equal um + end + end + + describe 'table' do + it 'generates an update statement' do + um = Arel::UpdateManager.new Table.engine + um.table Table.new(:users) + um.to_sql.must_be_like %{ UPDATE "users" } + end + + it 'chains' do + um = Arel::UpdateManager.new Table.engine + um.table(Table.new(:users)).must_equal um + end + end + + describe 'where' do + it 'generates a where clause' do + table = Table.new :users + um = Arel::UpdateManager.new Table.engine + um.table table + um.where table[:id].eq(1) + um.to_sql.must_be_like %{ + UPDATE "users" WHERE "users"."id" = 1 + } + end + + it 'chains' do + table = Table.new :users + um = Arel::UpdateManager.new Table.engine + um.table table + um.where(table[:id].eq(1)).must_equal um + end + end + + # HACK + # describe "TreeManager" do + # subject do + # table = Table.new :users + # Arel::UpdateManager.new(Table.engine).tap do |manager| + # manager.table table + # manager.where table[:id].eq(1) + # end + # end + # + # it_should_behave_like "TreeManager" + # end + end +end diff --git a/test/visitors/test_join_sql.rb b/test/visitors/test_join_sql.rb new file mode 100644 index 0000000000..3dc70d7dd6 --- /dev/null +++ b/test/visitors/test_join_sql.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +module Arel + module Visitors + describe 'the join_sql visitor' do + before do + @visitor = JoinSql.new Table.engine + end + + describe 'inner join' do + it 'should visit left if left is a join' do + t = Table.new :users + join = Nodes::InnerJoin.new t, t, Nodes::On.new(t[:id]) + j2 = Nodes::InnerJoin.new join, t, Nodes::On.new(t[:id]) + @visitor.accept(j2).must_be_like %{ + INNER JOIN "users" ON "users"."id" + INNER JOIN "users" ON "users"."id" + } + end + end + + describe 'outer join' do + it 'should visit left if left is a join' do + t = Table.new :users + join = Nodes::OuterJoin.new t, t, Nodes::On.new(t[:id]) + j2 = Nodes::OuterJoin.new join, t, Nodes::On.new(t[:id]) + @visitor.accept(j2).must_be_like %{ + LEFT OUTER JOIN "users" ON "users"."id" + LEFT OUTER JOIN "users" ON "users"."id" + } + end + end + end + end +end diff --git a/test/visitors/test_oracle.rb b/test/visitors/test_oracle.rb new file mode 100644 index 0000000000..8b5732f287 --- /dev/null +++ b/test/visitors/test_oracle.rb @@ -0,0 +1,111 @@ +require 'spec_helper' + +module Arel + module Visitors + describe 'the oracle visitor' do + before do + @visitor = Oracle.new Table.engine + end + + it 'modifies order when there is distinct and first value' do + # *sigh* + select = "DISTINCT foo.id, FIRST_VALUE(projects.name) OVER (foo) AS alias_0__" + stmt = Nodes::SelectStatement.new + stmt.cores.first.projections << Nodes::SqlLiteral.new(select) + stmt.orders << Nodes::SqlLiteral.new('foo') + sql = @visitor.accept(stmt) + sql.must_be_like %{ + SELECT #{select} ORDER BY alias_0__ + } + end + + it 'is idempotent with crazy query' do + # *sigh* + select = "DISTINCT foo.id, FIRST_VALUE(projects.name) OVER (foo) AS alias_0__" + stmt = Nodes::SelectStatement.new + stmt.cores.first.projections << Nodes::SqlLiteral.new(select) + stmt.orders << Nodes::SqlLiteral.new('foo') + + sql = @visitor.accept(stmt) + sql2 = @visitor.accept(stmt) + check sql.must_equal sql2 + end + + it 'splits orders with commas' do + # *sigh* + select = "DISTINCT foo.id, FIRST_VALUE(projects.name) OVER (foo) AS alias_0__" + stmt = Nodes::SelectStatement.new + stmt.cores.first.projections << Nodes::SqlLiteral.new(select) + stmt.orders << Nodes::SqlLiteral.new('foo, bar') + sql = @visitor.accept(stmt) + sql.must_be_like %{ + SELECT #{select} ORDER BY alias_0__, alias_1__ + } + end + + describe 'Nodes::SelectStatement' do + describe 'limit' do + it 'adds a rownum clause' do + stmt = Nodes::SelectStatement.new + stmt.limit = 10 + sql = @visitor.accept stmt + sql.must_be_like %{ SELECT WHERE ROWNUM <= 10 } + end + + it 'is idempotent' do + stmt = Nodes::SelectStatement.new + stmt.orders << Nodes::SqlLiteral.new('foo') + stmt.limit = 10 + sql = @visitor.accept stmt + sql2 = @visitor.accept stmt + check sql.must_equal sql2 + end + + it 'creates a subquery when there is order_by' do + stmt = Nodes::SelectStatement.new + stmt.orders << Nodes::SqlLiteral.new('foo') + stmt.limit = 10 + sql = @visitor.accept stmt + sql.must_be_like %{ + SELECT * FROM (SELECT ORDER BY foo) WHERE ROWNUM <= 10 + } + end + + it 'creates a subquery when there is DISTINCT' do + stmt = Nodes::SelectStatement.new + stmt.cores.first.projections << Nodes::SqlLiteral.new('DISTINCT id') + stmt.limit = 10 + sql = @visitor.accept stmt + sql.must_be_like %{ + SELECT * FROM (SELECT DISTINCT id) WHERE ROWNUM <= 10 + } + end + + it 'creates a different subquery when there is an offset' do + stmt = Nodes::SelectStatement.new + stmt.limit = 10 + stmt.offset = Nodes::Offset.new(10) + sql = @visitor.accept stmt + sql.must_be_like %{ + SELECT * FROM ( + SELECT raw_sql_.*, rownum raw_rnum_ + FROM (SELECT ) raw_sql_ + WHERE rownum <= 20 + ) + WHERE raw_rnum_ > 10 + } + end + + it 'is idempotent with different subquery' do + stmt = Nodes::SelectStatement.new + stmt.limit = 10 + stmt.offset = Nodes::Offset.new(10) + sql = @visitor.accept stmt + sql2 = @visitor.accept stmt + check sql.must_equal sql2 + end + end + end + end + end +end diff --git a/test/visitors/test_postgres.rb b/test/visitors/test_postgres.rb new file mode 100644 index 0000000000..70b935c185 --- /dev/null +++ b/test/visitors/test_postgres.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +module Arel + module Visitors + describe 'the postgres visitor' do + before do + @visitor = PostgreSQL.new Table.engine + end + + it 'should produce a lock value' do + @visitor.accept(Nodes::Lock.new).must_be_like %{ + FOR UPDATE + } + end + end + end +end diff --git a/test/visitors/test_to_sql.rb b/test/visitors/test_to_sql.rb new file mode 100644 index 0000000000..f7455d7fa3 --- /dev/null +++ b/test/visitors/test_to_sql.rb @@ -0,0 +1,134 @@ +require 'spec_helper' + +module Arel + module Visitors + describe 'the to_sql visitor' do + before do + @visitor = ToSql.new Table.engine + @attr = Table.new(:users)[:id] + end + + describe 'equality' do + it 'should handle false' do + sql = @visitor.accept Nodes::Equality.new(false, false) + sql.must_be_like %{ 'f' = 'f' } + end + + it 'should use the column to quote' do + table = Table.new(:users) + sql = @visitor.accept Nodes::Equality.new(table[:id], '1-fooo') + sql.must_be_like %{ "users"."id" = 1 } + end + end + + it "should visit_DateTime" do + @visitor.accept DateTime.now + end + + it "should visit_Float" do + @visitor.accept 2.14 + end + + it "should visit_Hash" do + @visitor.accept({:a => 1}) + end + + it "should visit_BigDecimal" do + @visitor.accept BigDecimal.new('2.14') + end + + it "should visit_Date" do + @visitor.accept Date.today + end + + it "should visit_Arel_Nodes_And" do + node = Nodes::And.new @attr.eq(10), @attr.eq(11) + @visitor.accept(node).must_be_like %{ + "users"."id" = 10 AND "users"."id" = 11 + } + end + + it "should visit_Arel_Nodes_Or" do + node = Nodes::Or.new @attr.eq(10), @attr.eq(11) + @visitor.accept(node).must_be_like %{ + "users"."id" = 10 OR "users"."id" = 11 + } + end + + it "should visit visit_Arel_Attributes_Time" do + attr = Attributes::Time.new(@attr.relation, @attr.name, @attr.column) + @visitor.accept attr + end + + it "should visit_TrueClass" do + test = @attr.eq(true) + test.left.column.type = :boolean + @visitor.accept(test).must_be_like %{ "users"."id" = 't' } + end + + describe "Nodes::Ordering" do + it "should know how to visit" do + node = @attr.desc + @visitor.accept(node).must_be_like %{ + "users"."id" DESC + } + end + end + + describe "Nodes::In" do + it "should know how to visit" do + node = @attr.in [1, 2, 3] + @visitor.accept(node).must_be_like %{ + "users"."id" IN (1, 2, 3) + } + end + + it "should turn empty right to NULL" do + node = @attr.in [] + @visitor.accept(node).must_be_like %{ + "users"."id" IN (NULL) + } + end + + it 'can handle two dot ranges' do + node = @attr.in 1..3 + @visitor.accept(node).must_be_like %{ + "users"."id" BETWEEN 1 AND 3 + } + end + + it 'can handle three dot ranges' do + node = @attr.in 1...3 + @visitor.accept(node).must_be_like %{ + "users"."id" >= 1 AND "users"."id" < 3 + } + end + + it 'uses the same column for escaping values' do + visitor = Class.new(ToSql) do + attr_accessor :expected + + def quote value, column = nil + raise unless column == expected + super + end + end + in_node = Nodes::In.new @attr, %w{ a b c } + visitor = visitor.new(Table.engine) + visitor.expected = @attr.column + visitor.accept(in_node).must_equal %("users"."id" IN ('a', 'b', 'c')) + end + end + + describe 'Equality' do + it "should escape strings" do + test = @attr.eq 'Aaron Patterson' + test.left.column.type = :string + @visitor.accept(test).must_be_like %{ + "users"."id" = 'Aaron Patterson' + } + end + end + end + end +end |