linkage 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. data/.gitignore +10 -0
  2. data/Gemfile +15 -13
  3. data/Gemfile.lock +67 -37
  4. data/Guardfile +0 -2
  5. data/Rakefile +122 -25
  6. data/lib/linkage/comparator.rb +172 -0
  7. data/lib/linkage/comparators/binary.rb +12 -0
  8. data/lib/linkage/comparators/compare.rb +46 -0
  9. data/lib/linkage/comparators/within.rb +32 -0
  10. data/lib/linkage/configuration.rb +285 -153
  11. data/lib/linkage/data.rb +32 -7
  12. data/lib/linkage/dataset.rb +107 -32
  13. data/lib/linkage/decollation.rb +93 -0
  14. data/lib/linkage/expectation.rb +21 -0
  15. data/lib/linkage/expectations/exhaustive.rb +63 -0
  16. data/lib/linkage/expectations/simple.rb +168 -0
  17. data/lib/linkage/field.rb +30 -4
  18. data/lib/linkage/field_set.rb +6 -3
  19. data/lib/linkage/function.rb +50 -3
  20. data/lib/linkage/functions/binary.rb +30 -0
  21. data/lib/linkage/functions/cast.rb +54 -0
  22. data/lib/linkage/functions/length.rb +29 -0
  23. data/lib/linkage/functions/strftime.rb +12 -11
  24. data/lib/linkage/functions/trim.rb +8 -0
  25. data/lib/linkage/group.rb +20 -0
  26. data/lib/linkage/import_buffer.rb +5 -16
  27. data/lib/linkage/meta_object.rb +139 -0
  28. data/lib/linkage/result_set.rb +74 -17
  29. data/lib/linkage/runner/single_threaded.rb +125 -10
  30. data/lib/linkage/version.rb +3 -0
  31. data/lib/linkage.rb +11 -0
  32. data/linkage.gemspec +16 -121
  33. data/test/config.yml +5 -0
  34. data/test/helper.rb +73 -8
  35. data/test/integration/test_collation.rb +45 -0
  36. data/test/integration/test_configuration.rb +268 -0
  37. data/test/integration/test_cross_linkage.rb +4 -17
  38. data/test/integration/test_dataset.rb +45 -2
  39. data/test/integration/test_dual_linkage.rb +40 -24
  40. data/test/integration/test_functions.rb +22 -0
  41. data/test/integration/test_result_set.rb +85 -0
  42. data/test/integration/test_scoring.rb +84 -0
  43. data/test/integration/test_self_linkage.rb +5 -0
  44. data/test/integration/test_within_comparator.rb +100 -0
  45. data/test/unit/comparators/test_compare.rb +105 -0
  46. data/test/unit/comparators/test_within.rb +57 -0
  47. data/test/unit/expectations/test_exhaustive.rb +111 -0
  48. data/test/unit/expectations/test_simple.rb +303 -0
  49. data/test/unit/functions/test_binary.rb +54 -0
  50. data/test/unit/functions/test_cast.rb +98 -0
  51. data/test/unit/functions/test_length.rb +52 -0
  52. data/test/unit/functions/test_strftime.rb +17 -13
  53. data/test/unit/functions/test_trim.rb +11 -4
  54. data/test/unit/test_comparator.rb +124 -0
  55. data/test/unit/test_configuration.rb +137 -175
  56. data/test/unit/test_data.rb +44 -0
  57. data/test/unit/test_dataset.rb +73 -21
  58. data/test/unit/test_decollation.rb +201 -0
  59. data/test/unit/test_field.rb +38 -14
  60. data/test/unit/test_field_set.rb +12 -8
  61. data/test/unit/test_function.rb +83 -16
  62. data/test/unit/test_group.rb +28 -0
  63. data/test/unit/test_import_buffer.rb +13 -27
  64. data/test/unit/test_meta_object.rb +208 -0
  65. data/test/unit/test_result_set.rb +221 -3
  66. metadata +82 -190
