From d0df7f2b12b201edbef0d59360224e68f9a0a447 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 14 Oct 2007 20:46:06 +0000 Subject: Memcached sessions: add session data on initialization; don't silently discard exceptions; add unit tests. Closes #9823. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7885 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../controller/session/mem_cache_store_test.rb | 178 +++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 actionpack/test/controller/session/mem_cache_store_test.rb (limited to 'actionpack/test') diff --git a/actionpack/test/controller/session/mem_cache_store_test.rb b/actionpack/test/controller/session/mem_cache_store_test.rb new file mode 100644 index 0000000000..db408bf21c --- /dev/null +++ b/actionpack/test/controller/session/mem_cache_store_test.rb @@ -0,0 +1,178 @@ +require "#{File.dirname(__FILE__)}/../../abstract_unit" +require 'action_controller/cgi_process' +require 'action_controller/cgi_ext' +require "mocha" + + +class CGI::Session + def cache + dbman.instance_variable_get(:@cache) + end +end + + +class MemCacheStoreTest < Test::Unit::TestCase + + SESSION_KEY_RE = /^session:[0-9a-z]+/ + CONN_TEST_KEY = 'connection_test' + MULTI_TEST_KEY = '0123456789' + TEST_DATA = 'Hello test' + + def self.get_mem_cache_if_available + begin + require 'memcache' + cache = MemCache.new('127.0.0.1') + # Test availability of the connection + cache.set(CONN_TEST_KEY, 1) + unless cache.get(CONN_TEST_KEY) == 1 + puts 'Warning: memcache server available but corrupted.' + return nil + end + rescue LoadError, MemCache::MemCacheError + return nil + end + return cache + end + + CACHE = get_mem_cache_if_available + + + def test_initialization + assert_raise(ArgumentError) { new_session('session_id' => '!invalid_id') } + new_session do |s| + assert_equal Hash.new, s.cache.get('session:' + s.session_id) + end + end + + + def test_storage + d = rand(0xffff) + new_session do |s| + session_key = 'session:' + s.session_id + unless CACHE + s.cache.expects(:get).with(session_key) \ + .returns(:test => d) + s.cache.expects(:set).with(session_key, + has_entry(:test, d), + 0) + end + s[:test] = d + s.close + assert_equal d, s.cache.get(session_key)[:test] + assert_equal d, s[:test] + end + end + + + def test_deletion + new_session do |s| + session_key = 'session:' + s.session_id + unless CACHE + s.cache.expects(:delete) + s.cache.expects(:get).with(session_key) \ + .returns(nil) + end + s[:test] = rand(0xffff) + s.delete + assert_nil s.cache.get(session_key) + end + end + + + def test_other_session_retrieval + new_session do |sa| + unless CACHE + sa.cache.expects(:set).with('session:' + sa.session_id, + has_entry(:test, TEST_DATA), + 0) + end + sa[:test] = TEST_DATA + sa.close + new_session('session_id' => sa.session_id) do |sb| + unless CACHE + sb.cache.expects(:[]).with('session:' + sb.session_id) \ + .returns(:test => TEST_DATA) + end + assert_equal(TEST_DATA, sb[:test]) + end + end + end + + + def test_multiple_sessions + s_slots = Array.new(10) + operation = :write + last_data = nil + reads = writes = 0 + 50.times do + current = rand(10) + s_slots[current] ||= new_session('session_id' => MULTI_TEST_KEY, + 'new_session' => true) + s = s_slots[current] + case operation + when :write + last_data = rand(0xffff) + unless CACHE + s.cache.expects(:set).with('session:' + MULTI_TEST_KEY, + { :test => last_data }, + 0) + end + s[:test] = last_data + s.close + writes += 1 + when :read + # Make CGI::Session#[] think there was no data retrieval yet. + # Normally, the session caches the data during its lifetime. + s.instance_variable_set(:@data, nil) + unless CACHE + s.cache.expects(:[]).with('session:' + MULTI_TEST_KEY) \ + .returns(:test => last_data) + end + d = s[:test] + assert_equal(last_data, d, "OK reads: #{reads}, OK writes: #{writes}") + reads += 1 + end + operation = rand(5) == 0 ? :write : :read + end + end + + + + private + def obtain_session_options + options = { 'database_manager' => CGI::Session::MemCacheStore, + 'session_key' => '_test_app_session' + } + # if don't have running memcache server we use mock instead + unless CACHE + options['cache'] = c = mock + c.stubs(:[]).with(regexp_matches(SESSION_KEY_RE)) + c.stubs(:get).with(regexp_matches(SESSION_KEY_RE)) \ + .returns(Hash.new) + c.stubs(:add).with(regexp_matches(SESSION_KEY_RE), + instance_of(Hash), + 0) + end + options + end + + + def new_session(options = {}) + with_cgi do |cgi| + @options = obtain_session_options.merge(options) + session = CGI::Session.new(cgi, @options) + yield session if block_given? + return session + end + end + + def with_cgi + ENV['REQUEST_METHOD'] = 'GET' + ENV['HTTP_HOST'] = 'example.com' + ENV['QUERY_STRING'] = '' + + cgi = CGI.new('query', StringIO.new('')) + yield cgi if block_given? + cgi + end +end -- cgit v1.2.3