Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions test/test_dalli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,191 @@
assert_equal "server2.example.com", s2
end

it "accept array with comma separated strings" do
dc = Dalli::Client.new(["server1.example.com:11211,server2.example.com:11211", "server3.example.com:11211"])
ring = dc.send(:ring)
assert_equal 3, ring.servers.size
end

it "use MEMCACHE_SERVERS env variable when no servers specified" do
old = ENV["MEMCACHE_SERVERS"]
ENV["MEMCACHE_SERVERS"] = "env-server.example.com:11211"
dc = Dalli::Client.new(nil)
ring = dc.send(:ring)
assert_equal "env-server.example.com", ring.servers.first.hostname
dc.close
ensure
ENV["MEMCACHE_SERVERS"] = old
end

describe 'multi block' do
let(:dc) { Dalli::Client.new('localhost:11211') }

it 'sets and restores Thread.current[:dalli_multi]' do
assert_nil Thread.current[:dalli_multi]
dc.multi do
assert_equal true, Thread.current[:dalli_multi]
end
assert_nil Thread.current[:dalli_multi]
end

it 'restores previous multi state on nested calls' do
dc.multi do
assert_equal true, Thread.current[:dalli_multi]
dc.multi do
assert_equal true, Thread.current[:dalli_multi]
end
assert_equal true, Thread.current[:dalli_multi]
end
assert_nil Thread.current[:dalli_multi]
end

it 'restores state even when block raises' do
begin
dc.multi do
raise RuntimeError, 'boom'
end
rescue RuntimeError
end
assert_nil Thread.current[:dalli_multi]
end
end

describe '#with' do
let(:dc) { Dalli::Client.new('localhost:11211') }

it 'yields self' do
dc.with do |client|
assert_same dc, client
end
end

it 'returns block result' do
result = dc.with { |c| 42 }
assert_equal 42, result
end
end

describe '#close and #reset' do
let(:dc) { Dalli::Client.new('localhost:11211') }

it 'close resets the ring to nil' do
dc.send(:ring)
refute_nil dc.instance_variable_get(:@ring)
dc.close
assert_nil dc.instance_variable_get(:@ring)
end

it 'reset is an alias for close' do
assert_equal dc.method(:close), dc.method(:reset)
end

it 'close is safe when ring is nil' do
dc.close
dc.close
end
end

describe 'key helpers' do
describe 'with namespace' do
let(:dc) { Dalli::Client.new('localhost:11211', namespace: 'ns') }

it 'key_with_namespace prepends namespace' do
assert_equal 'ns:mykey', dc.send(:key_with_namespace, 'mykey')
end

it 'key_without_namespace strips namespace prefix' do
assert_equal 'mykey', dc.send(:key_without_namespace, 'ns:mykey')
end
end

describe 'without namespace' do
let(:dc) { Dalli::Client.new('localhost:11211') }

it 'key_with_namespace returns key as-is' do
assert_equal 'mykey', dc.send(:key_with_namespace, 'mykey')
end

it 'key_without_namespace returns key as-is' do
assert_equal 'mykey', dc.send(:key_without_namespace, 'mykey')
end

it 'namespace returns nil' do
assert_nil dc.send(:namespace)
end
end

it 'namespace returns string for symbol namespace' do
dc = Dalli::Client.new('localhost:11211', namespace: :test_ns)
assert_equal 'test_ns', dc.send(:namespace)
end

it 'namespace calls proc when namespace is a Proc' do
call_count = 0
dc = Dalli::Client.new('localhost:11211', namespace: Proc.new { call_count += 1; 'dynamic' })
assert_equal 'dynamic', dc.send(:namespace)
assert_equal 1, call_count
end
end

describe 'ttl_or_default' do
let(:dc) { Dalli::Client.new('localhost:11211') }

it 'returns the given ttl as integer' do
assert_equal 300, dc.send(:ttl_or_default, 300)
end

it 'returns default expires_in when ttl is nil' do
dc = Dalli::Client.new('localhost:11211', expires_in: 600)
assert_equal 600, dc.send(:ttl_or_default, nil)
end

it 'returns 0 when both ttl and expires_in are nil' do
assert_equal 0, dc.send(:ttl_or_default, nil)
end

it 'raises ArgumentError for unconvertible ttl' do
assert_raises ArgumentError do
dc.send(:ttl_or_default, [])
end
end
end

