aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/test/controller/session/mem_cache_store_test.rb
blob: 9ab927a01ffd5b57bc977c0b3f08088ca65da1e8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
require 'abstract_unit'

class CGI::Session
  def cache
    dbman.instance_variable_get(:@cache)
  end
end


uses_mocha 'MemCacheStore tests' do
if defined? MemCache::MemCacheError

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

end # defined? MemCache
end # uses_mocha