rsolr 0.12.0 → 1.0.3

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.
@@ -0,0 +1,216 @@
1
+ require 'spec_helper'
2
+ describe "RSolr::Client" do
3
+
4
+ module ClientHelper
5
+ def client
6
+ @client ||= (
7
+ connection = RSolr::Connection.new
8
+ RSolr::Client.new connection, :url => "http://localhost:9999/solr"
9
+ )
10
+ end
11
+ end
12
+
13
+ context "initialize" do
14
+ it "should accept whatevs and set it as the @connection" do
15
+ RSolr::Client.new(:whatevs).connection.should == :whatevs
16
+ end
17
+ end
18
+
19
+ context "send_and_receive" do
20
+ include ClientHelper
21
+ it "should forward these method calls the #connection object" do
22
+ [:get, :post, :head].each do |meth|
23
+ client.connection.should_receive(:execute).
24
+ and_return({:status => 200, :body => "{}", :headers => {}})
25
+ client.send_and_receive '', :method => meth, :params => {}, :data => nil, :headers => {}
26
+ end
27
+ end
28
+ end
29
+
30
+ context "post" do
31
+ include ClientHelper
32
+ it "should pass the expected params to the connection's #execute method" do
33
+ request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
34
+ client.connection.should_receive(:execute).
35
+ with(client, hash_including(request_opts)).
36
+ and_return(
37
+ :body => "",
38
+ :status => 200,
39
+ :headers => {"Content-Type"=>"text/plain"}
40
+ )
41
+ client.post "update", request_opts
42
+ end
43
+ end
44
+
45
+ context "xml" do
46
+ include ClientHelper
47
+ it "should return an instance of RSolr::Xml::Generator" do
48
+ client.xml.should be_a RSolr::Xml::Generator
49
+ end
50
+ end
51
+
52
+ context "add" do
53
+ include ClientHelper
54
+ it "should send xml to the connection's #post method" do
55
+ client.connection.should_receive(:execute).
56
+ with(
57
+ client, hash_including({
58
+ :path => "update",
59
+ :headers => {"Content-Type"=>"text/xml"},
60
+ :method => :post,
61
+ :data => "<xml/>"
62
+ })
63
+ ).
64
+ and_return(
65
+ :body => "",
66
+ :status => 200,
67
+ :headers => {"Content-Type"=>"text/xml"}
68
+ )
69
+ client.xml.should_receive(:add).
70
+ with({:id=>1}, {:commitWith=>10}).
71
+ and_return("<xml/>")
72
+ client.add({:id=>1}, :add_attributes => {:commitWith=>10})
73
+ end
74
+ end
75
+
76
+ context "update" do
77
+ include ClientHelper
78
+ it "should send data to the connection's #post method" do
79
+ client.connection.should_receive(:execute).
80
+ with(
81
+ client, hash_including({
82
+ :path => "update",
83
+ :headers => {"Content-Type"=>"text/xml"},
84
+ :method => :post,
85
+ :data => "<optimize/>"
86
+ })
87
+ ).
88
+ and_return(
89
+ :body => "",
90
+ :status => 200,
91
+ :headers => {"Content-Type"=>"text/xml"}
92
+ )
93
+ client.update(:data => "<optimize/>")
94
+ end
95
+ end
96
+
97
+ context "post based helper methods:" do
98
+ include ClientHelper
99
+ [:commit, :optimize, :rollback].each do |meth|
100
+ it "should send a #{meth} message to the connection's #post method" do
101
+ client.connection.should_receive(:execute).
102
+ with(
103
+ client, hash_including({
104
+ :path => "update",
105
+ :headers => {"Content-Type"=>"text/xml"},
106
+ :method => :post,
107
+ :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>"
108
+ })
109
+ ).
110
+ and_return(
111
+ :body => "",
112
+ :status => 200,
113
+ :headers => {"Content-Type"=>"text/xml"}
114
+ )
115
+ client.send meth
116
+ end
117
+ end
118
+ end
119
+
120
+ context "delete_by_id" do
121
+ include ClientHelper
122
+ it "should send data to the connection's #post method" do
123
+ client.connection.should_receive(:execute).
124
+ with(
125
+ client, hash_including({
126
+ :path => "update",
127
+ :headers => {"Content-Type"=>"text/xml"},
128
+ :method => :post,
129
+ :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id></delete>"
130
+ })
131
+ ).
132
+ and_return(
133
+ :body => "",
134
+ :status => 200,
135
+ :headers => {"Content-Type"=>"text/xml"}
136
+ )
137
+ client.delete_by_id 1
138
+ end
139
+ end
140
+
141
+ context "delete_by_query" do
142
+ include ClientHelper
143
+ it "should send data to the connection's #post method" do
144
+ client.connection.should_receive(:execute).
145
+ with(
146
+ client, hash_including({
147
+ :path => "update",
148
+ :headers => {"Content-Type"=>"text/xml"},
149
+ :method => :post,
150
+ :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query fq=\"category:&quot;trash&quot;\"/></delete>"
151
+ })
152
+ ).
153
+ and_return(
154
+ :body => "",
155
+ :status => 200,
156
+ :headers => {"Content-Type"=>"text/xml"}
157
+ )
158
+ client.delete_by_query :fq => "category:\"trash\""
159
+ end
160
+ end
161
+
162
+ context "adapt_response" do
163
+ include ClientHelper
164
+ it 'should not try to evaluate ruby when the :qt is not :ruby' do
165
+ body = '{:time=>"NOW"}'
166
+ result = client.adapt_response({:params=>{}}, {:status => 200, :body => body, :headers => {}})
167
+ result.should == body
168
+ end
169
+
170
+ it 'should evaluate ruby responses when the :wt is :ruby' do
171
+ body = '{:time=>"NOW"}'
172
+ result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
173
+ result.should == {:time=>"NOW"}
174
+ end
175
+
176
+ it "ought raise a RSolr::Error::InvalidRubyResponse when the ruby is indeed frugged, or even fruggified" do
177
+ lambda {
178
+ client.adapt_response({:params=>{:wt => :ruby}}, {:status => 200, :body => "<woops/>", :headers => {}})
179
+ }.should raise_error RSolr::Error::InvalidRubyResponse
180
+ end
181
+
182
+ end
183
+
184
+ context "build_request" do
185
+ include ClientHelper
186
+ it 'should return a request context array' do
187
+ result = client.build_request('select',
188
+ :method => :post,
189
+ :params => {:q=>'test', :fq=>[0,1]},
190
+ :data => "data",
191
+ :headers => {}
192
+ )
193
+ [/fq=0/, /fq=1/, /q=test/, /wt=ruby/].each do |pattern|
194
+ result[:query].should match pattern
195
+ end
196
+ result[:data].should == "data"
197
+ result[:headers].should == {}
198
+ end
199
+
200
+ it "should set the Content-Type header to application/x-www-form-urlencoded if a hash is passed in to the data arg" do
201
+ result = client.build_request('select',
202
+ :method => :post,
203
+ :data => {:q=>'test', :fq=>[0,1]},
204
+ :headers => {}
205
+ )
206
+ result[:query].should == "wt=ruby"
207
+ [/fq=0/, /fq=1/, /q=test/].each do |pattern|
208
+ result[:data].should match pattern
209
+ end
210
+ result[:data].should_not match /wt=ruby/
211
+ result[:headers].should == {"Content-Type" => "application/x-www-form-urlencoded"}
212
+ end
213
+
214
+ end
215
+
216
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ describe "RSolr::Connection" do
3
+
4
+ context "setup_raw_request" do
5
+ c = RSolr::Connection.new
6
+ base_url = "http://localhost:8983/solr"
7
+ client = RSolr::Client.new c, :url => base_url
8
+ req = c.send :setup_raw_request, {:headers => {"content-type" => "text/xml"}, :method => :get, :uri => URI.parse(base_url + "/select?q=*:*")}
9
+ req.path.should == "/solr/select?q=*:*"
10
+ headers = {}
11
+ req.each_header{|k,v| headers[k] = v}
12
+ headers.should == {"content-type"=>"text/xml"}
13
+ end
14
+
15
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ describe "RSolr::Error" do
3
+ def generate_error_with_backtrace(request, response)
4
+ raise RSolr::Error::Http.new request, response
5
+ rescue RSolr::Error::Http => exception
6
+ exception
7
+ end
8
+
9
+ context "when the response body is wrapped in a <pre> element" do
10
+ before do
11
+ response_lines = (1..15).to_a.map { |i| "line #{i}" }
12
+
13
+ @request = mock :[] => "mocked"
14
+ @response = {
15
+ :body => "<pre>" + response_lines.join("\n") + "</pre>",
16
+ :status => 400
17
+ }
18
+ end
19
+
20
+ it "only shows the first eleven lines of the response" do
21
+ error = generate_error_with_backtrace @request, @response
22
+ error.to_s.should match(/line 1\n.+line 11\n\n/m)
23
+ end
24
+
25
+ it "shows only one line when the response is one line long" do
26
+ @response[:body] = "<pre>failed</pre>"
27
+
28
+ error = generate_error_with_backtrace @request, @response
29
+ error.to_s.should match(/Error: failed/)
30
+ end
31
+ end
32
+
33
+ context "when the response body is not wrapped in a <pre> element" do
34
+ before do
35
+ response_lines = (1..15).to_a.map { |i| "line #{i}" }
36
+
37
+ @request = mock :[] => "mocked"
38
+ @response = {
39
+ :body => response_lines.join("\n"),
40
+ :status => 400
41
+ }
42
+ end
43
+
44
+ it "only shows the first eleven lines of the response" do
45
+ error = generate_error_with_backtrace @request, @response
46
+ error.to_s.should match(/line 1\n.+line 11\n\n/m)
47
+ end
48
+
49
+ it "shows only one line when the response is one line long" do
50
+ @response[:body] = "failed"
51
+
52
+ error = generate_error_with_backtrace @request, @response
53
+ error.to_s.should match(/Error: failed/)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ describe "RSolr::Pagination" do
3
+ context "build_paginated_request" do
4
+ it "should create the proper solr params and query string" do
5
+ c = RSolr::Client.new(nil, {})#.extend(RSolr::Pagination::Client)
6
+ r = c.build_paginated_request 3, 25, "select", {:params => {:q => "test"}}
7
+ #r[:page].should == 3
8
+ #r[:per_page].should == 25
9
+ r[:params]["start"].should == 50
10
+ r[:params]["rows"].should == 25
11
+ r[:uri].query.should =~ /rows=25/
12
+ r[:uri].query.should =~ /start=50/
13
+ end
14
+ end
15
+ context "paginate" do
16
+ it "should build a paginated request context and call execute" do
17
+ c = RSolr::Client.new(nil, {})#.extend(RSolr::Pagination::Client)
18
+ c.should_receive(:execute).with(hash_including({
19
+ #:page => 1,
20
+ #:per_page => 10,
21
+ :params => {
22
+ "rows" => 10,
23
+ "start" => 0,
24
+ :wt => :ruby
25
+ }
26
+ }))
27
+ c.paginate 1, 10, "select"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ describe "RSolr" do
3
+
4
+ it "has a version that can be read via #version or VERSION" do
5
+ RSolr.version.should == RSolr::VERSION
6
+ end
7
+
8
+ it "can escape" do
9
+ RSolr.should be_a(RSolr::Char)
10
+ RSolr.escape("this string").should == "this\\ string"
11
+ end
12
+
13
+ context "connect" do
14
+ it "should return a RSolr::Client instance" do
15
+ RSolr.connect.should be_a(RSolr::Client)
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+ describe "RSolr::Uri" do
3
+
4
+ context "class-level methods" do
5
+
6
+ let(:uri){ RSolr::Uri }
7
+
8
+ it "should return a URI object with a trailing slash" do
9
+ u = uri.create 'http://apache.org'
10
+ u.path[0].should == ?/
11
+ end
12
+
13
+ it "should return the bytesize of a string" do
14
+ uri.bytesize("test").should == 4
15
+ end
16
+
17
+ it "should convert a solr query string from a hash w/o a starting ?" do
18
+ hash = {:q => "gold", :fq => ["mode:one", "level:2"]}
19
+ query = uri.params_to_solr hash
20
+ query[0].should_not == ??
21
+ [/q=gold/, /fq=mode%3Aone/, /fq=level%3A2/].each do |p|
22
+ query.should match p
23
+ end
24
+ query.split('&').size.should == 3
25
+ end
26
+
27
+ context "escape_query_value" do
28
+
29
+ it 'should escape &' do
30
+ uri.params_to_solr(:fq => "&").should == 'fq=%26'
31
+ end
32
+
33
+ it 'should convert spaces to +' do
34
+ uri.params_to_solr(:fq => "me and you").should == 'fq=me+and+you'
35
+ end
36
+
37
+ it 'should escape comlex queries, part 1' do
38
+ my_params = {'fq' => '{!raw f=field_name}crazy+\"field+value'}
39
+ expected = 'fq=%7B%21raw+f%3Dfield_name%7Dcrazy%2B%5C%22field%2Bvalue'
40
+ uri.params_to_solr(my_params).should == expected
41
+ end
42
+
43
+ it 'should escape complex queries, part 2' do
44
+ my_params = {'q' => '+popularity:[10 TO *] +section:0'}
45
+ expected = 'q=%2Bpopularity%3A%5B10+TO+%2A%5D+%2Bsection%3A0'
46
+ uri.params_to_solr(my_params).should == expected
47
+ end
48
+
49
+ it 'should escape properly' do
50
+ uri.escape_query_value('+').should == '%2B'
51
+ uri.escape_query_value('This is a test').should == 'This+is+a+test'
52
+ uri.escape_query_value('<>/\\').should == '%3C%3E%2F%5C'
53
+ uri.escape_query_value('"').should == '%22'
54
+ uri.escape_query_value(':').should == '%3A'
55
+ end
56
+
57
+ it 'should escape brackets' do
58
+ uri.escape_query_value('{').should == '%7B'
59
+ uri.escape_query_value('}').should == '%7D'
60
+ end
61
+
62
+ it 'should escape exclamation marks!' do
63
+ uri.escape_query_value('!').should == '%21'
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+ describe "RSolr::Xml" do
3
+
4
+ let(:generator){ RSolr::Xml::Generator.new }
5
+
6
+ # call all of the simple methods...
7
+ # make sure the xml string is valid
8
+ # ensure the class is actually Solr::XML
9
+ [:optimize, :rollback, :commit].each do |meth|
10
+ it "#{meth} should generator xml" do
11
+ result = generator.send(meth)
12
+ result.should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>"
13
+ end
14
+ end
15
+
16
+ context :add do
17
+
18
+ it 'should yield a Message::Document object when #add is called with a block' do
19
+ documents = [{:id=>1, :name=>'sam', :cat=>['cat 1', 'cat 2']}]
20
+ add_attrs = {:boost=>200.00}
21
+ result = generator.add(documents, add_attrs) do |doc|
22
+ doc.field_by_name(:name).attrs[:boost] = 10
23
+ doc.fields.size.should == 4
24
+ doc.fields_by_name(:cat).size.should == 2
25
+ end
26
+ result.should match(%r(name="cat">cat 1</field>))
27
+ result.should match(%r(name="cat">cat 2</field>))
28
+ result.should match(%r(<add boost="200.0">))
29
+ result.should match(%r(boost="10"))
30
+ result.should match(%r(<field name="id">1</field>))
31
+ end
32
+
33
+ # add a single hash ("doc")
34
+ it 'should create an add from a hash' do
35
+ data = {
36
+ :id=>1,
37
+ :name=>'matt'
38
+ }
39
+ result = generator.add(data)
40
+ result.should match(/<field name="name">matt<\/field>/)
41
+ result.should match(/<field name="id">1<\/field>/)
42
+ end
43
+
44
+ # add an array of hashes
45
+ it 'should create many adds from an array of hashes' do
46
+ data = [
47
+ {
48
+ :id=>1,
49
+ :name=>'matt'
50
+ },
51
+ {
52
+ :id=>2,
53
+ :name=>'sam'
54
+ }
55
+ ]
56
+ message = generator.add(data)
57
+ expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><add><doc><field name=\"id\">1</field><field name=\"name\">matt</field></doc><doc><field name=\"id\">2</field><field name=\"name\">sam</field></doc></add>"
58
+ message.should match(/<field name="name">matt<\/field>/)
59
+ message.should match(/<field name="name">sam<\/field>/)
60
+ end
61
+
62
+ # multiValue field support test, thanks to Fouad Mardini!
63
+ it 'should create multiple fields from array values' do
64
+ data = {
65
+ :id => 1,
66
+ :name => ['matt1', 'matt2']
67
+ }
68
+ result = generator.add(data)
69
+ result.should match(/<field name="name">matt1<\/field>/)
70
+ result.should match(/<field name="name">matt2<\/field>/)
71
+ end
72
+
73
+ it 'should create an add from a single Message::Document' do
74
+ document = RSolr::Xml::Document.new
75
+ document.add_field('id', 1)
76
+ document.add_field('name', 'matt', :boost => 2.0)
77
+ result = generator.add(document)
78
+ result.should match(Regexp.escape('<?xml version="1.0" encoding="UTF-8"?>'))
79
+ result.should match(/<field name="id">1<\/field>/)
80
+ result.should match Regexp.escape('boost="2.0"')
81
+ result.should match Regexp.escape('name="name"')
82
+ result.should match Regexp.escape('matt</field>')
83
+ end
84
+
85
+ it 'should create adds from multiple Message::Documents' do
86
+ documents = (1..2).map do |i|
87
+ doc = RSolr::Xml::Document.new
88
+ doc.add_field('id', i)
89
+ doc.add_field('name', "matt#{i}")
90
+ doc
91
+ end
92
+ result = generator.add(documents)
93
+ result.should match(/<field name="name">matt1<\/field>/)
94
+ result.should match(/<field name="name">matt2<\/field>/)
95
+ end
96
+
97
+ end
98
+
99
+ context :delete_by_id do
100
+
101
+ it 'should create a doc id delete' do
102
+ generator.delete_by_id(10).should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>10</id></delete>"
103
+ end
104
+
105
+ it 'should create many doc id deletes' do
106
+ generator.delete_by_id([1, 2, 3]).should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id><id>2</id><id>3</id></delete>"
107
+ end
108
+
109
+ end
110
+
111
+ context :delete_by_query do
112
+ it 'should create a query delete' do
113
+ generator.delete_by_query('status:"LOST"').should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query>status:\"LOST\"</query></delete>"
114
+ end
115
+
116
+ it 'should create many query deletes' do
117
+ generator.delete_by_query(['status:"LOST"', 'quantity:0']).should == "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query>status:\"LOST\"</query><query>quantity:0</query></delete>"
118
+ end
119
+ end
120
+
121
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path('../../lib/rsolr', __FILE__)
data/tasks/rdoc.rake ADDED
@@ -0,0 +1,11 @@
1
+ # Rdoc
2
+ require "rdoc/task"
3
+
4
+ desc 'Generate documentation for the rsolr gem.'
5
+ RDoc::Task.new(:doc) do |rdoc|
6
+ rdoc.rdoc_dir = 'doc'
7
+ rdoc.title = 'RSolr'
8
+ rdoc.options << '--line-numbers' << '--inline-source'
9
+ rdoc.rdoc_files.include('README.rdoc')
10
+ rdoc.rdoc_files.include('lib/**/*.rb')
11
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,39 @@
1
+ require "rubygems"
2
+ require 'rspec'
3
+ require 'rspec/core/rake_task'
4
+
5
+ namespace :spec do
6
+
7
+ namespace :ruby do
8
+ desc 'run api specs through the Ruby implementations'
9
+ task :api do
10
+ puts "Ruby 1.8.7"
11
+ puts `rake spec:api`
12
+ puts "Ruby 1.9"
13
+ puts `rake1.9 spec:api`
14
+ puts "JRuby"
15
+ puts `jruby -S rake spec:api`
16
+ end
17
+ end
18
+
19
+ desc 'run api specs (mock out Solr dependency)'
20
+ RSpec::Core::RakeTask.new(:api) do |t|
21
+
22
+ t.pattern = [File.join('spec', 'spec_helper.rb')]
23
+ t.pattern += FileList[File.join('spec', 'api', '**', '*_spec.rb')]
24
+
25
+ t.verbose = true
26
+ t.rspec_opts = ['--color']
27
+ end
28
+
29
+ desc 'run integration specs'
30
+ RSpec::Core::RakeTask.new(:integration) do |t|
31
+
32
+ t.pattern = [File.join('spec', 'spec_helper.rb')]
33
+ t.pattern += FileList[File.join('spec', 'integration', '**', '*_spec.rb')]
34
+
35
+ t.verbose = true
36
+ t.rspec_opts = ['--color']
37
+ end
38
+
39
+ end