summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorazul <azul@leap.se>2015-09-30 10:51:21 +0200
committerazul <azul@leap.se>2015-09-30 10:51:21 +0200
commitd45f6c61f6a13be06f1977b857e0cb31e79c5317 (patch)
treed3089b334c2663ea2fd76cf62dea853bdd57b047
parenta894966e425f27c31e7da196658c6ddee3fc3714 (diff)
parentd4f10a8d47572bcab4c44878b952146732d64d2e (diff)
Merge pull request #194 from Alster-Hamburgers/feature/invite_code
Request for feedback on invite code feature
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock11
m---------app/assets/javascripts/srp0
-rw-r--r--app/assets/javascripts/users.js25
-rw-r--r--app/models/account.rb11
-rw-r--r--app/models/invite_code.rb22
-rw-r--r--app/models/invite_code_validator.rb28
-rw-r--r--app/models/user.rb6
-rw-r--r--app/views/users/new.html.haml12
-rw-r--r--config/defaults.yml1
-rw-r--r--config/locales/en/users.en.yml2
-rw-r--r--engines/billing/test/functional/customers_controller_test.rb1
-rw-r--r--engines/support/test/integration/create_ticket_test.rb9
-rw-r--r--engines/support/test/unit/account_extension_test.rb4
-rw-r--r--engines/support/test/unit/ticket_test.rb4
-rw-r--r--features/step_definitions/auth_steps.rb4
-rw-r--r--lib/tasks/invite_code.rake16
-rw-r--r--test/factories.rb1
-rw-r--r--test/functional/identities_controller_test.rb4
-rw-r--r--test/functional/v1/messages_controller_test.rb1
-rw-r--r--test/integration/api/cert_test.rb1
-rw-r--r--test/integration/api/smtp_cert_test.rb12
-rw-r--r--test/integration/api/srp_test.rb9
-rw-r--r--test/integration/browser/account_test.rb36
-rw-r--r--test/support/api_integration_test.rb7
-rw-r--r--test/support/browser_integration_test.rb37
-rw-r--r--test/unit/account_test.rb44
-rw-r--r--test/unit/invite_code_test.rb67
-rw-r--r--test/unit/invite_code_validator_test.rb30
-rw-r--r--test/unit/tmp_user_test.rb4
-rw-r--r--test/unit/token_test.rb1
-rw-r--r--test/unit/user_test.rb3
32 files changed, 375 insertions, 44 deletions
diff --git a/Gemfile b/Gemfile
index dd93a3c..f0d50df 100644
--- a/Gemfile
+++ b/Gemfile
@@ -13,6 +13,7 @@ end
## AUTHENTICATION
gem "ruby-srp", "~> 0.2.1"
gem "rails_warden"
+gem "coupon_code"
## LOCALIZATION
gem 'http_accept_language'
@@ -84,6 +85,11 @@ group :production do
gem 'SyslogLogger', '~> 2.0'
end
+group :development do
+ gem "better_errors", '1.1.0'
+ gem "binding_of_caller"
+end
+
group :debug do
gem 'debugger', :platforms => :mri_19
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 42dea53..5ffb0c8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -56,6 +56,11 @@ GEM
multi_json (~> 1.0)
addressable (2.3.6)
arel (3.0.3)
+ better_errors (1.1.0)
+ coderay (>= 1.0.0)
+ erubis (>= 2.6.6)
+ binding_of_caller (0.7.2)
+ debug_inspector (>= 0.0.1)
bootstrap-sass (2.3.2.2)
sass (~> 3.2)
braintree (2.38.0)
@@ -72,6 +77,7 @@ GEM
client_side_validations (~> 3.2.5)
simple_form (~> 2.1.0)
cliver (0.3.2)
+ coderay (1.1.0)
columnize (0.9.0)
couchrest (1.1.3)
mime-types (~> 1.15)
@@ -86,6 +92,7 @@ GEM
actionpack (~> 3.0)
couchrest
couchrest_model
+ coupon_code (0.0.1)
cucumber (1.3.17)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
@@ -99,6 +106,7 @@ GEM
nokogiri (~> 1.5)
rails (>= 3, < 5)
daemons (1.1.9)
+ debug_inspector (0.0.2)
debugger (1.6.8)
columnize (>= 0.3.1)
debugger-linecache (~> 1.2.0)
@@ -258,6 +266,8 @@ PLATFORMS
DEPENDENCIES
SyslogLogger (~> 2.0)
+ better_errors (= 1.1.0)
+ binding_of_caller
bootstrap-sass (= 2.3.2.2)
capybara
certificate_authority!
@@ -267,6 +277,7 @@ DEPENDENCIES
couchrest (~> 1.1.3)
couchrest_model (~> 2.0.0)
couchrest_session_store (= 0.3.0)
+ coupon_code
cucumber-rails
debugger
factory_girl_rails
diff --git a/app/assets/javascripts/srp b/app/assets/javascripts/srp
-Subproject 8f33d32d40b1e21ae7fb9a92c78a275422af421
+Subproject 9e1a41733468d4a3f5102b04277b9cd7b52d0a4
diff --git a/app/assets/javascripts/users.js b/app/assets/javascripts/users.js
index e0f1c9d..5217942 100644
--- a/app/assets/javascripts/users.js
+++ b/app/assets/javascripts/users.js
@@ -88,11 +88,34 @@
}
};
+ var account = {
+
+ // Returns the user's identity
+ login: function() {
+ return document.getElementById("srp_username").value;
+ },
+
+ // Returns the password currently typed in
+ password: function() {
+ return document.getElementById("srp_password").value;
+ },
+
+ // The user's id
+ id: function() {
+ return document.getElementById("user_param").value;
+ },
+
+ // Returns the invite code currently typed in
+ loginParams: function () {
+ return { "invite_code": document.getElementById("srp_invite_code").value };
+ }
+ }
+
//
// PUBLIC FUNCTIONS
//
- srp.session = new srp.Session();
+ srp.session = new srp.Session(account);
srp.signedUp = function() {
return srp.login();
diff --git a/app/models/account.rb b/app/models/account.rb
index af470ed..a5cd833 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -1,7 +1,7 @@
#
-# The Account model takes care of the livecycle of a user.
+# The Account model takes care of the lifecycle of a user.
# It composes a User record and it's identity records.
-# It also allows for other engines to hook into the livecycle by
+# It also allows for other engines to hook into the lifecycle by
# monkeypatching the create, update and destroy methods.
# There's an ActiveSupport load_hook at the end of this file to
# make this more easy.
@@ -20,6 +20,7 @@ class Account
user = nil
user = User.new(attrs)
user.save
+
if !user.tmp? && user.persisted?
identity = user.identity
identity.user_id = user.id
@@ -27,6 +28,12 @@ class Account
identity.errors.each do |attr, msg|
user.errors.add(attr, msg)
end
+
+ if APP_CONFIG[:invite_required]
+ user_invite_code = InviteCode.find_by_invite_code user.invite_code
+ user_invite_code.invite_count += 1
+ user_invite_code.save
+ end
end
rescue StandardError => ex
user.errors.add(:base, ex.to_s) if user
diff --git a/app/models/invite_code.rb b/app/models/invite_code.rb
new file mode 100644
index 0000000..30a6498
--- /dev/null
+++ b/app/models/invite_code.rb
@@ -0,0 +1,22 @@
+require 'coupon_code'
+
+class InviteCode < CouchRest::Model::Base
+ use_database 'invite_codes'
+ property :invite_code, String, :read_only => true
+ property :invite_count, Integer, :default => 0, :accessible => true
+
+ timestamps!
+
+ design do
+ view :by_invite_code
+ view :by_invite_count
+ end
+
+ def initialize(attributes = {}, options = {})
+ super(attributes, options)
+ write_attribute('invite_code', CouponCode.generate) if new?
+ end
+
+end
+
+
diff --git a/app/models/invite_code_validator.rb b/app/models/invite_code_validator.rb
new file mode 100644
index 0000000..f96ca4a
--- /dev/null
+++ b/app/models/invite_code_validator.rb
@@ -0,0 +1,28 @@
+class InviteCodeValidator < ActiveModel::Validator
+ def validate(user)
+
+ user_invite_code = InviteCode.find_by_invite_code user.invite_code
+
+ if not_existent?(user_invite_code)
+ add_error_to_user("This is not a valid code", user)
+
+ elsif count_greater_than_zero?(user_invite_code)
+ add_error_to_user("This code has already been used", user)
+ end
+ end
+
+ private
+ def not_existent?(code)
+ code == nil
+ end
+
+ def count_greater_than_zero?(code)
+ code.invite_count > 0
+ end
+
+ def add_error_to_user(error, user)
+ user.errors[:invite_code] << error
+ end
+end
+
+
diff --git a/app/models/user.rb b/app/models/user.rb
index d44df40..3daee0f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -8,7 +8,7 @@ class User < CouchRest::Model::Base
property :password_salt, String, :accessible => true
property :contact_email, String, :accessible => true
property :contact_email_key, String, :accessible => true
-
+ property :invite_code, String, :accessible => true
property :enabled, TrueClass, :default => true
# these will be null by default but we shouldn't ever pull them directly, but only via the methods that will return the full ServiceLevel
@@ -39,6 +39,10 @@ class User < CouchRest::Model::Base
:email => true,
:mx_with_fallback => true
+
+ validates_with InviteCodeValidator, on: :create, if: -> {APP_CONFIG[:invite_required]}
+
+
timestamps!
design do
diff --git a/app/views/users/new.html.haml b/app/views/users/new.html.haml
index 41a9d55..bc0b1af 100644
--- a/app/views/users/new.html.haml
+++ b/app/views/users/new.html.haml
@@ -15,7 +15,13 @@
= render :partial => 'warnings'
= simple_form_for(@user, form_options) do |f|
= f.input :login, :label => t(:username), :required => false, :input_html => { :id => :srp_username }
- = f.input :password, :required => false, :validate => true, :input_html => { :id => :srp_password }
- = f.input :password_confirmation, :required => false, :validate => true, :input_html => { :id => :srp_password_confirmation }
- = f.button :wrapped, cancel: home_path
+ = f.input :password, :label => t(:password), :required => false, :validate => true, :input_html => { :id => :srp_password }
+ = f.input :password_confirmation, :label => t(:password_confirmation), :required => false, :validate => true, :input_html => { :id => :srp_password_confirmation }
+
+ - if APP_CONFIG[:invite_required]
+ = f.input :invite_code, :label => t(:invite_code), :input_html => { :id => :srp_invite_code }
+ - else
+ = f.input :invite_code, :as => "hidden", :input_html => { :value => " ", :id => :srp_invite_code }
+ = f.button :wrapped, cancel: home_path
+-#
diff --git a/config/defaults.yml b/config/defaults.yml
index dfa2a9a..906b446 100644
--- a/config/defaults.yml
+++ b/config/defaults.yml
@@ -82,6 +82,7 @@ common: &common
- support
- billing
allow_registration: true
+ invite_required: false
config_file_paths:
soledad-service: 'public/1/config/soledad-service.json'
eip-service: 'public/1/config/eip-service.json'
diff --git a/config/locales/en/users.en.yml b/config/locales/en/users.en.yml
index 89307dd..4c6bbc0 100644
--- a/config/locales/en/users.en.yml
+++ b/config/locales/en/users.en.yml
@@ -8,6 +8,8 @@ en:
account_settings: "Account Settings"
username: "Username"
password: "Password"
+ password_confirmation: "Password confirmation"
+ invite_code: "Invite code"
change_password: "Change Password"
invalid_user_pass: "Not a valid username/password combination"
invalid_ephemeral: "Invalid random key used. This looked like an attempt to hack the site to us. If it wasn't please contact support so we can look into the issue."
diff --git a/engines/billing/test/functional/customers_controller_test.rb b/engines/billing/test/functional/customers_controller_test.rb
index cc82fc1..4d84fb0 100644
--- a/engines/billing/test/functional/customers_controller_test.rb
+++ b/engines/billing/test/functional/customers_controller_test.rb
@@ -5,6 +5,7 @@ class CustomersControllerTest < ActionController::TestCase
tests CustomerController
setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
@user = FactoryGirl.create :user
@other_user = FactoryGirl.create :user
#FakeBraintree.clear!
diff --git a/engines/support/test/integration/create_ticket_test.rb b/engines/support/test/integration/create_ticket_test.rb
index 90e9a8a..00f9a6b 100644
--- a/engines/support/test/integration/create_ticket_test.rb
+++ b/engines/support/test/integration/create_ticket_test.rb
@@ -2,6 +2,11 @@ require 'test_helper'
class CreateTicketTest < BrowserIntegrationTest
+ setup do
+ @testcode = InviteCode.new
+ @testcode.save!
+ end
+
test "can submit ticket anonymously" do
visit '/'
click_on 'Get Help'
@@ -29,7 +34,7 @@ class CreateTicketTest < BrowserIntegrationTest
end
test "prefills fields" do
- login FactoryGirl.create(:premium_user)
+ login FactoryGirl.create(:premium_user, :invite_code => @testcode.invite_code)
visit '/'
click_on "Support Tickets"
click_on "New Ticket"
@@ -48,7 +53,7 @@ class CreateTicketTest < BrowserIntegrationTest
end
test "cleared email field should remain clear" do
- login FactoryGirl.create(:premium_user)
+ login FactoryGirl.create(:premium_user, :invite_code => @testcode.invite_code)
visit '/'
click_on "Support Tickets"
click_on "New Ticket"
diff --git a/engines/support/test/unit/account_extension_test.rb b/engines/support/test/unit/account_extension_test.rb
index aba162c..0ecb1aa 100644
--- a/engines/support/test/unit/account_extension_test.rb
+++ b/engines/support/test/unit/account_extension_test.rb
@@ -2,6 +2,10 @@ require 'test_helper'
class AccountExtensionTest < ActiveSupport::TestCase
+ setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
+ end
+
test "destroying an account triggers ticket destruction" do
t = FactoryGirl.create :ticket_with_creator
u = t.created_by_user
diff --git a/engines/support/test/unit/ticket_test.rb b/engines/support/test/unit/ticket_test.rb
index c64e8f4..7b5281f 100644
--- a/engines/support/test/unit/ticket_test.rb
+++ b/engines/support/test/unit/ticket_test.rb
@@ -2,6 +2,10 @@ require 'test_helper'
class TicketTest < ActiveSupport::TestCase
+ setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
+ end
+
test "ticket with default attribs is valid" do
t = FactoryGirl.build :ticket
assert t.valid?
diff --git a/features/step_definitions/auth_steps.rb b/features/step_definitions/auth_steps.rb
index e75455a..52c92ee 100644
--- a/features/step_definitions/auth_steps.rb
+++ b/features/step_definitions/auth_steps.rb
@@ -1,5 +1,7 @@
Given /^I authenticated$/ do
- @user = FactoryGirl.create(:user)
+ @testcode = InviteCode.new
+ @testcode.save!
+ @user = FactoryGirl.create(:user, :invite_code => @testcode.invite_code)
@my_auth_token = Token.create user_id: @user.id
end
diff --git a/lib/tasks/invite_code.rake b/lib/tasks/invite_code.rake
new file mode 100644
index 0000000..f3bafac
--- /dev/null
+++ b/lib/tasks/invite_code.rake
@@ -0,0 +1,16 @@
+
+
+desc "Generate a batch of invite codes"
+task :generate_invites, [:n] => :environment do |task, args|
+
+ codes = args.n
+ codes = codes.to_i
+
+ codes.times do |x|
+ x = InviteCode.new
+ x.save
+ puts "#{x.invite_code} Code generated."
+
+ end
+end
+
diff --git a/test/factories.rb b/test/factories.rb
index 0734688..b6e1475 100644
--- a/test/factories.rb
+++ b/test/factories.rb
@@ -11,6 +11,7 @@ FactoryGirl.define do
login { Faker::Internet.user_name + '_' + SecureRandom.hex(4) }
password_verifier "1234ABCD"
password_salt "4321AB"
+ invite_code "testcode"
factory :user_with_settings do
email_forward { Faker::Internet.email }
diff --git a/test/functional/identities_controller_test.rb b/test/functional/identities_controller_test.rb
index fcdeaa2..e491c52 100644
--- a/test/functional/identities_controller_test.rb
+++ b/test/functional/identities_controller_test.rb
@@ -2,6 +2,10 @@ require 'test_helper'
class IdentitiesControllerTest < ActionController::TestCase
+ setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
+ end
+
test "admin can list active and blocked ids" do
login :is_admin? => true
get :index
diff --git a/test/functional/v1/messages_controller_test.rb b/test/functional/v1/messages_controller_test.rb
index 6f7ea5d..720d862 100644
--- a/test/functional/v1/messages_controller_test.rb
+++ b/test/functional/v1/messages_controller_test.rb
@@ -3,6 +3,7 @@ require 'test_helper'
class V1::MessagesControllerTest < ActionController::TestCase
setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
@user = FactoryGirl.build(:user)
@user.save
@message = Message.new(:text => 'a test message')
diff --git a/test/integration/api/cert_test.rb b/test/integration/api/cert_test.rb
index 118fb9f..772901d 100644
--- a/test/integration/api/cert_test.rb
+++ b/test/integration/api/cert_test.rb
@@ -2,6 +2,7 @@ require 'test_helper'
class CertTest < ApiIntegrationTest
+
test "retrieve eip cert" do
login
get '/1/cert', {}, RACK_ENV
diff --git a/test/integration/api/smtp_cert_test.rb b/test/integration/api/smtp_cert_test.rb
index 2f50ef3..681d509 100644
--- a/test/integration/api/smtp_cert_test.rb
+++ b/test/integration/api/smtp_cert_test.rb
@@ -3,8 +3,13 @@ require 'openssl'
class SmtpCertTest < ApiIntegrationTest
+ setup do
+ @testcode = InviteCode.new
+ @testcode.save!
+ end
+
test "retrieve smtp cert" do
- @user = FactoryGirl.create :user, effective_service_level_code: 2
+ @user = FactoryGirl.create :user, effective_service_level_code: 2, :invite_code => @testcode.invite_code
login
post '/1/smtp_cert', {}, RACK_ENV
assert_text_response
@@ -15,7 +20,7 @@ class SmtpCertTest < ApiIntegrationTest
end
test "cert and key" do
- @user = FactoryGirl.create :user, effective_service_level_code: 2
+ @user = FactoryGirl.create :user, effective_service_level_code: 2, :invite_code => @testcode.invite_code
login
post '/1/smtp_cert', {}, RACK_ENV
assert_text_response
@@ -27,7 +32,7 @@ class SmtpCertTest < ApiIntegrationTest
end
test "fingerprint is stored with identity" do
- @user = FactoryGirl.create :user, effective_service_level_code: 2
+ @user = FactoryGirl.create :user, effective_service_level_code: 2, :invite_code => @testcode.invite_code
login
post '/1/smtp_cert', {}, RACK_ENV
assert_text_response
@@ -41,6 +46,7 @@ class SmtpCertTest < ApiIntegrationTest
end
test "fetching smtp certs requires email account" do
+
login
post '/1/smtp_cert', {}, RACK_ENV
assert_access_denied
diff --git a/test/integration/api/srp_test.rb b/test/integration/api/srp_test.rb
index fbef47e..463abcd 100644
--- a/test/integration/api/srp_test.rb
+++ b/test/integration/api/srp_test.rb
@@ -1,5 +1,10 @@
class SrpTest < RackTest
+ setup do
+ @testcode = InviteCode.new
+ @testcode.save!
+ end
+
teardown do
if @user
cleanup_user
@@ -32,10 +37,10 @@ class SrpTest < RackTest
attr_reader :server_auth
- def register_user(login = "integration_test", password = 'srp, verify me!')
+ def register_user(login = "integration_test", password = 'srp, verify me!', invite_code = @testcode.invite_code)
cleanup_user(login)
post 'http://api.lvh.me:3000/1/users.json',
- user_params(login: login, password: password)
+ user_params(login: login, password: password, invite_code: invite_code)
assert(@user = User.find_by_login(login), 'user should have been created: %s' % last_response_errors)
@login = login
@password = password
diff --git a/test/integration/browser/account_test.rb b/test/integration/browser/account_test.rb
index aea5406..cbe7ba9 100644
--- a/test/integration/browser/account_test.rb
+++ b/test/integration/browser/account_test.rb
@@ -6,7 +6,7 @@ class AccountTest < BrowserIntegrationTest
Identity.destroy_all_disabled
end
- test "signup successfully" do
+ test "signup successfully when invited" do
username, password = submit_signup
assert page.has_content?("Welcome #{username}")
click_on 'Log Out'
@@ -16,6 +16,22 @@ class AccountTest < BrowserIntegrationTest
user.account.destroy
end
+ test "signup successfully without invitation" do
+ with_config invite_required: false do
+
+ username ||= "test_#{SecureRandom.urlsafe_base64}".downcase
+ password ||= SecureRandom.base64
+
+ visit '/users/new'
+ fill_in 'Username', with: username
+ fill_in 'Password', with: password
+ fill_in 'Password confirmation', with: password
+ click_on 'Sign Up'
+
+ assert page.has_content?("Welcome #{username}")
+ end
+ end
+
test "signup with username ending in dot json" do
username = Faker::Internet.user_name + '.json'
submit_signup username
@@ -47,6 +63,7 @@ class AccountTest < BrowserIntegrationTest
test "account destruction" do
username, password = submit_signup
+
click_on I18n.t('account_settings')
click_on I18n.t('destroy_my_account')
assert page.has_content?(I18n.t('account_destroyed'))
@@ -81,21 +98,6 @@ class AccountTest < BrowserIntegrationTest
end
end
- test "change password" do
- with_config user_actions: ['change_password'] do
- login
- click_on "Account Settings"
- within('#update_login_and_password') do
- fill_in 'Password', with: "other password"
- fill_in 'Password confirmation', with: "other password"
- click_on 'Save'
- end
- click_on 'Log Out'
- attempt_login(@user.login, "other password")
- assert page.has_content?("Welcome #{@user.login}")
- end
- end
-
test "change pgp key" do
with_config user_actions: ['change_pgp_key'] do
pgp_key = FactoryGirl.build :pgp_key
@@ -117,6 +119,8 @@ class AccountTest < BrowserIntegrationTest
# trying to seed an invalid A for srp login
test "detects attempt to circumvent SRP" do
+ InviteCodeValidator.any_instance.stubs(:validate)
+
user = FactoryGirl.create :user
visit '/login'
fill_in 'Username', with: user.login
diff --git a/test/support/api_integration_test.rb b/test/support/api_integration_test.rb
index bd10f11..4077920 100644
--- a/test/support/api_integration_test.rb
+++ b/test/support/api_integration_test.rb
@@ -3,8 +3,13 @@ class ApiIntegrationTest < ActionDispatch::IntegrationTest
DUMMY_TOKEN = Token.new
RACK_ENV = {'HTTP_AUTHORIZATION' => %Q(Token token="#{DUMMY_TOKEN.to_s}")}
+ setup do
+ @testcode = InviteCode.new
+ @testcode.save!
+ end
+
def login(user = nil)
- @user ||= user ||= FactoryGirl.create(:user)
+ @user ||= user ||= FactoryGirl.create(:user, :invite_code => @testcode.invite_code)
# DUMMY_TOKEN will be frozen. So let's use a dup
@token ||= DUMMY_TOKEN.dup
# make sure @token is up to date if it already exists
diff --git a/test/support/browser_integration_test.rb b/test/support/browser_integration_test.rb
index 1e2aa51..35887cc 100644
--- a/test/support/browser_integration_test.rb
+++ b/test/support/browser_integration_test.rb
@@ -37,6 +37,8 @@ class BrowserIntegrationTest < ActionDispatch::IntegrationTest
setup do
Capybara.current_driver = Capybara.javascript_driver
page.driver.add_headers 'ACCEPT-LANGUAGE' => 'en-EN'
+ @testcode = InviteCode.new
+ @testcode.save!
end
teardown do
@@ -45,19 +47,38 @@ class BrowserIntegrationTest < ActionDispatch::IntegrationTest
end
def submit_signup(username = nil, password = nil)
- username ||= "test_#{SecureRandom.urlsafe_base64}".downcase
- password ||= SecureRandom.base64
- visit '/users/new'
- fill_in 'Username', with: username
- fill_in 'Password', with: password
- fill_in 'Password confirmation', with: password
- click_on 'Sign Up'
- return username, password
+
+ with_config invite_required: true do
+
+ username ||= "test_#{SecureRandom.urlsafe_base64}".downcase
+ password ||= SecureRandom.base64
+ visit '/users/new'
+ fill_in 'Username', with: username
+ fill_in 'Password', with: password
+ fill_in 'Invite code', with: @testcode.invite_code
+ fill_in 'Password confirmation', with: password
+ click_on 'Sign Up'
+ return username, password
+ end
+
+ with_config invite_required: false do
+
+ username ||= "test_#{SecureRandom.urlsafe_base64}".downcase
+ password ||= SecureRandom.base64
+ visit '/users/new'
+ fill_in 'Username', with: username
+ fill_in 'Password', with: password
+ fill_in 'Password confirmation', with: password
+ click_on 'Sign Up'
+ return username, password
+ end
+
end
# currently this only works for tests with poltergeist.
# ApiIntegrationTest has a working implementation for RackTest
def login(user = nil)
+ InviteCodeValidator.any_instance.stubs(:validate)
@user ||= user ||= FactoryGirl.create(:user)
token = Token.create user_id: user.id
page.driver.add_header "Authorization", %Q(Token token="#{token}")
diff --git a/test/unit/account_test.rb b/test/unit/account_test.rb
index b2bfe27..6b814b6 100644
--- a/test/unit/account_test.rb
+++ b/test/unit/account_test.rb
@@ -2,12 +2,17 @@ require 'test_helper'
class AccountTest < ActiveSupport::TestCase
+ setup do
+ @testcode = InviteCode.new
+ @testcode.save!
+ end
+
teardown do
Identity.destroy_all_disabled
end
- test "create a new account" do
- user = Account.create(FactoryGirl.attributes_for(:user))
+ test "create a new account when invited" do
+ user = Account.create(FactoryGirl.attributes_for(:user, :invite_code => @testcode.invite_code))
assert user.valid?, "unexpected errors: #{user.errors.inspect}"
assert user.persisted?
assert id = user.identity
@@ -16,18 +21,28 @@ class AccountTest < ActiveSupport::TestCase
user.account.destroy
end
+ test "create a new account" do
+ with_config invite_required: false do
+ user = Account.create(FactoryGirl.attributes_for(:user))
+ assert user.valid?, "unexpected errors: #{user.errors.inspect}"
+ assert user.persisted?
+ user.account.destroy
+ end
+ end
+
+
test "create and remove a user account" do
# We keep an identity that will block the handle from being reused.
assert_difference "Identity.count" do
assert_no_difference "User.count" do
- user = Account.create(FactoryGirl.attributes_for(:user))
+ user = Account.create(FactoryGirl.attributes_for(:user, :invite_code => @testcode.invite_code))
user.account.destroy
end
end
end
test "change username and create alias" do
- user = Account.create(FactoryGirl.attributes_for(:user))
+ user = Account.create(FactoryGirl.attributes_for(:user, :invite_code => @testcode.invite_code))
old_id = user.identity
old_email = user.email_address
user.account.update(FactoryGirl.attributes_for(:user))
@@ -44,4 +59,25 @@ class AccountTest < ActiveSupport::TestCase
user.account.destroy
end
+ test "Invite code count goes up by 1 when the invite code is entered" do
+ with_config invite_required: true do
+ user = Account.create(FactoryGirl.attributes_for(:user, :invite_code => @testcode.invite_code))
+ user_code = InviteCode.find_by_invite_code user.invite_code
+ user_code.save
+ user.save
+ assert user.persisted?
+ assert_equal 1, user_code.invite_count
+ end
+
+ end
+
+ test "Invite code stays zero when invite code is not used" do
+ #user = Account.create(FactoryGirl.attributes_for(:user, :invite_code => @testcode.invite_code))
+ invalid_user = FactoryGirl.build(:user, :invite_code => @testcode.invite_code)
+ invalid_user.save
+ user_code = InviteCode.find_by_invite_code invalid_user.invite_code
+ user_code.save
+
+ assert_equal 0, user_code.invite_count
+ end
end
diff --git a/test/unit/invite_code_test.rb b/test/unit/invite_code_test.rb
new file mode 100644
index 0000000..b17d1bc
--- /dev/null
+++ b/test/unit/invite_code_test.rb
@@ -0,0 +1,67 @@
+require 'test_helper'
+
+class InviteCodeTest < ActiveSupport::TestCase
+
+ test "it is created with an invite code" do
+ code = InviteCode.new
+ assert_not_nil code.invite_code
+ end
+
+ test "the invite code can be read from couch db correctly" do
+ code1 = InviteCode.new
+ code1.save
+ code2 = InviteCode.find_by_invite_code code1.invite_code
+ assert_equal code1.invite_code, code2.invite_code
+ end
+
+ test "the invite code count gets set to 0 upon creation" do
+ code1 = InviteCode.new
+ code1.save
+ assert_equal code1.invite_count, 0
+ end
+
+
+ test "Invite count >0 is not accepted for new account signup" do
+ validator = InviteCodeValidator.new nil
+
+ user_code = InviteCode.new
+ user_code.invite_count = 1
+ user_code.save
+
+ user = FactoryGirl.build :user
+ user.invite_code = user_code.invite_code
+
+ validator.validate(user)
+
+ assert_equal ["This code has already been used"], user.errors[:invite_code]
+
+ end
+
+ test "Invite count 0 is accepted for new account signup" do
+ validator = InviteCodeValidator.new nil
+
+ user_code = InviteCode.create
+
+ user = FactoryGirl.build :user
+ user.invite_code = user_code.invite_code
+
+ validator.validate(user)
+
+ assert_equal [], user.errors[:invite_code]
+ end
+
+ test "There is an error message if the invite code does not exist" do
+ validator = InviteCodeValidator.new nil
+
+ user = FactoryGirl.build :user
+ user.invite_code = "wrongcode"
+
+ validator.validate(user)
+
+ assert_equal ["This is not a valid code"], user.errors[:invite_code]
+
+ end
+
+
+end
+
diff --git a/test/unit/invite_code_validator_test.rb b/test/unit/invite_code_validator_test.rb
new file mode 100644
index 0000000..ee8f1b3
--- /dev/null
+++ b/test/unit/invite_code_validator_test.rb
@@ -0,0 +1,30 @@
+require 'test_helper'
+
+class InviteCodeValidatorTest < ActiveSupport::TestCase
+ test "user should not be created with invalid invite code" do
+ with_config invite_required: true do
+ invalid_user = FactoryGirl.build(:user)
+
+ assert !invalid_user.valid?
+ end
+ end
+
+ test "user should be created with valid invite code" do
+ valid_user = FactoryGirl.build(:user)
+ valid_code = InviteCode.create
+ valid_user.invite_code = valid_code.invite_code
+
+ assert valid_user.valid?
+ end
+
+ test "trying to create a user with invalid invite code should add error" do
+ with_config invite_required: true do
+ invalid_user = FactoryGirl.build(:user, :invite_code => "a non-existent code")
+
+ invalid_user.valid?
+
+ errors = {invite_code: ["This is not a valid code"]}
+ assert_equal errors, invalid_user.errors.messages
+ end
+ end
+end \ No newline at end of file
diff --git a/test/unit/tmp_user_test.rb b/test/unit/tmp_user_test.rb
index 55b117f..9494377 100644
--- a/test/unit/tmp_user_test.rb
+++ b/test/unit/tmp_user_test.rb
@@ -2,6 +2,10 @@ require 'test_helper'
class TmpUserTest < ActiveSupport::TestCase
+ setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
+ end
+
test "test_user saved to tmp_users" do
begin
assert User.ancestors.include?(TemporaryUser)
diff --git a/test/unit/token_test.rb b/test/unit/token_test.rb
index 5468650..51c8d8e 100644
--- a/test/unit/token_test.rb
+++ b/test/unit/token_test.rb
@@ -4,6 +4,7 @@ class TokenTest < ActiveSupport::TestCase
include StubRecordHelper
setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
@user = find_record :user
end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
index c301923..9501d34 100644
--- a/test/unit/user_test.rb
+++ b/test/unit/user_test.rb
@@ -4,6 +4,7 @@ class UserTest < ActiveSupport::TestCase
include SRP::Util
setup do
+ InviteCodeValidator.any_instance.stubs(:validate)
@user = FactoryGirl.build(:user)
end
@@ -70,6 +71,8 @@ class UserTest < ActiveSupport::TestCase
assert_equal key, @user.public_key
end
+
+
#
## Regression tests
#