acts_as_textcaptcha 3.0.11 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ # simple wrapper for the textcaptcha.com API service
2
+ # loads and parses captcha question and answers
3
+
4
+ require 'rexml/document'
5
+
6
+ module ActsAsTextcaptcha
7
+
8
+ # raised if an empty response is returned
9
+ class EmptyResponseError < StandardError; end;
10
+
11
+ class TextcaptchaApi
12
+
13
+ ENDPOINT = 'http://textcaptcha.com/api/'
14
+
15
+ def self.fetch(api_key, options = {})
16
+ begin
17
+ url = uri_parser.parse("#{ENDPOINT}#{api_key}")
18
+ http = Net::HTTP.new(url.host, url.port)
19
+ if options[:http_open_timeout]
20
+ http.open_timeout = options[:http_open_timeout]
21
+ end
22
+ if options[:http_read_timeout]
23
+ http.read_timeout = options[:http_read_timeout]
24
+ end
25
+
26
+ response = http.get(url.path)
27
+ if response.body.empty?
28
+ raise ActsAsTextcaptcha::EmptyResponseError
29
+ else
30
+ return parse(response.body)
31
+ end
32
+ rescue SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
33
+ Errno::EHOSTUNREACH, EOFError, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
34
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
35
+ URI::InvalidURIError, ActsAsTextcaptcha::EmptyResponseError,
36
+ REXML::ParseException
37
+ # rescue from these errors and continue
38
+ end
39
+ end
40
+
41
+ def self.parse(xml)
42
+ parsed_xml = ActiveSupport::XmlMini.parse(xml)['captcha']
43
+ question = parsed_xml['question']['__content__']
44
+ if parsed_xml['answer'].is_a?(Array)
45
+ answers = parsed_xml['answer'].collect { |a| a['__content__'] }
46
+ else
47
+ answers = [parsed_xml['answer']['__content__']]
48
+ end
49
+
50
+ [question, answers]
51
+ end
52
+
53
+
54
+ private
55
+
56
+ def self.uri_parser
57
+ # URI.parse is deprecated in 1.9.2
58
+ URI.const_defined?(:Parser) ? URI::Parser.new : URI
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,34 @@
1
+ # A simple cache for storing Textcaptcha answers
2
+ # by default the underlying cache implementation is
3
+ # the standard Rails.cache (ActiveSupport::Cache)
4
+
5
+ module ActsAsTextcaptcha
6
+ class TextcaptchaCache
7
+
8
+ CACHE_KEY_PREFIX = 'acts_as_textcaptcha-'
9
+ DEFAULT_CACHE_EXPIRY_MINUTES = 10
10
+
11
+ def write(key, value, options = {})
12
+ unless options.has_key?(:expires_in)
13
+ options[:expires_in] = DEFAULT_CACHE_EXPIRY_MINUTES.minutes
14
+ end
15
+ Rails.cache.write(cache_key(key), value, options)
16
+ end
17
+
18
+ def read(key, options = nil)
19
+ Rails.cache.read(cache_key(key), options)
20
+ end
21
+
22
+ def delete(key, options = nil)
23
+ Rails.cache.delete(cache_key(key), options)
24
+ end
25
+
26
+ private
27
+
28
+ # since this cache may be shared with other objects
29
+ # a prefix is used in all cache keys
30
+ def cache_key(key)
31
+ "#{CACHE_KEY_PREFIX}#{key}"
32
+ end
33
+ end
34
+ end
@@ -1,17 +1,19 @@
1
1
  module ActsAsTextcaptcha
2
2
  module TextcaptchaHelper
3
3
 
4
- # builds html fields for spam question, answer and hidden encrypted spam answers
4
+ # builds html form fields for the textcaptcha
5
5
  def textcaptcha_fields(f, &block)
6
6
  model = f.object
7
7
  captcha_html = ''
8
8
  if model.perform_textcaptcha?
