acts_as_textcaptcha 3.0.11 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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