couchbase 1.3.4-x64-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +22 -0
  4. data/.yardopts +5 -0
  5. data/CONTRIBUTING.markdown +75 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +201 -0
  8. data/Makefile +3 -0
  9. data/README.markdown +649 -0
  10. data/RELEASE_NOTES.markdown +796 -0
  11. data/Rakefile +20 -0
  12. data/couchbase.gemspec +49 -0
  13. data/examples/chat-em/Gemfile +7 -0
  14. data/examples/chat-em/README.markdown +45 -0
  15. data/examples/chat-em/server.rb +82 -0
  16. data/examples/chat-goliath-grape/Gemfile +5 -0
  17. data/examples/chat-goliath-grape/README.markdown +50 -0
  18. data/examples/chat-goliath-grape/app.rb +67 -0
  19. data/examples/chat-goliath-grape/config/app.rb +20 -0
  20. data/examples/transcoders/Gemfile +3 -0
  21. data/examples/transcoders/README.markdown +59 -0
  22. data/examples/transcoders/cb-zcat +40 -0
  23. data/examples/transcoders/cb-zcp +45 -0
  24. data/examples/transcoders/gzip_transcoder.rb +49 -0
  25. data/examples/transcoders/options.rb +54 -0
  26. data/ext/couchbase_ext/.gitignore +4 -0
  27. data/ext/couchbase_ext/arguments.c +956 -0
  28. data/ext/couchbase_ext/arithmetic.c +307 -0
  29. data/ext/couchbase_ext/bucket.c +1370 -0
  30. data/ext/couchbase_ext/context.c +65 -0
  31. data/ext/couchbase_ext/couchbase_ext.c +1364 -0
  32. data/ext/couchbase_ext/couchbase_ext.h +644 -0
  33. data/ext/couchbase_ext/delete.c +163 -0
  34. data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
  35. data/ext/couchbase_ext/extconf.rb +168 -0
  36. data/ext/couchbase_ext/get.c +316 -0
  37. data/ext/couchbase_ext/gethrtime.c +129 -0
  38. data/ext/couchbase_ext/http.c +432 -0
  39. data/ext/couchbase_ext/multithread_plugin.c +1090 -0
  40. data/ext/couchbase_ext/observe.c +171 -0
  41. data/ext/couchbase_ext/plugin_common.c +171 -0
  42. data/ext/couchbase_ext/result.c +129 -0
  43. data/ext/couchbase_ext/stats.c +163 -0
  44. data/ext/couchbase_ext/store.c +542 -0
  45. data/ext/couchbase_ext/timer.c +192 -0
  46. data/ext/couchbase_ext/touch.c +186 -0
  47. data/ext/couchbase_ext/unlock.c +176 -0
  48. data/ext/couchbase_ext/utils.c +551 -0
  49. data/ext/couchbase_ext/version.c +142 -0
  50. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  51. data/lib/active_support/cache/couchbase_store.rb +430 -0
  52. data/lib/couchbase.rb +155 -0
  53. data/lib/couchbase/bucket.rb +457 -0
  54. data/lib/couchbase/cluster.rb +119 -0
  55. data/lib/couchbase/connection_pool.rb +58 -0
  56. data/lib/couchbase/constants.rb +12 -0
  57. data/lib/couchbase/result.rb +26 -0
  58. data/lib/couchbase/transcoder.rb +120 -0
  59. data/lib/couchbase/utils.rb +62 -0
  60. data/lib/couchbase/version.rb +21 -0
  61. data/lib/couchbase/view.rb +506 -0
  62. data/lib/couchbase/view_row.rb +272 -0
  63. data/lib/ext/multi_json_fix.rb +56 -0
  64. data/lib/rack/session/couchbase.rb +108 -0
  65. data/tasks/benchmark.rake +6 -0
  66. data/tasks/compile.rake +158 -0
  67. data/tasks/test.rake +100 -0
  68. data/tasks/util.rake +21 -0
  69. data/test/profile/.gitignore +1 -0
  70. data/test/profile/Gemfile +6 -0
  71. data/test/profile/benchmark.rb +195 -0
  72. data/test/setup.rb +178 -0
  73. data/test/test_arithmetic.rb +185 -0
  74. data/test/test_async.rb +316 -0
  75. data/test/test_bucket.rb +250 -0
  76. data/test/test_cas.rb +235 -0
  77. data/test/test_couchbase.rb +77 -0
  78. data/test/test_couchbase_connection_pool.rb +77 -0
  79. data/test/test_couchbase_rails_cache_store.rb +361 -0
  80. data/test/test_delete.rb +120 -0
  81. data/test/test_errors.rb +82 -0
  82. data/test/test_eventmachine.rb +70 -0
  83. data/test/test_format.rb +164 -0
  84. data/test/test_get.rb +407 -0
  85. data/test/test_stats.rb +57 -0
  86. data/test/test_store.rb +216 -0
  87. data/test/test_timer.rb +42 -0
  88. data/test/test_touch.rb +97 -0
  89. data/test/test_unlock.rb +119 -0
  90. data/test/test_utils.rb +58 -0
  91. data/test/test_version.rb +52 -0
  92. metadata +336 -0