9
- captcha_html += f.hidden_field(:spam_answers)
10
- if model.spam_answer
11
- captcha_html += f.hidden_field(:spam_answer)
12
- elsif model.spam_question
13
- captcha_html += capture(&block)
14
- end
9
+ if model.textcaptcha_key
10
+ captcha_html += f.hidden_field(:textcaptcha_key)
11
+ if model.textcaptcha_question
12
+ captcha_html += capture(&block)
13
+ elsif model.textcaptcha_answer
14
+ captcha_html += f.hidden_field(:textcaptcha_answer)
15
+ end
16
+ end
15
17
  end
16
18
 
17
19
  # Rails 2 compatability
@@ -1,3 +1,3 @@
1
1
  module ActsAsTextcaptcha
2
- VERSION = "3.0.11"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -1,5 +1,3 @@
1
- require 'bcrypt'
2
-
3
1
  namespace :textcaptcha do
4
2
 
5
3
  desc "Creates a template config file in config/textcaptcha.yml"
@@ -10,17 +8,14 @@ namespace :textcaptcha do
10
8
  puts "\nOoops, a textcaptcha config file at #{dest} already exists ... aborting.\n\n"
11
9
  else
12
10
  config = ''
13
- salt = BCrypt::Engine.generate_salt
14
11
  f = File.open(src, 'r')
15
12
  f.each_line { |line| config += line }
16
- config.gsub!(/RAKE_GENERATED_SALT_PLACEHOLDER/, salt)
17
13
  config.gsub!(/ api_key:(.*)# for gem test purposes only$/, " api_key: PASTE_YOUR_TEXTCAPCHA_API_KEY_HERE")
18
- config.gsub!(/ bcrypt_salt:(.*)# for gem test purposes only$/, " bcrypt_salt: #{salt}")
19
14
 
20
15
  f = File.new(dest, 'w')
21
16
  f.write(config)
22
17
  f.close
23
- puts "\ntextcaptcha.yml generated at #{dest} (with a new BCrypt salt)\nNOTE: edit this file and add your textcaptcha api key, grab one from http://textcaptcha.com/api\n\n"
18
+ puts "\ntextcaptcha.yml generated at #{dest}\nNOTE: edit this file and add your textcaptcha api key, grab one from http://textcaptcha.com/api\n\n"
24
19
  end
25
20
  end
26
21
  end
@@ -8,6 +8,10 @@ ActiveRecord::Schema.define(:version => 0) do
8
8
  t.string :name
9
9
  end
10
10
 
11
+ create_table :fast_comments, :force => true do |t|
12
+ t.string :name
13
+ end
14
+
11
15
  create_table :reviews, :force => true do |t|
12
16
  t.string :name
13
17
  end
@@ -1,16 +1,21 @@
1
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)+'./../lib'))
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)+'./../lib/acts_as_textcaptcha'))
2
2
 
3
3
  ENV['RAILS_ENV'] = 'test'
4
4
 
5
+ # confgure test coverage reporting
5
6
  if ENV['COVERAGE']
6
- require "simplecov"
7
+ require 'simplecov'
7
8
  SimpleCov.start do
8
9
  add_filter '/test/'
10
+ add_filter '/vendor/'
9
11
  end
10
12
  SimpleCov.at_exit do
11
13
  SimpleCov.result.format!
12
14
  `open ./coverage/index.html` if RUBY_PLATFORM =~ /darwin/
13
15
  end
16
+ elsif ENV['TRAVIS']
17
+ require 'coveralls'
18
+ Coveralls.wear!
14
19
  end
15
20
 
16
21
  require 'minitest/autorun'
@@ -18,9 +23,22 @@ require 'fakeweb'
18
23
 
19
24
  require 'rails/all'
20
25
 
26
+ # silence warnings about I18n locales
27
+ I18n.config.enforce_available_locales = true
28
+
21
29
  require 'acts_as_textcaptcha'
30
+ require 'textcaptcha_cache'
31
+ require 'textcaptcha_api'
22
32
  require './test/test_models'
23
33
 
24
34
  # load and initialize test db schema
25
35
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => 'acts_as_textcaptcha.sqlite3.db')
26
36
  load(File.dirname(__FILE__) + "/schema.rb")
