It also includes a new controller which handles verifying the user
after they follow the link in the email.
You will need to run a migration with this patch.
To set the hostname for where the user goes to verify themselves, update
the config/environments/{development, test, production}.rb file and enter
the correct public hostname.
It introduces a new model object, UserVerification. When a user
registers an account, an instance is created. As long as that object
exists, the user cannot do anything with the system.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/home_controller.rb | 25 ----
app/controllers/user_controller.rb | 34 +++++
app/models/user.rb | 16 +++
app/models/user_mailer.rb | 17 ++-
app/models/user_verification.rb | 45 +++++++
app/views/user_mailer/email_verification.html.erb | 14 ++
config/environments/development.rb | 27 ++--
config/environments/production.rb | 25 ++--
config/environments/test.rb | 25 ++--
config/routes.rb | 4 +
db/migrate/016_create_user_verifications.rb | 20 +++
db/schema.rb | 20 +++-
test/fixtures/user_verifications.yml | 6 +
test/fixtures/users.yml | 8 +-
test/functional/home_controller_test.rb | 33 +----
test/functional/user_controller_test.rb | 147 +++++++++++++++------
test/unit/user_test.rb | 26 ++++-
test/unit/user_verification_test.rb | 77 +++++++++++
18 files changed, 431 insertions(+), 138 deletions(-)
create mode 100644 app/models/user_verification.rb
create mode 100644 app/views/user/verify.html.erb
create mode 100644 app/views/user_mailer/email_verification.html.erb
create mode 100644 db/migrate/016_create_user_verifications.rb
create mode 100644 test/fixtures/user_verifications.yml
create mode 100644 test/unit/user_verification_test.rb
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index 35dd883..6b8030c 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -72,31 +72,6 @@ class HomeController < ApplicationController
@backlog = BacklogItem.find_all_by_state_and_owner_id(BacklogItem::STATE_ASSIGNED,
session[:user_id])
end
- # Allows the user to register a new account. Users may only register if they
- # are not currently logged into an account. TODO We need to authenticate the
- # email address entered by a user. TODO Accounts that aren't verified afew a
- # set time should be deleted.
- #
- def register
- if @user
- flash[:message] = 'You are already registered.'
- redirect_to :action => :dashboard
- else if request.post?
- user = User.new(:email => params[:email],
- :display_name => params[:display_name],
- :introduction => params[:introduction])
-
- user.privileges = UserPrivilege.new
-
- user.password = params[:password]
- user.save!
-
- flash[:message] = 'Thank you. Please log in with your new account.'
- redirect_to :action => :login
- end
- end
- end
-
# Allows the user to email him a new password.
#
def generate_new_password
diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb
index 6d13641..ebd6de6 100644
--- a/app/controllers/user_controller.rb
+++ b/app/controllers/user_controller.rb
@@ -26,6 +26,35 @@ class UserController < ApplicationController
end
+ def verify
+ if !(a)this_user.verified?
+ if params[:token]
+ if @this_user.verify(params[:token])
+ begin
+ User.transaction do
+ @this_user.verification.destroy
+ @this_user.verification = nil
+ @this_user.save!
+ end
+
+ flash[:message] = "#{(a)this_user.email} is now verified. Please
login."
+ redirect_to :controller => :home, :action => :login
+ rescue Exception => error
+ flash[:error] = "ERROR: #{error.message}"
+ redirect_to error_url
+ end
+ else
+ flash[:error] = 'Verification failed.'
+ end
+ else
+ flash[:error] = 'No token was provided.'
+ end
+ else
+ flash[:error] = "#{(a)this_user.display_name} is already verified."
+ redirect_to error_url
+ end
+ end
+
def backlog
end
@@ -36,8 +65,13 @@ class UserController < ApplicationController
User.transaction do
@this_user = User.new(params[:user])
@this_user.privileges = UserPrivilege.new
+ @this_user.verification = UserVerification.new(
+ :sent => Date.today,
+ :token => UserVerification.create_token)
@this_user.save!
+
+
UserMailer.deliver_email_verification(@this_user,(a)this_user.verification.token)
end
redirect_to :controller => :home, :action => :login
diff --git a/app/models/user.rb b/app/models/user.rb
index 6554bf4..7d26194 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -25,6 +25,7 @@ class User < ActiveRecord::Base
has_one :privileges, :class_name => "UserPrivilege", :dependent =>
:destroy
has_many :product_roles
+ has_one :verification, :class_name => "UserVerification", :dependent =>
:destroy
attr_accessor :password_confirmation
validates_confirmation_of :password
@@ -77,6 +78,21 @@ class User < ActiveRecord::Base
result
end
+ # Attempts to verify the user by checking the supplied token against the
+ # existing verification token.
+ #
+ def verify(token)
+ raise Exception.new("User has no pending verifications!") if verification
== nil
+
+ verification.token == token
+ end
+
+ # Returns whether the user has been verified.
+ #
+ def verified?
+ (verification == nil)
+ end
+
# Returns whether the user is allowed to create projects.
#
def create_projects?
diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb
index bd42800..896a688 100644
--- a/app/models/user_mailer.rb
+++ b/app/models/user_mailer.rb
@@ -1,4 +1,13 @@
class UserMailer < ActionMailer::Base
+ # Sends an email verification to the specified user.
+ #
+ def email_verification(user, token)
+ recipients user.email
+ from MAIL_CONFIG[:from]
+ subject "Email verification"
+ body :user => user, :token => token
+ end
+
# Send an e-mail to an user and notify him his new password.
#
def new_generated_password(user, new_password)
@@ -8,8 +17,8 @@ class UserMailer < ActionMailer::Base
body :user => user, :new_password => new_password
end
- # Send an email to a user to notify him that no activity has been detected
- # in his backlog.
+ # Send an email to a user to notify him that no activity has been detected in
+ # his backlog.
#
def no_activity_recorded(user, backlog_items)
recipients user.email
@@ -18,8 +27,8 @@ class UserMailer < ActionMailer::Base
body :user => user, :backlog_items => backlog_items
end
- # Send an email to a user to notify him that no task has been detected
- # in his backlog.
+ # Send an email to a user to notify him that no task has been detected in his
+ # backlog.
#
def no_task_recorded(user, backlog_items)
recipients user.email
diff --git a/app/models/user_verification.rb b/app/models/user_verification.rb
new file mode 100644
index 0000000..0431fe0
--- /dev/null
+++ b/app/models/user_verification.rb
@@ -0,0 +1,45 @@
+# user_verification.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+# A +UserVerification+ object only exists when a user account needs to be
+# verified.
+#
+# A verification contains a +token+ which must be received from the user in
+# order to complete the validation. The token is 16 bytes long and consists only
+# of alphanumeric characters.
+#
+class UserVerification < ActiveRecord::Base
+ TOKEN_CHARACTERS = ('a'..'z').to_a + ('A'..'Z').to_a +
('0'..'9').to_a
+ TOKEN_SIZE = 16
+ TOKEN_PATTERN = "^[[:alnum:]]{#{TOKEN_SIZE}}$"
+
+ validates_presence_of :user_id
+
+ validates_presence_of :token
+ validates_format_of :token,
+ :with => /#{TOKEN_PATTERN}/
+
+ validates_presence_of :sent
+
+ belongs_to :user
+
+ # Generates a verification token.
+ #
+ def self.create_token
+ (0...TOKEN_SIZE).collect { TOKEN_CHARACTERS[Kernel.rand(TOKEN_CHARACTERS.length)]
}.join
+ end
+end
diff --git a/app/views/user/verify.html.erb b/app/views/user/verify.html.erb
new file mode 100644
index 0000000..e69de29
diff --git a/app/views/user_mailer/email_verification.html.erb
b/app/views/user_mailer/email_verification.html.erb
new file mode 100644
index 0000000..3190ac4
--- /dev/null
+++ b/app/views/user_mailer/email_verification.html.erb
@@ -0,0 +1,14 @@
+Dear <%= @user.display_name %>,
+
+This email is sent to you to verify that the address you entered:
+
+<%= @user.email %>
+
+is valid. To confirm this email address, please go to:
+
+<%= email_verification_url :controller => "user", :action =>
"verify", :id => @user.id, :token => @token %>
+
+If this link fails, please go to the verification page and enter the token:
+
+<%= @token %>
+
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 9708140..420825d 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -1,18 +1,18 @@
-# developerment.rb
-# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+# developerment.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <
http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <
http://www.gnu.org/licenses/>.
#
# Settings specified here will take precedence over those in config/environment.rb
@@ -32,4 +32,5 @@ config.action_controller.perform_caching = false
config.action_view.cache_template_extensions = false
# Don't care if the mailer can't send
-config.action_mailer.raise_delivery_errors = false
+config.action_mailer.raise_delivery_errors = true
+config.action_mailer.default_url_options = { :host => 'localhost:3000' }
diff --git a/config/environments/production.rb b/config/environments/production.rb
index eb7195a..ada1fd1 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -1,18 +1,18 @@
-# production.rb
-# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+# production.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <
http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <
http://www.gnu.org/licenses/>.
#
# Settings specified here will take precedence over those in config/environment.rb
@@ -34,3 +34,4 @@ config.action_view.cache_template_loading = true
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
+config.action_mailer.default_url_options = { :host => 'www.example.com' }
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 00f6c34..e844d9b 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,18 +1,18 @@
-# test.rb
-# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+# test.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <
http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <
http://www.gnu.org/licenses/>.
#
# Settings specified here will take precedence over those in config/environment.rb
@@ -37,3 +37,4 @@ config.action_controller.allow_forgery_protection = false
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
+config.action_mailer.default_url_options = { :host => 'localhost' }
diff --git a/config/routes.rb b/config/routes.rb
index 1108059..0f9ef23 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -23,6 +23,10 @@ ActionController::Routing::Routes.draw do |map|
# Error handling
map.error "/error", :controller => 'home', :action =>
'error'
+ map.email_verification "verify/:id/:token",
+ :controller => 'user',
+ :action => 'verify'
+
# Install the default routes as the lowest priority.
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
diff --git a/db/migrate/016_create_user_verifications.rb
b/db/migrate/016_create_user_verifications.rb
new file mode 100644
index 0000000..7f947dc
--- /dev/null
+++ b/db/migrate/016_create_user_verifications.rb
@@ -0,0 +1,20 @@
+class CreateUserVerifications < ActiveRecord::Migration
+ def self.up
+ create_table :user_verifications do |t|
+ t.integer :user_id, :null => false
+ t.string :token, :null => false, :limit => 16
+ t.datetime :sent, :null => false
+
+ t.timestamps
+ end
+
+ add_index :user_verifications, :user_id, :unique => true
+
+ execute "alter table user_verifications add constraint
fk_user_verification_user
+ foreign key (user_id) references users(id)"
+ end
+
+ def self.down
+ drop_table :user_verifications
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4a20449..331a1e5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -23,6 +23,7 @@ ActiveRecord::Schema.define(:version => 15) do
end
add_index "backlog_items", ["sprint_id",
"user_story_id"], :name =>
"index_backlog_items_on_sprint_id_and_user_story_id", :unique => true
+ add_index "backlog_items", ["user_story_id"], :name =>
"fk_backlog_items_user_story"
create_table "product_roles", :force => true do |t|
t.integer "user_id", :null => false
@@ -32,7 +33,9 @@ ActiveRecord::Schema.define(:version => 15) do
t.datetime "updated_at"
end
- add_index "product_roles", ["product_id", "user_id"],
:name => "index_product_roles_on_user_id_and_product_id", :unique => true
+ add_index "product_roles", ["user_id", "product_id"],
:name => "index_product_roles_on_user_id_and_product_id", :unique => true
+ add_index "product_roles", ["product_id"], :name =>
"fk_user_role_product"
+ add_index "product_roles", ["role_id"], :name =>
"fk_user_role_role"
create_table "products", :force => true do |t|
t.integer "project_id"
@@ -44,6 +47,7 @@ ActiveRecord::Schema.define(:version => 15) do
end
add_index "products", ["name"], :name =>
"index_products_on_name", :unique => true
+ add_index "products", ["project_id"], :name =>
"fk_products_project"
create_table "projects", :force => true do |t|
t.integer "owner_id", :null => false
@@ -55,6 +59,7 @@ ActiveRecord::Schema.define(:version => 15) do
end
add_index "projects", ["name"], :name =>
"index_projects_on_name", :unique => true
+ add_index "projects", ["owner_id"], :name =>
"fk_product_owner"
create_table "remaining_hours_estimates", :force => true do |t|
t.integer "backlog_item_id", :null =>
false
@@ -65,6 +70,9 @@ ActiveRecord::Schema.define(:version => 15) do
t.datetime "estimated_on", :null =>
false
end
+ add_index "remaining_hours_estimates", ["backlog_item_id"], :name
=> "fk_remaining_hours_item"
+ add_index "remaining_hours_estimates", ["user_id"], :name =>
"fk_remaining_hours_user"
+
create_table "roles", :force => true do |t|
t.string "name", :null =>
false
t.boolean "can_manage_backlog_items", :default => false, :null =>
false
@@ -96,6 +104,8 @@ ActiveRecord::Schema.define(:version => 15) do
t.integer "status", :default => 0, :null =>
false
end
+ add_index "sprints", ["product_id"], :name =>
"fk_sprint_product"
+
create_table "tasks", :force => true do |t|
t.integer "backlog_item_id",
:null => false
t.integer "primary_id",
:null => false
@@ -107,6 +117,10 @@ ActiveRecord::Schema.define(:version => 15) do
t.datetime "updated_at"
end
+ add_index "tasks", ["backlog_item_id"], :name =>
"fk_task_backlog_item"
+ add_index "tasks", ["primary_id"], :name =>
"fk_task_primary"
+ add_index "tasks", ["backup_id"], :name =>
"fk_task_backup"
+
create_table "user_privileges", :force => true do |t|
t.integer "user_id", :null => false
t.boolean "admin_projects", :default => false, :null => false
@@ -115,6 +129,8 @@ ActiveRecord::Schema.define(:version => 15) do
t.datetime "updated_at"
end
+ add_index "user_privileges", ["user_id"], :name =>
"fk_privilege_user"
+
create_table "user_stories", :force => true do |t|
t.integer "product_id"
t.integer "priority"
@@ -125,6 +141,8 @@ ActiveRecord::Schema.define(:version => 15) do
t.datetime "updated_at"
end
+ add_index "user_stories", ["product_id"], :name =>
"fk_user_story_product"
+
create_table "users", :force => true do |t|
t.string "email", :limit => 128, :null => false
t.string "display_name", :limit => 128, :null => false
diff --git a/test/fixtures/user_verifications.yml b/test/fixtures/user_verifications.yml
new file mode 100644
index 0000000..20a7c19
--- /dev/null
+++ b/test/fixtures/user_verifications.yml
@@ -0,0 +1,6 @@
+# Read about fixtures at
http://ar.rubyonrails.org/classes/Fixtures.html
+
+unverified_user_verification:
+ user_id: <%= Fixtures.identify(:unverified_user) %>
+ token: <%= UserVerification.create_token %>
+ sent: <%= Date.today %>
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index 6c096e5..590d61b 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -33,4 +33,10 @@ user_admin:
email: user_admin(a)product.com
display_name: User admin
salt: <%= SALT %>
- hashed_password: <%= User.encrypted_password('fungi', SALT) %>
\ No newline at end of file
+ hashed_password: <%= User.encrypted_password('fungi', SALT) %>
+
+unverified_user:
+ email: unverified(a)user.com
+ display_name: Unverified user
+ salt: <%= SALT %>
+ hashed_password: <%= User.encrypted_password('unverified', SALT) %>
diff --git a/test/functional/home_controller_test.rb
b/test/functional/home_controller_test.rb
index b8aaf02..ab8739f 100644
--- a/test/functional/home_controller_test.rb
+++ b/test/functional/home_controller_test.rb
@@ -24,6 +24,11 @@ class HomeControllerTest < ActionController::TestCase
@controller = HomeController.new
@authenticated_user = users(:mcpierce)
@backlog =
BacklogItem.find_all_by_state_and_owner_id(BacklogItem::STATE_ASSIGNED,@authenticated_user.id)
+
+ # setup for testing mail
+ ActionMailer::Base.delivery_method = :test
+ ActionMailer::Base.perform_deliveries = true
+ ActionMailer::Base.deliveries = []
end
# Ensures that anonymous users are sent to the login page when they try to
@@ -78,34 +83,6 @@ class HomeControllerTest < ActionController::TestCase
assert_nil session[:user_id], 'User id was not deleted.'
end
- # Ensures that a registration attempt rejects a logged in user.
- #
- def test_register_for_authenticated_user
- get :register, nil, {:user_id => @authenticated_user.id}
-
- assert_redirected_to :action => :dashboard
- end
-
- # Ensures that registering a new account works as expected.
- #
- def test_register_create_account
- post :register, {:email => 'test(a)user.com',
- :display_name => 'Test User',
- :password => 'farkle',
- :password_confirmation => 'farkle' }
-
- assert_redirected_to :action => :login
- assert User.find_by_email('test(a)user.com'), 'User not created'
- end
-
- # Ensure that the registration page works as expected.
- #
- def test_register
- get :register
-
- assert_response :success
- end
-
# Ensures that the main page doesn't require the user be logged.
#
def test_index
diff --git a/test/functional/user_controller_test.rb
b/test/functional/user_controller_test.rb
index 584d52b..9812f04 100644
--- a/test/functional/user_controller_test.rb
+++ b/test/functional/user_controller_test.rb
@@ -1,137 +1,202 @@
# user_controller_test.rb
# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
-#
+#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
-#
+#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <
http://www.gnu.org/licenses/>.
-#
+#
require File.dirname(__FILE__) + '/../test_helper'
class UserControllerTest < ActionController::TestCase
fixtures :backlog_items
+ fixtures :user_verifications
fixtures :users
-
+
def setup
@user = users(:mcpierce)
@other_user = users(:jdonuts)
@admin = users(:user_admin)
- @backlog_items = BacklogItem.find_all_by_owner_id((a)user.id)
+ @backlog_items = BacklogItem.find_all_by_owner_id((a)user.id)
+
+ @verified_user = users(:mcpierce)
+ raise "User should be verified!" unless @verified_user.verified?
+
+ @unverified_user = users(:unverified_user)
end
-
+
# Ensures that viewing fails when the user id is missing or invalid.
- #
+ #
def test_view_with_invalid_id
get :view
-
+
assert_redirected_to error_path
-
+
get :view, {:id => 9999}
-
+
assert_redirected_to error_path
end
-
+
# Ensures that viewing a user works as expected.
- #
+ #
def test_view
get :view, {:id => users(:mcpierce).id}
-
+
assert_response :success
assert assigns['this_user'], 'Failed to load the user.'
assert assigns['my_backlog'], 'Failed to load the user\'s
backlog.'
- assert_equal @backlog_items.size, assigns['my_backlog'].size,
+ assert_equal @backlog_items.size, assigns['my_backlog'].size,
'Failed to load the right set of items.'
end
-
+
# Ensures that viewing a user backlog requires a user id.
- #
+ #
def test_backlog_without_id
get :backlog
-
+
assert_redirected_to error_path
end
-
+
# Ensures that the right backlog is loaded.
- #
+ #
def test_backlog
get :view, {:id => @user.id}
-
+
assert_response :success
assert_equal @backlog_items.size, assigns['my_backlog'].size,
'Did not load the right backlog items.'
end
-
+
# Ensures that editing requires a valid user id.
- #
+ #
def test_modify_with_invalid_id
get :modify,
{:id => 9999}
-
+
assert_redirected_to error_path
end
-
+
# Ensures that anonymous users cannot modify a user.
- #
+ #
def test_modify_as_anonymous
get :modify,
{:id => @user.id}
-
+
assert_redirected_to :controller => :home, :action => :login
end
-
+
# Ensures that other users cannot modify someone's account.
- #
+ #
def test_modify_as_someone_else
get :modify,
{:id => @user.id},
{:user_id => @other_user.id}
-
+
assert_redirected_to error_path
end
-
+
# Ensures that an admin can edit a user's account.
- #
+ #
def test_modify_as_admin
get :modify,
{:id => @user.id},
{:user_id => @admin.id}
-
+
assert_response :success
end
-
+
# Ensures that a user can edit himself.
- #
+ #
def test_modify
get :modify,
{:id => @user.id},
{:user_id => @user.id}
-
+
assert_response :success
end
-
+
# Ensures that saving the updated user details works.
#
def test_modify
email = 'farkle(a)projxp.com'
-
+
post :modify,
{:id => @user.id,
:user => {:email => email}},
{:user_id => @user.id}
-
+
assert_redirected_to :action => :view, :id => @user.id
-
+
result = User.find_by_email(email)
-
+
assert result, 'User was not updated.'
end
+
+ # Ensures that a user id must be present when verifying a user.
+ #
+ def test_verify_without_user_id
+ get :verify
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that the user id must be valid.
+ #
+ def test_verify_with_invalid_user_id
+ get :verify, {:id => 9999}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that a user who has already been verified sees an error message.
+ #
+ def test_verify_for_verified_user
+ get :verify,
+ {:id => @verified_user.id, :token => UserVerification.create_token}
+
+ assert_redirected_to error_url
+ end
+
+ # Ensures that a token is required.
+ #
+ def test_verify_without_token
+ get :verify, {:id => @unverified_user.id}
+
+ assert_template "verify"
+ end
+
+ # Ensures that an invalid token causes verification to fail.
+ #
+ def test_verify_with_invalid_token
+ get :verify,
+ {
+ :id => @unverified_user.id,
+ :token => UserVerification.create_token
+ }
+
+ assert_template "verify"
+ end
+
+ # Ensures that verification works as expected.
+ #
+ def test_verify
+ get :verify,
+ {
+ :id => @unverified_user.id,
+ :token => @unverified_user.verification.token
+ }
+
+ assert_redirected_to :controller => :home, :action => :login
+ result = User.find_by_id((a)unverified_user.id)
+ assert result.verified?, 'Verification failed to mark the user as verified.'
+ end
end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index 2b12f48..9955734 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -18,15 +18,39 @@
require File.dirname(__FILE__) + '/../test_helper'
class UserTest < ActiveSupport::TestCase
+ fixtures :users
def setup
@mcpierce = users(:mcpierce)
+
+ @verified_user = @mcpierce
+ raise "User should be verified!" unless @verified_user.verified?
+ @unverified_user = users(:unverified_user)
end
# Ensures that authenticating a user using the wrong password fails.
#
- def authenticate_with_wrong_password
+ def test_authenticate_with_wrong_password
result = User.authenticate((a)mcpierce.email, 'klefar')
assert_nil result
end
+
+ # Ensures that trying to verify a token for a user with no pending verification
+ # raises an exception.
+ #
+ def test_verify_for_verified_user
+ assert_raise(Exception) { @verified_user.verify(UserVerification.create_token) }
+ end
+
+ # Ensures that giving the wrong token does not verified the user.
+ #
+ def test_verify_with_wrong_token
+ flunk "User should not have verified the token." if
@unverified_user.verify(UserVerification.create_token)
+ end
+
+ # Ensures that an unverified user is marke as unverified.
+ #
+ def test_verified_with_unverified_user
+ flunk "User is not verified." if @unverified_user.verified?
+ end
end
diff --git a/test/unit/user_verification_test.rb b/test/unit/user_verification_test.rb
new file mode 100644
index 0000000..0171729
--- /dev/null
+++ b/test/unit/user_verification_test.rb
@@ -0,0 +1,77 @@
+# user_verification_test.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class UserVerificationTest < ActiveSupport::TestCase
+
+ def setup
+ @verification = UserVerification.new(
+ :user_id => 1,
+ :token => UserVerification.create_token,
+ :sent => Date.today)
+ end
+
+ # Ensures that a verification requires an associated user.
+ #
+ def test_valid_fails_without_user_id
+ @verification.user_id = nil
+
+ flunk "A user must be specified." if @verification.valid?
+ end
+
+ # Ensures that a verification token is required.
+ #
+ def test_valid_fails_without_token
+ @verification.token = nil
+
+ flunk "A token must be specified." if @verification.valid?
+ end
+
+ # Ensures that a verification token must be 16 alpha-numeric characters.
+ #
+ def test_valid_fails_with_invalid_token
+ @verification.token = "this is invalid!"
+
+ flunk "Tokens must be 16 alpha-numeric characters only." if
@verification.valid?
+ end
+
+ # Ensures that a sent date has to be set.
+ #
+ def test_valid_fails_without_sent_date
+ @verification.sent = nil
+
+ flunk "Verification must have a sent date." if @verification.valid?
+ end
+
+ # Ensures taht a well-formed verification is considered valid.
+ #
+ def test_valid
+ flunk "There is a problem in validation." unless @verification.valid?
+ end
+
+ # Ensures that the token generator always creates a 16-character alphanumeric
+ # token.
+ #
+ def test_create_token
+ (1..100).each do |num|
+ token = UserVerification.create_token
+
+ flunk "The token generator is broken." unless token =~
/#{UserVerification::TOKEN_PATTERN}/
+ end
+ end
+end
--
1.6.0.2