Rspec and Rails 4, update skip callback - ruby-on-rails

I try to run an update test with rspec for my rails application.
I'm using rspec 3.5 and rails 4.
The behaviour is supposed to be the following :
When i create a new service with a selling price, it's create a Price instance and set the relation with the service. Then, when i update my service, if there is no selling price, it's destroy the price record (requirement of the client to save space in database).
The process i implemented seems to be working fine, when i test with the UI and i check the count of Price record, it's decrease by one like it's suppose. However, the unit test if failing.
Here is the code :
Service Controller :
def update
#service.assign_attributes(service_params)
puts "update method"
respond_to do |format|
if #service.valid?
if params['preview']
#service.build_previews
format.js { render 'services/preview' }
else
#service.save!
format.html { redirect_to client_trip_days_path(#client, #trip), notice: t('flash.services.update.notice') }
end
else
format.html { render :edit }
format.js { render :new }
end
end
end
The callback in the Service model :
def create_or_update_price
puts "in create or update price"
if selling_price.present? && price.present?
self.price.update_columns(:trip_id => trip.id, :currency => trip.client.currency, :purchase_price => purchase_price, :selling_price => selling_price)
elsif selling_price.present? && !price.present?
self.price = RegularPrice.create(:trip => trip, :currency => trip.client.currency, :purchase_price => purchase_price, :selling_price => selling_price)
elsif !selling_price.present? && price.present?
self.price.destroy
end
end
The test :
it "updates the lodging and destroy the price" do
puts "nombre de service avant création : "
puts Service.count
puts "nombre de prix avant création : "
puts Price.count
lodging = FactoryGirl.create(:lodging_service, selling_price: 200)
puts "nombre de service après création : "
puts Service.count
puts "nombre de prix après création : "
puts Price.count
expect(lodging.reload.price).to be_present
puts "nombre de prix avant update : "
puts Price.count
puts "id"
puts lodging.id
patch :update, client_id: client.id, trip_id: trip.id, id: lodging.to_param, service: valid_attributes_no_more_price
# patch :update, client_id: client.id, trip_id: trip.id, id: lodging.id, service: valid_attributes_with_price
puts "nombre de prix après update : "
puts Price.count
# expect{
# patch :update, client_id: client.id, trip_id: trip.id, id: lodging.id, service: valid_attributes_no_more_price
# }.to change(RegularPrice, :count).by(0)
expect(lodging.reload.price).to be_nil
end
let(:valid_attributes_no_more_price) {
attributes_for(:lodging_service, trip: trip, selling_price: "")
}
As you can see, there is a lot of puts since i try to find what is wrong.
The output is :
nombre de service avant création :
0
nombre de prix avant création :
0
in create or update price
nombre de service après création :
1
nombre de prix après création :
1
nombre de prix avant update :
1
id
10
nombre de prix après update :
1
Failure/Error: expect(lodging.reload.price).to be_nil
expected: nil
got: #<RegularPrice id: 2, label: nil, currency: "CHF", selling_price: 200.0, priceable_type: "Service", p...ated_at: "2017-07-13 08:08:47", quantity: 1, type: "RegularPrice", position: 1, purchase_price: nil>
As we can see, it's look like the callback is not fired after the update, and the action in the controller is not reached.
Have you any idea what is going wrong?
Thanks :)
PS: I always have trouble to include code in my questions, is there a tutorial on how to make it?

Since you did not mentioned your ruby version I'll assume it's 2.
First of all you need to learn how to properly debug your code in order to fix the issues yourself.
Here is what you have to do:
1.Add pry gem to your app, pry-byebug there is also a version for ruby 1.9.3.
2.Add a break point in your code
it "updates the lodging and destroy the price" do
(...) # Code here.
binding.pry # The debugger will open a console at this point
(...) # Maybe more code here
end
3.Verify all the variable and their values and see where the problem lies
In case you could not find the issue with binding.pry in your rspec file before the expect add it to after the first line of the method, and you can step through the debugger by typing next in the console opened by pry (it will be where your rails server is runnig).
If that still does not help, try and add a binding.pry in your classes and see what is the state in there.
Spend a few minutes/hours now to learn debugging and it will save you days/weeks in long term. (while you are learning you don't really know how a program should behave and that extra knowledge is priceless, after a while you only need a debugger for extremely complicated issues).

Related

How to render images from Markdown files with Rails 5.2?

I based my applications inline help on Markdown files, implemented with Redcarpet gem. Contextual help is displayed as expected, but embedded images are not.
The help files structure in the project is:
public/help/administration/Connection
- Business_resources_hierarchy.png
- connections-fr.md
The connection-fr.md files contains:
# CONNECTIONS
Décrit la gestion des connexions aux ressources de l'infrastructure informatique.
## Principe de fonctionnement
La connaissance des ressources techniques comporte un certain niveau de complexité :
* adresses IP et protocoles des services
* login et mot de passe des utilisateurs techniques
* droits d'utilisation de la ressource
* multipliés par le nombre d'environnements déployés dans l'organisation (Dev, Test, Prod ...)
La gestion des connexions permet au métier de s'affranchir de cette complexité en offrant une vue métier des ressources nécessaires à la production statistique au travers d'une hiérarchie :
![Hiérarchie des resources métiers](Business_resources_hierarchy.png)
## Eléments d'infrastructure
1. Les ressources métiers - offrent une vue fonctionnelle des
Markdown is supported by the application_helper.rb:
module ApplicationHelper
### Implementing Help files management with Markdown
def markdown
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true, :space_after_headers => true, tables: true)
end
def displayHelp
puts "Help requested for: #{params[:page_name]}.#{params[:format]}"
# Parse the request from the page -> namespace/class/controller
if params[:page_name].index('/')
domain = params[:page_name].split('/')[0]
page = params[:page_name].split('/')[1]
else
domain = ''
page = params[:page_name]
end
method = params[:format] # The method from the controller is not used yet
# Build the help file path and name using the current locale
case page
when 'Change_Log' # Does it still exist?
filename = File.join(Rails.root, 'public', "CHANGELOG.md")
when 'Release_notes' # Does it still exist?
filename = File.join(Rails.root, 'public', "Release_notes.md")
else
filename = File.join(Rails.root,
'public',
'help',
domain,
page.classify,
"#{page}-#{I18n.locale.to_s[0,2]}.md"
)
end
puts "Requested help file: #{filename}"
if not File.file?(filename)
filename = File.join(Rails.root, 'public', 'help', "help-index-#{I18n.locale.to_s[0,2]}.md")
end
begin
file = File.open(filename, "rb")
markdown.render(file.read).html_safe.force_encoding('UTF-8')
rescue Errno::ENOENT
render :file => "public/404.html", :status => 404
end
end
end
I tried a few ways to define the path to the image file, but it does not show up. When submitting the file URL to Rails, it raises the following error:
No route matches [GET] "/Business_resources_hierarchy.PNG"
How to define the path to the file, or configure the helper so that the image is displayed?

ActiveRecord::QueryMethods not working within rspec test but in console it working fine

I have a test where I want to test that a book_group cannot delete when is associated with a book, this test failed but in the application, this feature works fine
describe 'callbacks' do
context 'before_validation' do
include_examples 'examples for strippable attributes', :book_group, :name, :description, :spawn_button_label
end
it 'is not destroyed and returns a base error if there is an book associated to the it' do
error = 'Cannot delete record because dependent books exist'
book_group.books << create(:book)
expect(book_group.destroy).to be false
end
end
I debugged into the test and found that the error is because this query not working as expected
First, I valid that these two models have an association
pry(#<RSpec::ExampleGroups::bookGroup::Callbacks>)> book_group.books
=> [#<book:0x0000563cb3f6eaf0
id: 1,
review_id: 1,
name: "Vernie Becker",
level: "site",
book_group_id: 831812,
author_book_id: nil]
I do the next query but its result is wrong
book_group.books.where(author_book_id: nil).order(id: :desc).first
=> nil
but this query within console working as expected
[4] pry(main)> #book_group.books.where(author_book_id: nil).order(id: :desc).first DEBUG
[2022-04-02T16:16:49.295Z] book Load (0.7ms) SELECT `books`.* FROM `books` WHERE `books`.`book_group_id` = 6 AND `books`.`author_book_id` IS NULL ORDER BY `books`.`id` DESC LIMIT 1
=> #<book:0x000055d2217339c8 id: 261, review_id: 1, name: "Base book site", level: "site", book_group_id: 6, book_template_id: 2, author_book_id: nil]
the book_group is created in this way
def create
ActiveRecord::Base.transaction do
#book_group = bookGroup.new(book_group_params)
#book_group.author_id = params[:author_id]
#book_group.save!
AllowedActorship.create_from_level_scoped_params(
book_group_params,
#book_group
)
render(
json: { message: #book_group },
status: :ok
)
end
end
I already have reset and prepared the bd, I'm not sure why it working so weird, I will say thank you for whatever helped with it.

Rails 5 - Sidekiq worker shows job done but nothing happens

I'm using Sidekiq for delayed jobs with sidekiq-status and sidekiq-ent gems. I've created a worker which is reponsible to update minor status to false when user is adult and has minor: true. This worker should be fired every day at midnight ET. Like below:
#initializers/sidekiq.rb
config.periodic do |mgr|
# every day between midnight 0 5 * * *
mgr.register("0 5 * * *", MinorWorker)
end
#app/workers/minor_worker.rb
class MinorWorker
include Sidekiq::Worker
def perform
User.adults.where(minor: true).remove_minor_status
rescue => e
Rails.logger.error("Unable to update minor field. Exception: #{e.message} : #{e.backtrace.join('\n')}")
end
end
#models/user.rb
class User < ApplicationRecord
scope :adults, -> { where('date_of_birth <= ?', 18.years.ago) }
def self.remove_minor_status
update(minor: false)
end
end
No I want to check this on my local machine - to do so I'm using gem 'timecop' to timetravel:
#application.rb
config.time_zone = 'Eastern Time (US & Canada)'
#config/environments/development.rb
config.after_initialize do
t = Time.local(2021, 12, 21, 23, 59, 0)
Timecop.travel(t)
end
After firing up sidekiq by bundle exec sidekiq and bundle exec rails s I'm waiting a minute and I see that worker shows up:
2021-12-21T22:59:00.130Z 25711 TID-ovvzr9828 INFO: Managing 3 periodic jobs
2021-12-21T23:00:00.009Z 25711 TID-ovw69k4ao INFO: Enqueued periodic job SettlementWorker with JID ddab15264f81e0b417e7dd83 for 2021-12-22 00:00:00 +0100
2021-12-21T23:00:00.011Z 25711 TID-ovw69k4ao INFO: Enqueued periodic job MinorWorker with JID 0bcd6b76d6ee4ff9e7850b35 for 2021-12-22 00:00:00 +0100
But it didn't do anything, the user's minor status is still set to minor: true:
2.4.5 :002 > User.last.date_of_birth
=> Mon, 22 Dec 2003
2.4.5 :001 > User.last.minor
=> true
Did I miss something?
EDIT
I have to add that when I'm trying to call this worker on rails c everything works well. I've got even a RSpec test which also passes:
RSpec.describe MinorWorker, type: :worker do
subject(:perform) { described_class.new.perform }
context 'when User has minor status' do
let(:user1) { create(:user, minor: true) }
it 'removes minor status' do
expect { perform }.to change { user1.reload.minor }.from(true).to(false)
end
context 'when user is adult' do
let(:registrant2) { create(:registrant) }
it 'not change minor status' do
expect(registrant2.reload.minor).to eq(false)
end
end
end
end
Since this is the class method update won't work
def self.remove_minor_status
update(minor: false)
end
Make use of #update_all
def self.remove_minor_status
update_all(minor: false)
end
Also, I think it's best practice to have some test cases to ensure the working of the methods.
As of now you can try this method from rails console and verify if they actually work
test "update minor status" do
user = User.create(date_of_birth: 19.years.ago, minor: true)
User.adults.where(minor: true).remove_minor_status
assert_equal user.reload.minor, false
end
I think you need to either do update_all or update each record by itself, like this:
User.adults.where(minor: true).update_all(minor: false)
or
class MinorWorker
include Sidekiq::Worker
def perform
users = User.adults.where(minor: true)
users.each { |user| user.remove_minor_status }
rescue => e
Rails.logger.error("Unable to update minor field. Exception: #{e.message} : #{e.backtrace.join('\n')}")
end
end
You may also want to consider changing update to update! so it throws an error if failing to be caught by your rescue in the job:
def self.remove_minor_status
update!(minor: false)
end

How to create plans without the "rails c" command ?

I took a shared server to deploy my app, to begin.
Everything works but I have some troubles to create my plans with stripe.
On localhost, I can use 'rails c' to create them but my server don't allow me to do it.
Here's the command to create plans from the console :
CreatePlan.call(stripe_id: 'test_plan', name: 'Test Plan', amount: 500, interval: 'month', description: 'Test Plan', published: false)
The create plan method is a service object : app/services/create_plan.rb
Here's my create_plan.rb :
class CreatePlan
def self.call(options={})
plan = Plan.new(options)
if !plan.valid?
return plan
end
begin
Stripe::Plan.create(
id: options[:stripe_id],
amount: options[:amount],
currency: 'usd',
interval: options[:interval],
name: options[:name],
)
rescue Stripe::StripeError => e
plan.errors[:base] << e.message
return plan
end
plan.save
return plan
end
end
How could I create my plans with no console ?
I tried with seeds.rb but it don't work.
To do stuff like this, make a rake task.
Like this:
namespace :stripe do
desc "Create stripe plans"
task :create_plans => :environment do
# Do the business
end
end
And then run rake stripe:create_plans on your server.

undefined method `include?' for nil:NilClass (NoMethodError)

I have the following code, and am using the ARGV. And I would make it so that when the user left the null ARGV, show some message.
=begin
TLDentifier developed by Arsh Leak. 2014.
Commands:
--display : Show all TLDs registered.
--help : Show more informations.
target.com
=end
class String
def green; "\033[32m#{self}\033[0m" end
def blue; "\033[34m#{self}\033[0m" end
def cyan; "\033[36m#{self}\033[0m" end
def bold; "\033[1m#{self}\033[22m" end
end
system("clear")
domains = {
".br" => "Brazilian",
".com" => "Comercial",
".aero" => "Aircraft",
".biz" => "Business",
".coop" => "Cooperative",
".edu" => "Educational",
".gov" => "Government",
".info" => "Information",
".int" => "International organization",
}
def head()
"TLDentifier".green.bold
end
puts head()
name = ARGV.first
puts "[#{name}]".blue.bold
domains.each do |domain, etn|
if name.include? (domain)
puts "["+domain.cyan+"] is a "+etn+" Domain."
elsif name == "--help"
system("clear")
puts head()
puts ""
puts "Development.".cyan
puts "Developed by "+"Arsh Leak. 2014."
puts ""
puts ""
puts "GitHub.".cyan
puts "github.com/4rsh"
puts ""
puts "Facebook.".cyan
puts "facebook.com/doxnetwork"
puts ""
puts "Blog.".cyan
puts "doxnetwork.com/"
puts ""
puts "Credits.".cyan
puts "Prof. Eric Weinstein."
puts ""
puts "How to.".cyan
puts "If you want to identify all TLDs, type:"
puts "$ ruby ltdentifier.rb --display".green
puts ""
puts "If you want to identify a specific TLD, type:"
puts "$ ruby ltdentifier.rb www.website.com/dir or www.website.com.".green
puts ""
elsif name == "--display"
head()
puts "Domain:".green+domain+" -"+" Entity: ".green+etn
end
end
And, the script return this error:
tld.rb:302:in block in <main>': undefined methodinclude?' for nil:NilClass (NoMethodError)
from tld.rb:301:in each'
from tld.rb:301:in'
When no arguments are passed, ARGV.first returns nil, which is not a String and thus doesn't respond to include?. nil acts like false, so you can test for this by putting code like
unless name
puts "Need at least one argument"
exit
end
before your first use of name.

Resources