37
+
38
+ # initialize a Rails.cache (use a basic memory store in tests)
39
+ RAILS_CACHE = ActiveSupport::Cache::MemoryStore.new
40
+
41
+ # additional helper methods for use in tests
42
+ def find_in_cache(key)
43
+ RAILS_CACHE.read("#{ActsAsTextcaptcha::TextcaptchaCache::CACHE_KEY_PREFIX}#{key}")
44
+ end
@@ -8,36 +8,36 @@ end
8
8
 
9
9
  class Comment < ActiveRecord::Base
10
10
  # inline options (symbol keys) with api_key only
11
- acts_as_textcaptcha :api_key => '8u5ixtdnq9csc84cok0owswgo',
12
- :bcrypt_salt => '$2a$10$j0bmycH.SVfD1b5mpEGPpe'
11
+ acts_as_textcaptcha :api_key => '8u5ixtdnq9csc84cok0owswgo'
12
+ end
13
+
14
+ class FastComment < ActiveRecord::Base
15
+ # inline options with super fast (0.006 seconds) cache expiry time
16
+ acts_as_textcaptcha :cache_expiry_minutes => '0.0001',
17
+ :questions => [{ :question => '1+1', :answers => '2,two' }]
13
18
  end
14
19
 
15
20
  class Review < ActiveRecord::Base
16
21
  # inline options with all possible options
17
- acts_as_textcaptcha 'api_key' => '8u5ixtdnq9csc84cok0owswgo',
18
- 'bcrypt_salt' => '$2a$10$j0bmycH.SVfD1b5mpEGPpe',
19
- 'bcrypt_cost' => '3',
20
- 'questions' => [{ 'question' => 'The green hat is what color?', 'answers' => 'green' }]
22
+ acts_as_textcaptcha :api_key => '8u5ixtdnq9csc84cok0owswgo',
23
+ :questions => [{ :question => 'The green hat is what color?', :answers => 'green' }]
21
24
  end
22
25
 
23
26
  class MovieReview < ActiveRecord::Base
24
27
  # inline options with all possible options
25
- acts_as_textcaptcha 'api_key' => '8u5ixtdnq9csc84cok0owswgo',
26
- 'bcrypt_salt' => '$2a$10$j0bmycH.SVfD1b5mpEGPpe',
27
- 'bcrypt_cost' => '3',
28
- 'questions' => [{ 'Question' => 'The green hat is what color?', 'answers' => nil }]
28
+ acts_as_textcaptcha 'api_key' => '8u5ixtdnq9csc84cok0owswgo',
29
+ 'questions' => [{ 'Question' => 'The green hat is what color?', 'answers' => nil }]
29
30
  end
30
31
 
31
32
  class Note < ActiveRecord::Base
32
33
  # inline options (string keys) with user defined questions only (no textcaptcha service)
33
- acts_as_textcaptcha 'questions' => [{ 'question' => '1+1', 'answers' => '2,two' }],
34
- 'bcrypt_salt' => '$2a$10$j0bmycH.SVfD1b5mpEGPpe'
34
+ acts_as_textcaptcha 'questions' => [{ 'question' => '1+1', 'answers' => '2,two' }]
35
35
 
36
36
  # allows toggling perform_textcaptcha on/off (default on)
37
37
  attr_accessor :turn_off_captcha
38
38
 
39
39
  def perform_textcaptcha?
40
- !turn_off_captcha
40
+ super && !turn_off_captcha
41
41
  end
42
42
  end
43
43
 
@@ -46,8 +46,7 @@ class Contact
46
46
  include ActiveModel::Validations
47
47
  include ActiveModel::Conversion
48
48
  extend ActsAsTextcaptcha::Textcaptcha
49
- acts_as_textcaptcha :questions => [{ :question => 'one+1', :answers => "2,two,апельсин" }],
50
- :bcrypt_salt => '$2a$10$j0bmycH.SVfD1b5mpEGPpe'
49
+ acts_as_textcaptcha :questions => [{ :question => 'one+1', :answers => "2,two,апельсин" }]
51
50
  end
52
51
 
53
52
  # ActiveRecord model using the strong parameters gem
@@ -55,6 +54,5 @@ require 'strong_parameters'
55
54
 
