discourse/spec/requests/search_controller_spec.rb
Sam Saffron 4dcc5f16f1 FEATURE: when under extreme load disable search
The global setting disable_search_queue_threshold
(DISCOURSE_DISABLE_SEARCH_QUEUE_THRESHOLD) which default to 1 second was
added.

This protection ensures that when the application is unable to keep up with
requests it will simply turn off search till it is not backed up.

To disable this protection set this to 0.
2019-07-02 11:22:01 +10:00

382 lines
10 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
describe SearchController do
fab!(:awesome_post) do
SearchIndexer.enable
Fabricate(:post, raw: 'this is my really awesome post')
end
fab!(:user) do
Fabricate(:user)
end
fab!(:user_post) do
SearchIndexer.enable
Fabricate(:post, raw: "#{user.username} is a cool person")
end
context "integration" do
before do
SearchIndexer.enable
end
before do
# TODO be a bit more strategic here instead of junking
# all of redis
$redis.flushall
end
after do
$redis.flushall
end
context "when overloaded" do
before do
global_setting :disable_search_queue_threshold, 0.2
end
let! :start_time do
freeze_time
Time.now
end
let! :current_time do
freeze_time 0.3.seconds.from_now
end
it "errors on #query" do
get "/search/query.json", headers: {
"HTTP_X_REQUEST_START" => "t=#{start_time.to_f}"
}, params: {
term: "hi there", include_blurb: true
}
expect(response.status).to eq(409)
end
it "no results and error on #index" do
get "/search.json", headers: {
"HTTP_X_REQUEST_START" => "t=#{start_time.to_f}"
}, params: {
q: "awesome"
}
expect(response.status).to eq(200)
data = JSON.parse(response.body)
expect(data["posts"]).to be_empty
expect(data["grouped_search_result"]["error"]).not_to be_empty
end
end
it "returns a 400 error if you search for null bytes" do
term = "hello\0hello"
get "/search/query.json", params: {
term: term, include_blurb: true
}
expect(response.status).to eq(400)
end
it "can search correctly" do
get "/search/query.json", params: {
term: 'awesome', include_blurb: true
}
expect(response.status).to eq(200)
data = JSON.parse(response.body)
expect(data['posts'].length).to eq(1)
expect(data['posts'][0]['id']).to eq(awesome_post.id)
expect(data['posts'][0]['blurb']).to eq(awesome_post.raw)
expect(data['topics'][0]['id']).to eq(awesome_post.topic_id)
end
it 'performs the query with a type filter' do
get "/search/query.json", params: {
term: user.username, type_filter: 'topic'
}
expect(response.status).to eq(200)
data = JSON.parse(response.body)
expect(data['posts'][0]['id']).to eq(user_post.id)
expect(data['users']).to be_blank
get "/search/query.json", params: {
term: user.username, type_filter: 'user'
}
expect(response.status).to eq(200)
data = JSON.parse(response.body)
expect(data['posts']).to be_blank
expect(data['users'][0]['id']).to eq(user.id)
end
context 'searching by topic id' do
it 'should not be restricted by minimum search term length' do
SiteSetting.min_search_term_length = 20000
get "/search/query.json", params: {
term: awesome_post.topic_id,
type_filter: 'topic',
search_for_id: true
}
expect(response.status).to eq(200)
data = JSON.parse(response.body)
expect(data['topics'][0]['id']).to eq(awesome_post.topic_id)
end
it "should return the right result" do
get "/search/query.json", params: {
term: user_post.topic_id,
type_filter: 'topic',
search_for_id: true
}
expect(response.status).to eq(200)
data = JSON.parse(response.body)
expect(data['topics'][0]['id']).to eq(user_post.topic_id)
end
end
end
context "#query" do
it "logs the search term" do
SiteSetting.log_search_queries = true
get "/search/query.json", params: { term: 'wookie' }
expect(response.status).to eq(200)
expect(SearchLog.where(term: 'wookie')).to be_present
json = JSON.parse(response.body)
search_log_id = json['grouped_search_result']['search_log_id']
expect(search_log_id).to be_present
log = SearchLog.where(id: search_log_id).first
expect(log).to be_present
expect(log.term).to eq('wookie')
end
it "doesn't log when disabled" do
SiteSetting.log_search_queries = false
get "/search/query.json", params: { term: 'wookie' }
expect(response.status).to eq(200)
expect(SearchLog.where(term: 'wookie')).to be_blank
end
end
context "#show" do
it "doesn't raise an error when search term not specified" do
get "/search"
expect(response.status).to eq(200)
end
it "raises an error when the search term length is less than required" do
get "/search.json", params: { q: 'ba' }
expect(response.status).to eq(400)
end
it "raises an error when search term is a hash" do
get "/search.json?q[foo]"
expect(response.status).to eq(400)
end
it "logs the search term" do
SiteSetting.log_search_queries = true
get "/search.json", params: { q: 'bantha' }
expect(response.status).to eq(200)
expect(SearchLog.where(term: 'bantha')).to be_present
end
it "doesn't log when disabled" do
SiteSetting.log_search_queries = false
get "/search.json", params: { q: 'bantha' }
expect(response.status).to eq(200)
expect(SearchLog.where(term: 'bantha')).to be_blank
end
end
context "search context" do
it "raises an error with an invalid context type" do
get "/search/query.json", params: {
term: 'test', search_context: { type: 'security', id: 'hole' }
}
expect(response.status).to eq(400)
end
it "raises an error with a missing id" do
get "/search/query.json",
params: { term: 'test', search_context: { type: 'user' } }
expect(response.status).to eq(400)
end
context "with a user" do
it "raises an error if the user can't see the context" do
get "/search/query.json", params: {
term: 'test', search_context: { type: 'private_messages', id: user.username }
}
expect(response).to be_forbidden
end
it 'performs the query with a search context' do
get "/search/query.json", params: {
term: 'test', search_context: { type: 'user', id: user.username }
}
expect(response.status).to eq(200)
end
end
end
context "#click" do
after do
SearchLog.clear_debounce_cache!
end
it "doesn't work wthout the necessary parameters" do
post "/search/click.json"
expect(response.status).to eq(400)
end
it "doesn't record the click for a different user" do
sign_in(user)
_, search_log_id = SearchLog.log(
term: SecureRandom.hex,
search_type: :header,
user_id: -10,
ip_address: '127.0.0.1'
)
post "/search/click", params: {
search_log_id: search_log_id,
search_result_id: 12345,
search_result_type: 'topic'
}
expect(response.status).to eq(200)
expect(SearchLog.find(search_log_id).search_result_id).to be_blank
end
it "records the click for a logged in user" do
sign_in(user)
_, search_log_id = SearchLog.log(
term: SecureRandom.hex,
search_type: :header,
user_id: user.id,
ip_address: '127.0.0.1'
)
post "/search/click.json", params: {
search_log_id: search_log_id,
search_result_id: 12345,
search_result_type: 'user'
}
expect(response.status).to eq(200)
expect(SearchLog.find(search_log_id).search_result_id).to eq(12345)
expect(SearchLog.find(search_log_id).search_result_type).to eq(SearchLog.search_result_types[:user])
end
it "records the click for an anonymous user" do
get "/"
ip_address = request.remote_ip
_, search_log_id = SearchLog.log(
term: SecureRandom.hex,
search_type: :header,
ip_address: ip_address
)
post "/search/click.json", params: {
search_log_id: search_log_id,
search_result_id: 22222,
search_result_type: 'topic'
}
expect(response.status).to eq(200)
expect(SearchLog.find(search_log_id).search_result_id).to eq(22222)
expect(SearchLog.find(search_log_id).search_result_type).to eq(SearchLog.search_result_types[:topic])
end
it "doesn't record the click for a different IP" do
_, search_log_id = SearchLog.log(
term: SecureRandom.hex,
search_type: :header,
ip_address: '192.168.0.19'
)
post "/search/click", params: {
search_log_id: search_log_id,
search_result_id: 22222,
search_result_type: 'topic'
}
expect(response.status).to eq(200)
expect(SearchLog.find(search_log_id).search_result_id).to be_blank
end
it "records the click for search result type category" do
get "/"
ip_address = request.remote_ip
_, search_log_id = SearchLog.log(
term: SecureRandom.hex,
search_type: :header,
ip_address: ip_address
)
post "/search/click.json", params: {
search_log_id: search_log_id,
search_result_id: 23456,
search_result_type: 'category'
}
expect(response.status).to eq(200)
expect(SearchLog.find(search_log_id).search_result_id).to eq(23456)
expect(SearchLog.find(search_log_id).search_result_type).to eq(SearchLog.search_result_types[:category])
end
it "records the click for search result type tag" do
get "/"
ip_address = request.remote_ip
tag = Fabricate(:tag, name: 'test')
_, search_log_id = SearchLog.log(
term: SecureRandom.hex,
search_type: :header,
ip_address: ip_address
)
post "/search/click.json", params: {
search_log_id: search_log_id,
search_result_id: tag.name,
search_result_type: 'tag'
}
expect(response.status).to eq(200)
expect(SearchLog.find(search_log_id).search_result_id).to eq(tag.id)
expect(SearchLog.find(search_log_id).search_result_type).to eq(SearchLog.search_result_types[:tag])
end
end
end