riak-client 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,10 +1,10 @@
1
1
  # A sample Gemfile
2
2
  source :gemcutter
3
3
 
4
- gem 'activesupport', '~>2.3.5' # '3.0.0'
4
+ gem 'activesupport', '~>2.3.5' #'~>3.0.0'
5
5
  gem 'i18n'
6
6
 
7
- gem 'rspec', "~>2.0.0.beta.18"
7
+ gem 'rspec', "~>2.0.0"
8
8
  gem 'fakeweb', ">=1.2"
9
9
  gem 'rack', '>=1.0'
10
10
  gem 'curb', '>=0.6'
@@ -1,22 +1,24 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activesupport (2.3.8)
4
+ activesupport (2.3.9)
5
5
  curb (0.7.8)
6
6
  diff-lcs (1.1.2)
7
7
  fakeweb (1.3.0)
8
8
  i18n (0.4.1)
9
9
  rack (1.2.1)
10
10
  rake (0.8.7)
11
- rspec (2.0.0.beta.20)
12
- rspec-core (= 2.0.0.beta.20)
13
- rspec-expectations (= 2.0.0.beta.20)
14
- rspec-mocks (= 2.0.0.beta.20)
15
- rspec-core (2.0.0.beta.20)
16
- rspec-expectations (2.0.0.beta.20)
11
+ rspec (2.0.0)
12
+ rspec-core (= 2.0.0)
13
+ rspec-expectations (= 2.0.0)
14
+ rspec-mocks (= 2.0.0)
15
+ rspec-core (2.0.0)
16
+ rspec-expectations (2.0.0)
17
17
  diff-lcs (>= 1.1.2)
18
- rspec-mocks (2.0.0.beta.20)
19
- yajl-ruby (0.7.7)
18
+ rspec-mocks (2.0.0)
19
+ rspec-core (= 2.0.0)
20
+ rspec-expectations (= 2.0.0)
21
+ yajl-ruby (0.7.8)
20
22
 
21
23
  PLATFORMS
22
24
  ruby
@@ -29,5 +31,5 @@ DEPENDENCIES
29
31
  i18n
30
32
  rack (>= 1.0)
31
33
  rake