56
55
  class StrongWidget < ActiveRecord::Base
57
56
  include ActiveModel::ForbiddenAttributesProtection
58
- acts_as_textcaptcha 'questions' => [{ 'question' => '1+1', 'answers' => '2,two' }],
59
- 'bcrypt_salt' => '$2a$10$j0bmycH.SVfD1b5mpEGPpe'
57
+ acts_as_textcaptcha 'questions' => [{ 'question' => '1+1', 'answers' => '2,two' }]
60
58
  end
@@ -0,0 +1,50 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/test_helper')
2
+
3
+ describe 'TextcaptchaApi' do
4
+
5
+ after(:each) do
6
+ FakeWeb.clean_registry
7
+ end
8
+
9
+ describe 'with a valid xml response' do
10
+
11
+ before(:each) do
12
+ body = "<captcha><question>1+1?</question><answer>1</answer><answer>2</answer><answer>3</answer></captcha>"
13
+ FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/abc|, :body => body)
14
+ end
15
+
16
+ it 'should fetch and parse an answer from the service' do
17
+ result = ActsAsTextcaptcha::TextcaptchaApi.fetch('abc')
18
+ result[0].must_equal '1+1?'
19
+ result[1].must_equal ['1', '2', '3']
20
+ end
21
+
22
+ it 'should allow http options to be set' do
23
+ result = ActsAsTextcaptcha::TextcaptchaApi.fetch('abc', { :http_read_timeout => 30,
24
+ :http_open_timeout => 5 })
25
+ result.length.must_equal 2
26
+ end
27
+ end
28
+
29
+ it 'should return nil when Net::HTTP errors occur' do
30
+ [
31
+ SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
32
+ Errno::EHOSTUNREACH, EOFError, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
33
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
34
+ URI::InvalidURIError
35
+ ].each do |error|
36
+ FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/xyz|, :exception => error)
37
+ ActsAsTextcaptcha::TextcaptchaApi.fetch('xyz').must_equal nil
38
+ end
39
+ end
40
+
41
+ it 'should return nil when body cannot be parsed as XML' do
42
+ FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/jibber|, :body => 'here be gibberish')
43
+ ActsAsTextcaptcha::TextcaptchaApi.fetch('jibber').must_equal nil
44
+ end
45
+
46
+ it 'should return nil when body is empty' do
47
+ FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/empty|, :body => '')
48
+ ActsAsTextcaptcha::TextcaptchaApi.fetch('empty').must_equal nil
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/test_helper')
2
+
3
+ describe 'TextcaptchaCache' do
4
+
5
+ before(:each) do
6
+ @cache = ActsAsTextcaptcha::TextcaptchaCache.new
7
+ @cache.write('mykey', [1,2,3])
8
+ end
9
+
10
+ it 'should write to the cache' do
11
+ @cache.write('my-new-key', 'abc')
12
+ @cache.read('my-new-key').must_equal 'abc'
13
+ end
14
+
15
+ it 'should read from the cache' do
16
+ @cache.read('mykey').must_equal [1,2,3]
17
+ end
18
+
19
+ it 'should delete from the cache' do
20
+ @cache.read('mykey').must_equal [1,2,3]
21
+ @cache.delete('mykey')
22
+
23
+ @cache.read('mykey').must_equal nil
24
+ end
25
+ end
@@ -17,13 +17,13 @@ describe 'TextcaptchaHelper' do
17
17
  @note.textcaptcha
18
18
  end
19
19
 
20
- def render_template(assigns)
20
+ def render_template(assigns = { :note => @note })
21
21
  template = <<-ERB
22
22
  <%= form_for(@note, :url => '/') do |f| %>
23
23
  <%= textcaptcha_fields(f) do %>
24
24
  <div class="field textcaptcha">
25
- <%= f.label :spam_answer, @note.spam_question %><br/>
26
- <%= f.text_field :spam_answer, :value => '' %>
25
+ <%= f.label :textcaptcha_answer, @note.textcaptcha_question %><br/>
26
+ <%= f.text_field :textcaptcha_answer, :value => '' %>
27
27
  </div>
28
28
  <% end %>
29
29
  <% end %>
