twimock 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +125 -0
  8. data/Rakefile +6 -0
  9. data/db/.gitkeep +0 -0
  10. data/lib/twimock/access_token.rb +31 -0
  11. data/lib/twimock/api/account/verify_credentials.rb +40 -0
  12. data/lib/twimock/api/application.rb +29 -0
  13. data/lib/twimock/api/intent/sessions.rb +60 -0
  14. data/lib/twimock/api/oauth/access_token.rb +65 -0
  15. data/lib/twimock/api/oauth/authenticate.rb +51 -0
  16. data/lib/twimock/api/oauth/request_token.rb +49 -0
  17. data/lib/twimock/api/oauth.rb +83 -0
  18. data/lib/twimock/api.rb +35 -0
  19. data/lib/twimock/application.rb +21 -0
  20. data/lib/twimock/auth_hash.rb +8 -0
  21. data/lib/twimock/config.rb +90 -0
  22. data/lib/twimock/database/table.rb +359 -0
  23. data/lib/twimock/database.rb +133 -0
  24. data/lib/twimock/errors.rb +13 -0
  25. data/lib/twimock/omniauth/strategies/twitter.rb +28 -0
  26. data/lib/twimock/omniauth_twitter.rb +36 -0
  27. data/lib/twimock/request_token.rb +23 -0
  28. data/lib/twimock/user.rb +58 -0
  29. data/lib/twimock/version.rb +3 -0
  30. data/lib/twimock.rb +39 -0
  31. data/spec/spec_helper.rb +18 -0
  32. data/spec/support/api_spec_helper.rb +30 -0
  33. data/spec/support/omniauth_twitter_helper.rb +26 -0
  34. data/spec/support/tables_helper.rb +54 -0
  35. data/spec/support/test_application_helper.rb +9 -0
  36. data/spec/twimock/access_token_spec.rb +128 -0
  37. data/spec/twimock/api/account/verify_credentials_spec.rb +125 -0
  38. data/spec/twimock/api/application_spec.rb +27 -0
  39. data/spec/twimock/api/intent/sessions_spec.rb +184 -0
  40. data/spec/twimock/api/oauth/access_token_spec.rb +185 -0
  41. data/spec/twimock/api/oauth/authenticate_spec.rb +96 -0
  42. data/spec/twimock/api/oauth/request_token_spec.rb +123 -0
  43. data/spec/twimock/api_spec.rb +81 -0
  44. data/spec/twimock/application_spec.rb +120 -0
  45. data/spec/twimock/auth_hash_spec.rb +7 -0
  46. data/spec/twimock/config_spec.rb +192 -0
  47. data/spec/twimock/database/table_spec.rb +769 -0
  48. data/spec/twimock/database_spec.rb +261 -0
  49. data/spec/twimock/omniauth_twitter_spec.rb +129 -0
  50. data/spec/twimock/request_token_spec.rb +140 -0
  51. data/spec/twimock/user_spec.rb +271 -0
  52. data/spec/twimock_spec.rb +76 -0
  53. data/twimock.gemspec +38 -0
  54. data/view/authenticate.html.erb +23 -0
  55. metadata +343 -0