@@ -0,0 +1,120 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require File.join(File.dirname(__FILE__), 'setup')
19
+
20
+ class TestStore < MiniTest::Test
21
+
22
+ def setup
23
+ @mock = start_mock
24
+ end
25
+
26
+ def teardown
27
+ stop_mock(@mock)
28
+ end
29
+
30
+ def test_trivial_delete
31
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
32
+ connection.set(uniq_id, "bar")
33
+ assert connection.delete(uniq_id)
34
+ assert_raises(Couchbase::Error::NotFound) do
35
+ connection.delete(uniq_id)
36
+ end
37
+ end
38
+
39
+ def test_delete_missing
40
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
41
+ assert_raises(Couchbase::Error::NotFound) do
42
+ connection.delete(uniq_id(:missing))
43
+ end
44
+ refute connection.delete(uniq_id(:missing), :quiet => true)
45
+ refute connection.quiet?
46
+ connection.quiet = true
47
+ refute connection.delete(uniq_id(:missing))
48
+ end
49
+
50
+ def test_delete_with_cas
51
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
52
+ cas = connection.set(uniq_id, "bar")
53
+ missing_cas = cas - 1
54
+ assert_raises(Couchbase::Error::KeyExists) do
55
+ connection.delete(uniq_id, :cas => missing_cas)
56
+ end
57
+ assert connection.delete(uniq_id, :cas => cas)
58
+ end
59
+
60
+ def test_allow_fixnum_as_cas_parameter
61
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
62
+ cas = connection.set(uniq_id, "bar")
63
+ assert connection.delete(uniq_id, cas)
64
+ end
65
+
66
+ def test_delete_with_prefix
67
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :key_prefix => "prefix:")
68
+ connection.set(uniq_id(:foo), "bar")
69
+ assert connection.delete(uniq_id(:foo))
70
+ assert_raises(Couchbase::Error::NotFound) do
71
+ connection.get(uniq_id(:foo))
72
+ end
73
+ end
74
+
75
+ def test_simple_multi_delete
76
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => true)
77
+ connection.set(uniq_id(1) => "bar", uniq_id(2) => "foo")
78
+ res = connection.delete(uniq_id(1), uniq_id(2))
79
+ assert res.is_a?(Hash)
80
+ assert res[uniq_id(1)]
81
+ assert res[uniq_id(2)]
82
+ end
83
+
84
+ def test_simple_multi_delete_missing
85
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => true)
86
+ connection.set(uniq_id(1) => "bar", uniq_id(2) => "foo")
87
+ res = connection.delete(uniq_id(1), uniq_id(:missing), :quiet => true)
88
+ assert res.is_a?(Hash)
89
+ assert res[uniq_id(1)]
90
+ refute res[uniq_id(:missing)]
91
+ end
92
+
93
+ def test_multi_delete_with_cas_check
94
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => true)
95
+ cas = connection.set(uniq_id(1) => "bar", uniq_id(2) => "foo")
96
+ res = connection.delete(uniq_id(1) => cas[uniq_id(1)], uniq_id(2) => cas[uniq_id(2)])
97
+ assert res.is_a?(Hash)
98
+ assert res[uniq_id(1)]
99
+ assert res[uniq_id(2)]
100
+ end
101
+
102
+ def test_multi_delete_missing_with_cas_check
103
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => true)
104
+ cas = connection.set(uniq_id(1) => "bar", uniq_id(2) => "foo")
105
+ res = connection.delete(uniq_id(1) => cas[uniq_id(1)], uniq_id(:missing) => cas[uniq_id(2)])
106
+ assert res.is_a?(Hash)
107
+ assert res[uniq_id(1)]
108
+ refute res[uniq_id(:missing)]
109
+ end
110
+
111
+ def test_multi_delete_with_cas_check_mismatch
112
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => true)
113
+ cas = connection.set(uniq_id(1) => "bar", uniq_id(2) => "foo")
114
+
115
+ assert_raises(Couchbase::Error::KeyExists) do
116
+ connection.delete(uniq_id(1) => cas[uniq_id(1)] + 1,
117
+ uniq_id(2) => cas[uniq_id(2)])
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,82 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require File.join(File.dirname(__FILE__), 'setup')
19
+ require 'digest/md5'
20
+
21
+ class TestErrors < MiniTest::Test
22
+
23
+ def setup
24
+ @mock = start_mock
25
+ end
26
+
27
+ def teardown
28
+ stop_mock(@mock)
29
+ end
30
+
31
+ def genkey(item)
32
+ tuple = [item["author"], item["message"]]
33
+ Digest::MD5.hexdigest(tuple.join('-'))
34
+ end
35
+
36
+ def test_graceful_add_with_collision
37
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
38
+ msg1 = {"author" => "foo", "message" => "hi all", "time" => "2012-01-12 11:29:09"}
39
+ key1 = uniq_id(genkey(msg1))
40
+ msg2 = {"author" => "foo", "message" => "hi all", "time" => "2012-01-12 11:29:30"}
41
+ key2 = uniq_id(genkey(msg2))
42
+
43
+ connection.add(key1, msg1)
44
+ begin
45
+ connection.add(key2, msg2)
46
+ rescue Couchbase::Error::KeyExists => ex
47
+ # using info from exception
48
+ # it could be done with cas operation, but we can save one request
49
+ # here (in real world cas operation will be more consistent because it
50
+ # fetch fresh version from the cluster)
51
+ #
52
+ # connection.cas(key2) do |msg|
53
+ # msg.merge("time" => [msg["time"], msg2["time"]])
54
+ # end
55
+ msg2 = msg1.merge("time" => [msg1["time"], msg2["time"]])
56
+ connection.set(key2, msg2, :cas => ex.cas)
57
+ end
58
+
59
+ msg3 = {"author" => "foo", "message" => "hi all",
60
+ "time" => ["2012-01-12 11:29:09", "2012-01-12 11:29:30"]}
61
+ key3 = uniq_id(genkey(msg3))
62
+ assert_equal msg3, connection.get(key3)
63
+
64
+ connection.run do |conn|
65
+ msg4 = {"author" => "foo", "message" => "hi all", "time" => "2012-01-12 11:45:34"}
66
+ key4 = uniq_id(genkey(msg4))
67
+
68
+ connection.add(key4, msg4) do |ret|
69
+ assert_equal :add, ret.operation
70
+ assert_equal key4, ret.key
71
+ msg4 = msg3.merge("time" => msg3["time"] + [msg4["time"]])
72
+ connection.set(ret.key, msg4, :cas => ret.cas)
73
+ end
74
+ end
75
+
76
+ msg5 = {"author" => "foo", "message" => "hi all",
77
+ "time" => ["2012-01-12 11:29:09", "2012-01-12 11:29:30", "2012-01-12 11:45:34"]}
78
+ key5 = uniq_id(genkey(msg5))
79
+ assert_equal msg5, connection.get(key5)
80
+ end
81
+
82
+ end
@@ -0,0 +1,70 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require File.join(File.dirname(__FILE__), 'setup')
19
+ require 'eventmachine'
20
+
21
+ class TestEventmachine < MiniTest::Test
22
+
23
+ def setup
24
+ @mock = start_mock
25
+ end
26
+
27
+ def teardown
28
+ stop_mock(@mock)
29
+ end
30
+
31
+ if RUBY_VERSION.to_f >= 1.9
32
+
33
+ def test_trivial_set
34
+ EM.run do
35
+ conn = Couchbase.new(:hostname => @mock.host, :port => @mock.port,
36
+ :engine => :eventmachine, :async => true)
37
+ conn.on_connect do |res|
38
+ assert res.success?, "connection must be successful"
39
+ conn.set(uniq_id, "bar") do |res|
40
+ assert res.success?, "the operation must be successful"
41
+ assert res.cas > 0, "the CAS value must be non-zero"
42
+ EM.stop
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def test_trivial_get
49
+ EM.run do
50
+ conn = Couchbase.new(:hostname => @mock.host, :port => @mock.port,
51
+ :engine => :eventmachine, :async => true)
52
+ conn.on_connect do |res|
53
+ assert res.success?, "connection must be successful"
54
+ conn.set(uniq_id, "bar") do |res|
55
+ assert res.success?, "the set operation must be successful"
56
+ cas = res.cas
57
+ conn.get(uniq_id) do |res|
58
+ assert res.success?, "the get operation must be successful"
59
+ assert_equal "bar", res.value
60
+ assert_equal cas, res.cas
61
+ EM.stop
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,164 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require File.join(File.dirname(__FILE__), 'setup')
19
+
20
+ class TestFormat < MiniTest::Test
21
+
22
+ ArbitraryClass = Struct.new(:name, :role)
23
+ class SkinyClass < Struct.new(:name, :role)
24
+ undef to_s rescue nil
25
+ undef to_json rescue nil
26
+ end
27
+
28
+ def setup
29
+ @mock = start_mock
30
+ end
31
+
32
+ def teardown
33
+ stop_mock(@mock)
34
+ end
35
+
36
+ def test_default_document_format
37
+ orig_doc = {'name' => 'Twoflower', 'role' => 'The tourist'}
38
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
39
+ assert_equal :document, connection.default_format
40
+ connection.set(uniq_id, orig_doc)
41
+ doc, flags, cas = connection.get(uniq_id, :extended => true)
42
+ assert_equal 0x00, flags & 0x11
43
+ assert doc.is_a?(Hash)
44
+ assert_equal 'Twoflower', doc['name']
45
+ assert_equal 'The tourist', doc['role']
46
+ end
47
+
48
+ def test_it_raises_error_for_document_format_when_neither_to_json_nor_to_s_defined
49
+ if (MultiJson.respond_to?(:engine) ? MultiJson.engine : MultiJson.adapter).name =~ /Yajl$/
50
+ orig_doc = SkinyClass.new("Twoflower", "The tourist")
51
+ refute orig_doc.respond_to?(:to_s)
52
+ refute orig_doc.respond_to?(:to_json)
53
+
54
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :default_format => :document)
55
+ assert_raises(Couchbase::Error::ValueFormat) do
56
+ connection.set(uniq_id, orig_doc)
57
+ end
58
+
59
+ class << orig_doc
60
+ def to_json
61
+ MultiJson.dump(:name => name, :role => role)
62
+ end
63
+ end
64
+ connection.set(uniq_id, orig_doc) # OK
65
+
66
+ class << orig_doc
67
+ undef to_json
68
+ def to_s
69
+ MultiJson.dump(:name => name, :role => role)
70
+ end
71
+ end
72
+ connection.set(uniq_id, orig_doc) # OK
73
+ end
74
+ end
75
+
76
+ def test_it_could_dump_arbitrary_class_using_marshal_format
77
+ orig_doc = ArbitraryClass.new("Twoflower", "The tourist")
78
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
79
+ connection.set(uniq_id, orig_doc, :format => :marshal)
80
+ doc, flags, cas = connection.get(uniq_id, :extended => true)
81
+ assert_equal Couchbase::Bucket::FMT_MARSHAL, flags & Couchbase::Bucket::FMT_MASK
82
+ assert doc.is_a?(ArbitraryClass)
83
+ assert_equal 'Twoflower', doc.name
84
+ assert_equal 'The tourist', doc.role
85
+ end
86
+
87
+ def test_it_accepts_only_string_in_plain_mode
88
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :default_format => :plain)
89
+ connection.set(uniq_id, "1")
90
+
91
+ assert_raises(Couchbase::Error::ValueFormat) do
92
+ connection.set(uniq_id, 1)
93
+ end
94
+
95
+ assert_raises(Couchbase::Error::ValueFormat) do
96
+ connection.set(uniq_id, {:foo => "bar"})
97
+ end
98
+ end
99
+
100
+ def test_bignum_conversion
101
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :default_format => :plain)
102
+ cas = 0xffff_ffff_ffff_ffff
103
+ assert cas.is_a?(Bignum)
104
+ assert_raises(Couchbase::Error::NotFound) do
105
+ connection.delete(uniq_id => cas)
106
+ end
107
+ end
108
+
109
+ def test_it_allows_to_turn_off_transcoder
110
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :transcoder => nil)
111
+ connection.set(uniq_id, "value", :flags => 0xffff_ffff)
112
+ doc, flags, _ = connection.get(uniq_id, :extended => true)
113
+ assert_equal "value", doc
114
+ assert_equal 0xffff_ffff, flags
115
+ end
116
+
117
+ require 'zlib'
118
+ # This class wraps any other transcoder and performs compression
119
+ # using zlib
120
+ class ZlibTranscoder
121
+ FMT_ZLIB = 0x04
122
+
123
+ def initialize(base)
124
+ @base = base
125
+ end
126
+
127
+ def dump(obj, flags, options = {})
128
+ obj, flags = @base.dump(obj, flags, options)
129
+ z = Zlib::Deflate.new(Zlib::BEST_SPEED)
130
+ buffer = z.deflate(obj, Zlib::FINISH)
131
+ z.close
132
+ [buffer, flags|FMT_ZLIB]
133
+ end
134
+
135
+ def load(blob, flags, options = {})
136
+ # decompress value only if Zlib flag set
137
+ if (flags & FMT_ZLIB) == FMT_ZLIB
138
+ z = Zlib::Inflate.new
139
+ blob = z.inflate(blob)
140
+ z.finish
141
+ z.close
142
+ end
143
+ @base.load(blob, flags, options)
144
+ end
145
+ end
146
+
147
+ def test_it_can_use_custom_transcoder
148
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
149
+ connection.transcoder = ZlibTranscoder.new(Couchbase::Transcoder::Document)
150
+ connection.set(uniq_id, {"foo" => "bar"})
151
+ doc, flags, _ = connection.get(uniq_id, :extended => true)
152
+ assert_equal({"foo" => "bar"}, doc)
153
+ assert_equal(ZlibTranscoder::FMT_ZLIB|Couchbase::Bucket::FMT_DOCUMENT, flags)
154
+ connection.transcoder = nil
155
+ doc = connection.get(uniq_id)
156
+ case RUBY_VERSION
157
+ when /^1\.8/
158
+ assert_equal "x\x01\xABVJ\xCB\xCFW\xB2RJJ,R\xAA\x05\0\x1Dz\x044", doc
159
+ else
160
+ assert_equal "x\u0001\xABVJ\xCB\xCFW\xB2RJJ,R\xAA\u0005\u0000\u001Dz\u00044", doc
161
+ end
162
+ end
163
+
164
+ end
data/test/test_get.rb ADDED
@@ -0,0 +1,407 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require File.join(File.dirname(__FILE__), 'setup')
19
+
20
+ class TestGet < MiniTest::Test
21
+
22
+ def setup
23
+ @mock = start_mock
24
+ end
25
+
26
+ def teardown
27
+ stop_mock(@mock)
28
+ end
29
+
30
+ def test_trivial_get
31
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
32
+ connection.set(uniq_id, "bar")
33
+ val = connection.get(uniq_id)
34
+ assert_equal "bar", val
35
+ end
36
+
37
+ def test_extended_get
38
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
39
+
40
+ orig_cas = connection.set(uniq_id, "bar")
41
+ val, flags, cas = connection.get(uniq_id, :extended => true)
42
+ assert_equal "bar", val
43
+ assert_equal 0x0, flags
44
+ assert_equal orig_cas, cas
45
+
46
+ orig_cas = connection.set(uniq_id, "bar", :flags => 0x1000)
47
+ val, flags, cas = connection.get(uniq_id, :extended => true)
48
+ assert_equal "bar", val
49
+ assert_equal 0x1000, flags
50
+ assert_equal orig_cas, cas
51
+ end
52
+
53
+ def test_multi_get
54
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
55
+
56
+ connection.set(uniq_id(1), "foo1")
57
+ connection.set(uniq_id(2), "foo2")
58
+
59
+ val1, val2 = connection.get(uniq_id(1), uniq_id(2))
60
+ assert_equal "foo1", val1
61
+ assert_equal "foo2", val2
62
+ end
63
+
64
+ def test_multi_get_extended
65
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
66
+
67
+ cas1 = connection.set(uniq_id(1), "foo1")
68
+ cas2 = connection.set(uniq_id(2), "foo2")
69
+
70
+ results = connection.get(uniq_id(1), uniq_id(2), :extended => true)
71
+ assert_equal ["foo1", 0x0, cas1], results[uniq_id(1)]
72
+ assert_equal ["foo2", 0x0, cas2], results[uniq_id(2)]
73
+ end
74
+
75
+ def test_multi_get_and_touch
76
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
77
+ connection.set(uniq_id(1), "foo1")
78
+ connection.set(uniq_id(2), "foo2")
79
+
80
+ results = connection.get(uniq_id(1) => 1, uniq_id(2) => 1)
81
+ assert results.is_a?(Hash)
82
+ assert_equal "foo1", results[uniq_id(1)]
83
+ assert_equal "foo2", results[uniq_id(2)]
84
+ sleep(2)
85
+ assert_raises(Couchbase::Error::NotFound) do
86
+ connection.get(uniq_id(1), uniq_id(2))
87
+ end
88
+ assert connection.get(uniq_id(1), uniq_id(2), :quiet => true).compact.empty?
89
+ end
90
+
91
+ def test_multi_get_and_touch_extended
92
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
93
+
94
+ cas1 = connection.set(uniq_id(1), "foo1")
95
+ cas2 = connection.set(uniq_id(2), "foo2")
96
+
97
+ results = connection.get({uniq_id(1) => 1, uniq_id(2) => 1}, :extended => true)
98
+ assert_equal ["foo1", 0x0, cas1], results[uniq_id(1)]
99
+ assert_equal ["foo2", 0x0, cas2], results[uniq_id(2)]
100
+ end
101
+
102
+ def test_multi_get_and_touch_with_single_key
103
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
104
+ connection.set(uniq_id, "foo1")
105
+
106
+ results = connection.get(uniq_id => 1)
107
+ assert results.is_a?(Hash)
108
+ assert_equal "foo1", results[uniq_id]
109
+ sleep(2)
110
+ assert_raises(Couchbase::Error::NotFound) do
111
+ connection.get(uniq_id)
112
+ end
113
+ end
114
+
115
+ def test_missing_in_quiet_mode
116
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => true)
117
+ cas1 = connection.set(uniq_id(1), "foo1")
118
+ cas2 = connection.set(uniq_id(2), "foo2")
119
+
120
+ val = connection.get(uniq_id(:missing))
121
+ refute(val)
122
+ val = connection.get(uniq_id(:missing), :extended => true)
123
+ refute(val)
124
+
125
+ val1, missing, val2 = connection.get(uniq_id(1), uniq_id(:missing), uniq_id(2))
126
+ assert_equal "foo1", val1
127
+ refute missing
128
+ assert_equal "foo2", val2
129
+
130
+ results = connection.get(uniq_id(1), uniq_id(:missing), uniq_id(2), :extended => true)
131
+ assert_equal ["foo1", 0x0, cas1], results[uniq_id(1)]
132
+ refute results[uniq_id(:missing)]
133
+ assert_equal ["foo2", 0x0, cas2], results[uniq_id(2)]
134
+ end
135
+
136
+ def test_it_allows_temporary_quiet_flag
137
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => false)
138
+ assert_raises(Couchbase::Error::NotFound) do
139
+ connection.get(uniq_id(:missing))
140
+ end
141
+ refute connection.get(uniq_id(:missing), :quiet => true)
142
+ end
143
+
144
+ def test_missing_in_verbose_mode
145
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port, :quiet => false)
146
+ connection.set(uniq_id(1), "foo1")
147
+ connection.set(uniq_id(2), "foo2")
148
+
149
+ assert_raises(Couchbase::Error::NotFound) do
150
+ connection.get(uniq_id(:missing))
151
+ end
152
+
153
+ assert_raises(Couchbase::Error::NotFound) do
154
+ connection.get(uniq_id(:missing), :extended => true)
155
+ end
156
+
157
+ assert_raises(Couchbase::Error::NotFound) do
158
+ connection.get(uniq_id(1), uniq_id(:missing), uniq_id(2))
159
+ end
160
+
161
+ assert_raises(Couchbase::Error::NotFound) do
162
+ connection.get(uniq_id(1), uniq_id(:missing), uniq_id(2), :extended => true)
163
+ end
164
+ end
165
+
166
+ def test_asynchronous_get
167
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
168
+ cas = connection.set(uniq_id, "foo", :flags => 0x6660)
169
+ res = []
170
+
171
+ suite = lambda do |conn|
172
+ res.clear
173
+ conn.get(uniq_id) # ignore result
174
+ conn.get(uniq_id) {|ret| res << ret}
175
+ handler = lambda {|ret| res << ret}
176
+ conn.get(uniq_id, &handler)
177
+ end
178
+
179
+ checks = lambda do
180
+ res.each do |r|
181
+ assert r.is_a?(Couchbase::Result)
182
+ assert r.success?
183
+ assert_equal uniq_id, r.key
184
+ assert_equal "foo", r.value
185
+ assert_equal 0x6660, r.flags
186
+ assert_equal cas, r.cas
187
+ end
188
+ end
189
+
190
+ connection.run(&suite)
191
+ checks.call
192
+
193
+ connection.run{ suite.call(connection) }
194
+ checks.call
195
+ end
196
+
197
+ def test_asynchronous_multi_get
198
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
199
+ connection.set(uniq_id(1), "foo")
200
+ connection.set(uniq_id(2), "bar")
201
+
202
+ res = {}
203
+ connection.run do |conn|
204
+ conn.get(uniq_id(1), uniq_id(2)) {|ret| res[ret.key] = ret.value}
205
+ end
206
+
207
+ assert res[uniq_id(1)]
208
+ assert_equal "foo", res[uniq_id(1)]
209
+ assert res[uniq_id(2)]
210
+ assert_equal "bar", res[uniq_id(2)]
211
+ end
212
+
213
+ def test_asynchronous_get_missing
214
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
215
+ connection.set(uniq_id, "foo")
216
+ res = {}
217
+ missing = []
218
+
219
+ get_handler = lambda do |ret|
220
+ assert_equal :get, ret.operation
221
+ if ret.success?
222
+ res[ret.key] = ret.value
223
+ else
224
+ if ret.error.is_a?(Couchbase::Error::NotFound)
225
+ missing << ret.key
226
+ else
227
+ raise ret.error
228
+ end
229
+ end
230
+ end
231
+
232
+ suite = lambda do |conn|
233
+ res.clear
234
+ missing.clear
235
+ conn.get(uniq_id(:missing1), &get_handler)
236
+ conn.get(uniq_id, uniq_id(:missing2), &get_handler)
237
+ end
238
+
239
+ connection.run(&suite)
240
+ refute res.has_key?(uniq_id(:missing1))
241
+ refute res.has_key?(uniq_id(:missing2))
242
+ assert_equal [uniq_id(:missing1), uniq_id(:missing2)], missing.sort
243
+ assert_equal "foo", res[uniq_id]
244
+
245
+ connection.quiet = true
246
+ connection.run(&suite)
247
+ assert_equal "foo", res[uniq_id]
248
+ assert res.has_key?(uniq_id(:missing1)) # handler was called with nil
249
+ refute res[uniq_id(:missing1)]
250
+ assert res.has_key?(uniq_id(:missing2))
251
+ refute res[uniq_id(:missing2)]
252
+ assert_empty missing
253
+ end
254
+
255
+ def test_get_using_brackets
256
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
257
+
258
+ orig_cas = connection.set(uniq_id, "foo", :flags => 0x1100)
259
+
260
+ val = connection[uniq_id]
261
+ assert_equal "foo", val
262
+
263
+ if RUBY_VERSION =~ /^1\.9/
264
+ eval <<-EOC
265
+ val, flags, cas = connection[uniq_id, :extended => true]
266
+ assert_equal "foo", val
267
+ assert_equal 0x1100, flags
268
+ assert_equal orig_cas, cas
269
+ EOC
270
+ end
271
+ end
272
+
273
+ def test_it_allows_to_store_nil
274
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
275
+
276
+ orig_cas = connection.set(uniq_id, nil)
277
+ assert orig_cas.is_a?(Numeric)
278
+
279
+ refute connection.get(uniq_id)
280
+ # doesn't raise NotFound exception
281
+ refute connection.get(uniq_id, :quiet => false)
282
+ # returns CAS
283
+ value, flags, cas = connection.get(uniq_id, :extended => true)
284
+ refute value
285
+ assert_equal 0x00, flags
286
+ assert_equal orig_cas, cas
287
+ end
288
+
289
+ def test_zero_length_string_is_not_nil
290
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
291
+
292
+ connection.set(uniq_id, "", :format => :document)
293
+ assert_equal "", connection.get(uniq_id)
294
+
295
+ connection.set(uniq_id, "", :format => :plain)
296
+ assert_equal "", connection.get(uniq_id)
297
+
298
+ connection.set(uniq_id, "", :format => :marshal)
299
+ assert_equal "", connection.get(uniq_id)
300
+
301
+ connection.set(uniq_id, nil, :format => :document)
302
+ assert_equal nil, connection.get(uniq_id, :quiet => false)
303
+
304
+ assert_raises Couchbase::Error::ValueFormat do
305
+ connection.set(uniq_id, nil, :format => :plain)
306
+ end
307
+
308
+ connection.set(uniq_id, nil, :format => :marshal)
309
+ assert_equal nil, connection.get(uniq_id, :quiet => false)
310
+ end
311
+
312
+ def test_format_forcing
313
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
314
+
315
+ connection.set(uniq_id, '{"foo":"bar"}', :format => :plain)
316
+ value, flags, _ = connection.get(uniq_id, :extended => true)
317
+ assert_equal '{"foo":"bar"}', value
318
+ assert_equal 0x02, flags
319
+
320
+ value, flags, _ = connection.get(uniq_id, :extended => true, :format => :document)
321
+ expected = {"foo" => "bar"}
322
+ assert_equal expected, value
323
+ assert_equal 0x02, flags
324
+
325
+ connection.prepend(uniq_id, "NOT-A-JSON")
326
+ assert_raises Couchbase::Error::ValueFormat do
327
+ connection.get(uniq_id, :format => :document)
328
+ end
329
+ end
330
+
331
+ # http://www.couchbase.com/issues/browse/RCBC-31
332
+ def test_consistent_behaviour_for_arrays
333
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
334
+
335
+ cas = connection.set(uniq_id("foo"), "foo")
336
+ connection.set(uniq_id("bar"), "bar")
337
+
338
+ assert_equal "foo", connection.get(uniq_id("foo"))
339
+ assert_equal ["foo"], connection.get([uniq_id("foo")])
340
+ assert_equal ["foo", "bar"], connection.get([uniq_id("foo"), uniq_id("bar")])
341
+ assert_equal ["foo", "bar"], connection.get(uniq_id("foo"), uniq_id("bar"))
342
+ expected = {uniq_id("foo") => ["foo", 0x00, cas]}
343
+ assert_equal expected, connection.get([uniq_id("foo")], :extended => true)
344
+ assert_raises TypeError do
345
+ connection.get([uniq_id("foo"), uniq_id("bar")], [uniq_id("foo")])
346
+ end
347
+ end
348
+
349
+ def test_get_with_lock_trivial
350
+ if @mock.real?
351
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
352
+ connection.set(uniq_id, "foo")
353
+ assert_equal "foo", connection.get(uniq_id, :lock => 1)
354
+ assert_raises Couchbase::Error::KeyExists do
355
+ connection.set(uniq_id, "bar")
356
+ end
357
+ sleep(2)
358
+ connection.set(uniq_id, "bar")
359
+ else
360
+ skip("implement GETL in CouchbaseMock.jar")
361
+ end
362
+ end
363
+
364
+ def test_multi_get_with_lock
365
+ if @mock.real?
366
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
367
+ connection.set(uniq_id(1), "foo1")
368
+ connection.set(uniq_id(2), "foo2")
369
+ assert_equal ["foo1", "foo2"], connection.get([uniq_id(1), uniq_id(2)], :lock => 1)
370
+ assert_raises Couchbase::Error::KeyExists do
371
+ connection.set(uniq_id(1), "bar")
372
+ end
373
+ assert_raises Couchbase::Error::KeyExists do
374
+ connection.set(uniq_id(2), "bar")
375
+ end
376
+ else
377
+ skip("implement GETL in CouchbaseMock.jar")
378
+ end
379
+ end
380
+
381
+ def test_multi_get_with_custom_locks
382
+ if @mock.real?
383
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
384
+ connection.set(uniq_id(1), "foo1")
385
+ connection.set(uniq_id(2), "foo2")
386
+ expected = {uniq_id(1) => "foo1", uniq_id(2) => "foo2"}
387
+ assert_equal expected, connection.get({uniq_id(1) => 1, uniq_id(2) => 2}, :lock => true)
388
+ assert_raises Couchbase::Error::KeyExists do
389
+ connection.set(uniq_id(1), "foo")
390
+ end
391
+ assert_raises Couchbase::Error::KeyExists do
392
+ connection.set(uniq_id(2), "foo")
393
+ end
394
+ else
395
+ skip("implement GETL in CouchbaseMock.jar")
396
+ end
397
+ end
398
+
399
+ def test_multi_get_result_hash_assembling
400
+ connection = Couchbase.new(:hostname => @mock.host, :port => @mock.port)
401
+ connection.set(uniq_id(1), "foo")
402
+ connection.set(uniq_id(2), "bar")
403
+
404
+ expected = {uniq_id(1) => "foo", uniq_id(2) => "bar"}
405
+ assert_equal expected, connection.get(uniq_id(1), uniq_id(2), :assemble_hash => true)
406
+ end
407
+ end