@@ -32,19 +32,37 @@ describe 'TextcaptchaHelper' do
32
32
  Template.new([], assigns, @controller).render(:inline => template)
33
33
  end
34
34
 
35
- it 'should render question and answer fields, with hidden spam_answers field' do
36
- html = render_template({:note => @note})
35
+ it 'should render question and answer fields, with hidden textcaptcha_key field' do
36
+ html = render_template
37
37
 
38
- html.must_match /\<label for\=\"note\_spam\_answer\"\>1\+1\<\/label\>/
39
- html.must_match /\<input id\=\"note_spam_answers\" name\=\"note\[spam\_answers\]\" type\=\"hidden\" value\=\"(.*)\" \/\>/
38
+ assert_match /\<label for\=\"note_textcaptcha_answer\"\>1\+1\<\/label\>/, html
39
+ assert_match /\<input id\=\"note_textcaptcha_answer\" name\=\"note\[textcaptcha_answer\]\" size\=\"30\" type\=\"text\" value\=\"\" \/>/, html
40
+ assert_match /\<input id\=\"note_textcaptcha_key\" name\=\"note\[textcaptcha_key\]\" type\=\"hidden\" value\=\"([0-9a-f]{32})\" \/\>/, html
40
41
  end
41
42
 
42
- it 'should render hidden answer and spam_answer fields when question has been answered OK (and not ask question)' do
43
- @note.spam_answer = 2
44
- html = render_template({:note => @note})
43
+ it 'should render hidden answer and textcaptcha_key when only answer is present' do
44
+ @note.textcaptcha_question = nil
45
+ @note.textcaptcha_answer = 2
46
+ html = render_template
45
47
 
46
- html.wont_match /\<label for\=\"note\_spam\_answer\"\>1\+1\<\/label\>/
47
- html.must_match /\<input id\=\"note_spam_answers\" name\=\"note\[spam\_answers\]\" type\=\"hidden\" value\=\"(.*)\" \/\>/
48
- html.must_match /\<input id\=\"note_spam_answer\" name\=\"note\[spam_answer\]\" type\=\"hidden\" value\=\"2\" \/\>/
48
+ refute_match /\<label for\=\"note_textcaptcha_answer\"\>1\+1\<\/label\>/, html
49
+ assert_match /\<input id\=\"note_textcaptcha_answer\" name\=\"note\[textcaptcha_answer\]\" type\=\"hidden\" value\=\"2\" \/>/, html
50
+ assert_match /\<input id\=\"note_textcaptcha_key\" name\=\"note\[textcaptcha_key\]\" type\=\"hidden\" value\=\"([0-9a-f]{32})\" \/\>/, html
51
+ end
52
+
53
+ it 'should not render any question or answer when perform_textcaptcha? is false' do
54
+ @note.turn_off_captcha = true
55
+ html = render_template
56
+
57
+ refute_match /note_textcaptcha_answer/, html
58
+ refute_match /note_textcaptcha_key/, html
59
+ end
60
+
61
+ it 'should not render any question or answer when textcaptcha_key is missing' do
62
+ @note.textcaptcha_key = nil
63
+ html = render_template
64
+
65
+ refute_match /note_textcaptcha_answer/, html
66
+ refute_match /note_textcaptcha_key/, html
49
67
  end
50
68
  end
@@ -10,40 +10,40 @@ describe 'Textcaptcha' do
10
10
  end
11
11
 
12
12
  it 'should validate an ActiveRecord object (with multiple correct answers)' do
13
- @note.spam_question.must_equal('1+1')
13
+ @note.textcaptcha_question.must_equal('1+1')
14
14
  @note.valid?.must_equal false
15
- @note.errors[:spam_answer].first.must_equal('is incorrect, try another question instead')
15
+ @note.errors[:textcaptcha_answer].first.must_equal('is incorrect, try another question instead')
16
16
 
17
- @note.spam_answer = 'two'
17
+ @note.textcaptcha_answer = 'two'
18
18
  @note.valid?.must_equal true
19
- @note.errors[:spam_answer].must_be_empty
19
+ @note.errors[:textcaptcha_answer].must_be_empty
20
20
 