@@ -0,0 +1,58 @@
1
+ require 'faker'
2
+ require 'twimock/database/table'
3
+ require 'twimock/access_token'
4
+ require 'twimock/request_token'
5
+
6
+ module Twimock
7
+ # TODO: 要改善 AccessTokenをUserから分離
8
+ class User < Database::Table
9
+ TABLE_NAME = :users
10
+ COLUMN_NAMES = [:id, :name, :twitter_id, :email, :password, :created_at]
11
+ CHILDREN = [ Twimock::AccessToken, Twimock::RequestToken ]
12
+ INFO_KEYS = [:id, :name, :created_at]
13
+
14
+ def initialize(options={})
15
+ opts = Hashie::Mash.new(options)
16
+ id = opts.id || opts.identifier
17
+ @id = (id.to_i > 0) ? id.to_i : (Faker::Number.number(10)).to_i
18
+ @name = opts.name || create_user_name
19
+ @twitter_id = opts.twitter_id || @name.downcase.gsub(" ", "_")
20
+ @email = opts.email || Faker::Internet.email
21
+ @password = opts.password || Faker::Internet.password
22
+ @created_at = opts.created_at
23
+ end
24
+
25
+ def info
26
+ info_hash = Hashie::Mash.new({})
27
+ INFO_KEYS.each { |key| info_hash[key] = self.instance_variable_get("@#{key}") }
28
+ info_hash.id_str = info_hash.id.to_s
29
+ info_hash
30
+ end
31
+
32
+ def generate_access_token(application_id=nil)
33
+ if application_id
34
+ application = Twimock::Application.find_by_id(application_id)
35
+ raise Twimock::Errors::ApplicationNotFound unless application
36
+ end
37
+
38
+ access_token = Twimock::AccessToken.new({ application_id: application_id })
39
+ if self.persisted?
40
+ access_token.user_id = self.id
41
+ access_token.save!
42
+ end
43
+ access_token
44
+ end
45
+
46
+ def self.find_by_tiwtter_id_or_email(value)
47
+ user = Twimock::User.find_by_twitter_id(value)
48
+ user ||= Twimock::User.find_by_email(value)
49
+ end
50
+
51
+ private
52
+
53
+ def create_user_name
54
+ n = Faker::Name.name
55
+ (n.include?("'") || n.include?(".")) ? create_user_name : n
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module Twimock
2
+ VERSION = "0.0.1"
3
+ end
data/lib/twimock.rb ADDED
@@ -0,0 +1,39 @@
1
+ require "active_support/time"
2
+ require "twimock/version"
3
+ require "twimock/database"
4
+ require "twimock/config"
5
+ require "twimock/application"
6
+ require "twimock/user"
7
+ require "twimock/access_token"
8
+ require "twimock/request_token"
9
+ require "twimock/auth_hash"
10
+ require "twimock/errors"
11
+ require "twimock/api"
12
+ require "twimock/omniauth_twitter"
13
+
14
+ module Twimock
15
+ extend self
16
+
17
+ def auth_hash(access_token_string=nil)
18
+ return Twimock::AuthHash.new unless validate_access_token_string(access_token_string)
19
+
20
+ if access_token = Twimock::AccessToken.find_by_string(access_token_string)
21
+ if user = Twimock::User.find_by_id(access_token.user_id)
22
+ hash = Twimock::AuthHash.new({
23
+ provider: "twitter",
24
+ uid: user.id,
25
+ info: { name: user.name },
26
+ credentials: { token: access_token.string, expires_at: Time.now + 60.days },
27
+ extra: { raw_info: { id: user.id, name: user.name } }
28
+ })
29
+ end
30
+ end
31
+ hash || Twimock::AuthHash.new
32
+ end
33
+
34
+ private
35
+
36
+ def validate_access_token_string(string)
37
+ string.kind_of?(String) && string.size > 0
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'simplecov'
4
+ require 'coveralls'
5
+
6
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
7
+ SimpleCov::Formatter::HTMLFormatter,
8
+ Coveralls::SimpleCov::Formatter
9
+ ]
10
+ SimpleCov.start do
11
+ add_filter '.bundle/'
12
+ add_filter '/spec/'
13
+ end
14
+
15
+ # spec/support 以下のヘルパーを読み込み
16
+ Dir[File.expand_path('../support/', __FILE__) + '/**/*.rb'].each {|f| require f}
17
+
18
+ require 'twimock'
@@ -0,0 +1,30 @@
1
+ module APISpecHelper
2
+ shared_examples 'TestRackApplication 200 OK' do
3
+ it 'should return 200 OK' do
4
+ expect(last_response.status).to eq 200
5
+ expect(last_response.body).to be_blank
6
+ expect(last_response.header).to be_blank
7
+ end
8
+ end
9
+
10
+ shared_examples 'API 401 UnAuthorized' do
11
+ it 'should return 401 Unauthorized' do
12
+ expect(last_response.status).to eq 401
13
+ expect(last_response.header).not_to be_blank
14
+ expect(last_response.header['Content-Length']).to eq last_response.body.bytesize.to_s
15
+ expect(last_response.header['Content-Type']).to eq "application/json; charset=utf-8"
16
+ expect(last_response.body).not_to be_blank
17
+ parsed_body = JSON.parse(last_response.body)
18
+ expect(parsed_body["error"]["code"]).to match /^Invalid.*/
19
+ end
20
+ end
21
+
22
+ shared_examples 'API 500 InternalServerError' do
23
+ it 'should return 500' do
24
+ expect(last_response.status).to eq 500
25
+ expect(last_response.header).not_to be_blank
26
+ expect(last_response.header['Content-Length']).to eq last_response.body.bytesize.to_s
27
+ expect(last_response.body).not_to be_blank
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module OmniAuthTwitterHelper
2
+ def create_request_env(request_token, request_secret)
3
+ {
4
+ 'rack.input' => StringIO.new(""),
5
+ 'rack.session' => { "session_id" => "123456",
6
+ "callback_path" => "/",
7
+ "omniauth.params" => {},
8
+ "omniauth.origin" => "http://example.com/authentication",
9
+ "oauth" => { "twitter" => { "callback_confirmed" => true,
10
+ "request_token" => request_token,
11
+ "request_secret" => request_secret } } },
12
+ 'rack.url_scheme' => "http",
13
+ "GATEWAY_INTERFACE" => "CGI/1.1",
14
+ "PATH_INFO" => "/users/auth/twitter",
15
+ "QUERY_STRING" => "",
16
+ "REMOTE_ADDR" => "127.0.0.1",
17
+ "REMOTE_HOST" => "localhost.localdomain",
18
+ "REQUEST_METHOD" => "GET",
19
+ "REQUEST_URI" => "http://example.com/users/auth/twitter",
20
+ "SERVER_NAME" => "example.com",
21
+ "SERVER_PORT" => "80",
22
+ "SERVER_PROTOCOL" => "HTTP/1.1",
23
+ "HTTP_HOST" => "example.com",
24
+ }
25
+ end
26
+ end
@@ -0,0 +1,54 @@
1
+ module TableHelper
2
+ def remove_dynamically_defined_all_method
3
+ klasses = [Twimock::Database::Table]
4
+ klasses.each do |klass|
5
+ remove_dynamically_defined_class_method(klass)
6
+ remove_dynamically_defined_instance_method(klass)
7
+ end
8
+ end
9
+
10
+ # テストで動的に定義したクラスメソッドを削除
11
+ def remove_dynamically_defined_class_method(klass)
12
+ klass.methods.each do |method_name|
13
+ if method_name.to_s =~ /^find_by_/ || method_name.to_s =~ /^find_all_by_/
14
+ klass.class_eval do
15
+ class_variable_set(:@@target_method_name, method_name)
16
+ class << self
17
+ remove_method class_variable_get(:@@target_method_name)
18
+ end
19
+ remove_class_variable(:@@target_method_name)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # テストで動的に定義したインスタンスメソッドを削除
26
+ def remove_dynamically_defined_instance_method(klass)
27
+ klass.column_names.each do |column_name|
28
+ getter = column_name
29
+ if klass.instance_methods.include?(getter)
30
+ klass.class_eval { remove_method getter }
31
+ end
32
+
33
+ setter = (column_name.to_s + "=").to_sym
34
+ if klass.instance_methods.include?(setter)
35
+ klass.class_eval { remove_method setter }
36
+ end
37
+ end
38
+ end
39
+
40
+ # Tableクラスでテストするために、一時的にDB Tableを作成する
41
+ def create_tables_table_for_test
42
+ db = Twimock::Database.new
43
+ db.connection.execute <<-SQL
44
+ CREATE TABLE TABLES (
45
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
46
+ text TEXT NOT NULL,
47
+ active BOOLEAN,
48
+ number INTEGER NOT NULL,
49
+ created_at DATETIME NOT NULL
50
+ );
51
+ SQL
52
+ db.disconnect!
53
+ end
54
+ end
@@ -0,0 +1,9 @@
1
+ module TestApplicationHelper
2
+ extend self
3
+
4
+ class TestRackApplication
5
+ def call(env)
6
+ [ 200, {}, [] ]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ describe Twimock::AccessToken do
4
+ include TableHelper
5
+
6
+ let(:db_name) { ".test" }
7
+ let(:table_name) { :access_tokens }
8
+ let(:column_names) { [ :id, :string, :secret, :application_id, :user_id, :created_at ] }
9
+
10
+ let(:id) { 1 }
11
+ let(:string) { "test_token" }
12
+ let(:secret) { "test_token_secret" }
13
+ let(:application_id) { 1 }
14
+ let(:user_id) { 1 }
15
+ let(:created_at) { Time.now }
16
+ let(:options) { { id: id,
17
+ string: string,
18
+ secret: secret,
19
+ application_id: application_id,
20
+ user_id: user_id,
21
+ created_at: created_at } }
22
+
23
+ after { remove_dynamically_defined_all_method }
24
+
25
+ describe '::TABLE_NAME' do
26
+ subject { Twimock::AccessToken::TABLE_NAME }
27
+ it { is_expected.to eq table_name }
28
+ end
29
+
30
+ describe '::COLUMN_NAMES' do
31
+ subject { Twimock::AccessToken::COLUMN_NAMES }
32
+ it { is_expected.to eq column_names }
33
+ end
34
+
35
+ describe '#initialize' do
36
+ context 'without option' do
37
+ subject { Twimock::AccessToken.new }
38
+ it { is_expected.to be_kind_of Twimock::AccessToken }
39
+
40
+ describe '.id' do
41
+ subject { Twimock::AccessToken.new.id }
42
+ it { is_expected.to be_nil }
43
+ end
44
+
45
+ describe '.string' do
46
+ subject { Twimock::AccessToken.new.string }
47
+ it { is_expected.to be_kind_of String }
48
+
49
+ describe '.size' do
50
+ subject { Twimock::AccessToken.new.string.size }
51
+ it { is_expected.to eq 50 }
52
+ end
53
+ end
54
+
55
+ describe '.secret' do
56
+ subject { Twimock::AccessToken.new.secret }
57
+ it { is_expected.to be_kind_of String }
58
+
59
+ describe '.size' do
60
+ subject { Twimock::AccessToken.new.secret.size }
61
+ it { is_expected.to eq 45 }
62
+ end
63
+ end
64
+
65
+ describe '.user_id' do
66
+ subject { Twimock::AccessToken.new.user_id }
67
+ it { is_expected.to be_nil }
68
+ end
69
+
70
+ describe '.application_id' do
71
+ subject { Twimock::AccessToken.new.application_id }
72
+ it { is_expected.to be_nil }
73
+ end
74
+
75
+ describe '.created_at' do
76
+ subject { Twimock::AccessToken.new.created_at }
77
+ it { is_expected.to be_nil }
78
+ end
79
+ end
80
+
81
+ context 'with id option but it is not integer' do
82
+ before { @opts = { id: "test_id" } }
83
+ subject { Twimock::AccessToken.new(@opts) }
84
+ it { is_expected.to be_kind_of Twimock::AccessToken }
85
+
86
+ describe '.id' do
87
+ subject { Twimock::AccessToken.new(@opts).id }
88
+ it { is_expected.to be_nil }
89
+ end
90
+ end
91
+
92
+ context 'with user_id option but it is not integer' do
93
+ before { @opts = { user_id: "test_id" } }
94
+ subject { Twimock::AccessToken.new(@opts) }
95
+ it { is_expected.to be_kind_of Twimock::AccessToken }
96
+
97
+ describe '.user_id' do
98
+ subject { Twimock::AccessToken.new(@opts).user_id }
99
+ it { is_expected.to be_nil }
100
+ end
101
+ end
102
+
103
+ context 'with application_id option but it is not integer' do
104
+ before { @opts = { application_id: "test_id" } }
105
+ subject { Twimock::AccessToken.new(@opts) }
106
+ it { is_expected.to be_kind_of Twimock::AccessToken }
107
+
108
+ describe '.application_id' do
109
+ subject { Twimock::AccessToken.new(@opts).application_id }
110
+ it { is_expected.to be_nil }
111
+ end
112
+ end
113
+
114
+ context 'with all options' do
115
+ subject { Twimock::AccessToken.new(options) }
116
+ it { is_expected.to be_kind_of Twimock::AccessToken }
117
+
118
+ context 'then attributes' do
119
+ it 'should set specified value by option' do
120
+ column_names.each do |column_name|
121
+ value = Twimock::AccessToken.new(options).send(column_name)
122
+ expect(value).to eq options[column_name]
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'rack/test'
3
+
4
+ describe Twimock::API::Account::VerifyCredentials do
5
+ include TestApplicationHelper
6
+ include Rack::Test::Methods
7
+
8
+ let(:method) { 'GET' }
9
+ let(:path) { '/1.1/account/verify_credentials.json' }
10
+ let(:authorization_regexp) { Regexp.new('OAuth oauth_consumer_key=\"(.*)\", oauth_nonce=\"(.*)\", oauth_signature=\"(.*)\", oauth_signature_method=\"(.*)\", oauth_timestamp=\"(.*)\", oauth_token=\"(.*)\", oauth_version=\"(.*)\".*') }
11
+
12
+ let(:oauth_consumer_key) { Twimock::Application.new.api_key }
13
+ let(:oauth_nonce) { "Tc400qacfXAoixQ5Tk9yeFjdBBrDb7U3Sdgs7WA8cM" }
14
+ let(:oauth_signature) { "I7LRwjN%2FRvqp53kia2fGCg%2FrBHo%3D" }
15
+ let(:oauth_signature_method) { "HMAC-SHA1" }
16
+ let(:oauth_timestamp) { "1422273906" }
17
+ let(:oauth_token) { Twimock::AccessToken.string }
18
+ let(:oauth_version) { "1.0" }
19
+ let(:authorization_header) { [ "OAuth oauth_consumer_key=\"#{oauth_consumer_key}\", oauth_nonce=\"#{oauth_nonce}\", oauth_signature=\"#{oauth_signature}\", oauth_signature_method=\"#{oauth_signature_method}\", oauth_timestamp=\"#{oauth_timestamp}\", oauth_token=\"#{oauth_token}\", oauth_version=\"#{oauth_version}\"" ] }
20
+
21
+ let(:body) { "" }
22
+ let(:header) { {} }
23
+ let(:test_app) { TestApplicationHelper::TestRackApplication.new }
24
+ let(:app) { Twimock::API::Account::VerifyCredentials.new(test_app) }
25
+
26
+ describe '::METHOD' do
27
+ subject { Twimock::API::Account::VerifyCredentials::METHOD }
28
+ it { is_expected.to eq method }
29
+ end
30
+
31
+ describe '::PATH' do
32
+ subject { Twimock::API::Account::VerifyCredentials::PATH }
33
+ it { is_expected.to eq path }
34
+ end
35
+
36
+ describe '::AUTHORIZATION_REGEXP' do
37
+ subject { Twimock::API::Account::VerifyCredentials::AUTHORIZATION_REGEXP }
38
+ it { is_expected.to eq authorization_regexp }
39
+ end
40
+
41
+ describe "GET '/1.1/account/verify_credentials.json'" do
42
+ context 'with authorization header' do
43
+ let(:db_name) { ".test" }
44
+ let(:database) { Twimock::Database.new }
45
+
46
+ before { stub_const("Twimock::Database::DEFAULT_DB_NAME", db_name) }
47
+ after { database.drop }
48
+
49
+ context 'that is correct' do
50
+ let(:header) { { "authorization" => authorization_header } }
51
+ let(:oauth_consumer_key) { @application.api_key }
52
+ let(:oauth_token) { @access_token.string }
53
+
54
+ before do
55
+ @application = Twimock::Application.new
56
+ @application.save!
57
+ @user = Twimock::User.new
58
+ @user.save!
59
+ @access_token = @user.generate_access_token(@application.id)
60
+ end
61
+
62
+ it 'should return 200 OK' do
63
+ get path, body, header
64
+
65
+ expect(last_response.status).to eq 200
66
+ expect(last_response.header).not_to be_blank
67
+ expect(last_response.header['Content-Length']).to eq last_response.body.bytesize.to_s
68
+ expect(last_response.body).not_to be_blank
69
+
70
+ # bodyの検証
71
+ parsed_body = JSON.parse(last_response.body)
72
+ expect(parsed_body['id']).to eq @user.id
73
+ expect(parsed_body['id_str']).to eq @user.id.to_s
74
+ expect(parsed_body['name']).to eq @user.name
75
+ expect(parsed_body['created_at']).to eq @user.created_at.to_s
76
+ end
77
+ end
78
+
79
+ context 'that is incorrect format' do
80
+ let(:consumer_key) { "test_consumer_key" }
81
+ before { get path, body, header }
82
+ it_behaves_like 'API 401 UnAuthorized'
83
+ end
84
+
85
+ context 'but consumer_key is invalid' do
86
+ before { get path, body, header }
87
+ it_behaves_like 'API 401 UnAuthorized'
88
+ end
89
+ end
90
+
91
+ context 'without authorization header', assert: :UnauthorizedAccountVerifyCredentials do
92
+ let(:authorization_header) { nil }
93
+ before { get path, body, header }
94
+ it_behaves_like 'API 401 UnAuthorized'
95
+ end
96
+
97
+ context 'raise error that is not catched' do
98
+ let(:header) { { "authorization" => authorization_header } }
99
+ let(:oauth_consumer_key) { @application.api_key }
100
+ let(:oauth_token) { @access_token.string }
101
+
102
+ before do
103
+ @application = Twimock::Application.new
104
+ @application.save!
105
+ @user = Twimock::User.new
106
+ @user.save!
107
+ @access_token = @user.generate_access_token(@application.id)
108
+ allow(Twimock::User).to receive(:find_by_id) { raise }
109
+ get path, body, header
110
+ end
111
+
112
+ it_behaves_like 'API 500 InternalServerError'
113
+ end
114
+ end
115
+
116
+ describe "GET '/test'" do
117
+ before { get '/test' }
118
+ it_behaves_like 'TestRackApplication 200 OK'
119
+ end
120
+
121
+ describe "POST '/1.1/account/verify_credentials.json'" do
122
+ before { post '/1.1/account/verify_credentials.json' }
123
+ it_behaves_like 'TestRackApplication 200 OK'
124
+ end
125
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Twimock::API::Application do
4
+ before do
5
+ allow_any_instance_of(Excon::Connection).to receive(:request) do
6
+ options = { status: status, headers: headers, body: body }
7
+ response = Excon::Response.new(options)
8
+ end
9
+ end
10
+
11
+ let(:status) { 200 }
12
+ let(:headers) { { "Content-Length" => 4, "Content-Type"=>"text/plain" } }
13
+ let(:body) { "test_body" }
14
+
15
+ let(:env) { { "rack.input" => StringIO.new(body),
16
+ "REQUEST_METHOD" => "GET",
17
+ "SERVER_NAME" => "api.twitter.com",
18
+ "SERVER_PORT" => "443",
19
+ "QUERY_STRING" => "",
20
+ "PATH_INFO" => "/",
21
+ "HTTPS" => "on" } }
22
+
23
+ describe 'call' do
24
+ subject { Twimock::API::Application.new.call(env) }
25
+ it { is_expected.to eq [ status, headers, [ body ] ] }
26
+ end
27
+ end