File: redis_client_test.rb

package info (click to toggle)
ruby-redis-client 0.28.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,116 kB
  • sloc: ansic: 7,517; ruby: 5,801; makefile: 246; sh: 123
file content (192 lines) | stat: -rw-r--r-- 5,232 bytes parent folder | download
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# frozen_string_literal: true

require "test_helper"

class RedisClientTest < RedisClientTestCase
  include ClientTestHelper
  include RedisClientTests

  def test_preselect_database
    client = new_client(db: 5)
    assert_includes client.call("CLIENT", "INFO"), " db=5 "
    client.call("SELECT", 6)
    assert_includes client.call("CLIENT", "INFO"), " db=6 "
    client.close
    assert_includes client.call("CLIENT", "INFO"), " db=5 "
  end

  def test_set_client_id
    client = new_client(id: "peter")
    assert_includes client.call("CLIENT", "INFO"), " name=peter "
    client.call("CLIENT", "SETNAME", "steven")
    assert_includes client.call("CLIENT", "INFO"), " name=steven "
    client.close
    assert_includes client.call("CLIENT", "INFO"), " name=peter "
  end

  def test_encoding
    @redis.call("SET", "str", "fée")
    str = @redis.call("GET", "str")

    assert_equal Encoding::UTF_8, str.encoding
    assert_predicate str, :valid_encoding?

    bytes = "\xFF\00"
    refute_predicate bytes, :valid_encoding?

    @redis.call("SET", "str", bytes.b)
    str = @redis.call("GET", "str")

    assert_equal Encoding::BINARY, str.encoding
    assert_predicate str, :valid_encoding?
  end

  def test_dns_resolution_failure
    client = RedisClient.new(host: "does-not-exist.example.com")
    error = assert_raises RedisClient::ConnectionError do
      client.call("PING")
    end

    assert_match(%r{ \(redis://does-not-exist.example.com:.*\)$}, error.message)
  end

  def test_older_server
    fake_redis5_driver = Class.new(RedisClient::RubyConnection) do
      def call_pipelined(commands, *, &_)
        if commands.any? { |c| c == ["HELLO", "3"] }
          raise RedisClient::CommandError, "ERR unknown command `HELLO`, with args beginning with: `3`"
        else
          super
        end
      end
    end
    client = new_client(driver: fake_redis5_driver)

    error = assert_raises RedisClient::UnsupportedServer do
      client.call("PING")
    end
    assert_includes error.message, "redis-client requires Redis 6+ with HELLO command available"
    assert_includes error.message, "(redis://"
  end

  def test_redis_6_server_with_missing_hello_command
    fake_redis6_driver = Class.new(RedisClient::RubyConnection) do
      def call_pipelined(commands, *, &_)
        if commands.any? { |c| c == ["HELLO", "3"] }
          raise RedisClient::CommandError, "ERR unknown command 'HELLO'"
        else
          super
        end
      end
    end
    client = new_client(driver: fake_redis6_driver)

    error = assert_raises RedisClient::UnsupportedServer do
      client.call("PING")
    end
    assert_includes error.message, "redis-client requires Redis 6+ with HELLO command available"
    assert_includes error.message, "(redis://"
  end

  def test_handle_async_raise
    10.times do |i|
      thread = Thread.new do
        loop do
          assert_equal "OK", @redis.call("SET", "key#{i}", i)
        end
      rescue RuntimeError
      end
      thread.join(rand(0.01..0.2))
      thread.raise("Timeout Error")
      refute_predicate thread.join, :alive?
      assert_equal i.to_s, @redis.call("GET", "key#{i}")
    end
  end

  def test_handle_async_thread_kill
    10.times do |i|
      thread = Thread.new do
        loop do
          assert_equal "OK", @redis.call("SET", "key#{i}", i)
        end
      rescue RuntimeError
      end
      thread.join(rand(0.01..0.2))
      thread.kill
      refute_predicate thread.join, :alive?
      assert_equal i.to_s, @redis.call("GET", "key#{i}")
    end
  end

  def test_measure_round_trip_delay
    assert_equal "OK", @redis.call("SET", "foo", "bar")
    assert_instance_of Float, @redis.measure_round_trip_delay
    assert_equal "OK", @redis.call("SET", "foo", "bar")
    @redis.close
    assert_instance_of Float, @redis.measure_round_trip_delay
  end

  def test_server_url
    assert_equal "redis://#{Servers::HOST}:#{Servers::REDIS.port}", @redis.server_url
  end

  def test_timeout
    assert_equal ClientTestHelper::DEFAULT_TIMEOUT, @redis.timeout
  end

  def test_db
    assert_equal 0, @redis.db
  end

  def test_id
    assert_nil @redis.id
  end

  def test_host
    assert_equal Servers::HOST, @redis.host
  end

  def test_port
    assert_equal Servers::REDIS.port, @redis.port
  end

  def test_path
    client = new_client(**unix_config)
    assert_equal Servers::REDIS.socket_file.to_s, client.path
  end

  def test_username
    username = "test"
    client = new_client(username: username)
    assert_equal username, client.username
  end

  def test_password
    password = "test"
    client = new_client(password: password)
    assert_equal password, client.password
  end

  if GC.respond_to?(:auto_compact)
    def test_gc_safety
      gc_stress_was = GC.stress
      gc_auto_compact_was = GC.auto_compact

      GC.stress = true
      GC.auto_compact = true

      client = new_client
      client.call("PING")

      list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(&:to_s)
      client.call("LPUSH", "list", list)

      3.times do
        assert_equal list, client.call("LRANGE", "list", 0, -1).reverse
      end
    ensure
      GC.stress = gc_stress_was
      GC.auto_compact = gc_auto_compact_was
    end
  end
end