mysql2/spec/mysql2/client_spec.rb

297 lines
9.6 KiB
Ruby
Raw Normal View History

2010-04-04 19:17:50 +00:00
# encoding: UTF-8
require 'spec_helper'
2010-04-04 19:17:50 +00:00
describe Mysql2::Client do
before(:each) do
@client = Mysql2::Client.new
end
if defined? Encoding
it "should raise an exception on create for invalid encodings" do
lambda {
c = Mysql2::Client.new(:encoding => "fake")
}.should raise_error(Mysql2::Error)
end
end
it "should accept connect flags and pass them to #connect" do
klient = Class.new(Mysql2::Client) do
attr_reader :connect_args
def connect *args
@connect_args ||= []
@connect_args << args
end
end
client = klient.new :flags => Mysql2::Client::FOUND_ROWS
client.connect_args.last.last.should == Mysql2::Client::FOUND_ROWS
end
it "should default flags to 0" do
klient = Class.new(Mysql2::Client) do
attr_reader :connect_args
def connect *args
@connect_args ||= []
@connect_args << args
end
end
client = klient.new
client.connect_args.last.last.should == 0
end
it "should have a global default_query_options hash" do
Mysql2::Client.should respond_to(:default_query_options)
end
2010-04-06 05:47:16 +00:00
it "should be able to connect via SSL options" do
pending("DON'T WORRY, THIS TEST PASSES :) - but is machine-specific. You need to have MySQL running with SSL configured and enabled. Then update the paths in this test to your needs and remove the pending state.")
ssl_client = nil
lambda {
ssl_client = Mysql2::Client.new(
:sslkey => '/path/to/client-key.pem',
:sslcert => '/path/to/client-cert.pem',
:sslca => '/path/to/ca-cert.pem',
:sslcapath => '/path/to/newcerts/',
:sslcipher => 'DHE-RSA-AES256-SHA'
)
}.should_not raise_error(Mysql2::Error)
results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a
results[0]['Variable_name'].should eql('Ssl_cipher')
results[0]['Value'].should_not be_nil
results[0]['Value'].class.should eql(String)
results[1]['Variable_name'].should eql('Ssl_version')
results[1]['Value'].should_not be_nil
results[1]['Value'].class.should eql(String)
end
it "should respond to #close" do
2010-07-30 06:22:42 +00:00
@client.should respond_to(:close)
end
it "should be able to close properly" do
@client.close.should be_nil
end
2010-04-04 19:17:50 +00:00
it "should respond to #query" do
2010-07-30 06:22:42 +00:00
@client.should respond_to(:query)
2010-04-04 19:17:50 +00:00
end
context "#query" do
it "should accept an options hash that inherits from Mysql2::Client.default_query_options" do
@client.query "SELECT 1", :something => :else
@client.query_options.should eql(@client.query_options.merge(:something => :else))
end
it "should return results as a hash by default" do
@client.query("SELECT 1").first.class.should eql(Hash)
end
it "should be able to return results as an array" do
@client.query("SELECT 1", :as => :array).first.class.should eql(Array)
@client.query("SELECT 1").each(:as => :array)
end
it "should be able to return results with symbolized keys" do
@client.query("SELECT 1", :symbolize_keys => true).first.keys[0].class.should eql(Symbol)
end
it "should not allow another query to be sent without fetching a result first" do
@client.query("SELECT 1", :async => true)
lambda {
@client.query("SELECT 1")
}.should raise_error(Mysql2::Error)
end
fix signal handling when waiting on queries This reverts the single-threaded select() optimization from commits 9a63a587c01e4ac116ba79e9cc23a3b3fddf589d and 0190457dbd6d963de1a16a3c5165ad346a0571d8. Under Ruby 1.9, the reverted optimization caused signal handlers to be delayed until the socket became readable. Under Ruby 1.8, matters are worse as receiving a signal during select() causes Errno::EINTR to be raised. There is now a (somewhat fragile) spec for testing signal handling during a "SELECT sleep(2)" query. Furthermore, the performance difference I measured on benchmark/thread_alone.rb was negligible (over 1000 iterations) between the two: Ruby 1.8.7-p249 Rehearsal ---------------------------------------------------- select 0.040000 0.020000 0.060000 ( 0.119448) rb_thread_select 0.050000 0.020000 0.070000 ( 0.132091) ------------------------------------------- total: 0.130000sec user system total real select 0.030000 0.030000 0.060000 ( 0.116471) rb_thread_select 0.050000 0.020000 0.070000 ( 0.119874) Ruby 1.9.2-p0 Rehearsal ---------------------------------------------------- select 0.050000 0.030000 0.080000 ( 0.134208) rb_thread_select 0.080000 0.000000 0.080000 ( 0.141316) ------------------------------------------- total: 0.160000sec user system total real select 0.050000 0.020000 0.070000 ( 0.123325) rb_thread_select 0.060000 0.010000 0.070000 ( 0.124075) Benchmarks were performed on an _empty_ mysql2_test table to maximize the relative time for the select(2)-related code path (vs reading data). The test was run on Debian Lenny, x86_64 and a stock 2.6.35.2 Linux kernel. Hardware was a Core2 Duo @ 1.6GHz with the performance CPU governor while on AC power to eliminate CPU speed fluctuations during the test.
2010-08-19 02:06:05 +00:00
# XXX this test is not deterministic (because Unix signal handling is not)
# and may fail on a loaded system
it "should run signal handlers while waiting for a response" do
mark = {}
trap(:USR1) { mark[:USR1] = Time.now }
begin
mark[:START] = Time.now
pid = fork do
sleep 1 # wait for client "SELECT sleep(2)" query to start
Process.kill(:USR1, Process.ppid)
sleep # wait for explicit kill to prevent GC disconnect
end
@client.query("SELECT sleep(2)")
mark[:END] = Time.now
mark.include?(:USR1).should be_true
(mark[:USR1] - mark[:START]).should >= 1
(mark[:USR1] - mark[:START]).should < 1.1
(mark[:END] - mark[:USR1]).should > 0.9
(mark[:END] - mark[:START]).should >= 2
(mark[:END] - mark[:START]).should < 2.1
Process.kill(:TERM, pid)
Process.waitpid2(pid)
ensure
trap(:USR1, 'DEFAULT')
end
end if RUBY_PLATFORM !~ /mingw|mswin/
end
2010-04-04 19:17:50 +00:00
it "should respond to #escape" do
2010-07-30 06:22:42 +00:00
@client.should respond_to(:escape)
2010-04-04 19:17:50 +00:00
end
it "#escape should return a new SQL-escape version of the passed string" do
@client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
end
it "#escape should return the passed string if nothing was escaped" do
str = "plain"
@client.escape(str).object_id.should eql(str.object_id)
end
it "should respond to #info" do
2010-07-30 06:22:42 +00:00
@client.should respond_to(:info)
2010-04-04 19:17:50 +00:00
end
it "#info should return a hash containing the client version ID and String" do
info = @client.info
info.class.should eql(Hash)
info.should have_key(:id)
info[:id].class.should eql(Fixnum)
info.should have_key(:version)
info[:version].class.should eql(String)
end
if defined? Encoding
context "strings returned by #info" do
it "should default to the connection's encoding if Encoding.default_internal is nil" do
Encoding.default_internal = nil
@client.info[:version].encoding.should eql(Encoding.find('utf-8'))
client2 = Mysql2::Client.new :encoding => 'ascii'
client2.info[:version].encoding.should eql(Encoding.find('us-ascii'))
end
it "should use Encoding.default_internal" do
Encoding.default_internal = Encoding.find('utf-8')
@client.info[:version].encoding.should eql(Encoding.default_internal)
Encoding.default_internal = Encoding.find('us-ascii')
@client.info[:version].encoding.should eql(Encoding.default_internal)
end
end
end
2010-04-04 19:17:50 +00:00
it "should respond to #server_info" do
2010-07-30 06:22:42 +00:00
@client.should respond_to(:server_info)
2010-04-04 19:17:50 +00:00
end
it "#server_info should return a hash containing the client version ID and String" do
server_info = @client.server_info
server_info.class.should eql(Hash)
server_info.should have_key(:id)
server_info[:id].class.should eql(Fixnum)
server_info.should have_key(:version)
server_info[:version].class.should eql(String)
end
if defined? Encoding
context "strings returned by #server_info" do
it "should default to the connection's encoding if Encoding.default_internal is nil" do
Encoding.default_internal = nil
@client.server_info[:version].encoding.should eql(Encoding.find('utf-8'))
client2 = Mysql2::Client.new :encoding => 'ascii'
client2.server_info[:version].encoding.should eql(Encoding.find('us-ascii'))
end
it "should use Encoding.default_internal" do
Encoding.default_internal = Encoding.find('utf-8')
@client.server_info[:version].encoding.should eql(Encoding.default_internal)
Encoding.default_internal = Encoding.find('us-ascii')
@client.server_info[:version].encoding.should eql(Encoding.default_internal)
end
end
end
it "should respond to #socket" do
2010-07-30 06:22:42 +00:00
@client.should respond_to(:socket)
end
it "#socket should return a Fixnum (file descriptor from C)" do
@client.socket.class.should eql(Fixnum)
@client.socket.should_not eql(0)
end
it "should raise a Mysql2::Error exception upon connection failure" do
lambda {
bad_client = Mysql2::Client.new :host => "dfjhdi9wrhw", :username => 'asdfasdf8d2h'
}.should raise_error(Mysql2::Error)
lambda {
good_client = Mysql2::Client.new
}.should_not raise_error(Mysql2::Error)
end
2010-04-07 16:16:30 +00:00
it "threaded queries should be supported" do
threads, results = [], {}
connect = lambda{ Mysql2::Client.new(:host => "localhost", :username => "root") }
Timeout.timeout(0.7) do
5.times {
threads << Thread.new do
results[Thread.current.object_id] = connect.call.query("SELECT sleep(0.5) as result")
end
}
end
threads.each{|t| t.join }
results.keys.sort.should eql(threads.map{|t| t.object_id }.sort)
end
2010-04-07 16:16:30 +00:00
it "evented async queries should be supported" do
# should immediately return nil
@client.query("SELECT sleep(0.1)", :async => true).should eql(nil)
io_wrapper = IO.for_fd(@client.socket)
loops = 0
loop do
if IO.select([io_wrapper], nil, nil, 0.05)
break
else
loops += 1
end
end
# make sure we waited some period of time
(loops >= 1).should be_true
result = @client.async_result
result.class.should eql(Mysql2::Result)
end
2010-04-07 16:36:36 +00:00
context 'write operations api' do
before(:each) do
@client.query "USE test"
@client.query "CREATE TABLE lastIdTest (`id` int(11) NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
end
after(:each) do
@client.query "DROP TABLE lastIdTest"
end
it "should respond to #last_id" do
@client.should respond_to(:last_id)
end
it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
@client.last_id.should eql(0)
@client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
@client.last_id.should eql(1)
end
it "should respond to #last_id" do
@client.should respond_to(:last_id)
end
it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
@client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
@client.affected_rows.should eql(1)
@client.query "UPDATE lastIdTest SET blah=4321 WHERE id=1"
@client.affected_rows.should eql(1)
end
2010-04-07 16:36:36 +00:00
end
end