describe 'normalize_options' do
it 'converts :compression to :compress' do
dc = Dalli::Client.new('localhost:11211', compression: true)
opts = dc.instance_variable_get(:@options)
assert opts[:compress]
refute opts.key?(:compression)
end

it 'converts expires_in to integer' do
dc = Dalli::Client.new('localhost:11211', expires_in: '300')
assert_equal 300, dc.instance_variable_get(:@options)[:expires_in]
end
end

describe 'validate_key' do
let(:dc) { Dalli::Client.new('localhost:11211') }

it 'truncates keys longer than 250 chars with md5 hash' do
long_key = 'x' * 300
result = dc.send(:validate_key, long_key)
assert_operator result.length, :<=, 250
assert_includes result, ':md5:'
end

it 'uses custom digest_class for long keys' do
require 'openssl'
dc = Dalli::Client.new('localhost:11211', digest_class: OpenSSL::Digest::SHA1)
long_key = 'y' * 300
result = dc.send(:validate_key, long_key)
assert_includes result, ':md5:'
sha1_hex = OpenSSL::Digest::SHA1.hexdigest(long_key)
assert_includes result, sha1_hex
end
end

describe 'using a live server' do

it "support get/set" do
Expand Down
63 changes: 63 additions & 0 deletions test/test_dalli_module.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true
require_relative 'helper'

describe 'Dalli module' do
describe 'version' do
it 'is defined' do
refute_nil Dalli::VERSION
end

it 'is a string' do
assert_kind_of String, Dalli::VERSION
end
end

describe 'error classes' do
it 'defines DalliError as a RuntimeError' do
assert Dalli::DalliError < RuntimeError
end

it 'defines NetworkError as a DalliError' do
assert Dalli::NetworkError < Dalli::DalliError
end

it 'defines RingError as a DalliError' do
assert Dalli::RingError < Dalli::DalliError
end

it 'defines MarshalError as a DalliError' do
assert Dalli::MarshalError < Dalli::DalliError
end

it 'defines UnmarshalError as a DalliError' do
assert Dalli::UnmarshalError < Dalli::DalliError
end

it 'defines ValueOverMaxSize as a DalliError' do
assert Dalli::ValueOverMaxSize < Dalli::DalliError
end
end

describe 'logger' do
after do
Dalli.logger = Logger.new(STDOUT)
Dalli.logger.level = Logger::ERROR
end

it 'has a default logger' do
Dalli.instance_variable_set(:@logger, nil)
refute_nil Dalli.logger
end

it 'allows setting a custom logger' do
custom = Logger.new(nil)
Dalli.logger = custom
assert_equal custom, Dalli.logger
end

it 'default_logger creates an INFO-level STDOUT logger' do
logger = Dalli.default_logger
assert_equal Logger::INFO, logger.level
end
end
end
61 changes: 61 additions & 0 deletions test/test_pid_cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true
require_relative 'helper'

describe Dalli::PIDCache do
it 'returns the current process PID' do
assert_equal Process.pid, Dalli::PIDCache.pid
end

it 'responds to .pid' do
assert_respond_to Dalli::PIDCache, :pid
end

if Process.respond_to?(:_fork)
describe 'on Ruby 3.1+ with Process._fork' do
it 'responds to update!' do
assert_respond_to Dalli::PIDCache, :update!
end

it 'updates pid to match Process.pid' do
Dalli::PIDCache.update!
assert_equal Process.pid, Dalli::PIDCache.pid
end

it 'has CoreExt prepended on Process' do
assert Process.singleton_class.ancestors.include?(Dalli::PIDCache::CoreExt)
end

if Process.respond_to?(:fork)
it 'resets pid cache in child after fork' do
parent_pid = Process.pid

rd, wr = IO.pipe
child_pid = Process.fork do
rd.close
wr.write Dalli::PIDCache.pid.to_s
wr.close
end
wr.close
cached_pid_in_child = rd.read.to_i
rd.close
Process.wait(child_pid)

assert_equal child_pid, cached_pid_in_child
refute_equal parent_pid, cached_pid_in_child
end
end
end
elsif !Process.respond_to?(:fork)
describe 'on JRuby/TruffleRuby without fork' do
it 'returns a fixed pid' do
assert_equal Process.pid, Dalli::PIDCache.pid
end
end
else
describe 'on Ruby 3.0 or older' do
it 'delegates directly to Process.pid' do
assert_equal Process.pid, Dalli::PIDCache.pid
end
end
end
end
Loading