21
- @note.spam_answer = '2'
21
+ @note.textcaptcha_answer = '2'
22
22
  @note.valid?.must_equal true
23
- @note.errors[:spam_answer].must_be_empty
23
+ @note.errors[:textcaptcha_answer].must_be_empty
24
24
  end
25
25
 
26
- it 'should strip whitespace and downcase spam answer' do
27
- @note.spam_answer = ' tWo '
26
+ it 'should strip whitespace and downcase answer' do
27
+ @note.textcaptcha_answer = ' tWo '
28
28
  @note.valid?.must_equal true
29
- @note.errors[:spam_answer].must_be_empty
29
+ @note.errors[:textcaptcha_answer].must_be_empty
30
30
  end
31
31
 
32
32
  it 'should always be valid when record has been saved' do
33
- @note.spam_answer = '2'
33
+ @note.textcaptcha_answer = '2'
34
34
  @note.save!
35
35
  @note.textcaptcha
36
36
 
37
- @note.spam_answer = 'wrong answer'
37
+ @note.textcaptcha_answer = 'wrong answer'
38
38
  @note.new_record?.must_equal false
39
39
  @note.valid?.must_equal true
40
- @note.errors[:spam_answer].must_be_empty
40
+ @note.errors[:textcaptcha_answer].must_be_empty
41
41
  end
42
42
 
43
43
  it 'should always be valid when perform_textcaptcha? is false' do
44
44
  @note.turn_off_captcha = true
45
45
  @note.valid?.must_equal true
46
- @note.errors[:spam_answer].must_be_empty
46
+ @note.errors[:textcaptcha_answer].must_be_empty
47
47
 
48
48
  @note.save.must_equal true
49
49
  end
@@ -52,24 +52,13 @@ describe 'Textcaptcha' do
52
52
  @contact = Contact.new
53
53
  @contact.textcaptcha
54
54
 
55
- @contact.spam_question.must_equal('one+1')
56
- @contact.spam_answer = 'wrong'
55
+ @contact.textcaptcha_question.must_equal('one+1')
56
+ @contact.textcaptcha_answer = 'wrong'
57
57
  @contact.valid?.must_equal false
58
58
 
59
- @contact.spam_answer = 'two'
59
+ @contact.textcaptcha_answer = 'two'
60
60
  @contact.valid?.must_equal true
61
- @contact.errors[:spam_answer].must_be_empty
62
- end
63
-
64
- it 'should allow validation to be skipped' do
65
- @note.valid?.must_equal false
66
- @note.skip_textcaptcha = true
67
- @note.valid?.must_equal true
68
- end
69
-
70
- it 'should protect skip_textcaptcha attribute from mass assignment' do
71
- @note = Note.new(:skip_textcaptcha => true)
72
- @note.skip_textcaptcha.must_equal nil
61
+ @contact.errors[:textcaptcha_answer].must_be_empty
73
62
  end
74
63
  end
75
64
 
@@ -80,36 +69,31 @@ describe 'Textcaptcha' do
80
69
  end
81
70
 
82
71
  it 'should work' do
83
- @widget.spam_answers.must_be_nil
84
72
  @widget.textcaptcha
85
- @widget.spam_question.must_equal('1+1')
73
+ @widget.textcaptcha_question.must_equal('1+1')
86
74
  @widget.valid?.must_equal false
87
- @widget.errors[:spam_answer].first.must_equal('is incorrect, try another question instead')
75
+ @widget.errors[:textcaptcha_answer].first.must_equal('is incorrect, try another question instead')
88
76
 
89
- @widget.spam_answer = 'two'
77
+ @widget.textcaptcha_answer = 'two'
90
78
  @widget.valid?.must_equal true
91
- @widget.errors[:spam_answer].must_be_empty
79
+ @widget.errors[:textcaptcha_answer].must_be_empty
92
80
  end
93
81
  end
94
82
 
95
- describe 'encryption' do
83
+ describe 'with fast expiry time' do
96
84
 
97
85
  before(:each) do
98
- @note = Note.new
86
+ @comment = FastComment.new
99
87
  end
100
88
 
