I have three models User, Post, Vote
I need to doesn't allow user to vote for his own post.
How I can make this in my models and test this in Rspec?
Post Model:
class Post < ActiveRecord::Base
attr_accessible :title, :main_text, :video, :photo, :tag
validates :title, presence: true, length: {minimum: 1, maximum: 200}
validates :main_text, presence: true, length: {minimum: 1}
belongs_to :user
has_many :votes
end
User Model:
class User < ActiveRecord::Base
attr_accessible :name, :email, :bio
has_many :posts
has_many :votes
validates :name, presence: true, length: {minimum: 1, maximum: 120}
validates :email, presence: true, length: {minimum: 5, maximum: 250}, uniqueness: true,
format: {:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i}
end
Vote model:
class Vote < ActiveRecord::Base
attr_accessible :user_id, :post_id, :units
belongs_to :user
belongs_to :post
validates :post_id, uniqueness: {scope: :user_id} #does not allow user to vote for the same post twice
end
My spec test for Vote:
require 'spec_helper'
describe Vote do
it "does not allow user to vote for the same post twice" do
user = User.create(name: "Nik", email: "nik#google.com" )
post = Post.create(title: "New Post", main_text: "Many, many, many...")
vote1 = Vote.create(user_id: user.id, post_id: post.id)
vote1.errors.should be_empty
vote2 = Vote.create(user_id: user.id, post_id: post.id)
vote2.errors.should_not be_empty
end
it "does not allow user to vote for his own post" do
user = User.create(name:"Nik", email:"a#a.ru")
post = Post.create(user_id: user.id, title: "New Post", main_text: "Many, many, many...")
vote1 = Vote.create(user_id: user.id, post_id: post.id)
vote1.errors.should_not be_empty
end
end
I've not tested the following code so it could not work or even kill your cats but try with a custom validation with add an error if the vote's user is the same of the post's user.
Note I return if the user or the post are nil for obvius reasons.
# In your vote model
validate :users_cant_vote_their_posts
def users_cant_vote_their_posts
return if user.nil? or post.nil?
if user_id == post.user_id
errors[:base] = "A user can't vote their posts"
end
end
EDIT: Here a possible test, here I'm using FactoryGirl to generate votes. Again this code is not tested (sorry for the pun)
describe Vote do
subject { vote }
let!(:vote) { FactoryGirl.build(:vote) }
it 'from factory should be valid' do
should be_valid
end
context 'when user try to double vote' do
before do
# Create another vote from user to post
FactoryGirl.create(:vote, :user => vote.user, :post => vote.post)
end
it { should_not be_valid }
end
context 'when user try to vote his posts' do
before do
# Set the user whom voted to the post's owner
vote.user = vote.post.user
end
it { should_not be_valid }
end
end
I don't know ruby, but you should check whether the user signed in matches the user that made the post. And if it does, deny the request to vote.
Sorry if this is not helpful :)
Related
I'm testing a model and I'm wondering when creating an object that requires a file how do I do that? For the group object below, the name attribute is required so I passed 'Sports', for the icon how should I pass a file, or what's the correct way to test such?
group_spec.rb
require 'rails_helper'
RSpec.describe Group, type: :model do
before :each do
user = User.create(name: 'Test', email: 'test#gmail.com', password: '123456',
password_confirmation: '123456')
#group = Group.create(name: 'Sports', icon: )
#group.user = user
end
it 'is not valid without a name' do
#group.name = nil
expect(#group).to_not be_valid
end
it 'is not valid without an icon' do
#group.icon = nil
expect(#group).to_not be_valid
end
end
my model
class Group < ApplicationRecord
belongs_to :user
has_many :categorizations
has_many :entities, through: :categorizations
has_one_attached :icon do |attachable|
attachable.variant :thumb, resize_to_limit: [100, 100]
end
# validations
validates :name, presence: true, length: { maximum: 40 }
validates :icon, presence: true
end
I am making a fitness web application as part of a project. This project has 5 models. User, Muscle_Groups, Diet, Workout and Meal. The associations are on the code below. As of now, I have a new page and a show page for the User. I want to redirect the user from the show page to the muscle_group index page where it will list all the muscles in a persons body. The User obviously has many Muscle_Groups, I want to seed the muscle_group index page with all muscle groups (biceps, back, legs, chest). The issue is, I need to create these instances with the user_id of the current user using the app and I have no idea how to do it at this point. I hope my brief explanation helps, my code is below.
class User < ApplicationRecord
has_many :diets
has_many :muscle_groups
before_save { self.email = email.downcase }
#before saving, the email is lowercased
validates :name, presence: true, length: { maximum: 50 }
#validates the names presence, max char length is 50
validates :weight, presence: true, numericality: true
validates :height, presence: true, numericality: true
validates_inclusion_of :gender, :in => %w( m f male Male female Female)
validates :age, presence: true, numericality: {only_integer: true }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
end
class Muscle_Group < ApplicationRecord
has_many :workouts
belongs_to :user
end
class Diet < ApplicationRecord
belongs_to :user
has_many :meals
end
class Meal < ApplicationRecord
belongs_to :diet
end
class Workout < ApplicationRecord
belongs_to :muscle_group
end
class UsersController < ApplicationController
def new #render's the sign up page for a new user
#user = User.new
end
def create #action to create the user
#user = User.create(user_params)
if #user.save
log_in #user
flash[:success] = "Are you ready to GitFit?!"
redirect_to #user #redirects to the user's show page, which is the main menu
else
render 'new'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name,
:email,
:weight,
:height,
:gender,
:age,
:password,
:password_confirmation)
end
end
class Muscle_GroupsController < ApplicationController
def index
#muscle_groups = Muscle_Group.all
end
end
Just create one sample user in the seed file and associate the muscle groups to him. Then login with this account and you will have the results.
For instance like this
# seed.rb
sample_user = User.create(name: "Joe", weight: 100, height: 180,
age: 23, email: "test#mail.com",
password: "123456")
MuscleGroup.create(user: sample_user, ...)
Diet.create(user: sample_user)
...
I don't know the exact fields and the measuring system you use, but it could look like that.
Another way in production would be to sign up as a user of the website. And then find yourself in the console (rails c) and add the muscle_group and diet and so on and connect it manually to your user.
I'm writing a cost for a create method that will add a comment to a post.
The comment belongs to a user and a post. And a post belongs to a user.
When I run my test I get a validation error saying that the username and email have already been taken. I've tried using build as well as build_stubbed in both my factories and in the test, but neither of them worked. I think it has to do with the fact that I'm using create, but I'm not entirely sure.
Any advice would be much appreciated
Here are my factories:
users.rb
FactoryGirl.define do
factory :user do
username "test_user"
email "test_user#email.com"
password "password"
end
factory :user_2, class: User do
username "test_user_2"
email "test_user_2#email.com"
password "password"
end
factory :invalid_user, class: User do
username ""
email ""
password ""
end
end
outlets.rb
FactoryGirl.define do
factory :outlet do
category "vent"
title "MyString"
body "MyText"
urgency 1
user factory: :user
end
factory :outlet_2, class: Outlet do
category "rant"
title "MyString_2"
body "MyText_2"
urgency 2
user factory: :user_2
end
factory :invalid_outlet, class: Outlet do
category "qualm"
title ""
body ""
urgency 3
user factory: :user
end
end
comments.rb
FactoryGirl.define do
factory :comment do
body "This is a comment"
user factory: :user
outlet factory: :outlet_2
end
factory :invalid_comment, class: Comment do
body "This is a comment"
user nil
outlet nil
end
end
Here is my test:
describe 'create' do
context 'with valid attributes' do
let(:outlet) { FactoryGirl.create(:outlet) }
let(:valid_comment_params) { FactoryGirl.attributes_for(:comment) }
it "creates a new comment" do
expect { post :create, params: { id: outlet, :comment => valid_comment_params } }.to change(Comment, :count).by(1)
end
end
end
Here are my models:
class Comment < ApplicationRecord
belongs_to :user
belongs_to :outlet
validates :body, :user, :outlet, presence: true
validates :body, length: { in: 1..1000 }
end
class Outlet < ApplicationRecord
belongs_to :user
has_many :comments
validates :category, :title, :body, :urgency, :user, presence: true
validates :title, length: { in: 1..60 }
validates :body, length: { in: 1..1000 }
validates :urgency, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 10 }
validates :category, inclusion: { in: ['vent', 'rant', 'qualm'] }
end
class User < ApplicationRecord
has_many :outlets
has_many :comments
validates :username, :email, :encrypted_password, presence: true
validates :username, :email, uniqueness: true
validates :password, length: { in: 5..30 }
# Include default devise modules. Others available are:
# :lockable, :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
end
So the problem here is you keep trying to create a user with the same email and username that you just created another user with. In order to avoid this in your factories, you should strive to make the values dynamic. Since the main issues currently are the uniqueness validations, lets start with those.
factory :user do
sequence(:username) { |n| "test_user#{n}" }
sequence(:email) { |n| "test_user#{n}#email.com" }
password "password"
end
that way, you can use the same factory to create 2 distinct users
user = FactoryGirl.create :user
user_2 = FactoryGirl.create :user
I need to unit test a promotions model where every campaign has a URL. There is a polymorphic association between promotions and reference_link. the reference link fixture:
reference_link:
linkable:
fix_1 (PromoCode)
How do I convince rails that the promotions fixture does indeed have a URL belonging to it?
In promotions test helper:
test "should have a URL associated with the promo code" do
promo_code = promo_codes(:fix_1)
promo_code.reference_link.url = nil
assert_not promo_code.valid?
promo_code2 = promo_codes(:fix_2)
assert promo_code2.valid?
end
promocode.rb
class PromoCode < ActiveRecord::Base
belongs_to :reward
has_one :reference_link, as: :linkable, dependent: :destroy
validates :email, presence: true
validates :code, presence: true
end
reference_link.rb
class ReferenceLink < ActiveRecord::Base
belongs_to :linkable, polymorphic: true,touch: true
validates :link_name, presence: true
validates :link_url, presence: true
validates_format_of :link_url, :with => /\A#{URI::regexp(['http', 'https'])}\z/
validates_length_of :link_url,:link_name, :maximum => 255
end
It's simple, you almost got it right. First, ensure the fixtures are correctly set:
# promo_codes.yml
promo_1:
email: 'foobar#gmail.com'
name: 'Foobar'
# reference_links.yml
reference_1:
linkable: promo_1
link_name: 'lorem'
link_url: 'http://loremipsum.com'
The tests:
# promotion_test.rb
test "should have a URL associated with the promo code" do
promo_code = promo_codes(:promo_1)
assert_eq true, promo_code.reference_link.present?
end
Do not forget that file naming is an important part of convention that Rails use.
My askings_controller.rb is below.
class AskingsController < ApplicationController
before_action :authenticate_user! , only: [:new , :create , :destroy]
def create
#asking=Asking.create(asking_params) do |c|
c.user=current_user
end
if #asking.save
flash[:success] = "依頼に成功しました。"
redirect_to #asking
else
render 'askings/new'
end
end
end
My factories/askings.rb is below.
FactoryGirl.define do
factory :asking do
association :user
sequence(:content){|i| "お願いします#{i}"}
lang "english"
person 'ネイティブ限定'
sex '男性限定'
usepoint 1
finished false
title "MyString"
deadline "2017-1-12"
deadline_time 19
end
end
My askings_contoller_spec.rb is below.
require 'rails_helper'
RSpec.describe AskingsController, type: :controller do
describe 'when login user' do
context 'Post #create' do
before do
#user=create(:user)
login_user(#user)
end
let(:asking_params) {attributes_for(:asking)}
it 'should make askings +1' do
expect{post :create, asking: asking_params}.to change(Asking, :count).by(1)
end
end
end
My model/asking.rb is below.
class Asking < ActiveRecord::Base
belongs_to :user
validates :title , presence: true , length: {maximum: 80}
validates :content , presence: true , length: {maximum: 800}
validates :lang , presence: true
validates :usepoint , presence: true
validates :person , presence: true
validates :sex , presence: true
validates :deadline , presence: true
validates :deadline_time , presence: true
end
Why do I have the error of 'expected #count to have changed by 1, but was changed by 0'?
When I remove 'validates :deadline_time , presence: true' from asking.rb , it works. But I think it isn't wrong.
Please help me.
In factories/askings.rb try to match the format of the deadline_time column. Right now you have the INT 19, perhaps try a string formatted for time (it would be nice to see the schema for Asking) .
Quick edit- My thought is that your factory is not making a valid asking, therefor the count is not increasing.