Elasticsearch: Undefined method 'perform' for #<Indexer...> [test env] - ruby-on-rails

I'm writing tests for my app (in Rspec) and lost a whole day trying to figure out where did I make a mistake with Capybara. I noticed that when a action is called (change_status), the following errors occur in the test.log:
undefined method `perform' for #<Indexer:0x000000095d9680>
and the changes are not saving. I have no idea where to start looking. I'm guessing it's a problem with Elasticsearch. Here's the whole action from the controller:
def change_status
product = Product.find_by(id: params[:id])
product.update_attribute(:status, params[:product][:status])
if params[:product][:status] == "accepted"
kind = Kind.where(name: "title").first
product.author.change_points({points: 2, kind: kind.id})
elsif params[:product][:status] == "rejected"
kind = Kind.where(name: "title").first
product.author.change_points({points: 1, kind: kind.id})
product.author.change_points({points: -3, kind: kind.id})
end
user_id = product.author_id
products = Product.where(author_id: user_id)
if products.where(status: "accepted").count == 3
user = User.find_by(id: user_id)
user.update_attribute(:approved, true)
kind = Kind.where(name: "title").first
user.change_points({points: 1, kind: kind.id})
redirect_to products_path
else
redirect_to action: "pending_products", id: user_id
end
end
Any suggestion? Besides this, the log is useless...
EDIT:
class Indexer
include Sidekiq::Worker
sidekiq_options queue: 'elasticsearch', retry: false, backtrace: true
Logger = Sidekiq.logger.level == Logger::DEBUG ? Sidekiq.logger : nil
Client = Elasticsearch::Client.new host: (ENV['ELASTICSEARCH_URL'] || 'http://localhost:9200'), logger: Logger
def perform(operation, klass, record_id, options={})
logger.debug [operation, "#{klass}##{record_id} #{options.inspect}"]
case operation.to_s
when /index|update/
record = klass.constantize.find(record_id)
record.__elasticsearch__.client = Client
record.__elasticsearch__.__send__ "#{operation}_document"
when /delete/
Client.delete index: klass.constantize.index_name, type: klass.constantize.document_type, id: record_id
else raise ArgumentError, "Unknown operation '#{operation}'"
end
end
end

Related

Service throwing "undefined method" error

I may be missing something obvious, but I can't figure out where my problem is. I have a service called GoodMorning (app/services/good_morning.rb)
class GoodMorning
def self.dawn(user)
if user.goal_days.where("day = ?", Date.today).count == 0
user.goals.each do |goal|
if goal[time.strftime("%A")] == true
GoalDay.create(goal_id: goal.id, body: goal.body, target: goal.target, actual: 0, day: Date.today)
end
end
session[:sun] = true
end
end
end
And then in a controller called Clearing (app/controllers/clearing_controller.rb) I call it:
def index
#user = current_user
#goals_today = GoalDay.where("user_id = ? AND day = ?", #user.id, Date.today)
if session[:sun] == true
if #goals_today.count == 0
session[:sun] = false
end
else
GoodMorning(#user).dawn
end
end
I get the following error:
undefined method `GoodMorning' for #
Thank you for any help seeing what I'm missing! I restarted the server etc.
Try...
GoodMorning.dawn(#user)

RSpec - testing race conditions

I'm trying to write a RSpec controller test checking for race conditions based on this blog post, but created Threads don't "see" the Users created at the beginning of the test.
The blog post suggests to change config.use_transactional_fixtures to false, but even with this setting Users aren't visible.
Here's the test:
it "avoids race conditions" do
u = create(:user, :without_settings)
u_2 = create(:user, :without_settings)
wait_for_it = true
threads = Array.new(4).map {
Thread.new do
true while wait_for_it
# send the request
signed_post(
u,
api_v1_blockades_path,
params: {
blockade: {
blocked_user_id: u_2.id,
reason: 11
}
}
)
end
}
wait_for_it = false
threads.map(&:join)
expect(u.blockades.count).to eq(1)
end
For authentication i'm using the ApiAuth gem:
before_action :api_authenticate
private
def current_user
#current_user ||= User.find_by(id: ApiAuth.access_id(request))
end
def api_authenticate
head(:unauthorized) unless current_user && ApiAuth.authentic?(request, current_user.auth_token)
end

Query fetches wrong value and NilClass in rails controller

Hi in the following code although the where query inside create method i.e. variable #count_of_fav_texts_present fetches 1 record in the rails console, but in controller the value of #count_of_fav_texts_present is zero and going inside the first if clause.
Also in the internal if clause the query for find_by i.e variable var_fav_text is giving NilClass. Although when I check it in console the value is not Nil, and it has one record.
I am very new to Rails and I am not sure what mistake I am making. Please help.
class NewfavoriteTextsController < ApplicationController
before_action :set_text
before_action :set_favgroup
before_action :authenticate_user!
def create
#count_of_fav_texts_present = Favorite.where(favorited_id: #text_id, user_id: current_user.id).count
if #count_of_fav_texts_present == 0
if Favorite.create(favorited: #text, user: current_user)
if Newfavorite.create(favorite_group_id: #fav_group, newfavorited: #text)
var_fav_text = Favorite.find_by(favorited_id: #text_id, user_id: current_user.id)
cnt_of_var = var_fav_text.counter
var_fav_text.counter = cnt_of_var + 1
var_fav_text.save
else
# do something
end
else
# do something
end
else
# for condition when var is greater than 0
if Newfavorite.create(favorite_group_id: #fav_group, newfavorited: #text)
var_fav_text = Favorite.find_by(favorited_id: #text_id, user_id: current_user.id)
cnt_of_var = var_fav_text.counter
var_fav_text.counter = cnt_of_var + 1
var_fav_text.save
else
# do something
end
end
end
def destroy
# do something
end
private
def set_text
#text = Text.find(params[:text_id] || params[:id])
end
def set_favgroup
#fav_group = params[:fav_group_id]
end
end
Thanks in advance.

Understanding how to test a class using RSpec

The main thing I am looking to achieve from this question is understanding. With some assistance I have been looking at refactoring my controller code into more manageable modules/classes so that I can test them effectively. I have an example here that I would like to work on, my question is how would I test the class Sale:
class TransactionsController < ApplicationController
def create
payment = BraintreeTransaction::VerifyPayment.new(params, #user_id, #transaction_total)
payment.run(params)
if payment.success?
redirect_to thank_you_path
else
flash.now[:alert] = payment.error
flash.keep
redirect_to new_transaction_path
end
end
module BraintreeTransaction
class VerifyPayment
def initialize(params, user_id, total)
#transaction_total = total
#user_id = user_id
#params = params
#error_message = nil
end
def run(params)
#result = BraintreeTransaction::Sale.new.braintree_hash(params, #transaction_total)
if #result.success?
#cart_items = CartItem.where(user_id: #user_id).where.not(image_id: nil)
#cart_items.destroy_all
create_real_user
update_completed_transaction
guest_user.destroy
#success = true
else
update_transaction
#error_message = BraintreeErrors::Errors.new.error_message(#result)
end
end
def success?
#success
end
def error
#error_message
end
end
module BraintreeTransaction
class Sale
def braintree_hash(params, total)
Braintree::Transaction.sale(
amount: total,
payment_method_nonce: params[:payment_method_nonce],
device_data: params[:device_data],
customer: {
first_name: params[:first_name],
last_name: params[:last_name],
email: params[:email],
phone: params[:phone]
},
billing: {
first_name: params[:first_name],
last_name: params[:last_name],
company: params[:company],
street_address: params[:street_address],
locality: params[:locality],
region: params[:region],
postal_code: params[:postal_code]
},
shipping: {
first_name: params[:shipping_first_name].presence || params[:first_name].presence,
last_name: params[:shipping_last_name].presence || params[:last_name].presence,
company: params[:shipping_company].presence || params[:company].presence,
street_address: params[:shipping_street_address].presence || params[:street_address].presence,
locality: params[:shipping_locality].presence || params[:locality].presence,
region: params[:shipping_region].presence || params[:region].presence,
postal_code: params[:shipping_postal_code].presence || params[:postal_code].presence
},
options: {
submit_for_settlement: true,
store_in_vault_on_success: true
}
)
end
end
end
I don't know if I am looking at this wrong but this piece of code here BraintreeTransaction::Sale.new.braintree_hash is what I want to test and I want to ensure that when called the class receives a hash ?
Update
So far I have come up with this (though I am not 100% confident it is the correct approach ?)
require 'rails_helper'
RSpec.describe BraintreeTransaction::Sale do
#transaction_total = 100
let(:params) { FactoryGirl.attributes_for(:braintree_transaction, amount: #transaction_total) }
it 'recieves a hash when creating a payment' do
expect_any_instance_of(BraintreeTransaction::Sale).to receive(:braintree_hash).with(params, #transaction_total).and_return(true)
end
end
I get an error returned which I don't understand
Failure/Error: DEFAULT_FAILURE_NOTIFIER = lambda { |failure, _opts| raise failure }
Exactly one instance should have received the following message(s) but didn't: braintree_hash
I might not be spot on but I would answer the way I would have tackled the issue. There are three ways you can write a test that hits the code you want to test.
Write a unit test for braintree_hash for BraintreeTransaction::Sale object
Write a controller unit method for create method in TransactionsController controller
write an integration test for route for create method in TransactionsController.
These are the ways you can start exploring.
A couple of things here. All the suggestions for refactoring your code (from your other question Writing valuable controller tests - Rspec) apply here. I can make further suggestions on this code, if helpful.
In your test, I believe your problem is that you never actually call BraintreeTransaction.new.braintree_hash(params) (which should be called immediately following your expect_any_instance_of declaration). And so no instances ever receive the message(s).

My mail_type attribute is getting updated.. but i can't figure out why?

def update
debugger
#email_blast = EmailBlast.find(params[:id])
if #email_blast.update_attributes(params[:email_blast])
debugger
# changes mail_type here
flash[:notice] = 'Email Blast Saved.'
if params[:id] == "1"
Delayed::Job.enqueue MassEmail.new(params[:email_blast][:subject], params[:email_blast][:body])
elsif params[:id] == "2"
Delayed::Job.enqueue OrgBlast.new(params[:email_blast][:subject], params[:email_blast][:body])
elsif params[:id] == "3"
Delayed::Job.enqueue MagicEmail.new(params[:email_blast][:subject], params[:email_blast][:body])
elsif params[:id] == "4"
Delayed::Job.enqueue OrgMagicEmail.new(params[:email_blast][:subject], params[:email_blast][:body])
end
redirect_to edit_admin_email_blast_path(params[:id])
end
end
How strange is that? Once this controller code is passed, #email_blast.mail_type gets changed to "card_holder" . Why would that happen? the Params on the first debugger return :
(rdb:407) #email_blast = EmailBlast.find(params[:id])
#<EmailBlast id: 3, subject: "HQMagic Email", body: "asdfasdfasdfasdfas<br />\r\nd<br />\r\nfas<br />\r\ndf<br...", mail_type: "magic_email", created_at: "2010-10-28 14:57:48", updated_at: "2010-11-04 20:51:45">
And the second :
{"body"=>"asdfasdfasdfasdfas<br />\r\nd<br />\r\nfas<br />\r\ndf<br />\r\nasdf<br />\r\nas<br />\r\ndf<br />\r\nasd<br />\r\nfasd<br />\r\nfa<br />\r\nsd<br />\r\nfasd", "mail_type"=>"card_holders", "id"=>"3", "subject"=>"HQMagic Email"}
If you're calling update_attributes and params[:email_blast][:mail_type] is defined, then it will be reassigned. You can always set this parameter as protected to avoid this.

Resources