diff options
32 files changed, 375 insertions, 44 deletions
| @@ -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    # | 
