rsql 0.2.7 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+
5
+ begin
6
+ require 'rubygems'
7
+ rescue LoadError
8
+ end
9
+ require 'mocha'
10
+
11
+ $: << File.expand_path(File.join(File.dirname(__FILE__),'..','lib')) << File.dirname(__FILE__)
12
+ require 'rsql/mysql_results.rb'
13
+ require 'rsql/eval_context.rb'
14
+ require 'rsql/commands.rb'
15
+
16
+ class TestCommands < Test::Unit::TestCase
17
+
18
+ include RSQL
19
+
20
+ def setup
21
+ @orig_stdout = $stdout
22
+ $stdout = @strout = StringIO.new
23
+ @ctx = EvalContext.new
24
+ @conn = mock('Mysql')
25
+ @conn.expects(:list_dbs).returns([])
26
+ MySQLResults.conn = @conn
27
+ end
28
+
29
+ def teardown
30
+ $stdout = @orig_stdout
31
+ end
32
+
33
+ def test_simple_ruby
34
+ cmds = Commands.new('. puts :hello', :display_by_column)
35
+ assert_equal(false, cmds.empty?)
36
+ assert_not_nil(cmds.last)
37
+ cmds.run!(@ctx)
38
+ assert_equal('hello', @strout.string.chomp)
39
+ end
40
+
41
+ def test_simple_sql
42
+ cmds = Commands.new('do some silly stuff', :display_by_column)
43
+ @conn.expects(:query).with(instance_of(String)).returns(nil)
44
+ @conn.expects(:affected_rows).returns(1)
45
+ cmds.run!(@ctx)
46
+ assert_match(/Query OK, 1 row affected/, @strout.string)
47
+ end
48
+
49
+ def test_separators
50
+ cmds = Commands.new('. puts :hello\; puts :world;', :display_by_column)
51
+ cmds.run!(@ctx)
52
+ assert_equal('hello'+$/+'world', @strout.string.chomp)
53
+
54
+ # make sure our logic to handle eval'd blocks with args works
55
+ @strout.string = ''
56
+ cmds = Commands.new('. Proc.new{|a| puts a.inspect} | @results.value.call(:fancy)', :display_by_column)
57
+ cmds.run!(@ctx)
58
+ assert_equal(':fancy', @strout.string.chomp)
59
+ end
60
+
61
+ def test_multiple
62
+ @conn.expects(:query).with('one thing').returns(nil)
63
+ @conn.expects(:affected_rows).returns(1)
64
+ cmds = Commands.new('. "one thing" ; . puts :hello.inspect', :display_by_column)
65
+ cmds.run!(@ctx)
66
+ assert_match(/^QueryOK,1rowaffected\(\d+.\d+sec\):hello$/,
67
+ @strout.string.gsub(/\s+/,''))
68
+ end
69
+
70
+ def test_bangs
71
+ cmds = Commands.new('silly stuff ! this => that', :display_by_column)
72
+ @conn.expects(:query).with('silly stuff').returns(nil)
73
+ @conn.expects(:affected_rows).returns(13)
74
+ cmds.run!(@ctx)
75
+ assert_match(/Query OK, 13 rows affected/, @strout.string)
76
+
77
+ # now test logic to continue if it _doesn't_ look like a bang
78
+ cmds = Commands.new('silly stuff ! more things', :display_by_column)
79
+ @conn.expects(:query).with('silly stuff ! more things').returns(nil)
80
+ @conn.expects(:affected_rows).returns(4)
81
+ cmds.run!(@ctx)
82
+ assert_match(/Query OK, 4 rows affected/, @strout.string)
83
+ end
84
+
85
+ end # class TestCommands
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'tempfile'
5
+
6
+ begin
7
+ require 'rubygems'
8
+ rescue LoadError
9
+ end
10
+ require 'mocha'
11
+
12
+ $: << File.expand_path(File.join(File.dirname(__FILE__),'..','lib')) << File.dirname(__FILE__)
13
+ require 'rsql/mysql_results.rb'
14
+ require 'rsql/eval_context.rb'
15
+
16
+ class TestEvalContext < Test::Unit::TestCase
17
+
18
+ include RSQL
19
+
20
+ def setup
21
+ @conn = mock('Mysql')
22
+ @conn.expects(:list_dbs).returns([])
23
+ @conn.expects(:query).with(instance_of(String)).returns(nil)
24
+ @conn.expects(:affected_rows).returns(0)
25
+ MySQLResults.conn = @conn
26
+ @ctx = EvalContext.new
27
+ @ctx.load(File.join(File.dirname(__FILE__),'..','example.rsqlrc'))
28
+ end
29
+
30
+ def test_reload
31
+ orig = $stdout
32
+ $stdout = out = StringIO.new
33
+ @conn.expects(:query).with(instance_of(String)).returns(nil)
34
+ @conn.expects(:affected_rows).returns(0)
35
+ @ctx.safe_eval('reload', nil, out)
36
+ assert_match(/loaded: .+?example.rsqlrc/, out.string)
37
+ ensure
38
+ $stdout = orig
39
+ end
40
+
41
+ def test_eval
42
+ out = StringIO.new
43
+
44
+ # test a simple string registration
45
+ val = @ctx.safe_eval('cleanup_example', nil, out)
46
+ assert_equal('DROP TEMPORARY TABLE IF EXISTS rsql_example;', val)
47
+ assert_equal(true, out.string.empty?)
48
+
49
+ # test a block registration
50
+ val = @ctx.safe_eval('fill_table', nil, out)
51
+ assert_match(/(INSERT IGNORE INTO .+?){10}/, val)
52
+ assert_equal(true, out.string.empty?)
53
+
54
+ # test results handling and output redirection
55
+ res = mock
56
+ res.expects(:each_hash).yields({'value' => '2352'})
57
+ val = @ctx.safe_eval('to_report', res, out)
58
+ assert_equal(nil, val)
59
+ assert_equal("There are 1 small values and 0 big values.", out.string.chomp)
60
+ end
61
+
62
+ def test_list
63
+ out = StringIO.new
64
+ val = @ctx.safe_eval('list', nil, out)
65
+ assert_match(/usage\s+description/, out.string)
66
+ end
67
+
68
+ def test_params
69
+ val = @ctx.safe_eval('params("ft", @registrations[:fill_table].block)', nil, nil)
70
+ assert_equal('', val)
71
+ val = @ctx.safe_eval('params("sv", @registrations[:save_values].block)', nil, nil)
72
+ assert_equal('(fn)', val)
73
+ end
74
+
75
+ def test_desc
76
+ out = StringIO.new
77
+ err = StringIO.new
78
+ orig_err = $stderr
79
+
80
+ $stderr = err
81
+ val = @ctx.safe_eval('desc max_rows', nil, out)
82
+ $sterr = orig_err
83
+ assert_equal('', out.string)
84
+ assert_equal('refusing to describe EvalContext#max_rows',
85
+ err.string.chomp)
86
+
87
+ err.string = ''
88
+ $stderr = err
89
+ val = @ctx.safe_eval('desc :sldkfjas', nil, out)
90
+ $sterr = orig_err
91
+ assert_equal('', out.string)
92
+ assert_equal('nothing registered as sldkfjas', err.string.chomp)
93
+
94
+ err.string = ''
95
+ $stderr = err
96
+ val = @ctx.safe_eval('desc :version', nil, out)
97
+ $sterr = orig_err
98
+ assert_equal('', out.string)
99
+ assert_equal('refusing to describe the version method', err.string.chomp)
100
+
101
+ err.string = ''
102
+ out.string = ''
103
+ val = @ctx.safe_eval('desc :cleanup_example', nil, out)
104
+ assert_equal('', err.string)
105
+ assert_match(
106
+ /^\s*\[.+\/example.rsqlrc:\d+\]\s+DROP TEMPORARY TABLE IF EXISTS \#\{@rsql_table\}\s*$/,
107
+ out.string)
108
+
109
+ out.string = ''
110
+ val = @ctx.safe_eval('desc :to_report', nil, out)
111
+ lines = out.string.split($/).select{|l|l.any?}
112
+ assert_match(/^\[.+\/example.rsqlrc:\d+\]$/, lines[0])
113
+ assert_match(/^register .+ do$/, lines[1])
114
+ assert_match(/^\s+puts/, lines[-2])
115
+ assert_match(/^end$/, lines[-1])
116
+ assert_equal(13, lines.size)
117
+ end
118
+
119
+ def test_complete
120
+ out = @ctx.complete('')
121
+ assert_equal(20, out.size, out.inspect)
122
+ assert_equal(['version'], @ctx.complete('v'))
123
+ assert_equal(['.version'], @ctx.complete('.v'))
124
+ end
125
+
126
+ def test_bang_eval
127
+ @ctx.bangs = {'time' => :relative_time}
128
+ t = (Time.now - 2532435).to_s
129
+ assert_equal(t, @ctx.bang_eval('do no harm', t))
130
+ assert_equal(' 29 days ago', @ctx.bang_eval('time', t))
131
+ end
132
+
133
+ def test_humanize
134
+ out = StringIO.new
135
+ assert_equal(' 9.16 GB',
136
+ @ctx.safe_eval('humanize_bytes(9832742324)', nil, out))
137
+ assert_equal(9835475108,
138
+ @ctx.safe_eval('dehumanize_bytes(" 9.16 GB")', nil, out))
139
+ assert_equal(' 20.9%',
140
+ @ctx.safe_eval('humanize_percentage(0.209384)', nil, out))
141
+ assert(out.string.empty?)
142
+ end
143
+
144
+ def test_hex
145
+ bin = ''
146
+ 100.times{|i| bin << i}
147
+ hex = @ctx.to_hexstr(bin)
148
+ assert_equal('0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' <<
149
+ '... (68 bytes hidden)', hex)
150
+
151
+ out = StringIO.new
152
+ assert_equal('0x1234', @ctx.safe_eval('hexify("1234")', nil, out))
153
+ assert_equal('0x1234', @ctx.safe_eval('hexify("0x1234")', nil, out))
154
+ assert_equal('0x1234', @ctx.safe_eval('hexify(0x1234)', nil, out))
155
+ assert(out.string.empty?)
156
+ end
157
+
158
+ def test_safe_save
159
+ out = StringIO.new
160
+ @ctx.safe_eval('@mystuff = {:one => 1, :two => 2}', nil, out)
161
+ tf = Tempfile.new('mystuff')
162
+ @ctx.safe_eval("safe_save(@mystuff, '#{tf.path}')", nil, out)
163
+ tf = tf.path + '.yml'
164
+ assert_equal("Saved: #{tf}", out.string.chomp)
165
+ assert_equal({:one => 1, :two => 2}, YAML.load_file(tf))
166
+
167
+ # now make sure it keeps one backup copy
168
+ out = StringIO.new
169
+ @ctx.safe_eval('@mystuff = {:one => 1}', nil, out)
170
+ @ctx.safe_eval("safe_save(@mystuff, '#{tf}')", nil, out)
171
+ assert_equal("Saved: #{tf}", out.string.chomp)
172
+ assert_equal({:one => 1}, YAML.load_file(tf))
173
+ assert_equal({:one => 1, :two => 2}, YAML.load_file(tf+'~'))
174
+ ensure
175
+ File.unlink(tf) if File.exists?(tf)
176
+ File.unlink(tf+'~') if File.exists?(tf+'~')
177
+ end
178
+
179
+ end # class TestEvalContext
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+
5
+ begin
6
+ require 'rubygems'
7
+ rescue LoadError
8
+ end
9
+ require 'mocha'
10
+
11
+ $: << File.expand_path(File.join(File.dirname(__FILE__),'..','lib')) << File.dirname(__FILE__)
12
+ require 'rsql/mysql_results.rb'
13
+
14
+ class TestMySQLResults < Test::Unit::TestCase
15
+
16
+ include RSQL
17
+
18
+ def setup
19
+ MySQLResults.conn = nil
20
+ MySQLResults.database_name = nil
21
+ MySQLResults.reset_cache
22
+ end
23
+
24
+ def test_databases
25
+ assert_equal([], MySQLResults.databases)
26
+ conn = mock('Mysql')
27
+ conn.expects(:list_dbs).returns(['accounts'])
28
+ conn.expects(:select_db)
29
+ conn.expects(:list_tables).returns([])
30
+ MySQLResults.conn = conn
31
+ assert_equal(['accounts'], MySQLResults.databases)
32
+ end
33
+
34
+ def test_tables
35
+ assert_equal([], MySQLResults.tables)
36
+ MySQLResults.reset_cache
37
+
38
+ conn = mock('Mysql')
39
+ conn.expects(:list_dbs).returns(['accounts'])
40
+ conn.expects(:select_db)
41
+ conn.expects(:list_tables).returns(['users','groups'])
42
+ MySQLResults.conn = conn
43
+
44
+ assert_equal([], MySQLResults.tables)
45
+ MySQLResults.database_name = 'accounts'
46
+ assert_equal(['groups','users'], MySQLResults.tables)
47
+ assert_equal(['groups','users'], MySQLResults.tables('accounts'))
48
+ end
49
+
50
+ def test_complete
51
+ assert_equal([], MySQLResults.complete(nil))
52
+
53
+ conn = mock('Mysql')
54
+ conn.expects(:list_dbs).returns(['Accounts','Devices','Locations'])
55
+ conn.expects(:select_db).times(3)
56
+ tbls = sequence(:tbls)
57
+ conn.expects(:list_tables).in_sequence(tbls).returns(['Prefs','Names'])
58
+ conn.expects(:list_tables).in_sequence(tbls).returns(['IPs'])
59
+ conn.expects(:list_tables).in_sequence(tbls).returns(['Street','City','State'])
60
+ MySQLResults.conn = conn
61
+
62
+ assert_equal(['Accounts','Devices','Locations'], MySQLResults.complete(''))
63
+ assert_equal(['Accounts'], MySQLResults.complete('a'))
64
+
65
+ MySQLResults.database_name = 'Accounts'
66
+ assert_equal(['Devices','Locations','Names','Prefs'], MySQLResults.complete(''))
67
+ assert_equal(['Names'], MySQLResults.complete('n'))
68
+
69
+ assert_equal(['Accounts.Names','Accounts.Prefs'], MySQLResults.complete('accounts.'))
70
+ end
71
+
72
+ def test_query
73
+ f1 = mock('f1')
74
+ f1.expects(:name).returns('c1').times(12)
75
+ f1.expects(:type).returns(1).times(2)
76
+ f2 = mock('f2')
77
+ f2.expects(:name).returns('c2').times(11)
78
+ f2.expects(:type).returns(1).times(2)
79
+
80
+ res = mock('results')
81
+ res.expects(:num_rows).returns(2).times(2)
82
+ res.expects(:fetch_fields).returns([f1,f2])
83
+
84
+ rows = sequence(:rows)
85
+ res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
86
+ res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
87
+ res.expects(:fetch_row).in_sequence(rows).returns(nil)
88
+
89
+ conn = mock('Mysql')
90
+ conn.expects(:list_dbs).returns([])
91
+ conn.expects(:query).with(instance_of(String)).returns(res)
92
+ conn.expects(:affected_rows).returns(1)
93
+ MySQLResults.conn = conn
94
+
95
+ bangs = mock('bangs')
96
+ bangs.expects(:bang_eval).with(instance_of(String),instance_of(String)).
97
+ returns('val').times(4)
98
+
99
+ mres = MySQLResults.query('ignored', bangs)
100
+ assert_equal('ignored', mres.sql)
101
+ assert_equal(true, mres.any?)
102
+ assert_equal(false, mres.empty?)
103
+ assert_equal(2, mres.num_rows)
104
+ assert_equal({"c1"=>"val", "c2"=>"val"}, mres[0])
105
+ assert_equal({"c1"=>"val", "c2"=>"val"}, mres[1])
106
+ assert_equal(nil, mres[2])
107
+
108
+ cnt = 0
109
+ mres.each_hash do |row|
110
+ cnt += 1
111
+ assert_equal({"c1"=>"val", "c2"=>"val"}, row)
112
+ end
113
+ assert_equal(2, cnt)
114
+
115
+ dout = StringIO.new
116
+ mres.display_by_column(dout)
117
+ assert_match(/^c1c2--------valvalvalval--------2rowsinset/,
118
+ dout.string.gsub(/\s+/,''))
119
+
120
+ dout = StringIO.new
121
+ mres.display_by_batch(dout)
122
+ assert_equal('valvalvalval', dout.string.gsub(/\s+/,''))
123
+
124
+ dout = StringIO.new
125
+ mres.display_by_line(dout)
126
+ assert_match(/^\*+1.row\*+c1:valc2:val\*+2.row\*+c1:valc2:val2rowsinset/,
127
+ dout.string.gsub(/\s+/,''))
128
+ end
129
+
130
+ def test_grep
131
+ f1 = mock('f1')
132
+ f1.stubs(:name).returns('c1')
133
+ f1.stubs(:type).returns(1)
134
+ f2 = mock('f2')
135
+ f2.stubs(:name).returns('c2')
136
+ f2.stubs(:type).returns(1)
137
+
138
+ res = mock('results')
139
+ res.stubs(:num_rows).returns(2)
140
+ res.stubs(:fetch_fields).returns([f1,f2])
141
+
142
+ rows = sequence(:rows)
143
+ res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
144
+ res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
145
+ res.expects(:fetch_row).in_sequence(rows).returns(nil)
146
+
147
+ conn = mock('Mysql')
148
+ conn.stubs(:list_dbs).returns([])
149
+ conn.stubs(:query).with(instance_of(String)).returns(res)
150
+ conn.stubs(:affected_rows).returns(1)
151
+ MySQLResults.reset_history
152
+ MySQLResults.conn = conn
153
+
154
+ mres = MySQLResults.query('ignored1', eval_context=nil, raw=true)
155
+ assert_equal(false, mres.grep(/val/))
156
+
157
+ rows = sequence(:rows)
158
+ res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
159
+ res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
160
+ res.expects(:fetch_row).in_sequence(rows).returns(nil)
161
+
162
+ mres = MySQLResults.query('ignored2', eval_context=nil, raw=true)
163
+ assert_equal(true, mres.grep('v1', :fixed))
164
+ assert_equal("\e[31;1mv1\e[0m.1", mres[0]['c1'])
165
+ assert_equal("\e[31;1mv1\e[0m.2", mres[0]['c2'])
166
+
167
+ rows = sequence(:rows)
168
+ res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
169
+ res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
170
+ res.expects(:fetch_row).in_sequence(rows).returns(nil)
171
+
172
+ mres = MySQLResults.query('ignored3', eval_context=nil, raw=true)
173
+ assert_equal(false, mres.grep('v', :fixed, :inverse))
174
+
175
+ rows = sequence(:rows)
176
+ res.expects(:fetch_row).in_sequence(rows).returns(['v1.1','v1.2'])
177
+ res.expects(:fetch_row).in_sequence(rows).returns(['v2.1','v2.2'])
178
+ res.expects(:fetch_row).in_sequence(rows).returns(nil)
179
+
180
+ mres = MySQLResults.query('ignored4', eval_context=nil, raw=true)
181
+ assert_equal(true, mres.grep('v1.1', :nocolor))
182
+ assert_equal("v1.1", mres[0]['c1'])
183
+
184
+ # fixme: technically should be in it's only test...
185
+ cmds = []
186
+ 4.times{|i| cmds << "ignored#{i+1}"}
187
+ assert_equal(cmds, MySQLResults.history)
188
+ assert_equal([cmds.last], MySQLResults.history(1))
189
+ assert_equal(cmds, MySQLResults.history(15))
190
+ end
191
+
192
+ end # class TestMySQLResults