2013-02-28 14:52:35 -05:00
|
|
|
# encoding: utf-8
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
require 'spec_helper'
|
2013-08-01 20:31:36 -04:00
|
|
|
require_dependency 'search'
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
describe Search do
|
|
|
|
|
2014-04-11 16:07:39 -04:00
|
|
|
class TextHelper
|
|
|
|
extend ActionView::Helpers::TextHelper
|
|
|
|
end
|
|
|
|
|
2013-05-13 21:59:55 -04:00
|
|
|
before do
|
|
|
|
ActiveRecord::Base.observers.enable :search_observer
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def first_of_type(results, type)
|
|
|
|
return nil if results.blank?
|
|
|
|
results.each do |r|
|
|
|
|
return r[:results].first if r[:type] == type
|
|
|
|
end
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2013-08-04 09:15:05 -04:00
|
|
|
def result_ids_for_type(results, type)
|
|
|
|
results.find do |group|
|
|
|
|
group[:type] == type
|
|
|
|
end[:results].map {|r| r[:id]}
|
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
context 'post indexing observer' do
|
|
|
|
before do
|
2013-02-05 14:16:51 -05:00
|
|
|
@category = Fabricate(:category, name: 'america')
|
2013-03-11 09:51:24 -04:00
|
|
|
@topic = Fabricate(:topic, title: 'sam saffron test topic', category: @category)
|
2013-02-05 14:16:51 -05:00
|
|
|
@post = Fabricate(:post, topic: @topic, raw: 'this <b>fun test</b> <img src="bla" title="my image">')
|
2013-05-22 15:33:33 -04:00
|
|
|
@indexed = @post.post_search_data.search_data
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
2013-02-25 11:42:20 -05:00
|
|
|
it "should include body in index" do
|
2013-02-05 14:16:51 -05:00
|
|
|
@indexed.should =~ /fun/
|
|
|
|
end
|
2013-02-25 11:42:20 -05:00
|
|
|
it "should include title in index" do
|
2013-02-05 14:16:51 -05:00
|
|
|
@indexed.should =~ /sam/
|
|
|
|
end
|
2013-02-25 11:42:20 -05:00
|
|
|
it "should include category in index" do
|
2013-02-05 14:16:51 -05:00
|
|
|
@indexed.should =~ /america/
|
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
it "should pick up on title updates" do
|
2013-02-06 20:09:31 -05:00
|
|
|
@topic.title = "harpi is the new title"
|
2013-02-05 14:16:51 -05:00
|
|
|
@topic.save!
|
2013-05-22 15:33:33 -04:00
|
|
|
@post.post_search_data.reload
|
|
|
|
|
|
|
|
@indexed = @post.post_search_data.search_data
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
@indexed.should =~ /harpi/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
context 'user indexing observer' do
|
|
|
|
before do
|
2013-02-05 14:16:51 -05:00
|
|
|
@user = Fabricate(:user, username: 'fred', name: 'bob jones')
|
2013-05-22 15:33:33 -04:00
|
|
|
@indexed = @user.user_search_data.search_data
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
it "should pick up on username" do
|
2013-02-05 14:16:51 -05:00
|
|
|
@indexed.should =~ /fred/
|
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
it "should pick up on name" do
|
2013-02-05 14:16:51 -05:00
|
|
|
@indexed.should =~ /jone/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
context 'category indexing observer' do
|
|
|
|
before do
|
2013-02-05 14:16:51 -05:00
|
|
|
@category = Fabricate(:category, name: 'america')
|
2013-05-22 15:33:33 -04:00
|
|
|
@indexed = @category.category_search_data.search_data
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
it "should pick up on name" do
|
2013-02-05 14:16:51 -05:00
|
|
|
@indexed.should =~ /america/
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns something blank on a nil search' do
|
|
|
|
ActiveRecord::Base.expects(:exec_sql).never
|
2013-05-22 14:36:14 -04:00
|
|
|
Search.new(nil).execute.should be_blank
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2013-03-07 10:52:01 -05:00
|
|
|
it 'does not search when the search term is too small' do
|
|
|
|
ActiveRecord::Base.expects(:exec_sql).never
|
2013-05-22 14:36:14 -04:00
|
|
|
Search.new('evil', min_search_term_length: 5).execute.should be_blank
|
2013-03-07 10:52:01 -05:00
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
it 'escapes non alphanumeric characters' do
|
2013-05-22 14:36:14 -04:00
|
|
|
Search.new('foo :!$);}]>@\#\"\'').execute.should be_blank # There are at least three levels of sanitation for Search.query!
|
2013-08-26 16:25:02 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "doesn't raise an error when single quotes are present" do
|
|
|
|
Search.new("'hello' world").execute.should be_blank # There are at least three levels of sanitation for Search.query!
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'works when given two terms with spaces' do
|
2013-05-22 14:36:14 -04:00
|
|
|
lambda { Search.new('evil trout').execute }.should_not raise_error
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'users' do
|
|
|
|
let!(:user) { Fabricate(:user) }
|
2013-05-22 14:36:14 -04:00
|
|
|
let(:result) { first_of_type( Search.new('bruce', type_filter: 'user').execute, 'user') }
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
it 'returns a result' do
|
|
|
|
result.should be_present
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'has the display name as the title' do
|
2013-05-23 11:13:23 -04:00
|
|
|
result[:title].should == user.username
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'has the avatar_template is there so it can hand it to the client' do
|
2013-05-23 11:13:23 -04:00
|
|
|
result[:avatar_template].should_not be_nil
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'has a url for the record' do
|
2013-05-23 11:13:23 -04:00
|
|
|
result[:url].should == "/users/#{user.username_lower}"
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'topics' do
|
2013-05-12 20:48:32 -04:00
|
|
|
let(:topic) { Fabricate(:topic) }
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2014-02-16 21:54:51 -05:00
|
|
|
|
|
|
|
context 'search within topic' do
|
|
|
|
|
|
|
|
def new_post(raw, topic)
|
|
|
|
Fabricate(:post, topic: topic, topic_id: topic.id, user: topic.user, raw: raw)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'displays multiple results within a topic' do
|
2014-02-18 16:59:18 -05:00
|
|
|
|
2014-02-16 21:54:51 -05:00
|
|
|
topic = Fabricate(:topic)
|
|
|
|
topic2 = Fabricate(:topic)
|
|
|
|
|
|
|
|
new_post('this is the other post I am posting', topic2)
|
2014-06-20 01:48:34 -04:00
|
|
|
new_post('this is my fifth post I am posting', topic2)
|
|
|
|
|
2014-02-16 21:54:51 -05:00
|
|
|
post1 = new_post('this is the other post I am posting', topic)
|
|
|
|
post2 = new_post('this is my first post I am posting', topic)
|
|
|
|
post3 = new_post('this is a real long and complicated bla this is my second post I am Posting birds
|
|
|
|
with more stuff bla bla', topic)
|
|
|
|
post4 = new_post('this is my fourth post I am posting', topic)
|
|
|
|
|
2014-02-18 16:59:18 -05:00
|
|
|
# update posts_count
|
|
|
|
topic.reload
|
|
|
|
|
2014-02-16 21:54:51 -05:00
|
|
|
results = Search.new('posting', search_context: post1.topic).execute.find do |r|
|
|
|
|
r[:type] == "topic"
|
|
|
|
end[:results]
|
|
|
|
|
|
|
|
results.find{|r| r[:title].include? 'birds'}.should_not be_nil
|
|
|
|
|
|
|
|
results.map{|r| r[:id]}.should == [
|
|
|
|
post1.topic_id,
|
|
|
|
"_#{post2.id}",
|
|
|
|
"_#{post3.id}",
|
2014-06-17 03:53:45 -04:00
|
|
|
"_#{post4.id}"]
|
2014-02-16 21:54:51 -05:00
|
|
|
|
2014-06-20 01:48:34 -04:00
|
|
|
# stop words should work
|
|
|
|
results = Search.new('this', search_context: post1.topic).execute.find do |r|
|
|
|
|
r[:type] == "topic"
|
|
|
|
end[:results]
|
|
|
|
|
|
|
|
results.length.should == 4
|
|
|
|
|
2014-02-16 21:54:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
context 'searching the OP' do
|
2014-04-11 16:07:39 -04:00
|
|
|
let!(:post) { Fabricate(:post_with_long_raw_content, topic: topic, user: topic.user) }
|
|
|
|
let(:result) { first_of_type(Search.new('hundred', type_filter: 'topic', include_blurbs: true).execute, 'topic') }
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2013-05-12 20:48:32 -04:00
|
|
|
it 'returns a result correctly' do
|
2013-02-05 14:16:51 -05:00
|
|
|
result.should be_present
|
2013-05-23 11:13:23 -04:00
|
|
|
result[:title].should == topic.title
|
|
|
|
result[:url].should == topic.relative_url
|
2014-04-11 16:07:39 -04:00
|
|
|
result[:blurb].should == TextHelper.excerpt(post.raw, 'hundred', radius: 100)
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
2013-05-13 17:04:41 -04:00
|
|
|
end
|
|
|
|
|
2013-06-28 21:22:17 -04:00
|
|
|
context 'searching for a post' do
|
|
|
|
let!(:post) { Fabricate(:post, topic: topic, user: topic.user) }
|
|
|
|
let!(:reply) { Fabricate(:basic_reply, topic: topic, user: topic.user) }
|
|
|
|
let(:result) { first_of_type(Search.new('quote', type_filter: 'topic').execute, 'topic') }
|
|
|
|
|
|
|
|
it 'returns the post' do
|
|
|
|
result.should be_present
|
|
|
|
result[:title].should == topic.title
|
|
|
|
result[:url].should == reply.url
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-05-13 17:04:41 -04:00
|
|
|
context "search for a topic by id" do
|
2013-08-14 23:19:37 -04:00
|
|
|
let(:result) { first_of_type(Search.new(topic.id, type_filter: 'topic', min_search_term_length: 1).execute, 'topic') }
|
2013-05-13 17:04:41 -04:00
|
|
|
|
|
|
|
it 'returns the topic' do
|
|
|
|
result.should be_present
|
2013-05-23 11:13:23 -04:00
|
|
|
result[:title].should == topic.title
|
|
|
|
result[:url].should == topic.relative_url
|
2013-05-13 17:04:41 -04:00
|
|
|
end
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2013-05-13 17:04:41 -04:00
|
|
|
context "search for a topic by url" do
|
2013-05-22 14:36:14 -04:00
|
|
|
let(:result) { first_of_type(Search.new(topic.relative_url, type_filter: 'topic').execute, 'topic') }
|
2013-05-13 17:04:41 -04:00
|
|
|
|
|
|
|
it 'returns the topic' do
|
|
|
|
result.should be_present
|
2013-05-23 11:13:23 -04:00
|
|
|
result[:title].should == topic.title
|
|
|
|
result[:url].should == topic.relative_url
|
2013-05-13 17:04:41 -04:00
|
|
|
end
|
2013-05-12 20:48:32 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'security' do
|
|
|
|
let!(:post) { Fabricate(:post, topic: topic, user: topic.user) }
|
2013-06-28 21:22:17 -04:00
|
|
|
|
2013-05-12 20:48:32 -04:00
|
|
|
def result(current_user)
|
2013-05-22 14:36:14 -04:00
|
|
|
first_of_type(Search.new('hello', guardian: current_user).execute, 'topic')
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2013-05-12 20:48:32 -04:00
|
|
|
it 'secures results correctly' do
|
|
|
|
category = Fabricate(:category)
|
|
|
|
|
|
|
|
topic.category_id = category.id
|
|
|
|
topic.save
|
|
|
|
|
2013-07-13 21:24:16 -04:00
|
|
|
category.set_permissions(:staff => :full)
|
2013-05-12 20:48:32 -04:00
|
|
|
category.save
|
|
|
|
|
|
|
|
result(nil).should_not be_present
|
|
|
|
result(Fabricate(:user)).should_not be_present
|
|
|
|
result(Fabricate(:admin)).should be_present
|
|
|
|
|
2013-02-25 11:42:20 -05:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2013-02-28 14:52:35 -05:00
|
|
|
context 'cyrillic topic' do
|
|
|
|
let!(:cyrillic_topic) { Fabricate(:topic) do
|
|
|
|
user
|
|
|
|
title { sequence(:title) { |i| "Тестовая запись #{i}" } }
|
|
|
|
end
|
|
|
|
}
|
|
|
|
let!(:post) {Fabricate(:post, topic: cyrillic_topic, user: cyrillic_topic.user)}
|
2013-05-22 14:36:14 -04:00
|
|
|
let(:result) { first_of_type(Search.new('запись').execute, 'topic') }
|
2013-02-28 14:52:35 -05:00
|
|
|
|
|
|
|
it 'finds something when given cyrillic query' do
|
|
|
|
result.should be_present
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
context 'categories' do
|
|
|
|
|
|
|
|
let!(:category) { Fabricate(:category) }
|
2013-05-13 04:04:03 -04:00
|
|
|
def result
|
2013-05-22 14:36:14 -04:00
|
|
|
first_of_type(Search.new('amazing').execute, 'category')
|
2013-05-13 04:04:03 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2013-05-12 20:48:32 -04:00
|
|
|
it 'returns the correct result' do
|
2013-05-13 04:04:03 -04:00
|
|
|
r = result
|
|
|
|
r.should be_present
|
2013-05-23 11:13:23 -04:00
|
|
|
r[:title].should == category.name
|
|
|
|
r[:url].should == "/category/#{category.slug}"
|
2013-05-13 04:04:03 -04:00
|
|
|
|
2013-07-13 21:24:16 -04:00
|
|
|
category.set_permissions({})
|
2013-05-13 04:04:03 -04:00
|
|
|
category.save
|
|
|
|
|
|
|
|
result.should_not be_present
|
2013-02-25 11:42:20 -05:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
context 'type_filter' do
|
|
|
|
|
|
|
|
let!(:user) { Fabricate(:user, username: 'amazing', email: 'amazing@amazing.com') }
|
|
|
|
let!(:category) { Fabricate(:category, name: 'amazing category', user: user) }
|
2013-02-25 11:42:20 -05:00
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
context 'user filter' do
|
2013-05-22 14:36:14 -04:00
|
|
|
let(:results) { Search.new('amazing', type_filter: 'user').execute }
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
it "returns a user result" do
|
|
|
|
results.detect {|r| r[:type] == 'user'}.should be_present
|
|
|
|
results.detect {|r| r[:type] == 'category'}.should be_blank
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'category filter' do
|
2013-05-22 14:36:14 -04:00
|
|
|
let(:results) { Search.new('amazing', type_filter: 'category').execute }
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2013-05-21 04:56:04 -04:00
|
|
|
it "returns a category result" do
|
2013-02-05 14:16:51 -05:00
|
|
|
results.detect {|r| r[:type] == 'user'}.should be_blank
|
|
|
|
results.detect {|r| r[:type] == 'category'}.should be_present
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2013-05-24 14:03:45 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'search_context' do
|
|
|
|
|
|
|
|
context 'user as a search context' do
|
2013-05-24 16:17:09 -04:00
|
|
|
let(:coding_horror) { Fabricate(:coding_horror) }
|
2013-05-24 14:03:45 -04:00
|
|
|
|
|
|
|
Given!(:post) { Fabricate(:post) }
|
2013-05-24 16:17:09 -04:00
|
|
|
Given!(:coding_horror_post) { Fabricate(:post, user: coding_horror )}
|
2013-08-04 09:15:05 -04:00
|
|
|
When(:search_user) { Search.new('hello', search_context: post.user).execute }
|
2013-05-24 16:17:09 -04:00
|
|
|
|
2013-08-04 09:15:05 -04:00
|
|
|
# should find topic created by searched user first
|
|
|
|
Then { first_of_type(search_user, 'topic')[:id].should == post.topic_id }
|
2013-05-24 14:03:45 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'category as a search context' do
|
|
|
|
let(:category) { Fabricate(:category) }
|
|
|
|
let(:topic) { Fabricate(:topic, category: category) }
|
2013-05-24 16:17:09 -04:00
|
|
|
let(:topic_no_cat) { Fabricate(:topic) }
|
2013-05-24 14:03:45 -04:00
|
|
|
|
|
|
|
Given!(:post) { Fabricate(:post, topic: topic, user: topic.user ) }
|
2013-08-04 09:15:05 -04:00
|
|
|
Given!(:another_post) { Fabricate(:post, topic: topic_no_cat, user: topic.user ) }
|
|
|
|
When(:search_cat) { Search.new('hello', search_context: category).execute }
|
|
|
|
# should find topic in searched category first
|
|
|
|
Then { first_of_type(search_cat, 'topic')[:id].should == topic.id }
|
2013-05-24 14:03:45 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
|
|
|
|
end
|
|
|
|
|
2014-06-25 19:58:49 -04:00
|
|
|
describe 'Chinese search' do
|
|
|
|
it 'splits English / Chinese' do
|
|
|
|
SiteSetting.default_locale = 'zh_CN'
|
|
|
|
data = Search.prepare_data('Discourse社区指南').split(' ')
|
|
|
|
data.should == ['Discourse', '社区','指南']
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'finds chinese topic based on title' do
|
|
|
|
SiteSetting.default_locale = 'zh_TW'
|
|
|
|
topic = Fabricate(:topic, title: 'My Title Discourse社区指南')
|
|
|
|
Fabricate(:post, topic: topic)
|
|
|
|
|
|
|
|
Search.new('社区指南').execute[0][:results][0][:id].should == topic.id
|
|
|
|
Search.new('指南').execute[0][:results][0][:id].should == topic.id
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|