I'm using factory_bot to create objects for organisation, but here organisations are created before create call, and because of validation create request is not processed.
it 'should increase organisation count by 1' do
expect do
post :create, params: { organisation: attributes_for(:organisation)}, xhr: true
end.to change(Organisation, :count).by(1)
end
What did I do wrong in this?
(0.6ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
(0.2ms) BEGIN
(0.2ms) SAVEPOINT active_record_1
Organisation Exists (0.7ms) SELECT 1 AS one FROM "organisations" WHERE "organisations"."name" = $1 LIMIT $2 [["name", "Organisation 2"], ["LIMIT", 1]]
Organisation Exists (0.2ms) SELECT 1 AS one FROM "organisations" WHERE "organisations"."email" = $1 LIMIT $2 [["email", "organisation#domain.com"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "organisations" ("name", "created_at", "updated_at", "email", "phone_number", "location") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["name", "Organisation 2"], ["created_at", "2018-01-27 05:36:13.649627"], ["updated_at", "2018-01-27 05:36:13.649627"], ["email", "organisation#domain.com"], ["phone_number", "+918292929292"], ["location", "Karur"]]
Processing by Admins::OrganisationsController#create as JS
Parameters: {"organisation"=>{"email"=>"organisation#domain.com", "location"=>"Karur", "name"=>"Organisation 2", "phone_number"=>"+918292929292"}}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 127], ["LIMIT", 1]]
(0.1ms) SAVEPOINT active_record_1
Organisation Exists (0.3ms) SELECT 1 AS one FROM "organisations" WHERE "organisations"."name" = $1 LIMIT $2 [["name", "Organisation 2"], ["LIMIT", 1]]
Organisation Exists (0.5ms) SELECT 1 AS one FROM "organisations" WHERE "organisations"."email" = $1 LIMIT $2 [["email", "organisation#domain.com"], ["LIMIT", 1]]
When I try to print the count,
it 'should increase organisation count by 1' do
p Organisation.count
login_admin
p Organisation.count
expect do
post :create, params: {organisation: attributes_for(:organisation)}, xhr: true
end.to change(Organisation, :count).by(1)
end
end
the log,
Run options: include {:full_description=>/Admins::OrganisationsController\ POST\ \#\ create\ with\ valid\ params\ should\ increase\ organisation\ count\ by\ 1/}
0
1
expected #count to have changed by 1, but was changed by 0
./spec/controllers/admins/organisations_controller_spec.rb:71:in `block (4 levels) in <top (required)>'
-e:1:in `load'
-e:1:in `<main>'
Organisation is created when admin object is created. There will an association between admin and organisation.
Show your login_admin method and factories of admin and organisation if further help needed.
Related
I added this lines of code to create action:
def create
super
#card = Card.find(params[:card_id])
#card.update(:user_id=>current_user)
end
And everything works fine, user gets created, card gets updated, but after redirect this happens:
Couldn't find Card with 'id'=
Extracted source (around line #14):
def create
super
#card = Card.find(params[:card_id])
#card.update(:user_id=>current_user)
end
I checked my terminal to find out the reason why this happens, and it seems that create action triggers twice for no reason:
Started POST "/users" for ::1 at 2020-08-12 11:04:34 +0300
Processing by Users::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"q1W0+ZhzK85uHTcp1x4jKHvCG0ukIgj2JxZuAy6vuLQl/vPqJVu6eXSEWviYTnWC4cXAJk2xCJhl8mgoWzXIAA==", "user"=>{"name"=>"Терл Кабот", "email"=>"tafff1#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "card_id"=>"2000012606"}, "commit"=>"Sign up"}
Card Load (1.0ms) SELECT "cards".* FROM "cards" WHERE "cards"."id" = $1 LIMIT $2 [["id", 2000012606], ["LIMIT", 1]]
(0.0ms)
BEGIN
User Exists (1.0ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "tafff1#gmail.com"], ["LIMIT", 1]]
SQL (1.0ms) INSERT INTO "users" ("email", "encrypted_password", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["email", "tafff1#gmail.com"], ["encrypted_password", "$2a$12$qTrv/zFUxULi9sqWgYlY/uPjQoJsZxB8PJK2ae/e6YfAFT40ci47e"], ["name", "Терл Кабот"], ["created_at", "2020-08-12 08:04:35.174621"], ["updated_at", "2020-08-12 08:04:35.174621"]]
SQL (1.0ms) UPDATE "cards" SET "user_id" = $1, "updated_at" = $2 WHERE "cards"."id" = $3 [["user_id", 17], ["updated_at", "2020-08-12 08:04:35.178626"], ["id", 2000012606]]
(1.0ms) COMMIT
Redirected to http://localhost:3000/
Card Load (0.0ms) SELECT "cards".* FROM "cards" WHERE "cards"."id" = $1 LIMIT $2 [["id", nil], ["LIMIT", 1]]
Completed 404 Not Found in 378ms (ActiveRecord: 6.0ms)
ActiveRecord::RecordNotFound (Couldn't find Card with 'id'=):
is there any solution for this?
EDIT: I gave up and just changed card and user logic, now user belongs to card, so I dont have to update cards user_id from devises create action.
The card_id is nested in the user key, so it will be: params[:user][:card_id]
I have
class CustomSessionsController < Devise::SessionsController
def create
#user = resource # needed for Merit
super
end
protected
def after_sign_in_path_for(resource)
#user = resource # needed for Merit
resource.update_streak
super
And
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true, model_name: 'User' do |user|
puts user.inspect
user.streak.count >= 3
end
But it gives the error
[merit] no target_obj found on Rule#applies?
And I can't access the model and it doesn't grant the badge or log the user. What is wrong? I followed the guide.
https://github.com/merit-gem/merit/wiki/How-to-grant-badges-on-user-using-Devise
It's doing something.
Processing by CustomSessionsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"gqUQjF9hfzJdQqxAAQJxv7bi+kZYwuv1NWtOP0YhkbjHKwnfa5WAb/CkRZ5c+Xi5yVlnJ2v774w3XLhTa1b1sQ==", "user"=>{"email"=>"student#gmail.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
User Load (6.0ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["email", "student#gmail.com"]]
(7.0ms) BEGIN
SQL (3.0ms) UPDATE "users" SET "last_sign_in_at" = $1, "current_sign_in_at" = $2, "sign_in_count" = $3, "updated_at" = $4 WHERE "users"."id" = $5 [["last_sign_in_at", "2018-08-09 05:38:58.345271"], ["current_sign_in_at", "2018-08-10 01:40:51.644592"], ["sign_in_count", 15], ["updated_at", "2018-08-10 01:40:51.668609"], ["id", 3]]
(25.0ms) COMMIT
Streak Load (21.0ms) SELECT "streaks".* FROM "streaks" WHERE "streaks"."user_id" = $1 LIMIT 1 [["user_id", 3]]
Redirected to http://localhost:3000/
(1.0ms) BEGIN
SQL (7.0ms) INSERT INTO "merit_actions" ("user_id", "action_method", "target_model", "target_data", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["user_id", 3], ["action_method", "create"], ["target_model", "custom_sessions"], ["target_data", "--- \n...\n"], ["created_at", "2018-08-10 01:40:53.539847"], ["updated_at", "2018-08-10 01:40:53.539847"]]
(8.0ms) COMMIT
Merit::Action Load (6.0ms) SELECT "merit_actions".* FROM "merit_actions" WHERE "merit_actions"."processed" = $1 [["processed", "f"]]
(3.0ms) BEGIN
SQL (2.0ms) UPDATE "merit_actions" SET "processed" = $1, "updated_at" = $2 WHERE "merit_actions"."id" = $3 [["processed", "t"], ["updated_at", "2018-08-10 01:40:53.581875"], ["id", 17]]
(20.0ms) COMMIT
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
[merit] no target_obj found on Rule#applies?
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
[merit] no target_obj found on Rule#applies?
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT 1
Completed 302 Found in 2567ms (ActiveRecord: 293.2ms)
Merit 2.4, Rails 4.2.
I tried
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true do
puts current_user.inspect
current_user.streak.count >= 3
end
But it gave
[merit] no target found: uninitialized constant CustomSession. base_target_finder.rb:13:in 'find'
error NameError (undefined local variable or method 'current_user'
I tried
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true, to: :itself do |user|
puts user.inspect
user.streak.count >= 3
end
def create
#custom_session = resource # needed for Merit
def after_sign_in_path_for(resource)
#custom_session = resource # needed for Merit
But it gave
[merit] no target found: uninitialized constant CustomSession. C:/ruby23/lib/ruby/gems/2.3.0/gems/merit-2.4.0/lib/merit/base_target_finder.rb:13:in `find'
true
Completed 500 Internal Server Error in 2181ms (ActiveRecord: 177.1ms)
NoMethodError (undefined method `streak' for true:TrueClass):
app/models/merit/badge_rules.rb:43:in `block in initialize'
I got it working with
grant_on 'custom_sessions#create', badge: :streak, level: 3, temporary: true, model_name: 'User', to: :itself do |user|
def create
super
#custom_session = resource # needed for Merit
But I don't know why because the /users/sign_in path does not have an :id parameter.
https://github.com/merit-gem/merit#how-merit-finds-the-target-object
Merit would fetch the Article object from the database, found by the :id param sent in that update action.
Does Rails actually cache the query result? The documentation says that same query will be never executed twice on the same request:
1.7 SQL Caching
The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory.
I did an experiment to proof that Rails actually cache the query:
def test
data = ""
User.find(1).update(first_name: 'Suwir Suwirr')
data << User.find(1).first_name
data << "\n"
User.find(1).update(first_name: 'Pengguna')
data << User.find(1).first_name
data << "\n"
render plain: data
end
If the result is cached, i would get the same result for each User.find(1). However, the result was Rails does not actually cache the query; i was expecting the update does not reflected on the result since it was "cached":
Suwir Suwirr
Pengguna
But the console says that it was cached: (Please highlight the CACHE word)
Started GET "/diag/test" for 10.0.2.2 at 2017-02-21 10:30:16 +0700
Processing by DiagController#test as HTML
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.1ms) BEGIN
SQL (0.4ms) UPDATE "users" SET "first_name" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["first_name", "Suwir Suwirr"], ["updated_at", 2017-02-21 03:30:16 UTC], ["id", 1]]
(16.5ms) COMMIT
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.1ms) BEGIN
SQL (0.3ms) UPDATE "users" SET "first_name" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["first_name", "Pengguna"], ["updated_at", 2017-02-21 03:30:16 UTC], ["id", 1]]
(0.9ms) COMMIT
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendering text template
Rendered text template (0.0ms)
Completed 200 OK in 380ms (Views: 3.5ms | ActiveRecord: 21.9ms)
So my question, does Rails actually cache the query result? Or, only several query result on some request?
Update: Using Batch #update_all
I made another experiment to "fool" the query logic. Now Rails does not "cache" the query. Why this behaviour can happen?
# Controller
def test
data = ""
User.where(id: 1).update_all(first_name: 'Suwir Suwirr')
data << User.find(1).first_name
data << "\n"
User.where(id: 1).update_all(first_name: 'Pengguna')
data << User.find(1).first_name
data << "\n"
logger.info 'hi'
render plain: data
end
# Console
Started GET "/diag/test" for 10.0.2.2 at 2017-02-21 10:45:43 +0700
Processing by DiagController#test as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
SQL (13.8ms) UPDATE "users" SET "first_name" = 'Suwir Suwirr' WHERE "users"."id" = $1 [["id", 1]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
SQL (2.9ms) UPDATE "users" SET "first_name" = 'Pengguna' WHERE "users"."id" = $1 [["id", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
hi
Rendering text template
Rendered text template (0.0ms)
Completed 200 OK in 28ms (Views: 0.8ms | ActiveRecord: 17.8ms)
# Browser result
Suwir Suwirr
Pengguna
I was stupid.
Yes, Rails does actually cache the query, but update and destroy will invalidate its query cache. update_all is basically iterating each record with update.
I tried the experiment by really "fooling" the ActiveRecord query mechanism. And yes, it works.
# Controller
def test
data = ""
ActiveRecord::Base.connection.execute('UPDATE "users" SET "first_name" = \'Suwir Suwirr\' WHERE "users"."id" = 1')
data << User.find(1).first_name
data << "\n"
ActiveRecord::Base.connection.execute('UPDATE "users" SET "first_name" = \'Pengguna\' WHERE "users"."id" = 1')
data << User.find(1).first_name
data << "\n"
render plain: data
end
# Browser
Suwir Suwirr
Suwir Suwirr
I have written an integration test for a signup form that writes to two tables/models. It fails with the message "Organization.count" didn't change by 1. Is there, however, a way to see with what kind of error message the test fails (the form works in development, although there is an issue with displaying error messages, so I don't understand why the test fails). So I mean the error message you would have seen if you had done the same exact thing on the server to help me find out why it didn't save the organization/user.
test "valid combined organization user signup" do
get new_path
assert_template 'organizations/new'
assert_difference ['Organization.count', 'User.count'], 1 do
post organizations_path, organization: { name: "Abc1",
bag: "aef1",
users_attributes: [email: "test#test.br",
username: "abc1",
password: "foobar",
password_confirmation: "foobar"] }
end
end
Perhaps the test log can help? I see here IS NULL LIMIT for user email and username. I don't understand why, given the test syntax...
Started POST "/organizations"
Processing by OrganizationsController#create as HTML
Parameters: {"organization"=>{"name"=>"Abc def 1", "bag"=>"adef1", "users_attributes"=>[{"email"=>"adef1#uniqu.br", "username"=>"adef1", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}]}}
[1m[36m (0.2ms)[0m [1mSAVEPOINT active_record_1[0m
[1m[35mOrganization Exists (0.6ms)[0m SELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."name") = LOWER('Abc def 1') LIMIT 1
[1m[36mOrganization Exists (0.3ms)[0m [1mSELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."bag") = LOWER('adef1') LIMIT 1[0m
[1m[35mOrganization Exists (0.3ms)[0m SELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."name") = LOWER('Abc def 1') LIMIT 1
[1m[36mOrganization Exists (0.3ms)[0m [1mSELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."bag") = LOWER('adef1') LIMIT 1[0m
[1m[35mSQL (0.4ms)[0m INSERT INTO "organizations" ("name", "bag", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "Abc def 1"], ["bag", "adef1"], ["created_at", "2015-06-27 16:28:56.179789"], ["updated_at", "2015-06-27 16:28:56.179789"]]
[1m[36mUser Exists (0.6ms)[0m [1mSELECT 1 AS one FROM "users" WHERE "users"."email" IS NULL LIMIT 1[0m
[1m[35m (0.6ms)[0m SELECT "users"."email" FROM "users" ORDER BY "users"."username" ASC
[1m[36mUser Exists (0.2ms)[0m [1mSELECT 1 AS one FROM "users" WHERE "users"."username" IS NULL LIMIT 1[0m
[1m[35m (0.2ms)[0m ROLLBACK TO SAVEPOINT active_record_1
The controller method is posted here: stackoverflow.com/q/31072646/4499505 (organizations_path refers to the create method)
If I compare the test log with the development log (since in development the form does work), for development it makes the post below, which seem to differ from the post in the test log. How should I adjust the test syntax to create the same post as in development?
Parameters: {"utf8"=>"✓", "authenticity_token"=>"***", "organization"=>{"name"=>"test60", "bag"=>"tes60", "users_attributes"=>{"0"=>{"email"=>"test60#example.com", "username"=>"test60", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}}, "commit"=>"Register"}
Update: To match the development log, I changed the post line within the test to:
post organizations_path, organization: { name: "Abc def 1",
bag: "adef1",
users_attributes: { "0" => {email: "adef1#uniqu.br",
username: "adef1",
password: "foobar",
password_confirmation: "foobar"}} }
end
Now in the test log it no longer has IS NULL LIMIT but it still rolls back and thus the test still fails with the same message:
[1m[35mSQL (0.5ms)[0m INSERT INTO "organizations" ("name", "bag", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "Abc def 1"], ["bag", "adef1"], ["created_at", "2015-06-27 19:49:45.430750"], ["updated_at", "2015-06-27 19:49:45.430750"]]
[1m[36mUser Exists (0.4ms)[0m [1mSELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('adef1#uniqu.br') LIMIT 1[0m
[1m[35m (0.5ms)[0m SELECT "users"."email" FROM "users" ORDER BY "users"."username" ASC
[1m[36mUser Exists (0.5ms)[0m [1mSELECT 1 AS one FROM "users" WHERE LOWER("users"."username") = LOWER('adef1') LIMIT 1[0m
[1m[35m (0.2ms)[0m ROLLBACK TO SAVEPOINT active_record_1
Your test code is not the same as what your development code is doing. You need to add brackets inside of the users_attributes array:
test "valid combined organization user signup" do
get new_path
assert_template 'organizations/new'
assert_difference ['Organization.count', 'User.count'], 1 do
post organizations_path,
organization: { name: "Abc1",
bag: "aef1",
users_attributes: { "0" => { email: "test#test.br",
username: "abc1",
password: "foobar",
password_confirmation: "foobar" } } }
end
end
This is why your log says it is trying to find a user whose email IS NULL, because it wasn't able to retrieve the email attribute properly, so it just defaulted to NULL instead of test#test.br.
Edit: updated to match working syntax. OP was also reporting that once the syntax was fixed, there was an error with the User model not passing a minimum length validation on username which he fixed by simply using a longer username in the test data.
I am using Devise in my app, and trying to write a spec for updating email ID. The UI works, but the spec is failing. I am reloading the user object as well, before testing against changed email ID.
One thing that I do observe is that there is UPDATE USERS statement being run on the database.
What is the correct way to write the spec?
RSpec spec:
it "should update email" do
#user = subject.current_user
patch :update, id: #user, user: {:email => "john.doe#example1.com"}
#user.reload
expect(#user.email).to eq("john.doe#example1.com")
end
RSpec Error Log:
1) Users::RegistrationsController logged in user should update email
Failure/Error: expect(#user.email).to eq("john.doe#example1.com")
expected: "john.doe#example1.com"
got: "john.doe#example.com"
(compared using ==)
test.log:
ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
(0.2ms) BEGIN
(0.4ms) SAVEPOINT active_record_1
User Exists (0.9ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = 'john.doe#example.com' LIMIT 1
SQL (4.1ms) INSERT INTO "users" ("created_at", "email", "encrypted_password", "first_name", "last_name", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Sat, 22 Jun 2013 13:12:28 UTC +00:00], ["email", "john.doe#example.com"], ["encrypted_password", "$2a$04$LIRWzphxstpCLTuZjicA..YMW.Ei2V/LlYWP32gfx39nBjhFg5tLe"], ["first_name", "John"], ["last_name", "Doe"], ["updated_at", Sat, 22 Jun 2013 13:12:28 UTC +00:00]]
(0.1ms) RELEASE SAVEPOINT active_record_1
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 457 ORDER BY "users"."id" ASC LIMIT 1
Processing by Users::RegistrationsController#update as HTML
Parameters: {"id"=>"457", "user"=>{"email"=>"john.doe#example1.com"}}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 457]]
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'john.doe#example1.com' AND "users"."id" != 457) LIMIT 1
Rendered devise/registrations/edit.html.slim within layouts/application (0.2ms)
Completed 200 OK in 73ms (Views: 4.0ms | ActiveRecord: 1.0ms)
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 457]]
(0.2ms) ROLLBACK
Try
it "should update email" do
#user = subject.current_user
patch :update, id: #user, user: {:email => "john.doe#example1.com"}
#user.reload.email.should == "john.doe#example1.com"
end
Or alternatively
it "should update email" do
#user = subject.current_user
expect {
patch :update, id: #user, user: {:email => "john.doe#example1.com"}
#user.reload
}.to change(#user, :email).to("john.doe#example1.com")
end