typo 4.0.0 → 4.0.1
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/app/controllers/admin/comments_controller.rb +1 -1
- data/app/controllers/admin/content_controller.rb +1 -3
- data/app/controllers/admin/feedback_controller.rb +36 -31
- data/app/controllers/admin/sidebar_controller.rb +13 -2
- data/app/controllers/admin/users_controller.rb +2 -1
- data/app/controllers/articles_controller.rb +10 -19
- data/app/controllers/xml_controller.rb +2 -2
- data/app/helpers/admin/base_helper.rb +7 -3
- data/app/helpers/application_helper.rb +2 -2
- data/app/helpers/articles_helper.rb +5 -4
- data/app/models/article.rb +16 -10
- data/app/models/blog.rb +4 -10
- data/app/models/comment.rb +17 -36
- data/app/models/content.rb +31 -53
- data/app/models/content_state/base.rb +46 -3
- data/app/models/content_state/draft.rb +2 -9
- data/app/models/content_state/ham.rb +31 -0
- data/app/models/content_state/just_marked_as_ham.rb +10 -0
- data/app/models/content_state/just_marked_as_spam.rb +23 -0
- data/app/models/content_state/just_presumed_ham.rb +37 -0
- data/app/models/content_state/just_published.rb +15 -8
- data/app/models/content_state/new.rb +3 -10
- data/app/models/content_state/presumed_ham.rb +27 -0
- data/app/models/content_state/presumed_spam.rb +31 -0
- data/app/models/content_state/publication_pending.rb +7 -9
- data/app/models/content_state/published.rb +10 -9
- data/app/models/content_state/spam.rb +23 -0
- data/app/models/content_state/unclassified.rb +29 -0
- data/app/models/content_state/withdrawn.rb +28 -0
- data/app/models/email_notifier.rb +0 -1
- data/app/models/feedback.rb +151 -0
- data/app/models/trackback.rb +22 -29
- data/app/views/admin/feedback/_item.rhtml +5 -5
- data/app/views/admin/feedback/list.rhtml +13 -11
- data/app/views/admin/general/index.rhtml +8 -4
- data/app/views/admin/users/show.rhtml +7 -1
- data/app/views/articles/read.rhtml +2 -2
- data/bin/typo +7 -6
- data/components/plugins/sidebars/recent_comments_controller.rb +1 -1
- data/config/environment.rb +2 -0
- data/db/migrate/046_fixup_forthcoming_publications.rb +1 -1
- data/db/migrate/048_remove_count_caching.rb +31 -0
- data/db/migrate/049_move_feedback_to_new_state_machine.rb +33 -0
- data/db/schema.mysql-v3.sql +3 -4
- data/db/schema.mysql.sql +3 -4
- data/db/schema.postgresql.sql +3 -4
- data/db/schema.rb +12 -17
- data/db/schema.sqlite.sql +3 -4
- data/db/schema.sqlserver.sql +3 -4
- data/db/schema_version +1 -1
- data/doc/Installer.txt +4 -0
- data/lib/jabber_notify.rb +6 -2
- data/lib/sidebars/plugin.rb +2 -1
- data/lib/tasks/release.rake +5 -4
- data/lib/typo_version.rb +1 -1
- data/public/stylesheets/administration.css +22 -2
- data/test/fixtures/blogs.yml +2 -0
- data/test/fixtures/contents.yml +7 -7
- data/test/functional/admin/users_controller_test.rb +3 -0
- data/test/functional/articles_controller_test.rb +16 -1
- data/test/mocks/test/xmlrpc_mock.rb +5 -4
- data/test/unit/article_test.rb +16 -4
- data/test/unit/comment_test.rb +57 -35
- data/test/unit/content_state/factory_test.rb +7 -6
- data/test/unit/ping_test.rb +14 -0
- data/test/unit/trackback_test.rb +16 -15
- metadata +26 -26
- data/config/database.yml-pgsql +0 -17
- data/config/database.yml.sqlite +0 -14
- data/config/mail.yml +0 -8
- data/config/mongrel.conf +0 -2
- data/db/converters/mt-import.rb +0 -72
- data/db/development_structure.sql +0 -691
- data/installer/rails-installer.rb +0 -527
- data/installer/rails-installer/commands.rb +0 -118
- data/installer/rails-installer/web-servers.rb +0 -110
- data/log/development.log-1 +0 -991
- data/log/development.log-2 +0 -422
- data/log/development.log-3 +0 -429
- data/log/development.log-4 +0 -174
- data/svk-commitP6cVv.tmp +0 -1
- data/vendor/ruby-mp3info/lib/mp3info.rb +0 -720
data/lib/typo_version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
TYPO_VERSION = '4.0.
|
1
|
+
TYPO_VERSION = '4.0.1'
|
@@ -81,9 +81,8 @@
|
|
81
81
|
|
82
82
|
#tabs-container {
|
83
83
|
height: 20px;
|
84
|
-
width:
|
84
|
+
width: auto;
|
85
85
|
padding: 14px 0 0 100px;
|
86
|
-
height: 16px;
|
87
86
|
margin-top: -40px;
|
88
87
|
border-bottom: 1px solid #637987;
|
89
88
|
background: #F8F7EF;
|
@@ -628,3 +627,24 @@ div.user {
|
|
628
627
|
.feedbackbody td {
|
629
628
|
border-bottom: 1px dotted #ccc;
|
630
629
|
}
|
630
|
+
|
631
|
+
.presumed_ham td {
|
632
|
+
border-bottom: 1px dotted #ccc;
|
633
|
+
}
|
634
|
+
|
635
|
+
.presumed_spam td {
|
636
|
+
border-bottom: 1px dotted #ccc;
|
637
|
+
}
|
638
|
+
|
639
|
+
.ham td {
|
640
|
+
border-bottom: 1px dotted #ccc;
|
641
|
+
}
|
642
|
+
|
643
|
+
.spam td {
|
644
|
+
border-bottom: 1px dotted #ccc;
|
645
|
+
}
|
646
|
+
|
647
|
+
.spam .state {
|
648
|
+
font-weight: bold
|
649
|
+
}
|
650
|
+
|
data/test/fixtures/blogs.yml
CHANGED
@@ -6,6 +6,7 @@ default:
|
|
6
6
|
show_extended_on_rss: true
|
7
7
|
itunes_owner: ""
|
8
8
|
blog_name: test blog
|
9
|
+
title_prefix: true
|
9
10
|
limit_article_display: 10
|
10
11
|
sp_url_limit: 3
|
11
12
|
use_gravatar: false
|
@@ -43,6 +44,7 @@ second:
|
|
43
44
|
show_extended_on_rss: true
|
44
45
|
itunes_owner: ""
|
45
46
|
blog_name: second test blog
|
47
|
+
title_prefix: false
|
46
48
|
limit_article_display: 10
|
47
49
|
sp_url_limit: 0
|
48
50
|
use_gravatar: false
|
data/test/fixtures/contents.yml
CHANGED
@@ -96,7 +96,7 @@ spam_comment:
|
|
96
96
|
updated_at: 2005-01-01 02:00:00
|
97
97
|
published_at: 2005-01-01 02:00:00
|
98
98
|
guid: 12313123123123123
|
99
|
-
state: ContentState::
|
99
|
+
state: ContentState::Ham
|
100
100
|
|
101
101
|
comment2:
|
102
102
|
type: Comment
|
@@ -113,14 +113,14 @@ comment2:
|
|
113
113
|
updated_at: 2005-01-01 02:00:01
|
114
114
|
published_at: 2005-01-01 02:00:01
|
115
115
|
guid: 453456456456456
|
116
|
-
state: ContentState::
|
116
|
+
state: ContentState::Ham
|
117
117
|
|
118
118
|
comment3:
|
119
119
|
type: Comment
|
120
120
|
blog_id: 1
|
121
121
|
id: 12
|
122
122
|
published: false
|
123
|
-
state: ContentState::
|
123
|
+
state: ContentState::PresumedSpam
|
124
124
|
article_id: 1
|
125
125
|
author: Foo Bar
|
126
126
|
body: Zzzzzz
|
@@ -135,7 +135,7 @@ trackback1:
|
|
135
135
|
id: 7
|
136
136
|
article_id: 2
|
137
137
|
published: false
|
138
|
-
state: ContentState::
|
138
|
+
state: ContentState::PresumedSpam
|
139
139
|
blog_name: Trackback Blog
|
140
140
|
title: Trackback Entry
|
141
141
|
url: http://www.example.com
|
@@ -150,7 +150,7 @@ trackback2:
|
|
150
150
|
blog_id: 1
|
151
151
|
id: 8
|
152
152
|
article_id: 1
|
153
|
-
state: ContentState::
|
153
|
+
state: ContentState::PresumedHam
|
154
154
|
published: true
|
155
155
|
blog_name: Trackback Blog
|
156
156
|
title: Trackback Entry
|
@@ -167,7 +167,7 @@ trackback3:
|
|
167
167
|
id: 13
|
168
168
|
article_id: 1
|
169
169
|
published: true
|
170
|
-
state: ContentState::
|
170
|
+
state: ContentState::PresumedHam
|
171
171
|
blog_name: Trackback Blog 2
|
172
172
|
title: Trackback Entry 2
|
173
173
|
url: http://www.example.com
|
@@ -255,7 +255,7 @@ old_comment:
|
|
255
255
|
updated_at: 2004-05-02 20:00:02
|
256
256
|
published_at: 2004-05-02 20:00:02
|
257
257
|
published: true
|
258
|
-
state: ContentState::
|
258
|
+
state: ContentState::Ham
|
259
259
|
|
260
260
|
second_blog_article:
|
261
261
|
type: Article
|
@@ -107,6 +107,19 @@ class ArticlesControllerTest < Test::Unit::TestCase
|
|
107
107
|
assert_response :success
|
108
108
|
assert_rendered_file "archives"
|
109
109
|
end
|
110
|
+
|
111
|
+
def test_blog_title
|
112
|
+
blogs(:default).title_prefix = true
|
113
|
+
get :permalink, :year => 2004, :month => 06, :day => 01, :title => "article-3"
|
114
|
+
assert_response :success
|
115
|
+
assert_tag :tag => 'title', :content => /^test blog : Article 3!$/
|
116
|
+
|
117
|
+
blogs(:default).title_prefix = false
|
118
|
+
get :permalink, :year => 2004, :month => 06, :day => 01, :title => "article-3"
|
119
|
+
assert_response :success
|
120
|
+
assert_tag :tag => 'title', :content => /^Article 3!$/
|
121
|
+
|
122
|
+
end
|
110
123
|
|
111
124
|
# Permalinks
|
112
125
|
def test_permalink
|
@@ -116,7 +129,7 @@ class ArticlesControllerTest < Test::Unit::TestCase
|
|
116
129
|
assert_not_nil assigns(:article)
|
117
130
|
assert_equal contents(:article3), assigns(:article)
|
118
131
|
end
|
119
|
-
|
132
|
+
|
120
133
|
# Posts for given day
|
121
134
|
def test_find_by_date
|
122
135
|
get :find_by_date, :year => 2004, :month => 06, :day => 01
|
@@ -354,6 +367,8 @@ class ArticlesControllerTest < Test::Unit::TestCase
|
|
354
367
|
get :read, :id => contents(:article1).id
|
355
368
|
assert_response :success
|
356
369
|
assert_template "read"
|
370
|
+
|
371
|
+
assert_equal contents(:article1).comments.to_a.select{|c| c.published?}, contents(:article1).published_comments
|
357
372
|
|
358
373
|
assert_tag :tag => "ol",
|
359
374
|
:attributes => { :id => "commentList"},
|
@@ -2,26 +2,27 @@ module XMLRPC
|
|
2
2
|
class XMLRPC::Client
|
3
3
|
attr_reader :method_name, :args
|
4
4
|
attr_accessor :uri
|
5
|
+
$xmlrpc_pings = []
|
5
6
|
|
6
7
|
def initialize
|
7
8
|
end
|
8
9
|
|
9
10
|
def self.new2(uri)
|
10
|
-
|
11
|
+
$xmlrpc_pings ||= []
|
11
12
|
client = new
|
12
13
|
client.uri = uri
|
13
14
|
client
|
14
15
|
end
|
15
16
|
|
16
17
|
def self.pings
|
17
|
-
|
18
|
+
$xmlrpc_pings
|
18
19
|
end
|
19
20
|
|
20
21
|
def call(name, *args)
|
21
22
|
@method_name = name
|
22
23
|
@args = *args
|
23
|
-
|
24
|
-
|
24
|
+
$xmlrpc_pings ||= []
|
25
|
+
$xmlrpc_pings << self
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
data/test/unit/article_test.rb
CHANGED
@@ -15,10 +15,10 @@ class ArticleTest < Test::Unit::TestCase
|
|
15
15
|
assert @articles.include?(i.is_a?(Symbol) ? contents(i) : i)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def test_blog
|
20
20
|
a = Article.new
|
21
|
-
|
21
|
+
|
22
22
|
assert_equal(1, a.blog_id)
|
23
23
|
assert_kind_of(Blog, a.blog)
|
24
24
|
end
|
@@ -119,10 +119,10 @@ class ArticleTest < Test::Unit::TestCase
|
|
119
119
|
|
120
120
|
assert_kind_of Article,b
|
121
121
|
assert_equal 0, b.tags.size
|
122
|
-
|
122
|
+
|
123
123
|
c = Article.new(:title => 'Foo', :keywords => 'test "tag test" web2.0')
|
124
124
|
c.keywords_to_tags
|
125
|
-
|
125
|
+
|
126
126
|
assert_equal 3, c.tags.size
|
127
127
|
assert_equal ['test', 'tagtest', 'web2.0'].sort, c.tags.collect(&:name).sort
|
128
128
|
end
|
@@ -248,4 +248,16 @@ class ArticleTest < Test::Unit::TestCase
|
|
248
248
|
assert_equal(2, Article.find_all_by_date(2004,03).size)
|
249
249
|
assert_equal(1, Article.find_all_by_date(2004,03,01).size)
|
250
250
|
end
|
251
|
+
|
252
|
+
def test_withdrawal
|
253
|
+
art = Article.find(contents(:article1).id)
|
254
|
+
assert art.published?
|
255
|
+
assert ! art.withdrawn?
|
256
|
+
art.withdraw!
|
257
|
+
assert ! art.published?
|
258
|
+
assert art.withdrawn?
|
259
|
+
art.reload
|
260
|
+
assert ! art.published?
|
261
|
+
assert art.withdrawn?
|
262
|
+
end
|
251
263
|
end
|
data/test/unit/comment_test.rb
CHANGED
@@ -27,49 +27,51 @@ class CommentTest < Test::Unit::TestCase
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_reject_spam_rbl
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
cmt = Comment.new do |c|
|
31
|
+
c.author = "Spammer"
|
32
|
+
c.body = %{This is just some random text. <a href="http://chinaaircatering.com">without any senses.</a>. Please disregard.}
|
33
|
+
c.url = "http://buy-computer.us"
|
34
|
+
c.ip = "212.42.230.206"
|
35
|
+
end
|
36
|
+
assert cmt.spam?
|
37
37
|
end
|
38
38
|
|
39
39
|
def test_not_spam_but_rbl_lookup_succeeds
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
cmt = Comment.new do |c|
|
41
|
+
c.author = "Not a Spammer"
|
42
|
+
c.body = "Useful commentary!"
|
43
|
+
c.url = "http://www.bofh.org.uk"
|
44
|
+
c.ip = "10.10.10.10"
|
45
|
+
end
|
46
|
+
assert !cmt.spam?
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_reject_spam_pattern
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
50
|
+
cmt = Comment.new do |c|
|
51
|
+
c.author = "Another Spammer"
|
52
|
+
c.body = "Texas hold-em poker crap"
|
53
|
+
c.url = "http://texas.hold-em.us"
|
54
|
+
end
|
55
|
+
assert cmt.spam?
|
56
56
|
end
|
57
57
|
|
58
58
|
def test_reject_spam_uri_limit
|
59
|
-
c = Comment.new
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
c = Comment.new do |c|
|
60
|
+
c.author = "Yet Another Spammer"
|
61
|
+
c.body = %{ <a href="http://www.one.com/">one</a> <a href="http://www.two.com/">two</a> <a href="http://www.three.com/">three</a> <a href="http://www.four.com/">four</a> }
|
62
|
+
c.url = "http://www.uri-limit.com"
|
63
|
+
c.ip = "123.123.123.123"
|
64
|
+
end
|
64
65
|
|
65
|
-
|
66
|
+
assert c.spam?
|
66
67
|
end
|
67
68
|
|
68
69
|
def test_reject_article_age
|
69
|
-
c = Comment.new
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
c = Comment.new do |c|
|
71
|
+
c.author = "Old Spammer"
|
72
|
+
c.body = "Old trackback body"
|
73
|
+
c.article = contents(:inactive_article)
|
74
|
+
end
|
73
75
|
|
74
76
|
assert ! c.save
|
75
77
|
assert c.errors.invalid?('article_id')
|
@@ -93,10 +95,11 @@ class CommentTest < Test::Unit::TestCase
|
|
93
95
|
end
|
94
96
|
|
95
97
|
def test_xss_rejection
|
96
|
-
c = Comment.new
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
c = Comment.new do |c|
|
99
|
+
c.body = "Test foo <script>do_evil();</script>"
|
100
|
+
c.author = 'Bob'
|
101
|
+
c.article_id = 1
|
102
|
+
end
|
100
103
|
|
101
104
|
# Test each filter to make sure that we don't allow scripts through.
|
102
105
|
# Yes, this is ugly.
|
@@ -114,7 +117,26 @@ class CommentTest < Test::Unit::TestCase
|
|
114
117
|
c = Comment.find(contents(:comment2).id)
|
115
118
|
assert c.withdraw!
|
116
119
|
assert ! c.published?
|
117
|
-
assert c.
|
120
|
+
assert c.spam?
|
121
|
+
c.reload
|
118
122
|
assert ! c.published?
|
123
|
+
assert c.spam?
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_published
|
127
|
+
a = Article.new(:title => 'foo', :blog_id => 1)
|
128
|
+
assert a.save
|
129
|
+
|
130
|
+
assert_equal 0, a.published_comments.size
|
131
|
+
c = a.comments.build(:body => 'foo', :author => 'bob', :published => true, :published_at => Time.now)
|
132
|
+
assert c.save
|
133
|
+
assert c.published?
|
134
|
+
a.reload
|
135
|
+
|
136
|
+
assert_equal 1, a.published_comments.size
|
137
|
+
c.withdraw!
|
138
|
+
|
139
|
+
a = Article.new(:title => 'foo', :blog_id => 1)
|
140
|
+
assert_equal 0, a.published_comments.size
|
119
141
|
end
|
120
142
|
end
|
@@ -34,7 +34,7 @@ class ContentState::FactoryTest < Test::Unit::TestCase
|
|
34
34
|
def test_write_new_state
|
35
35
|
content = MockContent.new(true)
|
36
36
|
|
37
|
-
|
37
|
+
ContentState::New.instance.enter_hook(content)
|
38
38
|
|
39
39
|
assert_equal false, content.published?
|
40
40
|
assert_nil content.published_at
|
@@ -42,34 +42,35 @@ class ContentState::FactoryTest < Test::Unit::TestCase
|
|
42
42
|
|
43
43
|
def test_write_publication_pending_state
|
44
44
|
content = MockContent.new(true, true, 1.hour.from_now)
|
45
|
-
|
45
|
+
ContentState::PublicationPending.instance.enter_hook(content)
|
46
46
|
assert_equal false, content.published?
|
47
47
|
assert content.published_at > Time.now
|
48
48
|
end
|
49
49
|
|
50
50
|
def test_write_draft_state
|
51
51
|
content = MockContent.new(true, nil, nil)
|
52
|
-
|
52
|
+
ContentState::Draft.instance.enter_hook(content)
|
53
53
|
assert_equal false, content.published
|
54
54
|
assert_nil content.published_at
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_write_just_published_state
|
58
58
|
content = MockContent.new(true, nil, nil)
|
59
|
-
|
59
|
+
ContentState::JustPublished.instance.enter_hook(content)
|
60
60
|
assert_equal true, content.published
|
61
61
|
assert content.published_at <= Time.now
|
62
62
|
|
63
63
|
published_at = 10.minutes.ago
|
64
64
|
content = MockContent.new(true, nil, published_at)
|
65
|
-
|
65
|
+
ContentState::JustPublished.instance.enter_hook(content)
|
66
66
|
assert_equal true, content.published
|
67
67
|
assert_equal published_at, content.published_at
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_write_published_state
|
71
71
|
content = MockContent.new
|
72
|
-
assert ContentState::Published.instance.
|
72
|
+
assert ContentState::Published.instance.enter_hook(content)
|
73
|
+
assert_equal true, content.published
|
73
74
|
end
|
74
75
|
|
75
76
|
class MockContent < Struct.new(:new_record, :published, :published_at)
|
data/test/unit/ping_test.rb
CHANGED
@@ -33,6 +33,20 @@ class PingTest < Test::Unit::TestCase
|
|
33
33
|
assert_pingback_sent
|
34
34
|
end
|
35
35
|
|
36
|
+
def test_ping_sent_on_save
|
37
|
+
Net::HTTP.next_response = self
|
38
|
+
|
39
|
+
art = Blog.default.articles.build \
|
40
|
+
:body => %{<link rel="pingback" href="http://anotherblog.org/xml-rpc" />},
|
41
|
+
:title => 'Test the pinging',
|
42
|
+
:published => true
|
43
|
+
assert art.save
|
44
|
+
sent_ping = XMLRPC::Client.pings.last
|
45
|
+
assert !art.just_published?
|
46
|
+
art.reload
|
47
|
+
assert !art.just_published?
|
48
|
+
end
|
49
|
+
|
36
50
|
def assert_pingback_sent
|
37
51
|
Net::HTTP.next_response = self
|
38
52
|
ping = contents(:article1).pings.build("url" =>
|
data/test/unit/trackback_test.rb
CHANGED
@@ -18,26 +18,27 @@ class TrackbackTest < Test::Unit::TestCase
|
|
18
18
|
assert tb.save
|
19
19
|
assert tb.errors.empty?
|
20
20
|
assert tb.guid.size > 15
|
21
|
-
|
21
|
+
assert !tb.spam?
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_reject_spam_rbl
|
25
|
-
tb = Trackback.new
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
tb = Trackback.new do |tb|
|
26
|
+
tb.blog_name = "Spammer"
|
27
|
+
tb.title = "Spammy trackback"
|
28
|
+
tb.excerpt = %{This is just some random text. <a href="http://chinaaircatering.com">without any senses.</a>. Please disregard.}
|
29
|
+
tb.url = "http://buy-computer.us"
|
30
|
+
tb.ip = "212.42.230.206"
|
31
|
+
end
|
32
|
+
|
33
|
+
assert tb.spam?
|
33
34
|
end
|
34
35
|
|
35
36
|
def test_reject_spam_pattern
|
36
|
-
tb = Trackback.new
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
tb = Trackback.new do |tb|
|
38
|
+
tb.blog_name = "Another Spammer"
|
39
|
+
tb.title = "Spammy trackback"
|
40
|
+
tb.excerpt = "Texas hold-em poker crap"
|
41
|
+
end
|
42
|
+
assert tb.spam?
|
42
43
|
end
|
43
44
|
end
|