tweetwine 0.3.2 → 0.4.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.
- data/.gitignore +6 -0
- data/CHANGELOG.rdoc +9 -0
- data/Gemfile +5 -13
- data/LICENSE.txt +1 -1
- data/README.md +3 -2
- data/Rakefile +8 -2
- data/lib/tweetwine/character_encoding.rb +1 -1
- data/lib/tweetwine/cli.rb +9 -3
- data/lib/tweetwine/config.rb +3 -3
- data/lib/tweetwine/exceptions.rb +54 -0
- data/lib/tweetwine/http.rb +1 -1
- data/lib/tweetwine/{util.rb → support.rb} +19 -12
- data/lib/tweetwine/tweet.rb +69 -0
- data/lib/tweetwine/twitter.rb +70 -72
- data/lib/tweetwine/ui.rb +36 -41
- data/lib/tweetwine/uri.rb +31 -0
- data/lib/tweetwine/version.rb +15 -0
- data/lib/tweetwine.rb +6 -64
- data/man/tweetwine.7 +4 -3
- data/man/tweetwine.7.ronn +3 -2
- data/release-script.txt +10 -0
- data/test/example/authorization_example.rb +40 -0
- data/test/example/example_helper.rb +1 -1
- data/test/example/global_options_example.rb +64 -0
- data/test/example/search_statuses_example.rb +36 -31
- data/test/example/show_followers_example.rb +1 -1
- data/test/example/show_friends_example.rb +1 -1
- data/test/example/show_home_example.rb +17 -29
- data/test/example/show_mentions_example.rb +2 -2
- data/test/example/show_user_example.rb +14 -12
- data/test/example/update_status_example.rb +9 -9
- data/test/example/use_http_proxy_example.rb +7 -6
- data/test/example/{application_behavior_example.rb → user_help_example.rb} +6 -39
- data/test/unit/config_test.rb +1 -1
- data/test/unit/http_test.rb +1 -21
- data/test/unit/oauth_test.rb +11 -11
- data/test/unit/{util_test.rb → support_test.rb} +37 -38
- data/test/unit/tweet_helper.rb +83 -0
- data/test/unit/tweet_test.rb +153 -0
- data/test/unit/twitter_test.rb +240 -248
- data/test/unit/ui_test.rb +174 -78
- data/test/unit/unit_helper.rb +18 -6
- data/test/unit/uri_test.rb +41 -0
- data/test/unit/url_shortener_test.rb +7 -7
- data/tweetwine.gemspec +12 -22
- metadata +52 -73
@@ -3,9 +3,9 @@
|
|
3
3
|
require "example_helper"
|
4
4
|
|
5
5
|
Feature "show user's tweets" do
|
6
|
-
in_order_to "to see what's going on with a specific user"
|
7
6
|
as_a "authenticated user"
|
8
|
-
i_want_to "see
|
7
|
+
i_want_to "see a specific user's tweets"
|
8
|
+
in_order_to "to know what the user has been doing"
|
9
9
|
|
10
10
|
USER_URL = "https://api.twitter.com/1/statuses/user_timeline.json?count=20&page=1&screen_name=%s"
|
11
11
|
USER_FIXTURE = fixture_file('user.json')
|
@@ -17,11 +17,7 @@ Feature "show user's tweets" do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
Then "the application shows my tweets" do
|
20
|
-
|
21
|
-
@output[1].should == "@chris wait me until the garden"
|
22
|
-
@output[2].should == ""
|
23
|
-
@output[3].should == "jillv, 3 days ago:"
|
24
|
-
@output[4].should == "so boring to wait"
|
20
|
+
should_output_tweets
|
25
21
|
end
|
26
22
|
end
|
27
23
|
|
@@ -32,11 +28,17 @@ Feature "show user's tweets" do
|
|
32
28
|
end
|
33
29
|
|
34
30
|
Then "the application shows the user's tweets" do
|
35
|
-
|
36
|
-
@output[1].should == "@chris wait me until the garden"
|
37
|
-
@output[2].should == ""
|
38
|
-
@output[3].should == "jillv, 3 days ago:"
|
39
|
-
@output[4].should == "so boring to wait"
|
31
|
+
should_output_tweets
|
40
32
|
end
|
41
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def should_output_tweets
|
38
|
+
@output[0].should == "jillv, in reply to chris, 9 hours ago:"
|
39
|
+
@output[1].should == "@chris wait me until the garden"
|
40
|
+
@output[2].should == ""
|
41
|
+
@output[3].should == "jillv, 3 days ago:"
|
42
|
+
@output[4].should == "so boring to wait"
|
43
|
+
end
|
42
44
|
end
|
@@ -4,9 +4,9 @@ require "example_helper"
|
|
4
4
|
require "yaml"
|
5
5
|
|
6
6
|
Feature "update my status (send new tweet)" do
|
7
|
-
in_order_to "tell something about me to the world"
|
8
7
|
as_a "authenticated user"
|
9
8
|
i_want_to "update my status"
|
9
|
+
in_order_to "tell something about me to the world"
|
10
10
|
|
11
11
|
RUBYGEMS_FIXTURE = fixture_file('shorten_rubygems.html')
|
12
12
|
RUBYGEMS_FULL_URL = 'http://rubygems.org/'
|
@@ -146,14 +146,14 @@ Feature "update my status (send new tweet)" do
|
|
146
146
|
@shorten_rubygems_body = "#{SHORTEN_CONFIG[:url_param_name]}=#{RUBYGEMS_FULL_URL_ENC}"
|
147
147
|
@shorten_rubylang_body = "#{SHORTEN_CONFIG[:url_param_name]}=#{RUBYLANG_FULL_URL_ENC}"
|
148
148
|
stub_http_request(SHORTEN_METHOD, SHORTEN_CONFIG[:service_url]).
|
149
|
-
|
150
|
-
|
149
|
+
with(:body => @shorten_rubygems_body).
|
150
|
+
to_return(:body => RUBYGEMS_FIXTURE)
|
151
151
|
stub_http_request(SHORTEN_METHOD, SHORTEN_CONFIG[:service_url]).
|
152
|
-
|
153
|
-
|
152
|
+
with(:body => @shorten_rubylang_body).
|
153
|
+
to_return(:body => RUBYLANG_FIXTURE)
|
154
154
|
stub_http_request(:post, UPDATE_URL).
|
155
|
-
|
156
|
-
|
155
|
+
with(:body => BODY_WITH_SHORT_URLS).
|
156
|
+
to_return(:body => UPDATE_FIXTURE_WITH_URLS)
|
157
157
|
@output = start_cli %W{--no-colors update #{STATUS_WITH_FULL_URLS}}, %w{y}
|
158
158
|
end
|
159
159
|
|
@@ -169,8 +169,8 @@ Feature "update my status (send new tweet)" do
|
|
169
169
|
Scenario "disable URL shortening for status updates" do
|
170
170
|
When "I have configured URL shortening, start the application with 'update' command with --no-url-shorten option, input status containing URLs, and confirm" do
|
171
171
|
stub_http_request(:post, UPDATE_URL).
|
172
|
-
|
173
|
-
|
172
|
+
with(:body => BODY_WITH_SHORT_URLS).
|
173
|
+
to_return(:body => UPDATE_FIXTURE_WITH_URLS)
|
174
174
|
@output = start_cli %W{--no-colors --no-url-shorten update #{STATUS_WITH_SHORT_URLS}}, %w{y}
|
175
175
|
end
|
176
176
|
|
@@ -3,13 +3,15 @@
|
|
3
3
|
require "example_helper"
|
4
4
|
|
5
5
|
Feature "using HTTP proxy" do
|
6
|
-
in_order_to "tweet behind an HTTP proxy"
|
7
6
|
as_a "authenticated user"
|
8
|
-
i_want_to "
|
7
|
+
i_want_to "use HTTP proxy"
|
8
|
+
in_order_to "tweet behind a firewall"
|
9
|
+
|
10
|
+
HOME_URL = "https://api.twitter.com/1/statuses/home_timeline.json?count=20&page=1"
|
9
11
|
|
10
12
|
def setup
|
11
13
|
super
|
12
|
-
stub_http_request(:get,
|
14
|
+
stub_http_request(:get, HOME_URL).to_return(:body => fixture_file('home.json'))
|
13
15
|
end
|
14
16
|
|
15
17
|
Scenario "enable proxy via environment variable" do
|
@@ -20,7 +22,6 @@ Feature "using HTTP proxy" do
|
|
20
22
|
|
21
23
|
Then "the application uses the proxy to fetch my home timeline" do
|
22
24
|
should_use_proxy
|
23
|
-
@output[0].should == "pelit, 11 days ago:"
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
@@ -32,7 +33,6 @@ Feature "using HTTP proxy" do
|
|
32
33
|
|
33
34
|
Then "the application uses the proxy to fetch my home timeline" do
|
34
35
|
should_use_proxy
|
35
|
-
@output[0].should == "pelit, 11 days ago:"
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -44,7 +44,6 @@ Feature "using HTTP proxy" do
|
|
44
44
|
|
45
45
|
Then "the application does not use the proxy to fetch my home timeline" do
|
46
46
|
should_not_use_proxy
|
47
|
-
@output[0].should == "pelit, 11 days ago:"
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
@@ -55,10 +54,12 @@ Feature "using HTTP proxy" do
|
|
55
54
|
nh.proxy_class?.should == true
|
56
55
|
nh.instance_variable_get(:@proxy_address).should == PROXY_HOST
|
57
56
|
nh.instance_variable_get(:@proxy_port).should == PROXY_PORT
|
57
|
+
assert_requested(:get, HOME_URL)
|
58
58
|
end
|
59
59
|
|
60
60
|
def should_not_use_proxy
|
61
61
|
net_http.proxy_class?.should == false
|
62
|
+
assert_requested(:get, HOME_URL)
|
62
63
|
end
|
63
64
|
|
64
65
|
def net_http
|
@@ -1,14 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
require "example_helper"
|
4
|
-
require "fixture/oauth"
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
Feature "application behavior" do
|
9
|
-
in_order_to "know about the application"
|
5
|
+
Feature "user help" do
|
10
6
|
as_a "user"
|
11
|
-
i_want_to "see
|
7
|
+
i_want_to "see help and error messages"
|
8
|
+
in_order_to "know how to use the application"
|
12
9
|
|
13
10
|
%w{-v version ver v}.each do |arg|
|
14
11
|
Scenario "show version with '#{arg}'" do
|
@@ -48,8 +45,9 @@ Usage: #{CLI::EXEC_NAME} [global_options...] [<command>] [command_options...]
|
|
48
45
|
--no-colors Disable ANSI colors for output.
|
49
46
|
--no-http-proxy Disable HTTP(S) proxy.
|
50
47
|
--no-url-shorten Disable URL shortening.
|
51
|
-
-n, --num <n> Number of
|
52
|
-
-p, --page <p> Page number for
|
48
|
+
-n, --num <n> Number of tweets per page (default 20).
|
49
|
+
-p, --page <p> Page number for tweets (default 1).
|
50
|
+
-r, --reverse Show tweets in reverse order (default false).
|
53
51
|
-u, --username <user> User to authenticate (default '#{USER}').
|
54
52
|
-v, --version Show version and exit.
|
55
53
|
|
@@ -139,35 +137,4 @@ Usage: tweetwine help [<command>]
|
|
139
137
|
@status.exitstatus.should == UnknownCommandError.status_code
|
140
138
|
end
|
141
139
|
end
|
142
|
-
|
143
|
-
Scenario "authorize user with OAuth and save access token" do
|
144
|
-
When "I start the application with 'home' command and the command fails due to me being unauthorized" do
|
145
|
-
@command_url = "https://api.twitter.com/1/statuses/home_timeline.json?count=20&page=1"
|
146
|
-
stub_http_request(METHOD, REQUEST_TOKEN_URL).to_return(:body => REQUEST_TOKEN_RESPONSE)
|
147
|
-
stub_http_request(METHOD, ACCESS_TOKEN_URL).to_return(:body => ACCESS_TOKEN_RESPONSE)
|
148
|
-
stub_http_request(:get, @command_url).
|
149
|
-
to_return(:status => [401, 'Unauthorized']).then.
|
150
|
-
to_return(:body => fixture_file('home.json'))
|
151
|
-
@output = nil
|
152
|
-
@config_contents = nil
|
153
|
-
@config_mode = nil
|
154
|
-
config_file = 'tweetwine.tmp'
|
155
|
-
in_temp_dir do
|
156
|
-
@output = start_cli %W{--no-colors -f #{config_file} home}, [PIN], {}
|
157
|
-
@config_contents = YAML.load_file(config_file)
|
158
|
-
@config_mode = file_mode(config_file)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
Then "the application authorizes me, saves access token to config file, and tries the command again" do
|
163
|
-
assert_requested(METHOD, REQUEST_TOKEN_URL)
|
164
|
-
assert_requested(METHOD, ACCESS_TOKEN_URL)
|
165
|
-
assert_requested(:get, @command_url, :headers => {'Authorization' => /^OAuth /}, :times => 2)
|
166
|
-
@output[0].should == "Please authorize: #{AUTHORIZE_URL}"
|
167
|
-
@output[1].should =~ /^Enter PIN:/
|
168
|
-
@output[2].should == "F1-kausi alkaa marraskuussa http://bit.ly/1qQwjQ"
|
169
|
-
@config_contents['oauth_access'].empty?.should == false
|
170
|
-
@config_mode.should == 0600
|
171
|
-
end
|
172
|
-
end
|
173
140
|
end
|
data/test/unit/config_test.rb
CHANGED
data/test/unit/http_test.rb
CHANGED
@@ -14,13 +14,9 @@ class HttpTest < UnitTestCase
|
|
14
14
|
PAYLOAD = {:msg => 'gloomy night'}
|
15
15
|
|
16
16
|
setup do
|
17
|
-
stub_sleep
|
18
17
|
mock_ui
|
19
18
|
@client = Http::Client.new
|
20
|
-
|
21
|
-
|
22
|
-
teardown do
|
23
|
-
restore_sleep
|
19
|
+
def @client.sleep(*args); end # speed up tests
|
24
20
|
end
|
25
21
|
|
26
22
|
%w{http https}.each do |scheme|
|
@@ -178,22 +174,6 @@ class HttpTest < UnitTestCase
|
|
178
174
|
end
|
179
175
|
end
|
180
176
|
end
|
181
|
-
|
182
|
-
private
|
183
|
-
|
184
|
-
def stub_sleep
|
185
|
-
Kernel.class_eval do
|
186
|
-
alias_method :__original_sleep, :sleep
|
187
|
-
define_method(:sleep) { |*args| }
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
def restore_sleep
|
192
|
-
Kernel.class_eval do
|
193
|
-
remove_method :sleep
|
194
|
-
alias_method :sleep, :__original_sleep
|
195
|
-
end
|
196
|
-
end
|
197
177
|
end
|
198
178
|
|
199
179
|
end
|
data/test/unit/oauth_test.rb
CHANGED
@@ -27,15 +27,15 @@ class OAuthTest < UnitTestCase
|
|
27
27
|
|
28
28
|
should "raise AuthorizationError if OAuth dance fails due to HTTP 4xx response" do
|
29
29
|
@http.expects(:post).
|
30
|
-
|
31
|
-
|
30
|
+
with(REQUEST_TOKEN_URL).
|
31
|
+
raises(HttpError.new(401, 'Unauthorized'))
|
32
32
|
assert_raise(AuthorizationError) { @oauth.authorize }
|
33
33
|
end
|
34
34
|
|
35
35
|
should "pass other exceptions than due to HTTP 4xx responses through" do
|
36
36
|
@http.expects(:post).
|
37
|
-
|
38
|
-
|
37
|
+
with(REQUEST_TOKEN_URL).
|
38
|
+
raises(HttpError.new(503, 'Service Unavailable'))
|
39
39
|
assert_raise(HttpError) { @oauth.authorize }
|
40
40
|
end
|
41
41
|
|
@@ -56,16 +56,16 @@ class OAuthTest < UnitTestCase
|
|
56
56
|
|
57
57
|
def expect_complete_oauth_dance
|
58
58
|
@http.expects(:post).
|
59
|
-
|
60
|
-
|
59
|
+
with(REQUEST_TOKEN_URL).
|
60
|
+
returns(REQUEST_TOKEN_RESPONSE)
|
61
61
|
@ui.expects(:info).
|
62
|
-
|
62
|
+
with("Please authorize: #{AUTHORIZE_URL}")
|
63
63
|
@ui.expects(:prompt).
|
64
|
-
|
65
|
-
|
64
|
+
with('Enter PIN').
|
65
|
+
returns(PIN)
|
66
66
|
@http.expects(:post).
|
67
|
-
|
68
|
-
|
67
|
+
with(ACCESS_TOKEN_URL).
|
68
|
+
returns(ACCESS_TOKEN_RESPONSE)
|
69
69
|
end
|
70
70
|
|
71
71
|
def fake_http_connection_and_request
|
@@ -6,13 +6,44 @@ require "time"
|
|
6
6
|
|
7
7
|
module Tweetwine::Test
|
8
8
|
|
9
|
-
class
|
10
|
-
include
|
9
|
+
class SupportTest < UnitTestCase
|
10
|
+
include Support
|
11
11
|
|
12
|
-
context "for
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
context "for determining emptiness" do
|
13
|
+
[
|
14
|
+
[nil, true, "nil"],
|
15
|
+
["", true, "empty string"],
|
16
|
+
["foo", false, "nonempty string"],
|
17
|
+
[[], true, "empty array"],
|
18
|
+
[%w{a b}, false, "nonempty array"]
|
19
|
+
].each do |(subject, emptiness, description)|
|
20
|
+
should "return #{emptiness} for blank? with #{description}" do
|
21
|
+
assert_equal emptiness, blank?(subject)
|
22
|
+
end
|
23
|
+
|
24
|
+
should "return #{!emptiness} for present? with #{description}" do
|
25
|
+
assert_equal !emptiness, present?(subject)
|
26
|
+
end
|
27
|
+
|
28
|
+
should "return non-empty subject for presence, when subject is #{description}" do
|
29
|
+
actual = presence(subject)
|
30
|
+
expected = present?(subject) ? subject : nil
|
31
|
+
assert_same expected, actual
|
32
|
+
end
|
33
|
+
|
34
|
+
should "return value of block for presence, when subject is #{description}" do
|
35
|
+
actual = presence(subject) { |s| s.size }
|
36
|
+
expected = present?(subject) ? subject.size : nil
|
37
|
+
assert_same expected, actual
|
38
|
+
end
|
39
|
+
|
40
|
+
should "allow presence to be used with || operator, when subject is #{description}" do
|
41
|
+
alternative = "hey"
|
42
|
+
actual = presence(subject) || alternative
|
43
|
+
expected = present?(subject) ? subject : alternative
|
44
|
+
assert_same expected, actual
|
45
|
+
end
|
46
|
+
end
|
16
47
|
end
|
17
48
|
|
18
49
|
context "for humanizing time differences" do
|
@@ -154,38 +185,6 @@ class UtilTest < UnitTestCase
|
|
154
185
|
end
|
155
186
|
end
|
156
187
|
|
157
|
-
context "for percent-encoding strings" do
|
158
|
-
[
|
159
|
-
%w{a a},
|
160
|
-
%w{B B},
|
161
|
-
%w{3 3},
|
162
|
-
%w{. period},
|
163
|
-
%w{- dash},
|
164
|
-
%w{_ underscore},
|
165
|
-
].each do |char, desc|
|
166
|
-
should "not encode safe characters, case #{desc}" do
|
167
|
-
assert_equal char, percent_encode(char)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
should "encode space character with percent-encoding, not with '+' character" do
|
172
|
-
assert_equal "%20", percent_encode(" ")
|
173
|
-
end
|
174
|
-
|
175
|
-
[
|
176
|
-
%w{& %26 ampersand},
|
177
|
-
%w{? %3F question mark},
|
178
|
-
%w{/ %2F slash},
|
179
|
-
%w{: %3A colon},
|
180
|
-
%w{, %2C comma}
|
181
|
-
].each do |char, expected, desc|
|
182
|
-
should "encode unsafe characters that URI.encode leaves by default unencoded, case #{desc}" do
|
183
|
-
assert_equal char, URI.encode(char)
|
184
|
-
assert_equal expected, percent_encode(char)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
188
|
context "for unescaping HTML" do
|
190
189
|
[
|
191
190
|
%w{a a},
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Tweetwine::Test
|
4
|
+
module TweetHelper
|
5
|
+
FIELD_PATHS = {
|
6
|
+
:from_user => %w{screen_name},
|
7
|
+
:to_user => %w{status in_reply_to_screen_name},
|
8
|
+
:retweet => %w{retweeted_status},
|
9
|
+
:created_at => %w{status created_at},
|
10
|
+
:status => %w{status text}
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
DEFAULT_FIELD_VALUES = {
|
14
|
+
:from_user => 'fred',
|
15
|
+
:to_user => nil,
|
16
|
+
:retweet => nil,
|
17
|
+
:created_at => Time.utc(2011, 'feb', 17, 22, 28, 0).iso8601,
|
18
|
+
:status => nil
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
RECORD_SKELETON = {
|
22
|
+
'screen_name' => nil,
|
23
|
+
'retweeted_status' => nil,
|
24
|
+
'status' => {
|
25
|
+
'in_reply_to_screen_name' => nil,
|
26
|
+
'created_at' => nil,
|
27
|
+
'text' => nil
|
28
|
+
}.freeze
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
def create_record(fields = {})
|
32
|
+
record = create_nonrt_record(nonrt_fields(fields))
|
33
|
+
modify_to_rt_record(record, fields[:rt_user]) if fields[:rt_user]
|
34
|
+
record
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_tweet(fields = {})
|
38
|
+
Tweetwine::Tweet.new(create_record(fields), FIELD_PATHS)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def find_hash_of_field_path(hash, path)
|
44
|
+
path = [path] unless path.is_a? Array
|
45
|
+
if path.size > 1
|
46
|
+
hash_path, field = path[0..-2], path.last
|
47
|
+
[Tweetwine::Support.find_hash_path(hash, hash_path), field]
|
48
|
+
else
|
49
|
+
[hash, path.first]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def nonrt_fields(fields)
|
54
|
+
DEFAULT_FIELD_VALUES.merge(fields.reject { |(k, v)| k == :rt_user })
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_nonrt_record(fields)
|
58
|
+
FIELD_PATHS.inject(deep_copy(RECORD_SKELETON)) do |result, (path_name, path_actual)|
|
59
|
+
hash, field = find_hash_of_field_path(result, path_actual)
|
60
|
+
hash[field] = fields[path_name]
|
61
|
+
result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def modify_to_rt_record(record, rt_user)
|
66
|
+
rt_hash, rt_field = find_hash_of_field_path(record, FIELD_PATHS[:retweet])
|
67
|
+
fr_hash, fr_field = find_hash_of_field_path(record, FIELD_PATHS[:from_user])
|
68
|
+
st_hash, st_field = find_hash_of_field_path(record, FIELD_PATHS[:status])
|
69
|
+
rt_hash[rt_field] = {
|
70
|
+
fr_field => fr_hash[fr_field].dup,
|
71
|
+
'status' => st_hash.dup
|
72
|
+
}
|
73
|
+
fr_hash[fr_field] = rt_user
|
74
|
+
st_hash[st_field] = 'retweeted status text. you should not see me.'
|
75
|
+
cr_hash, cr_field = find_hash_of_field_path(record, FIELD_PATHS[:created_at])
|
76
|
+
cr_hash[cr_field] = 'old created timestamp. you should not see me.'
|
77
|
+
end
|
78
|
+
|
79
|
+
def deep_copy(obj)
|
80
|
+
Marshal.load(Marshal.dump(obj))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'unit_helper'
|
4
|
+
require 'tweet_helper'
|
5
|
+
|
6
|
+
module Tweetwine::Test
|
7
|
+
|
8
|
+
class TweetTest < UnitTestCase
|
9
|
+
include TweetHelper
|
10
|
+
|
11
|
+
context "for initialization" do
|
12
|
+
should "raise exception if from user field is not found" do
|
13
|
+
assert_raise(ArgumentError) { create_tweet(:from_user => nil) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "for equivalence" do
|
18
|
+
should "equal to another tweet with same content" do
|
19
|
+
status = 'foo'
|
20
|
+
first = create_tweet(:status => status)
|
21
|
+
second = create_tweet(:status => status)
|
22
|
+
assert_equal first, second
|
23
|
+
end
|
24
|
+
|
25
|
+
should "not equal to another tweet with different content" do
|
26
|
+
first = create_tweet(:status => 'ahem')
|
27
|
+
second = create_tweet(:status => 'hmph')
|
28
|
+
assert_not_equal first, second
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "for handling regular tweet" do
|
33
|
+
setup do
|
34
|
+
@status = 'lurking'
|
35
|
+
@tweet = create_tweet(:status => @status)
|
36
|
+
end
|
37
|
+
|
38
|
+
should "detect tweet as not retweet" do
|
39
|
+
assert_equal false, @tweet.retweet?
|
40
|
+
end
|
41
|
+
|
42
|
+
should "not have retweeting user" do
|
43
|
+
assert_nil @tweet.rt_user
|
44
|
+
end
|
45
|
+
|
46
|
+
should "have originating user field" do
|
47
|
+
assert_equal(DEFAULT_FIELD_VALUES[:from_user], @tweet.from_user)
|
48
|
+
end
|
49
|
+
|
50
|
+
should "detect tweet as not being a reply" do
|
51
|
+
assert_equal false, @tweet.reply?
|
52
|
+
end
|
53
|
+
|
54
|
+
should "have no destination user" do
|
55
|
+
assert_nil @tweet.to_user
|
56
|
+
end
|
57
|
+
|
58
|
+
should "detect having creation timestamp" do
|
59
|
+
assert_equal true, @tweet.timestamped?
|
60
|
+
end
|
61
|
+
|
62
|
+
should "have creation timestamp" do
|
63
|
+
assert_equal Time.parse(DEFAULT_FIELD_VALUES[:created_at]), @tweet.created_at
|
64
|
+
end
|
65
|
+
|
66
|
+
should "detect having status" do
|
67
|
+
assert_equal true, @tweet.status?
|
68
|
+
end
|
69
|
+
|
70
|
+
should "have status" do
|
71
|
+
assert_equal @status, @tweet.status
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "for handling replying tweet" do
|
76
|
+
setup do
|
77
|
+
@to_user = 'jacko'
|
78
|
+
@tweet = create_tweet(:to_user => @to_user)
|
79
|
+
end
|
80
|
+
|
81
|
+
should "detect tweet as being a reply" do
|
82
|
+
assert_equal true, @tweet.reply?
|
83
|
+
end
|
84
|
+
|
85
|
+
should "have destination user" do
|
86
|
+
assert_equal @to_user, @tweet.to_user
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "for handling retweet" do
|
91
|
+
setup do
|
92
|
+
@rt_user = 'jonathan'
|
93
|
+
@status = 'tweet worth retweeting'
|
94
|
+
@tweet = create_tweet(:rt_user => @rt_user, :status => @status)
|
95
|
+
end
|
96
|
+
|
97
|
+
should "detect tweet as retweet" do
|
98
|
+
assert_equal true, @tweet.retweet?
|
99
|
+
end
|
100
|
+
|
101
|
+
should "have retweeting user" do
|
102
|
+
assert_equal @rt_user, @tweet.rt_user
|
103
|
+
end
|
104
|
+
|
105
|
+
should "have originating user" do
|
106
|
+
assert_equal DEFAULT_FIELD_VALUES[:from_user], @tweet.from_user
|
107
|
+
end
|
108
|
+
|
109
|
+
should "detect having creation timestamp of the original tweet" do
|
110
|
+
assert_equal true, @tweet.timestamped?
|
111
|
+
end
|
112
|
+
|
113
|
+
should "have creation timestamp of the original tweet" do
|
114
|
+
assert_equal Time.parse(DEFAULT_FIELD_VALUES[:created_at]), @tweet.created_at
|
115
|
+
end
|
116
|
+
|
117
|
+
should "detect having status of the original tweet" do
|
118
|
+
assert_equal true, @tweet.status?
|
119
|
+
end
|
120
|
+
|
121
|
+
should "have status of the original tweet" do
|
122
|
+
assert_equal @status, @tweet.status
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "for handling tweet with just user info" do
|
127
|
+
setup do
|
128
|
+
@tweet = create_tweet(:to_user => nil, :status => nil, :created_at => nil)
|
129
|
+
end
|
130
|
+
|
131
|
+
should "detect tweet as not retweet" do
|
132
|
+
assert_equal false, @tweet.retweet?
|
133
|
+
end
|
134
|
+
|
135
|
+
should "have originating user field" do
|
136
|
+
assert_equal(DEFAULT_FIELD_VALUES[:from_user], @tweet.from_user)
|
137
|
+
end
|
138
|
+
|
139
|
+
should "detect tweet as not being a reply" do
|
140
|
+
assert_equal false, @tweet.reply?
|
141
|
+
end
|
142
|
+
|
143
|
+
should "detect having no creation timestamp" do
|
144
|
+
assert_equal false, @tweet.timestamped?
|
145
|
+
end
|
146
|
+
|
147
|
+
should "detect having no status" do
|
148
|
+
assert_equal false, @tweet.status?
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|