101
- it 'should encrypt spam_answers (joined by - seperator) MD5 digested and using BCrypt engine with salt' do
102
- @note.spam_answers.must_be_nil
103
- @note.textcaptcha
104
- encrypted_answers = [2,' TwO '].collect { |answer| BCrypt::Engine.hash_secret(Digest::MD5.hexdigest(answer.to_s.strip.downcase), '$2a$10$j0bmycH.SVfD1b5mpEGPpe', 1) }.join('-')
105
- @note.spam_answers.must_equal('$2a$10$j0bmycH.SVfD1b5mpEGPpePFe1wBxOn7Brr9lMuLRxv6lg4ZYjJ22-$2a$10$j0bmycH.SVfD1b5mpEGPpe8v5mqqpDaExuS/hZu8Xkq8krYL/T8P.')
106
- @note.spam_answers.must_equal(encrypted_answers)
107
- end
89
+ it 'should work' do
90
+ @comment.textcaptcha
91
+ @comment.textcaptcha_question.must_equal('1+1')
92
+ @comment.textcaptcha_answer = 'two'
93
+ sleep(0.01)
108
94
 
109
- it 'should raise error if bcyrpt salt is invalid' do
110
- @note.textcaptcha_config[:bcrypt_salt] = 'bad salt'
111
- proc { @note.textcaptcha }.must_raise BCrypt::Errors::InvalidSalt
112
- @note.textcaptcha_config[:bcrypt_salt] ='$2a$10$j0bmycH.SVfD1b5mpEGPpe'
95
+ @comment.valid?.must_equal false
96
+ @comment.errors[:textcaptcha_answer].first.must_equal('was not submitted quickly enough, try another question instead')
113
97
  end
114
98
  end
115
99
 
@@ -119,17 +103,17 @@ describe 'Textcaptcha' do
119
103
  FakeWeb.clean_registry
120
104
  end
121
105
 
122
- it 'should generate spam question from the service' do
106
+ it 'should generate a question from the service' do
123
107
  @review = Review.new
124
108
 
125
109
  @review.textcaptcha
126
- @review.spam_question.wont_be_nil
127
- @review.spam_question.wont_equal('The green hat is what color?')
110
+ @review.textcaptcha_question.wont_be_nil
111
+ @review.textcaptcha_question.wont_equal('The green hat is what color?')
128
112
 
129
- @review.spam_answers.wont_be_nil
113
+ find_in_cache(@review.textcaptcha_key).wont_be_nil
130
114
 
131
115
  @review.valid?.must_equal false
132
- @review.errors[:spam_answer].first.must_equal('is incorrect, try another question instead')
116
+ @review.errors[:textcaptcha_answer].first.must_equal('is incorrect, try another question instead')
133
117
  end
134
118
 
135
119
  it 'should parse a single answer from XML response' do
@@ -139,9 +123,8 @@ describe 'Textcaptcha' do
139
123
  FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => body)
140
124
 
141
125
  @review.textcaptcha
142
- @review.spam_question.must_equal(question)
143
- @review.spam_answers.must_equal('$2a$10$j0bmycH.SVfD1b5mpEGPpecvhlumIBvWXI4HQWk0xa74DebZDx772')
144
- @review.spam_answers.split('-').length.must_equal(1)
126
+ @review.textcaptcha_question.must_equal(question)
127
+ find_in_cache(@review.textcaptcha_key).must_equal(['f6f7fec07f372b7bd5eb196bbca0f3f4'])
145
128
  end
146
129
 
147
130
  it 'should parse multiple answers from XML response' do
@@ -151,76 +134,46 @@ describe 'Textcaptcha' do
151
134
  FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => body)
152
135
 
153
136
  @review.textcaptcha
154
- @review.spam_question.must_equal(question)
155
- @review.spam_answers.split('-').length.must_equal(3)
137
+ @review.textcaptcha_question.must_equal(question)
138
+ find_in_cache(@review.textcaptcha_key).length.must_equal(3)
156
139
  end
157
140
 