@@ -0,0 +1,100 @@
1
+ require 'helper'
2
+
3
+ module IntegrationTests
4
+ class TestWithinComparator < Test::Unit::TestCase
5
+ test "within comparator with no simple expectations" do
6
+ database_for('sqlite') do |db|
7
+ db.create_table(:foo) { primary_key(:id); Integer(:num) }
8
+ db.create_table(:bar) { primary_key(:id); Integer(:num) }
9
+ db[:foo].import([:id, :num], (1..10).collect { |i| [i, i] })
10
+ db[:bar].import([:id, :num], (1..10).collect { |i| [i, i] })
11
+ end
12
+
13
+ db_opts = database_options_for('sqlite')
14
+ dataset_1 = Linkage::Dataset.new(db_opts, "foo")
15
+ dataset_2 = Linkage::Dataset.new(db_opts, "bar")
16
+ conf = dataset_1.link_with(dataset_2) do
17
+ lhs[:num].must be_within(5).of(rhs[:num])
18
+ save_results_in(db_opts)
19
+ end
20
+
21
+ runner = Linkage::SingleThreadedRunner.new(conf)
22
+ runner.execute
23
+
24
+ database_for('sqlite') do |db|
25
+ assert_equal db[:scores].count, 100
26
+ db[:scores].order(:record_1_id, :record_2_id).each do |score|
27
+ if (score[:record_2_id] - score[:record_1_id]).abs <= 5
28
+ assert_equal 1, score[:score], score.inspect
29
+ else
30
+ assert_equal 0, score[:score], score.inspect
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ test "within comparator with simple expectations" do
37
+ database_for('sqlite') do |db|
38
+ db.create_table(:foo) { primary_key(:id); Integer(:num); String(:parity) }
39
+ db.create_table(:bar) { primary_key(:id); Integer(:num); String(:parity) }
40
+ db[:foo].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
41
+ db[:bar].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
42
+ end
43
+
44
+ db_opts = database_options_for('sqlite')
45
+ dataset_1 = Linkage::Dataset.new(db_opts, "foo")
46
+ dataset_2 = Linkage::Dataset.new(db_opts, "bar")
47
+ conf = dataset_1.link_with(dataset_2) do
48
+ lhs[:parity].must == rhs[:parity]
49
+ lhs[:num].must be_within(5).of(rhs[:num])
50
+ save_results_in(db_opts)
51
+ end
52
+
53
+ runner = Linkage::SingleThreadedRunner.new(conf)
54
+ runner.execute
55
+
56
+ database_for('sqlite') do |db|
57
+ assert_equal db[:scores].count, 50
58
+ db[:scores].order(:record_1_id, :record_2_id).each do |score|
59
+ if (score[:record_2_id] - score[:record_1_id]).abs <= 5
60
+ assert_equal 1, score[:score]
61
+ else
62
+ assert_equal 0, score[:score]
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ test "within comparator with simple expectations and functions" do
69
+ database_for('sqlite') do |db|
70
+ db.create_table(:foo) { primary_key(:id); Integer(:num); String(:parity) }
71
+ db.create_table(:bar) { primary_key(:id); Integer(:num); String(:parity) }
72
+ db[:foo].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
73
+ db[:bar].import([:id, :num, :parity], (1..10).collect { |i| [i, i, i % 2 == 0 ? "even" : "odd"] })
74
+ end
75
+
76
+ db_opts = database_options_for('sqlite')
77
+ dataset_1 = Linkage::Dataset.new(db_opts, "foo")
78
+ dataset_2 = Linkage::Dataset.new(db_opts, "bar")
79
+ conf = dataset_1.link_with(dataset_2) do
80
+ lhs[:parity].must == rhs[:parity]
81
+ cast(lhs[:num], 'integer').must be_within(5).of(cast(rhs[:num], 'integer'))
82
+ save_results_in(db_opts)
83
+ end
84
+
85
+ runner = Linkage::SingleThreadedRunner.new(conf)
86
+ runner.execute
87
+
88
+ database_for('sqlite') do |db|
89
+ assert_equal db[:scores].count, 50
90
+ db[:scores].order(:record_1_id, :record_2_id).each do |score|
91
+ if (score[:record_2_id] - score[:record_1_id]).abs <= 5
92
+ assert_equal 1, score[:score]
93
+ else
94
+ assert_equal 0, score[:score]
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,105 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestCompare < Test::Unit::TestCase
4
+ def self.const_missing(name)
5
+ if Linkage::Comparators.const_defined?(name)
6
+ Linkage::Comparators.const_get(name)
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ test "subclass of Binary" do
13
+ assert_equal Binary, Compare.superclass
14
+ end
15
+
16
+ test "valid parameters" do
17
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
18
+ meta_object_2 = stub('meta object', :object => '>', :ruby_type => { :type => String }, :static? => true, :raw? => true)
19
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
20
+ assert_nothing_raised do
21
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
22
+ end
23
+ end
24
+
25
+ test "score for not equal to" do
26
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
27
+ meta_object_2 = stub('meta object', :object => '!=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
28
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
29
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
30
+ assert_equal 1, comp.score({:foo => 10}, {:bar => 5})
31
+ assert_equal 0, comp.score({:foo => 5}, {:bar => 5})
32
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 5})
33
+ end
34
+
35
+ test "score for greater than" do
36
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
37
+ meta_object_2 = stub('meta object', :object => '>', :ruby_type => { :type => String }, :static? => true, :raw? => true)
38
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
39
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
40
+ assert_equal 1, comp.score({:foo => 10}, {:bar => 5})
41
+ assert_equal 0, comp.score({:foo => 5}, {:bar => 5})
42
+ assert_equal 0, comp.score({:foo => 0}, {:bar => 5})
43
+ end
44
+
45
+ test "score for greater than or equal to" do
46
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
47
+ meta_object_2 = stub('meta object', :object => '>=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
48
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
49
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
50
+ assert_equal 1, comp.score({:foo => 10}, {:bar => 5})
51
+ assert_equal 1, comp.score({:foo => 5}, {:bar => 5})
52
+ assert_equal 0, comp.score({:foo => 0}, {:bar => 5})
53
+ end
54
+
55
+ test "score for less than or equal to" do
56
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
57
+ meta_object_2 = stub('meta object', :object => '<=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
58
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
59
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
60
+ assert_equal 0, comp.score({:foo => 10}, {:bar => 5})
61
+ assert_equal 1, comp.score({:foo => 5}, {:bar => 5})
62
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 5})
63
+ end
64
+
65
+ test "score for less than" do
66
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
67
+ meta_object_2 = stub('meta object', :object => '<', :ruby_type => { :type => String }, :static? => true, :raw? => true)
68
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
69
+ comp = Compare.new(meta_object_1, meta_object_2, meta_object_3)
70
+ assert_equal 0, comp.score({:foo => 10}, {:bar => 5})
71
+ assert_equal 0, comp.score({:foo => 5}, {:bar => 5})
72
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 5})
73
+ end
74
+
75
+ test "registers itself" do
76
+ assert_equal Compare, Linkage::Comparator['compare']
77
+ end
78
+
79
+ test "requires argument from each side" do
80
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
81
+ meta_object_2 = stub('meta object', :object => '>=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
82
+ meta_object_3 = stub('meta object', :name => :bar, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
83
+ assert_raises do
84
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
85
+ end
86
+ end
87
+
88
+ test "requires that 3rd argument has the same type as the first argument" do
89
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
90
+ meta_object_2 = stub('meta object', :object => '>=', :ruby_type => { :type => String }, :static? => true, :raw? => true)
91
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Date }, :static? => false)
92
+ assert_raises do
93
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
94
+ end
95
+ end
96
+
97
+ test "requires raw operator to be >, >=, <=, <, or !=" do
98
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
99
+ meta_object_2 = stub('meta object', :object => 'foo', :ruby_type => { :type => String }, :static? => true, :raw? => true)
100
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
101
+ assert_raises do
102
+ Compare.new(meta_object_1, meta_object_2, meta_object_3)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,57 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestWithin < Test::Unit::TestCase
4
+ def self.const_missing(name)
5
+ if Linkage::Comparators.const_defined?(name)
6
+ Linkage::Comparators.const_get(name)
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ test "subclass of Binary" do
13
+ assert_equal Linkage::Comparators::Binary, Within.superclass
14
+ end
15
+
16
+ test "valid parameters" do
17
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
18
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
19
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
20
+ assert_nothing_raised do
21
+ Within.new(meta_object_1, meta_object_2, meta_object_3)
22
+ end
23
+ end
24
+
25
+ test "score" do
26
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
27
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
28
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Integer }, :static? => false)
29
+ comp = Within.new(meta_object_1, meta_object_2, meta_object_3)
30
+ assert_equal 1, comp.score({:foo => 123}, {:bar => 124})
31
+ assert_equal 1, comp.score({:foo => 124}, {:bar => 123})
32
+ assert_equal 1, comp.score({:foo => 0}, {:bar => 123})
33
+ assert_equal 0, comp.score({:foo => 0}, {:bar => 124})
34
+ end
35
+
36
+ test "registers itself" do
37
+ assert_equal Within, Linkage::Comparator['within']
38
+ end
39
+
40
+ test "requires argument from each side" do
41
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
42
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
43
+ meta_object_3 = stub('meta object', :name => :bar, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
44
+ assert_raises do
45
+ Within.new(meta_object_1, meta_object_2, meta_object_3)
46
+ end
47
+ end
48
+
49
+ test "requires that 3rd argument has the same type as the first argument" do
50
+ meta_object_1 = stub('meta object', :name => :foo, :side => :lhs, :ruby_type => { :type => Integer }, :static? => false)
51
+ meta_object_2 = stub('meta object', :object => 123, :ruby_type => { :type => Fixnum }, :static? => true, :object => 123)
52
+ meta_object_3 = stub('meta object', :name => :bar, :side => :rhs, :ruby_type => { :type => Date }, :static? => false)
53
+ assert_raises do
54
+ Within.new(meta_object_1, meta_object_2, meta_object_3)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,111 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestExhaustive < Test::Unit::TestCase
4
+ test "initialize with comparator, threshold, and mode" do
5
+ comparator = stub('comparator')
6
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
7
+ assert_equal comparator, exp.comparator
8
+ assert_equal 100, exp.threshold
9
+ assert_equal :min, exp.mode
10
+ end
11
+
12
+ test "kind is :self when comparator has same args on both sides" do
13
+ meta_object_1 = stub('meta object 1')
14
+ meta_object_2 = stub('meta object 2')
15
+ meta_object_1.expects(:objects_equal?).with(meta_object_2).returns(true)
16
+ comparator = stub('comparator', {
17
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2]
18
+ })
19
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
20
+ assert_equal :self, exp.kind
21
+ end
22
+
23
+ test "kind is :cross when comparator has args with same dataset but different number of args" do
24
+ meta_object_1 = stub('meta object 1')
25
+ meta_object_2 = stub('meta object 2')
26
+ meta_object_3 = stub('meta object 3')
27
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(true)
28
+ comparator = stub('comparator', {
29
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2, meta_object_3]
30
+ })
31
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
32
+ assert_equal :cross, exp.kind
33
+ end
34
+
35
+ test "kind is :cross when comparator has args with same dataset but different objects" do
36
+ meta_object_1 = stub('meta object 1')
37
+ meta_object_2 = stub('meta object 2')
38
+ meta_object_1.expects(:objects_equal?).with(meta_object_2).returns(false)
39
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(true)
40
+ comparator = stub('comparator', {
41
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2]
42
+ })
43
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
44
+ assert_equal :cross, exp.kind
45
+ end
46
+
47
+ test "kind is :dual when comparator has args with different datasets and different number of args" do
48
+ meta_object_1 = stub('meta object 1')
49
+ meta_object_2 = stub('meta object 2')
50
+ meta_object_3 = stub('meta object 3')
51
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(false)
52
+ comparator = stub('comparator', {
53
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2, meta_object_3]
54
+ })
55
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
56
+ assert_equal :dual, exp.kind
57
+ end
58
+
59
+ test "kind is :dual when comparator has args with different datasets" do
60
+ meta_object_1 = stub('meta object 1')
61
+ meta_object_2 = stub('meta object 2')
62
+ meta_object_1.expects(:objects_equal?).with(meta_object_2).returns(false)
63
+ meta_object_1.expects(:datasets_equal?).with(meta_object_2).returns(false)
64
+ comparator = stub('comparator', {
65
+ :lhs_args => [meta_object_1], :rhs_args => [meta_object_2]
66
+ })
67
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
68
+ assert_equal :dual, exp.kind
69
+ end
70
+
71
+ test "apply_to lhs dataset" do
72
+ dataset = stub('dataset')
73
+ new_dataset = stub('new dataset')
74
+ meta_object_1 = stub('meta object 1', :name => :foo, :to_expr => :foo)
75
+ meta_object_2 = stub('meta object 2', :name => :bar, :to_expr => :bar)
76
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
77
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
78
+ dataset.expects(:select_more).with(:foo.as(:foo)).returns(new_dataset)
79
+ assert_equal new_dataset, exp.apply_to(dataset, :lhs)
80
+ end
81
+
82
+ test "apply_to rhs dataset" do
83
+ dataset = stub('dataset')
84
+ new_dataset = stub('new dataset')
85
+ meta_object_1 = stub('meta object 1', :name => :foo, :to_expr => :foo)
86
+ meta_object_2 = stub('meta object 2', :name => :bar, :to_expr => :bar)
87
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
88
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 100, :min)
89
+ dataset.expects(:select_more).with(:bar.as(:bar)).returns(new_dataset)
90
+ assert_equal new_dataset, exp.apply_to(dataset, :rhs)
91
+ end
92
+
93
+ test "satisfied? with :equal mode" do
94
+ meta_object_1 = stub('meta object 1')
95
+ meta_object_2 = stub('meta object 2')
96
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
97
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 50, :equal)
98
+ assert exp.satisfied?(50)
99
+ assert !exp.satisfied?(123)
100
+ end
101
+
102
+ test "satisfied? with :min mode" do
103
+ meta_object_1 = stub('meta object 1')
104
+ meta_object_2 = stub('meta object 2')
105
+ comparator = stub('comparator', :lhs_args => [meta_object_1], :rhs_args => [meta_object_2])
106
+ exp = Linkage::Expectations::Exhaustive.new(comparator, 50, :min)
107
+ assert exp.satisfied?(50)
108
+ assert exp.satisfied?(55)
109
+ assert !exp.satisfied?(45)
110
+ end
111
+ end
@@ -0,0 +1,303 @@
1
+ require 'helper'
2
+
3
+ class UnitTests::TestSimple < Test::Unit::TestCase
4
+ test "initialize with invalid operator" do
5
+ assert_raises(ArgumentError) do
6
+ Linkage::Expectations::Simple.new(stub(), stub(), :foo)
7
+ end
8
+ end
9
+
10
+ test "creating filter expectation from a dynamic object and a static object" do
11
+ field = stub('meta field', :static? => false, :side => :lhs)
12
+ object = stub('meta object', :static? => true)
13
+ exp = Linkage::Expectations::Simple.create(field, object, :==)
14
+ assert_kind_of Linkage::Expectations::Filter, exp
15
+ assert_equal :lhs, exp.side
16
+ end
17
+
18
+ test "creating expectation from two static objects raises error" do
19
+ object_1 = stub('meta object 1', :static? => true)
20
+ object_2 = stub('meta object 2', :static? => true)
21
+ assert_raises(ArgumentError) do
22
+ Linkage::Expectations::Simple.create(object_1, object_2, :==)
23
+ end
24
+ end
25
+
26
+ test "creating filter expectation from two dynamic objects with the same side" do
27
+ field_1 = stub('meta field 1', :static? => false, :side => :lhs)
28
+ field_2 = stub('meta field 2', :static? => false, :side => :lhs)
29
+ field_1.expects(:datasets_equal?).with(field_2).returns(true)
30
+ exp = Linkage::Expectations::Simple.create(field_1, field_2, :==)
31
+ assert_kind_of Linkage::Expectations::Filter, exp
32
+ assert_equal :lhs, exp.side
33
+ end
34
+
35
+ test "creating filter expectation from two dynamic objects with the same side but different datasets raises error" do
36
+ field_1 = stub('meta field 1', :static? => false, :side => :lhs)
37
+ field_2 = stub('meta field 2', :static? => false, :side => :lhs)
38
+ field_1.expects(:datasets_equal?).with(field_2).returns(false)
39
+ assert_raises(ArgumentError) do
40
+ Linkage::Expectations::Simple.create(field_1, field_2, :==)
41
+ end
42
+ end
43
+
44
+ test "creating self expectation from two dynamic objects with different sides" do
45
+ object_1 = stub('meta object 1', :static? => false, :side => :lhs)
46
+ object_2 = stub('meta object 2', :static? => false, :side => :rhs)
47
+ object_1.expects(:objects_equal?).with(object_2).returns(true)
48
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
49
+ assert_kind_of Linkage::Expectations::Self, exp
50
+ end
51
+
52
+ test "creating cross expectation from two dynamic objects with different sides but same dataset" do
53
+ object_1 = stub('meta object 1', :static? => false, :side => :lhs)
54
+ object_2 = stub('meta object 2', :static? => false, :side => :rhs)
55
+ object_1.expects(:objects_equal?).with(object_2).returns(false)
56
+ object_1.expects(:datasets_equal?).with(object_2).returns(true)
57
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
58
+ assert_kind_of Linkage::Expectations::Cross, exp
59
+ end
60
+
61
+ test "creating dual expectation from two dynamic objects with different sides and datasets" do
62
+ object_1 = stub('meta object 1', :static? => false, :side => :lhs)
63
+ object_2 = stub('meta object 2', :static? => false, :side => :rhs)
64
+ object_1.expects(:objects_equal?).with(object_2).returns(false)
65
+ object_1.expects(:datasets_equal?).with(object_2).returns(false)
66
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
67
+ assert_kind_of Linkage::Expectations::Dual, exp
68
+ end
69
+
70
+ test "apply_to with filter expectation (== operator)" do
71
+ dataset = stub('dataset')
72
+ meta_field_1 = stub('meta field 1', :static? => false, :side => :lhs)
73
+ meta_field_2 = stub('meta field 2', :static? => false, :side => :lhs)
74
+ meta_field_1.stubs(:datasets_equal?).with(meta_field_2).returns(true)
75
+
76
+ exp = Linkage::Expectations::Simple.create(meta_field_1, meta_field_2, :==)
77
+ meta_field_1.expects(:to_expr).returns(:foo)
78
+ meta_field_2.expects(:to_expr).returns(:bar)
79
+ dataset.expects(:filter).with({:foo => :bar}).returns(dataset)
80
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
81
+
82
+ dataset.expects(:filter).never
83
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
84
+ end
85
+
86
+ test "apply_to with filter expectation (!= operator)" do
87
+ dataset = stub('dataset')
88
+ meta_field_1 = stub('meta field 1', :static? => false, :side => :lhs)
89
+ meta_field_2 = stub('meta field 2', :static? => false, :side => :lhs)
90
+ meta_field_1.stubs(:datasets_equal?).with(meta_field_2).returns(true)
91
+
92
+ exp = Linkage::Expectations::Simple.create(meta_field_1, meta_field_2, :'!=')
93
+ meta_field_1.expects(:to_expr).returns(:foo)
94
+ meta_field_2.expects(:to_expr).returns(:bar)
95
+ dataset.expects(:filter).with(~{:foo => :bar}).returns(dataset)
96
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
97
+
98
+ dataset.expects(:filter).never
99
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
100
+ end
101
+
102
+ test "apply_to with filter expectation (> operator)" do
103
+ dataset = stub('dataset')
104
+ meta_field_1 = stub('meta field 1', :static? => false, :side => :lhs)
105
+ meta_field_2 = stub('meta field 2', :static? => false, :side => :lhs)
106
+ meta_field_1.stubs(:datasets_equal?).with(meta_field_2).returns(true)
107
+ exp = Linkage::Expectations::Simple.create(meta_field_1, meta_field_2, :>)
108
+
109
+ identifier_1 = Sequel::SQL::Identifier.new(:foo)
110
+ meta_field_1.expects(:to_identifier).returns(identifier_1)
111
+ identifier_2 = Sequel::SQL::Identifier.new(:bar)
112
+ meta_field_2.expects(:to_identifier).returns(identifier_2)
113
+ expected = Sequel::SQL::BooleanExpression.new(:>, identifier_1, identifier_2)
114
+
115
+ dataset.expects(:filter).with(expected).returns(dataset)
116
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
117
+
118
+ dataset.expects(:filter).never
119
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
120
+ end
121
+
122
+ test "apply_to with self expectation" do
123
+ dataset = stub('dataset')
124
+ object_1 = stub('meta object 1', {
125
+ :static? => false, :side => :lhs, :dataset => dataset,
126
+ :to_expr => :foo
127
+ })
128
+ object_2 = stub('meta object 2', {
129
+ :static? => false, :side => :rhs, :dataset => dataset,
130
+ :to_expr => :foo
131
+ })
132
+ object_1.expects(:objects_equal?).with(object_2).returns(true)
133
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
134
+
135
+ merged_field = stub('merged field', :name => :foo)
136
+ object_1.expects(:merge).with(object_2).returns(merged_field)
137
+ dataset.expects(:group_match_more).with({:meta_object => object_1, :alias => :foo}).returns(dataset)
138
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
139
+
140
+ dataset.expects(:group_match_more).with({:meta_object => object_2, :alias => :foo}).returns(dataset)
141
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
142
+ end
143
+
144
+ test "apply_to with cross expectation" do
145
+ dataset = stub('dataset')
146
+ object_1 = stub('meta object 1', {
147
+ :static? => false, :side => :lhs, :dataset => dataset,
148
+ :to_expr => :foo
149
+ })
150
+ object_2 = stub('meta object 2', {
151
+ :static? => false, :side => :rhs, :dataset => dataset,
152
+ :to_expr => :bar
153
+ })
154
+ object_1.stubs(:objects_equal?).with(object_2).returns(false)
155
+ object_1.stubs(:datasets_equal?).with(object_2).returns(true)
156
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
157
+
158
+ merged_field = stub('merged field', :name => :foo_bar)
159
+ object_1.expects(:merge).with(object_2).returns(merged_field)
160
+ dataset.expects(:group_match_more).with({:meta_object => object_1, :alias => :foo_bar}).returns(dataset)
161
+ assert_equal dataset, exp.apply_to(dataset, :lhs)
162
+
163
+ dataset.expects(:group_match_more).with({:meta_object => object_2, :alias => :foo_bar}).returns(dataset)
164
+ assert_equal dataset, exp.apply_to(dataset, :rhs)
165
+ end
166
+
167
+ test "apply_to with dual expectation" do
168
+ dataset_1 = stub('dataset 1')
169
+ object_1 = stub('meta object 1', {
170
+ :static? => false, :side => :lhs, :dataset => dataset_1,
171
+ :to_expr => :foo
172
+ })
173
+ dataset_2 = stub('dataset 2')
174
+ object_2 = stub('meta object 2', {
175
+ :static? => false, :side => :rhs, :dataset => dataset_2,
176
+ :to_expr => :foo
177
+ })
178
+ object_1.stubs(:objects_equal?).with(object_2).returns(false)
179
+ object_1.stubs(:datasets_equal?).with(object_2).returns(false)
180
+ exp = Linkage::Expectations::Simple.create(object_1, object_2, :==)
181
+
182
+ merged_field = stub('merged field', :name => :foo)
183
+ object_1.expects(:merge).with(object_2).returns(merged_field)
184
+ dataset_1.expects(:group_match_more).with({:meta_object => object_1, :alias => :foo}).returns(dataset_1)
185
+ assert_equal dataset_1, exp.apply_to(dataset_1, :lhs)
186
+
187
+ dataset_2.expects(:group_match_more).with({:meta_object => object_2, :alias => :foo}).returns(dataset_2)
188
+ assert_equal dataset_2, exp.apply_to(dataset_2, :rhs)
189
+ end
190
+
191
+ test "exactly! wraps each side inside the binary function" do
192
+ dataset_1 = stub('dataset 1')
193
+ field_1 = stub_field('field 1', :dataset => dataset_1)
194
+ meta_object_1 = stub('meta object 1', :static? => false, :side => :lhs,
195
+ :object => field_1, :dataset => dataset_1)
196
+
197
+ dataset_2 = stub('dataset 2')
198
+ field_2 = stub_field('field 2', :dataset => dataset_2)
199
+ meta_object_2 = stub('meta object 2', :static? => false, :side => :rhs,
200
+ :object => field_2, :dataset => dataset_2)
201
+ meta_object_1.stubs(:objects_equal?).with(meta_object_2).returns(true)
202
+
203
+ exp = Linkage::Expectations::Simple.create(meta_object_1, meta_object_2, :==)
204
+
205
+ klass = Linkage::Functions::Binary
206
+ func_1 = stub('binary function 1')
207
+ klass.expects(:new).with(field_1, :dataset => dataset_1).returns(func_1)
208
+ func_2 = stub('binary function 2')
209
+ klass.expects(:new).with(field_2, :dataset => dataset_2).returns(func_2)
210
+
211
+ new_meta_object_1 = stub('new meta object 1')
212
+ Linkage::MetaObject.expects(:new).with(func_1, :lhs).returns(new_meta_object_1)
213
+ new_meta_object_2 = stub('new meta object 2')
214
+ Linkage::MetaObject.expects(:new).with(func_2, :rhs).returns(new_meta_object_2)
215
+
216
+ exp.exactly!
217
+ # LAZY: assume the expectation set its meta objects appropriately
218
+ end
219
+
220
+ test "#same_except_side with two filter expectations" do
221
+ field = stub_field('foo')
222
+ meta_object_1 = stub('meta field 1', {
223
+ :static? => false, :side => :lhs, :object => field
224
+ })
225
+ meta_object_2 = stub('meta object', :static? => true)
226
+ meta_object_3 = stub('meta field 2', {
227
+ :static? => false, :side => :rhs, :object => field
228
+ })
229
+
230
+ exp_1 = Linkage::Expectations::Simple.create(meta_object_1, meta_object_2, :==)
231
+ exp_2 = Linkage::Expectations::Simple.create(meta_object_3, meta_object_2, :==)
232
+
233
+ meta_object_1.expects(:objects_equal?).with(meta_object_3).returns(true)
234
+ meta_object_2.expects(:objects_equal?).with(meta_object_2).returns(true)
235
+ assert exp_1.same_except_side?(exp_2)
236
+ end
237
+
238
+ test "#decollation_needed? is false when comparing non-string objects" do
239
+ dataset_1 = stub('dataset 1')
240
+ object_1 = stub('meta object 1', {
241
+ :static? => false, :side => :lhs, :dataset => dataset_1,
242
+ })
243
+ dataset_2 = stub('dataset 2')
244
+ object_2 = stub('meta object 2', {
245
+ :static? => false, :side => :rhs, :dataset => dataset_2,
246
+ })
247
+ merged_field = stub('merged field', :ruby_type => {:type => Fixnum})
248
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
249
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
250
+ assert !exp.decollation_needed?
251
+ end
252
+
253
+ test "#decollation_needed? is false when comparing two dynamic string objects with the same database type and same collation" do
254
+ dataset_1 = stub('dataset 1')
255
+ object_1 = stub('meta object 1', {
256
+ :static? => false, :side => :lhs, :dataset => dataset_1,
257
+ :collation => 'foo', :database_type => :mysql
258
+ })
259
+ dataset_2 = stub('dataset 2')
260
+ object_2 = stub('meta object 2', {
261
+ :static? => false, :side => :rhs, :dataset => dataset_2,
262
+ :collation => 'foo', :database_type => :mysql
263
+ })
264
+ merged_field = stub('merged field', :ruby_type => {:type => String})
265
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
266
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
267
+ assert !exp.decollation_needed?
268
+ end
269
+
270
+ test "#decollation_needed? is true when comparing two dynamic string objects with the same database type but different collations" do
271
+ dataset_1 = stub('dataset 1')
272
+ object_1 = stub('meta object 1', {
273
+ :static? => false, :side => :lhs, :dataset => dataset_1,
274
+ :collation => 'foo', :database_type => :mysql
275
+ })
276
+ dataset_2 = stub('dataset 2')
277
+ object_2 = stub('meta object 2', {
278
+ :static? => false, :side => :rhs, :dataset => dataset_2,
279
+ :collation => 'bar', :database_type => :mysql
280
+ })
281
+ merged_field = stub('merged field', :ruby_type => {:type => String})
282
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
283
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
284
+ assert exp.decollation_needed?
285
+ end
286
+
287
+ test "#decollation_needed? is true when comparing two dynamic string objects with same collation but different database types" do
288
+ dataset_1 = stub('dataset 1')
289
+ object_1 = stub('meta object 1', {
290
+ :static? => false, :side => :lhs, :dataset => dataset_1,
291
+ :collation => 'foo', :database_type => :foo
292
+ })
293
+ dataset_2 = stub('dataset 2')
294
+ object_2 = stub('meta object 2', {
295
+ :static? => false, :side => :rhs, :dataset => dataset_2,
296
+ :collation => 'foo', :database_type => :bar
297
+ })
298
+ merged_field = stub('merged field', :ruby_type => {:type => String})
299
+ object_1.stubs(:merge).with(object_2).returns(merged_field)
300
+ exp = Linkage::Expectations::Dual.new(object_1, object_2, :==)
301
+ assert exp.decollation_needed?
302
+ end
303
+ end