32
- rspec (~> 2.0.0.beta.18)
34
+ rspec (~> 2.0.0)
33
35
  yajl-ruby
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'htmldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('diff-lcs', 'htmldiff')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('diff-lcs', 'ldiff')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rackup' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('rack', 'rackup')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('rake', 'rake')
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", __FILE__)
10
+
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,173 @@
1
+ %% -------------------------------------------------------------------
2
+ %%
3
+ %% riak_kv_test_backend: storage engine based on ETS tables
4
+ %%
5
+ %% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved.
6
+ %%
7
+ %% This file is provided to you under the Apache License,
8
+ %% Version 2.0 (the "License"); you may not use this file
9
+ %% except in compliance with the License. You may obtain
10
+ %% a copy of the License at
11
+ %%
12
+ %% http://www.apache.org/licenses/LICENSE-2.0
13
+ %%
14
+ %% Unless required by applicable law or agreed to in writing,
15
+ %% software distributed under the License is distributed on an
16
+ %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ %% KIND, either express or implied. See the License for the
18
+ %% specific language governing permissions and limitations
19
+ %% under the License.
20
+ %%
21
+ %% -------------------------------------------------------------------
22
+
23
+ % @doc riak_kv_test_backend is a Riak storage backend using ets that exposes a reset function for efficiently clearing stored data.
24
+
25
+ -module(riak_kv_test_backend).
26
+ -behavior(riak_kv_backend).
27
+ -behavior(gen_server).
28
+ -ifdef(TEST).
29
+ -include_lib("eunit/include/eunit.hrl").
30
+ -endif.
31
+ -export([start/2,stop/1,get/2,put/3,list/1,list_bucket/2,delete/2,
32
+ is_empty/1, drop/1, fold/3, callback/3, reset/0]).
33
+
34
+ -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
35
+ terminate/2, code_change/3]).
36
+
37
+ % @type state() = term().
38
+ -record(state, {t}).
39
+
40
+ % @spec start(Partition :: integer(), Config :: proplist()) ->
41
+ % {ok, state()} | {{error, Reason :: term()}, state()}
42
+ start(Partition, _Config) ->
43
+ gen_server:start_link(?MODULE, [Partition], []).
44
+
45
+ % @spec reset() -> ok
46
+ reset() ->
47
+ Pids = lists:filter(fun(Item) ->
48
+ lists:prefix("test_backend", atom_to_list(Item))
49
+ end, registered()),
50
+ [gen_server:call(Pid, reset) || Pid <- Pids],
51
+ ok.
52
+
53
+ %% @private
54
+ init([Partition]) ->
55
+ PName = list_to_atom("test_backend" ++ integer_to_list(Partition)),
56
+ register(PName, self()),
57
+ {ok, #state{t=ets:new(list_to_atom(integer_to_list(Partition)),[])}}.
58
+
59
+ %% @private
60
+ handle_cast(_, State) -> {noreply, State}.
61
+
62
+ %% @private
63
+ handle_call(stop,_From,State) -> {reply, srv_stop(State), State};
64
+ handle_call({get,BKey},_From,State) -> {reply, srv_get(State,BKey), State};
65
+ handle_call({put,BKey,Val},_From,State) ->
66
+ {reply, srv_put(State,BKey,Val),State};
67
+ handle_call({delete,BKey},_From,State) -> {reply, srv_delete(State,BKey),State};
68
+ handle_call(list,_From,State) -> {reply, srv_list(State), State};
69
+ handle_call({list_bucket,Bucket},_From,State) ->
70
+ {reply, srv_list_bucket(State, Bucket), State};
71
+ handle_call(is_empty, _From, State) ->
72
+ {reply, ets:info(State#state.t, size) =:= 0, State};
73
+ handle_call(drop, _From, State) ->
74
+ ets:delete(State#state.t),
75
+ {reply, ok, State};
76
+ handle_call({fold, Fun0, Acc}, _From, State) ->
77
+ Fun = fun({{B,K}, V}, AccIn) -> Fun0({B,K}, V, AccIn) end,
78
+ Reply = ets:foldl(Fun, Acc, State#state.t),
79
+ {reply, Reply, State};
80
+ handle_call(reset, _From, State) ->
81
+ ets:delete_all_objects(State#state.t),
82
+ {reply, ok, State}.
83
+
84
+ % @spec stop(state()) -> ok | {error, Reason :: term()}
85
+ stop(SrvRef) -> gen_server:call(SrvRef,stop).
86
+ srv_stop(State) ->
87
+ true = ets:delete(State#state.t),
88
+ ok.
89
+
90
+ % get(state(), riak_object:bkey()) ->
91
+ % {ok, Val :: binary()} | {error, Reason :: term()}
92
+ % key must be 160b
93
+ get(SrvRef, BKey) -> gen_server:call(SrvRef,{get,BKey}).
94
+ srv_get(State, BKey) ->
95
+ case ets:lookup(State#state.t,BKey) of
96
+ [] -> {error, notfound};
97
+ [{BKey,Val}] -> {ok, Val};
98
+ Err -> {error, Err}
99
+ end.
100
+
101
+ % put(state(), riak_object:bkey(), Val :: binary()) ->
102
+ % ok | {error, Reason :: term()}
103
+ % key must be 160b
104
+ put(SrvRef, BKey, Val) -> gen_server:call(SrvRef,{put,BKey,Val}).
105
+ srv_put(State,BKey,Val) ->
106
+ true = ets:insert(State#state.t, {BKey,Val}),
107
+ ok.
108
+
109
+ % delete(state(), riak_object:bkey()) ->
110
+ % ok | {error, Reason :: term()}
111
+ % key must be 160b
112
+ delete(SrvRef, BKey) -> gen_server:call(SrvRef,{delete,BKey}).
113
+ srv_delete(State, BKey) ->
114
+ true = ets:delete(State#state.t, BKey),
115
+ ok.
116
+
117
+ % list(state()) -> [riak_object:bkey()]
118
+ list(SrvRef) -> gen_server:call(SrvRef,list).
119
+ srv_list(State) ->
120
+ MList = ets:match(State#state.t,{'$1','_'}),
121
+ list(MList,[]).
122
+ list([],Acc) -> Acc;
123
+ list([[K]|Rest],Acc) -> list(Rest,[K|Acc]).
124
+
125
+ % list_bucket(term(), Bucket :: riak_object:bucket()) -> [Key :: binary()]
126
+ list_bucket(SrvRef, Bucket) ->
127
+ gen_server:call(SrvRef,{list_bucket, Bucket}).
128
+ srv_list_bucket(State, {filter, Bucket, Fun}) ->
129
+ MList = lists:filter(Fun, ets:match(State#state.t,{{Bucket,'$1'},'_'})),
130
+ list(MList,[]);
131
+ srv_list_bucket(State, Bucket) ->
132
+ case Bucket of
133
+ '_' -> MatchSpec = {{'$1','_'},'_'};
134
+ _ -> MatchSpec = {{Bucket,'$1'},'_'}
135
+ end,
136
+ MList = ets:match(State#state.t,MatchSpec),
137
+ list(MList,[]).
138
+
139
+ is_empty(SrvRef) -> gen_server:call(SrvRef, is_empty).
140
+
141
+ drop(SrvRef) -> gen_server:call(SrvRef, drop).
142
+
143
+ fold(SrvRef, Fun, Acc0) -> gen_server:call(SrvRef, {fold, Fun, Acc0}, infinity).
144
+
145
+ %% Ignore callbacks for other backends so multi backend works
146
+ callback(_State, _Ref, _Msg) ->
147
+ ok.
148
+
149
+ %% @private
150
+ handle_info(_Msg, State) -> {noreply, State}.
151
+
152
+ %% @private
153
+ terminate(_Reason, _State) -> ok.
154
+
155
+ %% @private
156
+ code_change(_OldVsn, State, _Extra) -> {ok, State}.
157
+
158
+ %%
159
+ %% Test
160
+ %%
161
+ -ifdef(TEST).
162
+
163
+ % @private
164
+ simple_test() ->
165
+ riak_kv_backend:standard_test(?MODULE, []).
166
+
167
+ -ifdef(EQC).
168
+ %% @private
169
+ eqc_test() ->
170
+ ?assertEqual(true, backend_eqc:test(?MODULE, true)).
171
+
172
+ -endif. % EQC
173
+ -endif. % TEST
@@ -44,6 +44,9 @@ module Riak
44
44
  autoload :InvalidResponse, "riak/invalid_response"
45
45
  autoload :MapReduceError, "riak/map_reduce_error"
46
46
 
47
+ # Test server
48
+ autoload :TestServer, "riak/test_server"
49
+
47
50
  # Utility classes and mixins
48
51
  module Util
49
52
  autoload :Escape, "riak/util/escape"
@@ -35,9 +35,6 @@ module Riak
35
35
  attr_accessor :vclock
36
36
  alias_attribute :vector_clock, :vclock
37
37
 
38
- # @return [Object] the data stored in Riak at this object's key. Varies in format by content-type, defaulting to String from the response body.
39
- attr_accessor :data
40
-
41
38
  # @return [Set<Link>] a Set of {Riak::Link} objects for relationships between this object and other resources
42
39
  attr_accessor :links
43
40
 
@@ -50,6 +47,10 @@ module Riak
50
47
  # @return [Hash] a hash of any X-Riak-Meta-* headers that were in the HTTP response, keyed on the trailing portion
51
48
  attr_accessor :meta
52
49
 
50
+ # @return [Boolean] whether to attempt to prevent stale writes using conditional PUT semantics, If-None-Match: * or If-Match: {#etag}
51
+ # @see http://wiki.basho.com/display/RIAK/REST+API#RESTAPI-Storeaneworexistingobjectwithakey Riak Rest API Docs
52
+ attr_accessor :prevent_stale_writes
53
+
53
54
  # Loads a list of RObjects that were emitted from a MapReduce
54
55
  # query.
55
56
  # @param [Client] client A Riak::Client with which the results will be associated
@@ -89,7 +90,7 @@ module Riak
89
90
  end
90
91
  @conflict = response[:code].try(:to_i) == 300 && content_type =~ /multipart\/mixed/
91
92
  @siblings = nil
92
- @data = deserialize(response[:body]) if response[:body].present?
93
+ self.raw_data = response[:body] if response[:body].present?
93
94
  self
94
95
  end
95
96
 
@@ -115,12 +116,49 @@ module Riak
115
116
  self
116
117
  end
117
118
 
119
+ # @return [Object] the unmarshaled form of {#raw_data} stored in riak at this object's key
120
+ def data
121
+ if @raw_data && !@data
122
+ @data = deserialize(@raw_data)
123
+ @raw_data = nil
124
+ end
125
+ @data
126
+ end
127
+
128
+ # @param [Object] unmarshaled form of the data to be stored in riak. Object will be serialized using {#serialize} if a known content_type is used. Setting this overrides values stored with {#raw_data=}
129
+ # @return [Object] the object stored
130
+ def data=(new_data)
131
+ @raw_data = nil
132
+ @data = new_data
133
+ end
134
+
135
+ # @return [String] raw data stored in riak for this object's key
136
+ def raw_data
137
+ if @data && !@raw_data
138
+ @raw_data = serialize(@data)
139
+ @data = nil
140
+ end
141
+ @raw_data
142
+ end
143
+
144
+ # @param [String, IO] the raw data to be stored in riak at this key, will not be marshaled or manipulated prior to storage. Overrides any data stored by {#data=}
145
+ # @return [String] the data stored
146
+ def raw_data=(new_raw_data)
147
+ @data = nil
148
+ @raw_data = new_raw_data
149
+ end
150
+
118
151
  # HTTP header hash that will be sent along when storing the object
119
152
  # @return [Hash] hash of HTTP Headers
120
153
  def store_headers
121
154
  {}.tap do |hash|
122
155
  hash["Content-Type"] = @content_type
123
156
  hash["X-Riak-Vclock"] = @vclock if @vclock
157
+ if @prevent_stale_writes && @etag.present?
158
+ hash["If-Match"] = @etag
159
+ elsif @prevent_stale_writes
160
+ hash["If-None-Match"] = "*"
161
+ end
124
162
  unless @links.blank?
125
163
  hash["Link"] = @links.reject {|l| l.rel == "up" }.map(&:to_s).join(", ")
126
164
  end
@@ -153,7 +191,7 @@ module Riak
153
191
  raise ArgumentError, t("content_type_undefined") unless @content_type.present?
154
192
  params = {:returnbody => true}.merge(options)
155
193
  method, codes, path = @key.present? ? [:put, [200,204,300], "#{escape(@bucket.name)}/#{escape(@key)}"] : [:post, 201, escape(@bucket.name)]
156
- response = @bucket.client.http.send(method, codes, @bucket.client.prefix, path, params, serialize(data), store_headers)
194
+ response = @bucket.client.http.send(method, codes, @bucket.client.prefix, path, params, raw_data, store_headers)
157
195
  load(response)
158
196
  end
159
197
 
@@ -0,0 +1,231 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'riak'
15
+ require 'tempfile'
16
+ require 'expect'
17
+ require 'open3'
18
+ require 'riak/util/tcp_socket_extensions'
19
+
20
+ module Riak
21
+ class TestServer
22
+ APP_CONFIG_DEFAULTS = {
23
+ :riak_core => {
24
+ :web_ip => "127.0.0.1",
25
+ :web_port => 9000,
26
+ :handoff_port => 9001,
27
+ :ring_creation_size => 64
28
+ },
29
+ :riak_kv => {
30
+ :storage_backend => :riak_kv_test_backend,
31
+ :pb_ip => "127.0.0.1",
32
+ :pb_port => 9002,
33
+ :js_vm_count => 8,
34
+ :js_max_vm_mem => 8,
35
+ :js_thread_stack => 16,
36
+ :riak_kv_stat => true
37
+ },
38
+ :luwak => {
39
+ :enabled => false
40
+ }
41
+ }
42
+ VM_ARGS_DEFAULTS = {
43
+ "-name" => "riaktest#{rand(1000000).to_s}@127.0.0.1",
44
+ "-setcookie" => "#{rand(1000000).to_s}_#{rand(1000000).to_s}",
45
+ "+K" => true,
46
+ "+A" => 64,
47
+ "-smp" => "enable",
48
+ "-env ERL_MAX_PORTS" => 4096,
49
+ "-env ERL_FULLSWEEP_AFTER" => 10,
50
+ "-pa" => File.expand_path("../../../erl_src", __FILE__)
51
+ }
52
+ DEFAULTS = {
53
+ :app_config => APP_CONFIG_DEFAULTS,
54
+ :vm_args => VM_ARGS_DEFAULTS,
55
+ :temp_dir => File.join(Dir.tmpdir,'riaktest'),
56
+ }
57
+ attr_accessor :temp_dir, :app_config, :vm_args, :cin, :cout, :cerr, :cpid
58
+
59
+ def initialize(options={})
60
+ options = deep_merge(DEFAULTS.dup, options)
61
+ @temp_dir = File.expand_path(options[:temp_dir])
62
+ @bin_dir = File.expand_path(options[:bin_dir])
63
+ options[:app_config][:riak_core][:ring_state_dir] ||= File.join(@temp_dir, "data", "ring")
64
+ @app_config = options[:app_config]
65
+ @vm_args = options[:vm_args]
66
+ # For synchronizing start/stop/recycle
67
+ @mutex = Mutex.new
68
+ end
69
+
70
+ # Sets up the proper scripts, configuration and directories for
71
+ # the test server. Call at the top of your test suite (not in a
72
+ # setup method).
73
+ def prepare!
74
+ unless @prepared
75
+ create_temp_directories
76
+ @riak_script = File.join(@temp_bin, 'riak')
77
+ write_riak_script
78
+ write_vm_args
79
+ write_app_config
80
+ @prepared = true
81
+ end
82
+ end
83
+
84
+ # Starts the test server if it is not already running, and waits
85
+ # for it to respond to pings.
86
+ def start
87
+ if @prepared && !@started
88
+ @mutex.synchronize do
89
+ @cin, @cout, @cerr, @cpid = Open3.popen3("#{@riak_script} console")
90
+ @cin.puts
91
+ wait_for_erlang_prompt
92
+ @started = true
93
+ end
94
+ end
95
+ end
96
+
97
+ # Stops the test server if it is running.
98
+ def stop
99
+ if @started
100
+ @mutex.synchronize do
101
+ begin
102
+ @cin.puts "init:stop()."
103
+ rescue Errno::EPIPE
104
+ ensure
105
+ register_stop
106
+ end
107
+ end
108
+ true
109
+ end
110
+ end
111
+
112
+ # Whether the server has been started.
113
+ def started?
114
+ @started
115
+ end
116
+
117
+ # Causes the entire contents of the Riak in-memory backends to be
118
+ # dumped by performing a soft restart.
119
+ def recycle
120
+ if @started
121
+ @mutex.synchronize do
122
+ begin
123
+ if @app_config[:riak_kv][:storage_backend] == :riak_kv_test_backend
124
+ @cin.puts "riak_kv_test_backend:reset()."
125
+ wait_for_erlang_prompt
126
+ else
127
+ @cin.puts "init:restart()."
128
+ wait_for_erlang_prompt
129
+ wait_for_startup
130
+ end
131
+ rescue Errno::EPIPE
132
+ warn "Broken pipe when recycling, is Riak alive?"
133
+ register_stop
134
+ return false
135
+ end
136
+ end
137
+ true
138
+ else
139
+ start
140
+ end
141
+ end
142
+
143
+ # Cleans up any files and directories generated by the test
144
+ # server.
145
+ def cleanup
146
+ stop if @started
147
+ FileUtils.rm_rf(@temp_dir)
148
+ @prepared = false
149
+ end
150
+
151
+ private
152
+ def create_temp_directories
153
+ %w{bin etc log data pipe}.each do |dir|
154
+ instance_variable_set("@temp_#{dir}", File.expand_path(File.join(@temp_dir, dir)))
155
+ FileUtils.mkdir_p(instance_variable_get("@temp_#{dir}"))
156
+ end
157
+ end
158
+
159
+ def write_riak_script
160
+ File.open(@riak_script, 'wb') do |f|
161
+ File.readlines(File.join(@bin_dir, 'riak')).each do |line|
162
+ line.sub!(/(RUNNER_SCRIPT_DIR=)(.*)/, '\1' + @temp_bin)
163
+ line.sub!(/(RUNNER_ETC_DIR=)(.*)/, '\1' + @temp_etc)
164
+ line.sub!(/(RUNNER_USER=)(.*)/, '\1')
165
+ line.sub!(/(RUNNER_LOG_DIR=)(.*)/, '\1' + @temp_log)
166
+ line.sub!(/(PIPE_DIR=)(.*)/, '\1' + @temp_pipe)
167
+ if line.strip == "RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}"
168
+ line = "RUNNER_BASE_DIR=#{File.expand_path("..",@bin_dir)}\n"
169
+ end
170
+ f.write line
171
+ end
172
+ end
173
+ FileUtils.chmod(0755,@riak_script)
174
+ end
175
+
176
+ def write_vm_args
177
+ File.open(File.join(@temp_etc, 'vm.args'), 'wb') do |f|
178
+ f.write @vm_args.map {|k,v| "#{k} #{v}" }.join("\n")
179
+ end
180
+ end
181
+
182
+ def write_app_config
183
+ File.open(File.join(@temp_etc, 'app.config'), 'wb') do |f|
184
+ f.write to_erlang_config(@app_config) + '.'
185
+ end
186
+ end
187
+
188
+ def deep_merge(source, target)
189
+ source.merge(target) do |key, old_val, new_val|
190
+ if Hash === old_val && Hash === new_val
191
+ deep_merge(old_val, new_val)
192
+ else
193
+ new_val
194
+ end
195
+ end
196
+ end
197
+
198
+ def to_erlang_config(hash, depth = 1)
199
+ padding = ' ' * depth
200
+ parent_padding = ' ' * (depth-1)
201
+ values = hash.map do |k,v|
202
+ printable = case v
203
+ when Hash
204
+ to_erlang_config(v, depth+1)
205
+ when String
206
+ "\"#{v}\""
207
+ else
208
+ v.to_s
209
+ end
210
+ "{#{k}, #{printable}}"
211
+ end.join(",\n#{padding}")
212
+ "[\n#{padding}#{values}\n#{parent_padding}]"
213
+ end
214
+
215
+ def wait_for_startup
216
+ TCPSocket.wait_for_service_with_timeout(:host => @app_config[:riak_core][:web_ip],
217
+ :port => @app_config[:riak_core][:web_port], :timeout => 10)
218
+ end
219
+
220
+ def wait_for_erlang_prompt
221
+ @cout.expect(/\(#{Regexp.escape(vm_args["-name"])}\)\d+>/)
222
+ end
223
+
224
+ def register_stop
225
+ %w{@cin @cout @cerr}.each {|io| if instance_variable_get(io); instance_variable_get(io).close; instance_variable_set(io, nil) end }
226
+ _cpid = @cpid; @cpid = nil
227
+ at_exit { _cpid.join if _cpid.alive? }
228
+ @started = false
229
+ end
230
+ end
231
+ end
@@ -21,7 +21,7 @@ module Net::HTTPHeader
21
21
  respond_to?(:enum_for) and (block_given? or return enum_for(__method__))
22
22
  @header.each do |k,v|
23
23
  base_length = "#{k}: \r\n".length
24
- values = v.map {|i| i.split(", ") }.flatten
24
+ values = v.map {|i| i.to_s.split(", ") }.flatten
25
25
  while !values.empty?
26
26
  current_line = ""
27
27
  while values.first && current_line.length + base_length + values.first.length + 2 < 8192
@@ -0,0 +1,70 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require 'timeout'
15
+ require 'socket'
16
+
17
+ # Borrowed from Webrat and Selenium client, watches for TCP port
18
+ # liveness of the spawned server.
19
+ # @private
20
+ class TCPSocket
21
+ def self.wait_for_service(options)
22
+ verbose_wait until listening_service?(options)
23
+ end
24
+
25
+ def self.wait_for_service_termination(options)
26
+ verbose_wait while listening_service?(options)
27
+ end
28
+
29
+ def self.listening_service?(options)
30
+ Timeout::timeout(options[:timeout] || 20) do
31
+ begin
32
+ socket = TCPSocket.new(options[:host], options[:port])
33
+ socket.close unless socket.nil?
34
+ true
35
+ rescue Errno::ECONNREFUSED,
36
+ Errno::EBADF # Windows
37
+ false
38
+ end
39
+ end
40
+ end
41
+
42
+ def self.verbose_wait
43
+ # Removed the puts call so as not to clutter up test output.
44
+ sleep 2
45
+ end
46
+
47
+ def self.wait_for_service_with_timeout(options)
48
+ start_time = Time.now
49
+
50
+ until listening_service?(options)
51
+ verbose_wait
52
+
53
+ if options[:timeout] && (Time.now > start_time + options[:timeout])
54
+ raise SocketError.new("Socket did not open within #{options[:timeout]} seconds")
55
+ end
56
+ end
57
+ end
58
+
59
+ def self.wait_for_service_termination_with_timeout(options)
60
+ start_time = Time.now
61
+
62
+ while listening_service?(options)
63
+ verbose_wait
64
+
65
+ if options[:timeout] && (Time.now > start_time + options[:timeout])
66
+ raise SocketError.new("Socket did not terminate within #{options[:timeout]} seconds")
67
+ end
68
+ end
69
+ end
70
+ end
@@ -14,15 +14,30 @@
14
14
  require File.expand_path("../../spec_helper", File.dirname(__FILE__))
15
15
 
16
16
  describe Riak::CacheStore do
17
+ before :all do
18
+ if $test_server
19
+ @web_port = 9000
20
+ $test_server.start
21
+ end
22
+ end
23
+
17
24
  before do
18
- @cache = ActiveSupport::Cache.lookup_store(:riak_store)
25
+ @web_port ||= 8098
26
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :port => @web_port)
19
27
  @cleanup = true
20
28
  end
21
29
 
22
30
  after do
23
- @cache.bucket.keys(:force => true).each do |k|
24
- @cache.bucket.delete(k, :rw => 1) unless k.blank?
25
- end if @cleanup
31
+ if @cleanup
32
+ if $test_server
33
+ $test_server.recycle
34
+ Thread.current[:curl_easy_handle] = nil
35
+ else
36
+ @cache.bucket.keys(:force => true).each do |k|
37
+ @cache.bucket.delete(k, :rw => 1) unless k.blank?
38
+ end
39
+ end
40
+ end
26
41
  end
27
42
 
28
43
  describe "Riak integration" do
@@ -45,7 +60,7 @@ describe Riak::CacheStore do
45
60
  end
46
61
 
47
62
  it "should choose the bucket according to the initializer option" do
48
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :bucket => "foobar")
63
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :bucket => "foobar", :port => @web_port)
49
64
  @cache.bucket.name.should == "foobar"
50
65
  end
51
66
 
@@ -54,7 +69,7 @@ describe Riak::CacheStore do
54
69
  end
55
70
 
56
71
  it "should set the N value to the specified value" do
57
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :n_value => 1)
72
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :n_value => 1, :port => @web_port)
58
73
  @cache.bucket.n_value.should == 1
59
74
  end
60
75
 
@@ -63,7 +78,7 @@ describe Riak::CacheStore do
63
78
  end
64
79
 
65
80
  it "should set the bucket R default to the specified value" do
66
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :r => "quorum")
81
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :r => "quorum", :port => @web_port)
67
82
  @cache.bucket.r.should == "quorum"
68
83
  end
69
84
 
@@ -72,7 +87,7 @@ describe Riak::CacheStore do
72
87
  end
73
88
 
74
89
  it "should set the bucket W default to the specified value" do
75
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :w => "all")
90
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :w => "all", :port => @web_port)
76
91
  @cache.bucket.w.should == "all"
77
92
  end
78
93
 
@@ -81,7 +96,7 @@ describe Riak::CacheStore do
81
96
  end
82
97
 
83
98
  it "should set the bucket DW default to the specified value" do
84
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :dw => "quorum")
99
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :dw => "quorum", :port => @web_port)
85
100
  @cache.bucket.dw.should == "quorum"
86
101
  end
87
102
 
@@ -90,7 +105,7 @@ describe Riak::CacheStore do
90
105
  end
91
106
 
92
107
  it "should set the bucket RW default to the specified value" do
93
- @cache = ActiveSupport::Cache.lookup_store(:riak_store, :rw => "all")
108
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :rw => "all", :port => @web_port)
94
109
  @cache.bucket.rw.should == "all"
95
110
  end
96
111
  end
@@ -0,0 +1,174 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require File.expand_path("../../spec_helper", File.dirname(__FILE__))
15
+
16
+ if $test_server
17
+ describe Riak::TestServer do
18
+ before do
19
+ @server = $test_server
20
+ end
21
+
22
+ after do
23
+ @server.stop
24
+ @server.cleanup
25
+ end
26
+
27
+ describe "isolation from and modification of the existing install" do
28
+ before do
29
+ @server.prepare!
30
+ @riak_bin = "#{@server.temp_dir}/bin/riak"
31
+ @vm_args = "#{@server.temp_dir}/etc/vm.args"
32
+ @app_config = "#{@server.temp_dir}/etc/app.config"
33
+ end
34
+
35
+ describe "for app.config" do
36
+ it "should create the app.config file in the temporary directory" do
37
+ File.should be_exist(File.expand_path(@app_config))
38
+ end
39
+
40
+ it "should be a correct Erlang config" do
41
+ config = File.read(@app_config)
42
+ config[-2..-1].should == '].'
43
+ config[0..0].should == '['
44
+ end
45
+
46
+ it "should set the backend to use the cache backend" do
47
+ File.readlines(@app_config).should be_any do |line|
48
+ line =~ /\{storage_backend\s*,\s*(.*)\}/ && $1 == "riak_kv_cache_backend"
49
+ end
50
+ end
51
+
52
+ it "should set the default ports to 9000-9002" do
53
+ config = File.readlines(@app_config)
54
+ config.should be_any do |line|
55
+ line =~ /\{web_port\s*,\s*(.*)\}/ && $1 == "9000"
56
+ end
57
+ config.should be_any do |line|
58
+ line =~ /\{handoff_port\s*,\s*(.*)\}/ && $1 == "9001"
59
+ end
60
+ config.should be_any do |line|
61
+ line =~ /\{pb_port\s*,\s*(.*)\}/ && $1 == "9002"
62
+ end
63
+ end
64
+
65
+ it "should set the ring directory to point to the temporary directory" do
66
+ config = File.readlines(@app_config)
67
+ config.should be_any do |line|
68
+ line =~ /\{ring_state_dir\s*,\s*(.*)\}/ && $1 == File.join(@server.temp_dir, "data", "ring")
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "for vm.args" do
74
+ it "should create the vm.args file in the temporary directory" do
75
+ File.should be_exist(File.expand_path(@vm_args))
76
+ end
77
+
78
+ it "should set a quasi-random node name" do
79
+ File.readlines(@vm_args).should be_any do |line|
80
+ line =~ /^-name (.*)/ && $1 =~ /riaktest\d+@/
81
+ end
82
+ end
83
+
84
+ it "should set a quasi-random cookie" do
85
+ File.readlines(@vm_args).should be_any do |line|
86
+ line =~ /^-setcookie (.*)/ && $1 != "riak"
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "for the riak script" do
92
+ it "should create the script in the temporary directory" do
93
+ File.should be_exist(File.expand_path(@riak_bin))
94
+ end
95
+
96
+ it "should modify the RUNNER_SCRIPT_DIR to point to the temporary directory" do
97
+ File.readlines(@riak_bin).should be_any do |line|
98
+ line =~ /RUNNER_SCRIPT_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/bin")
99
+
100
+ end
101
+ end
102
+
103
+ it "should modify the RUNNER_ETC_DIR to point to the temporary directory" do
104
+ File.readlines(@riak_bin).should be_any do |line|
105
+ line =~ /RUNNER_ETC_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/etc")
106
+ end
107
+ end
108
+
109
+ it "should modify the RUNNER_USER to point to the current user" do
110
+ File.readlines(@riak_bin).should be_any do |line|
111
+ line =~ /RUNNER_USER=(.*)/ && $1 == (ENV['USER'] || `whoami`)
112
+ end
113
+ end
114
+
115
+ it "should modify the RUNNER_LOG_DIR to point to the temporary directory" do
116
+ File.readlines(@riak_bin).should be_any do |line|
117
+ line =~ /RUNNER_LOG_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/log")
118
+ end
119
+ end
120
+
121
+ it "should modify the RUNNER_BASE_DIR so that it is not relative" do
122
+ File.readlines(@riak_bin).should be_any do |line|
123
+ line =~ /RUNNER_BASE_DIR=(.*)/ && $1.strip != "${RUNNER_SCRIPT_DIR%/*}" && File.directory?($1)
124
+ end
125
+ end
126
+
127
+ it "should modify the PIPE_DIR to point to the temporary directory" do
128
+ File.readlines(@riak_bin).should be_any do |line|
129
+ line =~ /PIPE_DIR=(.*)/ && $1 == File.expand_path("#{@server.temp_dir}/pipe") && File.directory?($1)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ it "should cleanup the existing config" do
136
+ @server.prepare!
137
+ @server.cleanup
138
+ File.should_not be_directory(@server.temp_dir)
139
+ end
140
+
141
+ it "should start Riak in the background" do
142
+ @server.prepare!
143
+ @server.start.should be_true
144
+ @server.should be_started
145
+ end
146
+
147
+ it "should stop a started test server" do
148
+ @server.prepare!
149
+ @server.start.should be_true
150
+ @server.stop
151
+ @server.should_not be_started
152
+ end
153
+
154
+ it "should recycle the server contents" do
155
+ begin
156
+ @server.prepare!
157
+ @server.start.should be_true
158
+
159
+ client = Riak::Client.new(:port => 9000)
160
+ obj = client['test_bucket'].new("test_item")
161
+ obj.data = {"data" => "testing"}
162
+ obj.store rescue nil
163
+
164
+ @server.recycle
165
+ @server.should be_started
166
+ lambda do
167
+ client['test_bucket']['test_item']
168
+ end.should raise_error(Riak::FailedRequest)
169
+ ensure
170
+ @server.stop
171
+ end
172
+ end
173
+ end
174
+ end
@@ -118,6 +118,64 @@ describe Riak::RObject do
118
118
  end
119
119
  end
120
120
 
121
+ describe "data access methods" do
122
+ before :each do
123
+ @object = Riak::RObject.new(@bucket, "bar")
124
+ @object.content_type = "application/json"
125
+ end
126
+
127
+ describe "for raw data" do
128
+ describe "when unserialized data was already provided" do
129
+ before do
130
+ @object.data = { :some => :data }
131
+ end
132
+
133
+ it "should reset unserialized forms when stored" do
134
+ @object.raw_data = value = '{ "raw": "json" }'
135
+
136
+ @object.raw_data.should == value
137
+ @object.data.should == { "raw" => "json" }
138
+ end
139
+
140
+ it "should lazily serialize when read" do
141
+ @object.raw_data.should == '{"some":"data"}'
142
+ end
143
+ end
144
+
145
+ it "should not unnecessarily marshal/demarshal" do
146
+ @object.should_not_receive(:serialize)
147
+ @object.should_not_receive(:deserialize)
148
+ @object.raw_data = value = "{not even valid json!}}"
149
+ @object.raw_data.should == value
150
+ end
151
+ end
152
+
153
+ describe "for unserialized data" do
154
+ describe "when raw data was already provided" do
155
+ before do
156
+ @object.raw_data = '{"some":"data"}'
157
+ end
158
+
159
+ it "should reset previously stored raw data" do
160
+ @object.data = value = { "new" => "data" }
161
+ @object.raw_data.should == '{"new":"data"}'
162
+ @object.data.should == value
163
+ end
164
+
165
+ it "should lazily deserialize when read" do
166
+ @object.data.should == { "some" => "data" }
167
+ end
168
+ end
169
+
170
+ it "should not unnecessarily marshal/demarshal" do
171
+ @object.should_not_receive(:serialize)
172
+ @object.should_not_receive(:deserialize)
173
+ @object.data = value = { "some" => "data" }
174
+ @object.data.should == value
175
+ end
176
+ end
177
+ end
178
+
121
179
  describe "loading data from the response" do
122
180
  before :each do
123
181
  @object = Riak::RObject.new(@bucket, "bar")
@@ -130,9 +188,16 @@ describe Riak::RObject do
130
188
 
131
189
  it "should load the body data" do
132
190
  @object.load({:headers => {"content-type" => ["application/json"]}, :body => '{"foo":"bar"}'})
191
+ @object.raw_data.should be_present
133
192
  @object.data.should be_present
134
193
  end
135
194
 
195
+ it "should handle raw data properly" do
196
+ @object.should_not_receive(:deserialize) # optimize for the raw_data case, don't penalize people for using raw_data
197
+ @object.load({:headers => {"content-type" => ["application/json"]}, :body => body = '{"foo":"bar"}'})
198
+ @object.raw_data.should == body
199
+ end
200
+
136
201
  it "should deserialize the body data" do
137
202
  @object.should_receive(:deserialize).with("{}").and_return({})
138
203
  @object.load({:headers => {"content-type" => ["application/json"]}, :body => "{}"})
@@ -335,6 +400,23 @@ describe Riak::RObject do
335
400
  @object.store_headers.should_not have_key("X-Riak-Vclock")
336
401
  end
337
402
 
403
+ describe "when conditional PUTs are requested" do
404
+ before :each do
405
+ @object.prevent_stale_writes = true
406
+ end
407
+
408
+ it "should include an If-None-Match: * header" do
409
+ @object.store_headers.should have_key("If-None-Match")
410
+ @object.store_headers["If-None-Match"].should == "*"
411
+ end
412
+
413
+ it "should include an If-Match header with the etag when an etag is present" do
414
+ @object.etag = "foobar"
415
+ @object.store_headers.should have_key("If-Match")
416
+ @object.store_headers["If-Match"].should == @object.etag
417
+ end
418
+ end
419
+
338
420
  describe "when links are defined" do
339
421
  before :each do
340
422
  @object.links << Riak::Link.new("/riak/foo/baz", "next")
@@ -447,6 +529,22 @@ describe Riak::RObject do
447
529
  @object.key = "bar"
448
530
  end
449
531
 
532
+ describe "when the content is of a known serializable type" do
533
+ before :each do
534
+ @object.content_type = "application/json"
535
+ @headers = @object.store_headers
536
+ end
537
+
538
+ it "should not serialize content if #raw_data is used" do
539
+ storing = @object.raw_data = "{this is probably invalid json}}"
540
+ @http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:returnbody => true}, storing, @headers).and_return({:headers => {"x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
541
+ @object.should_not_receive(:serialize)
542
+ @object.should_not_receive(:deserialize)
543
+ @object.store
544
+ @object.raw_data.should == storing
545
+ end
546
+ end
547
+
450
548
  it "should issue a PUT request to the bucket, and update the object properties (returning the body by default)" do
451
549
  @http.should_receive(:put).with([200,204,300], "/riak/", "foo/bar", {:returnbody => true}, "This is some text.", @headers).and_return({:headers => {'location' => ["/riak/foo/somereallylongstring"], "x-riak-vclock" => ["areallylonghashvalue"]}, :code => 204})
452
550
  @object.store
@@ -16,14 +16,26 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
16
 
17
17
  require 'rubygems' # Use the gems path only for the spec suite
18
18
  require 'riak'
19
- require 'rspec/autorun'
19
+ require 'rspec'
20
20
  require 'fakeweb'
21
21
 
22
+ begin
23
+ require 'yaml'
24
+ config = YAML.load_file("spec/support/test_server.yml")
25
+ $test_server = Riak::TestServer.new(config.symbolize_keys)
26
+ $test_server.prepare!
27
+ $test_server.start
28
+ at_exit { $test_server.cleanup }
29
+ rescue => e
30
+ warn "Can't run Riak::TestServer specs. Specify the location of your Riak installation in spec/support/test_server.yml. See Riak::TestServer docs for more info."
31
+ warn e.inspect
32
+ end
33
+
22
34
  Dir[File.join(File.dirname(__FILE__), "support", "*.rb")].each {|f| require f }
23
35
 
24
36
  Rspec.configure do |config|
25
37
  config.mock_with :rspec
26
-
38
+
27
39
  config.before(:each) do
28
40
  FakeWeb.clean_registry
29
41
  end
@@ -0,0 +1,2 @@
1
+ bin_dir: /Users/sean/Development/riak/rel/riak/bin
2
+ temp_dir: /Users/sean/Development/ripple/.riaktest
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 8
8
- - 0
9
- version: 0.8.0
8
+ - 1
9
+ version: 0.8.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Sean Cribbs
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-31 00:00:00 -04:00
17
+ date: 2010-10-11 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -115,6 +115,13 @@ extensions: []
115
115
  extra_rdoc_files: []
116
116
 
117
117
  files:
118
+ - bin/htmldiff
119
+ - bin/ldiff
120
+ - bin/rackup
121
+ - bin/rake
122
+ - bin/rspec
123
+ - erl_src/riak_kv_test_backend.beam
124
+ - erl_src/riak_kv_test_backend.erl
118
125
  - Gemfile
119
126
  - Gemfile.lock
120
127
  - lib/active_support/cache/riak_store.rb
@@ -132,10 +139,12 @@ files:
132
139
  - lib/riak/map_reduce.rb
133
140
  - lib/riak/map_reduce_error.rb
134
141
  - lib/riak/robject.rb
142
+ - lib/riak/test_server.rb
135
143
  - lib/riak/util/escape.rb
136
144
  - lib/riak/util/fiber1.8.rb
137
145
  - lib/riak/util/headers.rb
138
146
  - lib/riak/util/multipart.rb
147
+ - lib/riak/util/tcp_socket_extensions.rb
139
148
  - lib/riak/util/translation.rb
140
149
  - lib/riak/walk_spec.rb
141
150
  - lib/riak.rb
@@ -144,6 +153,7 @@ files:
144
153
  - spec/fixtures/multipart-blank.txt
145
154
  - spec/fixtures/multipart-with-body.txt
146
155
  - spec/integration/riak/cache_store_spec.rb
156
+ - spec/integration/riak/test_server_spec.rb
147
157
  - spec/riak/bucket_spec.rb
148
158
  - spec/riak/client_spec.rb
149
159
  - spec/riak/curb_backend_spec.rb
@@ -160,6 +170,7 @@ files:
160
170
  - spec/support/http_backend_implementation_examples.rb
161
171
  - spec/support/mock_server.rb
162
172
  - spec/support/mocks.rb
173
+ - spec/support/test_server.yml
163
174
  has_rdoc: true
164
175
  homepage: http://seancribbs.github.com/ripple
165
176
  licenses: []
@@ -194,6 +205,7 @@ specification_version: 3
194
205
  summary: riak-client is a rich client for Riak, the distributed database by Basho.
195
206
  test_files:
196
207
  - spec/integration/riak/cache_store_spec.rb
208
+ - spec/integration/riak/test_server_spec.rb
197
209
  - spec/riak/bucket_spec.rb
198
210
  - spec/riak/client_spec.rb
199
211
  - spec/riak/curb_backend_spec.rb