158
- describe 'service is unavailable' do
159
-
160
- describe 'should fallback to a user defined question' do
161
-
162
- before(:each) do
163
- @review = Review.new
164
- end
165
-
166
- it 'when errors occur' do
167
- [SocketError, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
168
- Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, URI::InvalidURIError].each do |error|
169
- FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :exception => error)
170
- @review.textcaptcha
171
- @review.spam_question.must_equal('The green hat is what color?')
172
- @review.spam_answers.wont_be_nil
173
- end
174
- end
175
-
176
- it 'when response is OK but body cannot be parsed as XML' do
177
- FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => 'here be gibberish')
178
- @review.textcaptcha
179
- @review.spam_question.must_equal('The green hat is what color?')
180
- @review.spam_answers.wont_be_nil
181
- end
182
-
183
- it 'when response is OK but empty' do
184
- FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => '')
185
- @review.textcaptcha
186
- @review.spam_question.must_equal('The green hat is what color?')
187
- @review.spam_answers.wont_be_nil
188
- end
189
- end
141
+ it 'should fallback to a user defined question when api returns nil' do
142
+ @review = Review.new
143
+ FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :body => '')
144
+ @review.textcaptcha
145
+ @review.textcaptcha_question.must_equal('The green hat is what color?')
146
+ find_in_cache(@review.textcaptcha_key).wont_be_nil
190
147
  end
191
148
 
192
- it 'should not generate any spam question or answer when no user defined questions set' do
149
+ it 'should not generate any question or answer when no user defined questions set' do
193
150
  @comment = Comment.new
194
151
 
195
152
  FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :exception => SocketError)
196
153
  @comment.textcaptcha
197
- @comment.spam_question.must_equal 'ActsAsTextcaptcha >> no API key (or questions) set and/or the textcaptcha service is currently unavailable (answer ok to bypass)'
198
- @comment.spam_answers.must_equal 'ok'
154
+ @comment.textcaptcha_question.must_equal nil
155
+ @comment.textcaptcha_key.must_equal nil
199
156
  end
200
157
 
201
- it 'should not generate any spam question or answer when user defined questions set incorrectly' do
158
+ it 'should not generate any question or answer when user defined questions set incorrectly' do
202
159
  @comment = MovieReview.new
203
160
 
204
161
  FakeWeb.register_uri(:get, %r|http://textcaptcha\.com/api/|, :exception => SocketError)
205
162
  @comment.textcaptcha
206
- @comment.spam_question.must_equal 'ActsAsTextcaptcha >> no API key (or questions) set and/or the textcaptcha service is currently unavailable (answer ok to bypass)'
207
- @comment.spam_answers.must_equal 'ok'
163
+ @comment.textcaptcha_question.must_equal nil
164
+ @comment.textcaptcha_key.must_equal nil
208
165
  end
209
166
  end
210
167
 
211
168
  describe 'configuration' do
212
169
 
213
170
  it 'should be configured with inline hash' do
214
- Review.textcaptcha_config.must_equal({ :api_key => '8u5ixtdnq9csc84cok0owswgo',
215
- :bcrypt_salt => '$2a$10$j0bmycH.SVfD1b5mpEGPpe',
216
- :bcrypt_cost => '3',
217
- :questions => [{ 'question' => 'The green hat is what color?', 'answers' => 'green' }]})
171
+ Review.textcaptcha_config.must_equal({ :api_key => '8u5ixtdnq9csc84cok0owswgo',
172
+ :questions => [{ :question => 'The green hat is what color?', :answers => 'green' }]})
218
173
  end
219
174
 
220
175
  it 'should be configured with textcaptcha.yml' do
221
- Widget.textcaptcha_config[:api_key].must_equal '6eh1co0j12mi2ogcoggkkok4o'
222
- Widget.textcaptcha_config[:bcrypt_salt].must_equal '$2a$10$qhSefD6gKtmq6M0AzXk4CO'
223
- Widget.textcaptcha_config[:bcrypt_cost].must_equal 1
176
+ Widget.textcaptcha_config[:api_key].must_equal '6eh1co0j12mi2ogcoggkkok4o'
224
177
  Widget.textcaptcha_config[:questions].length.must_equal 10
225
178
  end
226
179
  end