shep 0.1.0.pre.alpha0
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.
- checksums.yaml +7 -0
- data/Copyright.txt +8 -0
- data/LICENSE.txt +697 -0
- data/README.md +101 -0
- data/Rakefile +52 -0
- data/doc/Shep/Entity/Account.html +193 -0
- data/doc/Shep/Entity/Context.html +165 -0
- data/doc/Shep/Entity/CustomEmoji.html +171 -0
- data/doc/Shep/Entity/MediaAttachment.html +175 -0
- data/doc/Shep/Entity/Notification.html +171 -0
- data/doc/Shep/Entity/Status.html +217 -0
- data/doc/Shep/Entity/StatusSource.html +167 -0
- data/doc/Shep/Entity/Status_Application.html +167 -0
- data/doc/Shep/Entity/Status_Mention.html +169 -0
- data/doc/Shep/Entity/Status_Tag.html +165 -0
- data/doc/Shep/Entity.html +1457 -0
- data/doc/Shep/Error/Caller.html +147 -0
- data/doc/Shep/Error/Http.html +329 -0
- data/doc/Shep/Error/Remote.html +143 -0
- data/doc/Shep/Error/Server.html +147 -0
- data/doc/Shep/Error/Type.html +233 -0
- data/doc/Shep/Error.html +149 -0
- data/doc/Shep/Session.html +4094 -0
- data/doc/Shep.html +128 -0
- data/doc/_index.html +300 -0
- data/doc/class_list.html +51 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +58 -0
- data/doc/css/style.css +497 -0
- data/doc/file.README.html +159 -0
- data/doc/file_list.html +56 -0
- data/doc/frames.html +17 -0
- data/doc/index.html +300 -0
- data/doc/js/app.js +314 -0
- data/doc/js/full_list.js +216 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +387 -0
- data/doc/top-level-namespace.html +110 -0
- data/lib/shep/entities.rb +164 -0
- data/lib/shep/entity_base.rb +378 -0
- data/lib/shep/exceptions.rb +78 -0
- data/lib/shep/session.rb +970 -0
- data/lib/shep/typeboxes.rb +180 -0
- data/lib/shep.rb +22 -0
- data/run_rake_test.example.sh +46 -0
- data/shep.gemspec +28 -0
- data/spec/data/smallimg.jpg +0 -0
- data/spec/data/smallish.jpg +0 -0
- data/spec/entity_common.rb +120 -0
- data/spec/entity_t1_spec.rb +168 -0
- data/spec/entity_t2_spec.rb +123 -0
- data/spec/entity_t3_spec.rb +30 -0
- data/spec/json_objects/account.1.json +25 -0
- data/spec/json_objects/account.2.json +36 -0
- data/spec/json_objects/status.1.json +85 -0
- data/spec/json_objects/status.2.json +59 -0
- data/spec/json_objects/status.3.json +95 -0
- data/spec/json_objects/status.4.json +95 -0
- data/spec/json_objects/status.5.json +74 -0
- data/spec/json_objects/status.6.json +140 -0
- data/spec/json_objects/status.7.json +84 -0
- data/spec/session_reader_1_unauth_spec.rb +366 -0
- data/spec/session_reader_2_auth_spec.rb +96 -0
- data/spec/session_writer_spec.rb +183 -0
- data/spec/spec_helper.rb +73 -0
- data/yard_helper.rb +30 -0
- metadata +154 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
{
|
2
|
+
"id": "110407677589865707",
|
3
|
+
"created_at": "2023-05-21T16:44:23.237Z",
|
4
|
+
"in_reply_to_id": null,
|
5
|
+
"in_reply_to_account_id": null,
|
6
|
+
"sensitive": false,
|
7
|
+
"spoiler_text": "",
|
8
|
+
"visibility": "unlisted",
|
9
|
+
"language": "en",
|
10
|
+
"uri": "https://botsin.space/users/testingtesting123/statuses/110407677589865707",
|
11
|
+
"url": "https://botsin.space/@testingtesting123/110407677589865707",
|
12
|
+
"replies_count": 0,
|
13
|
+
"reblogs_count": 0,
|
14
|
+
"favourites_count": 0,
|
15
|
+
"edited_at": null,
|
16
|
+
"favourited": false,
|
17
|
+
"reblogged": false,
|
18
|
+
"muted": false,
|
19
|
+
"bookmarked": false,
|
20
|
+
"pinned": false,
|
21
|
+
"content": "<p>This is a test</p><p><a href=\"https://botsin.space/tags/with\" class=\"mention hashtag\" rel=\"tag\">#<span>with</span></a> <a href=\"https://botsin.space/tags/some\" class=\"mention hashtag\" rel=\"tag\">#<span>some</span></a> <a href=\"https://botsin.space/tags/tags\" class=\"mention hashtag\" rel=\"tag\">#<span>tags</span></a> <a href=\"https://botsin.space/tags/for\" class=\"mention hashtag\" rel=\"tag\">#<span>for</span></a> <a href=\"https://botsin.space/tags/no\" class=\"mention hashtag\" rel=\"tag\">#<span>no</span></a> <a href=\"https://botsin.space/tags/reason\" class=\"mention hashtag\" rel=\"tag\">#<span>reason</span></a></p>",
|
22
|
+
"filtered": [],
|
23
|
+
"reblog": null,
|
24
|
+
"application": {
|
25
|
+
"name": "Web",
|
26
|
+
"website": null
|
27
|
+
},
|
28
|
+
"account": {
|
29
|
+
"id": "22411",
|
30
|
+
"username": "testingtesting123",
|
31
|
+
"acct": "testingtesting123",
|
32
|
+
"display_name": "scratchmonkey_15310",
|
33
|
+
"locked": false,
|
34
|
+
"bot": false,
|
35
|
+
"discoverable": null,
|
36
|
+
"group": false,
|
37
|
+
"created_at": "2018-01-20T00:00:00.000Z",
|
38
|
+
"note": "<p>Account used for bot testing; nothing to see here.</p><p><span class=\"h-card\"><a href=\"https://i.write.codethat.sucks/@suetanvil\" class=\"u-url mention\">@<span>suetanvil</span></a></span><br /><span class=\"h-card\"><a href=\"https://botsin.space/@benoitmandelbot\" class=\"u-url mention\">@<span>benoitmandelbot</span></a></span></p>",
|
39
|
+
"url": "https://botsin.space/@testingtesting123",
|
40
|
+
"avatar": "https://files.botsin.space/accounts/avatars/000/022/411/original/a701d93a6a1d0497.gif",
|
41
|
+
"avatar_static": "https://files.botsin.space/accounts/avatars/000/022/411/static/a701d93a6a1d0497.png",
|
42
|
+
"header": "https://botsin.space/headers/original/missing.png",
|
43
|
+
"header_static": "https://botsin.space/headers/original/missing.png",
|
44
|
+
"followers_count": 2,
|
45
|
+
"following_count": 3,
|
46
|
+
"statuses_count": 8,
|
47
|
+
"last_status_at": "2023-05-21",
|
48
|
+
"noindex": true,
|
49
|
+
"emojis": [],
|
50
|
+
"roles": [],
|
51
|
+
"fields": []
|
52
|
+
},
|
53
|
+
"media_attachments": [],
|
54
|
+
"mentions": [],
|
55
|
+
"tags": [
|
56
|
+
{
|
57
|
+
"name": "with",
|
58
|
+
"url": "https://botsin.space/tags/with"
|
59
|
+
},
|
60
|
+
{
|
61
|
+
"name": "some",
|
62
|
+
"url": "https://botsin.space/tags/some"
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"name": "tags",
|
66
|
+
"url": "https://botsin.space/tags/tags"
|
67
|
+
},
|
68
|
+
{
|
69
|
+
"name": "for",
|
70
|
+
"url": "https://botsin.space/tags/for"
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"name": "no",
|
74
|
+
"url": "https://botsin.space/tags/no"
|
75
|
+
},
|
76
|
+
{
|
77
|
+
"name": "reason",
|
78
|
+
"url": "https://botsin.space/tags/reason"
|
79
|
+
}
|
80
|
+
],
|
81
|
+
"emojis": [],
|
82
|
+
"card": null,
|
83
|
+
"poll": null
|
84
|
+
}
|
@@ -0,0 +1,366 @@
|
|
1
|
+
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
require_relative '../lib/shep'
|
4
|
+
|
5
|
+
include Shep
|
6
|
+
|
7
|
+
|
8
|
+
describe Session, :anonymous do
|
9
|
+
include_context :uses_temp_dir
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
it "retrieves account information." do
|
14
|
+
ss = Session.new(host: HOST)
|
15
|
+
acct = ss.fetch_account(READ_ACCOUNT)
|
16
|
+
|
17
|
+
expect( acct.class ) .to be Entity::Account
|
18
|
+
expect( acct.id ) .to eq READ_ACCOUNT
|
19
|
+
|
20
|
+
%i{username display_name locked bot created_at followers_count
|
21
|
+
following_count} .each {|fld|
|
22
|
+
expect( acct[fld] ) .to_not eq nil
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "keeps track of rate limit information." do
|
27
|
+
ss = Session.new(host: HOST)
|
28
|
+
acct = ss.fetch_account(READ_ACCOUNT)
|
29
|
+
|
30
|
+
expect( ss.rate_limit.remaining ) .to be <= ss.rate_limit.limit
|
31
|
+
|
32
|
+
# Race condition, but unlikely to be a problem
|
33
|
+
expect( ss.rate_limit.reset ) .to be > Time.now
|
34
|
+
end
|
35
|
+
|
36
|
+
it "retrieves followers and following accounts." do
|
37
|
+
ss = Session.new(host: HOST)
|
38
|
+
|
39
|
+
following = ss.each_following(READ_ACCOUNT, limit: 10).to_a
|
40
|
+
expect( following.size ) .to be <= 10
|
41
|
+
expect( following[0].class ) .to be Entity::Account
|
42
|
+
following = nil
|
43
|
+
|
44
|
+
followers = ss.each_follower(READ_ACCOUNT, limit: 10).to_a
|
45
|
+
expect( followers.size ) .to be <= 10
|
46
|
+
expect( followers[0].class ) .to be Entity::Account
|
47
|
+
end
|
48
|
+
|
49
|
+
it "retrieves statuses." do
|
50
|
+
ss = Session.new(host: HOST)
|
51
|
+
|
52
|
+
sts_all = ss.each_status(READ_ACCOUNT, limit: 50).to_a
|
53
|
+
expect( sts_all.size ) .to be > 0
|
54
|
+
|
55
|
+
# This is not complete, alas. The test account doesn't have many
|
56
|
+
# interactions so stuff like exclude_replies doesn't really change stuff.
|
57
|
+
sts_orig = ss.each_status(READ_ACCOUNT, limit: 50,
|
58
|
+
exclude_replies: true,
|
59
|
+
exclude_reblogs: true
|
60
|
+
).to_a
|
61
|
+
expect( sts_orig.size ) .to be > 0
|
62
|
+
#expect( sts_orig ) .to_not eq sts_all
|
63
|
+
|
64
|
+
|
65
|
+
sts_pinned = ss.each_status(READ_ACCOUNT, limit: 50,
|
66
|
+
pinned: true,
|
67
|
+
).to_a
|
68
|
+
expect( sts_pinned.size ) .to be > 0
|
69
|
+
expect( sts_pinned ) .to_not eq sts_all
|
70
|
+
|
71
|
+
# Retrieves statuses when no limit: value is set
|
72
|
+
count = 0
|
73
|
+
ss.each_status(READ_ACCOUNT) {|stat| count += 1; break if count > 41 }
|
74
|
+
expect( count ) .to be > 41
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
it "does more account stuff." do
|
79
|
+
ss = Session.new(host: HOST)
|
80
|
+
|
81
|
+
acct1 = ss.fetch_account_by_username(READ_ACCOUNT_HANDLE)
|
82
|
+
acct2 = ss.fetch_account(acct1.id)
|
83
|
+
expect( acct1 ) .to eq acct2
|
84
|
+
end
|
85
|
+
|
86
|
+
it "fetches individual statuses." do
|
87
|
+
ss = Session.new(host: HOST)
|
88
|
+
|
89
|
+
count = 0
|
90
|
+
ss.each_status(READ_ACCOUNT, limit: 5) {|ref_status|
|
91
|
+
new_status = ss.fetch_status(ref_status.id)
|
92
|
+
expect(new_status) .to eq ref_status
|
93
|
+
count += 1
|
94
|
+
}
|
95
|
+
expect(count) .to be > 0
|
96
|
+
end
|
97
|
+
|
98
|
+
it "leaves optional session fields nil if no token is given." do
|
99
|
+
ss = Session.new(host: HOST)
|
100
|
+
statuses = ss.each_status(READ_ACCOUNT, limit: 1).to_a
|
101
|
+
|
102
|
+
expect( statuses.size ) .to be > 0
|
103
|
+
status = statuses[0]
|
104
|
+
|
105
|
+
expect( status.favourited ) .to be nil
|
106
|
+
expect( status.reblogged ) .to be nil
|
107
|
+
expect( status.muted ) .to be nil
|
108
|
+
expect( status.bookmarked ) .to be nil
|
109
|
+
expect( status.pinned ) .to be nil
|
110
|
+
end
|
111
|
+
|
112
|
+
it "fetches status contexts." do
|
113
|
+
ss = Session.new(host: HOST)
|
114
|
+
|
115
|
+
# Search for the first status to have a reply
|
116
|
+
match_status = nil
|
117
|
+
match_context = nil
|
118
|
+
count = 0
|
119
|
+
ss.each_status(READ_ACCOUNT, limit: 500) {|ref_status|
|
120
|
+
count += 1
|
121
|
+
context = ss.fetch_context(ref_status.id)
|
122
|
+
unless context.descendants.empty?
|
123
|
+
match_status = ref_status
|
124
|
+
match_context = context
|
125
|
+
break
|
126
|
+
end
|
127
|
+
}
|
128
|
+
expect( count ) .to be > 0
|
129
|
+
expect( !!match_status && !!match_context ) .to be true
|
130
|
+
|
131
|
+
# Now fetch its descendent and the descendent's ancestors to
|
132
|
+
# confirm that the one we found is in it.
|
133
|
+
kid = ss.fetch_status(match_context.descendants[0].id)
|
134
|
+
kid_ancestors = ss.fetch_context(kid.id).ancestors
|
135
|
+
|
136
|
+
expect( kid_ancestors.map{|a| a.id} ) .to include(match_status.id)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "fetches accounts that boosted and/or favourited a status." do
|
140
|
+
ss = Session.new(host: HOST)
|
141
|
+
|
142
|
+
status_count = 0
|
143
|
+
boosted_found = 0
|
144
|
+
boosters_found = 0
|
145
|
+
faved_found = 0
|
146
|
+
favers_found = 0
|
147
|
+
ss.each_status(READ_ACCOUNT, limit: 500) do |status|
|
148
|
+
status_count += 1
|
149
|
+
|
150
|
+
if status.reblogs_count > 0
|
151
|
+
boosted_found += 1
|
152
|
+
boosts = ss.each_boost_acct(status.id, limit: 10).to_a
|
153
|
+
boosters_found += boosts.size
|
154
|
+
end
|
155
|
+
|
156
|
+
if status.favourites_count > 0
|
157
|
+
faved_found += 1
|
158
|
+
faves = ss.each_fave_acct(status.id, limit: 10).to_a
|
159
|
+
favers_found += faves.size
|
160
|
+
end
|
161
|
+
|
162
|
+
break if favers_found > 0 && boosters_found > 0
|
163
|
+
end
|
164
|
+
|
165
|
+
puts "\nfaves/boosts: searched #{status_count} statuses."
|
166
|
+
|
167
|
+
# This will be false if there are no favourited or no boosted
|
168
|
+
# statuses in the searched range of stats. That's not necessarily
|
169
|
+
# a bug here, but it's hard to test without assuming this so we
|
170
|
+
# test for it.
|
171
|
+
expect( faved_found > 0 && boosters_found > 0 ) .to be true
|
172
|
+
|
173
|
+
# Ditto for these. Depends on account visibility (I *think*)
|
174
|
+
expect( favers_found ) .to be > 0
|
175
|
+
expect( boosters_found ) .to be > 0
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
it "retrieves groups of items via block as well as enumerator." do
|
180
|
+
ss = Session.new(host: HOST)
|
181
|
+
|
182
|
+
count = 0
|
183
|
+
ss.each_following(READ_ACCOUNT, limit: 10) { count += 1 }
|
184
|
+
expect( count ) .to be > 0
|
185
|
+
|
186
|
+
count = 0
|
187
|
+
ss.each_follower(READ_ACCOUNT, limit: 10) { count += 1 }
|
188
|
+
expect( count ) .to be > 0
|
189
|
+
|
190
|
+
count = 0
|
191
|
+
ss.each_status(READ_ACCOUNT, limit: 10) { count += 1 }
|
192
|
+
expect( count ) .to be > 0
|
193
|
+
end
|
194
|
+
|
195
|
+
# TODO: move to own describe block.
|
196
|
+
it "retrieves status' media attachments." do
|
197
|
+
ss = Session.new(host: HOST)
|
198
|
+
|
199
|
+
# Find the earliest status with one or more media attachments
|
200
|
+
candidate = nil
|
201
|
+
ss.each_status(READ_ACCOUNT, limit: 500) { |stat|
|
202
|
+
if stat.media_attachments.size > 0
|
203
|
+
candidate = stat
|
204
|
+
break
|
205
|
+
end
|
206
|
+
}
|
207
|
+
|
208
|
+
# This can happen if there are no statuses with media attachments
|
209
|
+
# in READ_ACCOUNT, but in that case we can't test, so
|
210
|
+
# :shrug_emoji:
|
211
|
+
expect( candidate ) .to_not be nil
|
212
|
+
|
213
|
+
status, files = ss.fetch_status_with_media(candidate.id, tmp_dir)
|
214
|
+
expect( status ) .to eq candidate # BTW
|
215
|
+
expect( files.empty? ) .to be false
|
216
|
+
|
217
|
+
file0 = files.values[0]
|
218
|
+
expect( file0 ) .to_not be nil
|
219
|
+
|
220
|
+
# Ensure the file is present an non-empty. (Technically, it
|
221
|
+
# *could* be empty, but :shrug_emoji: as above).
|
222
|
+
expect( File.exist?( file0 ) ) .to be true
|
223
|
+
fsz = File.size( file0 )
|
224
|
+
expect( fsz ) .to be > 0
|
225
|
+
|
226
|
+
# Test refetch: false; ensure it doesn't overwrite if the file is
|
227
|
+
# present.
|
228
|
+
File.open(file0, "w") {|fh| } # truncate the file
|
229
|
+
expect( File.size( file0 ) ) .to eq 0
|
230
|
+
|
231
|
+
_, files2 = ss.fetch_status_with_media(candidate.id, tmp_dir, refetch: false)
|
232
|
+
expect( files2) .to eq files
|
233
|
+
|
234
|
+
# No overwrite
|
235
|
+
expect( File.size( file0 ) ) .to eq 0
|
236
|
+
|
237
|
+
# Now, ensure that refetch:false *does* overwrite the file
|
238
|
+
_, files3 = ss.fetch_status_with_media(candidate.id, tmp_dir, refetch: true)
|
239
|
+
expect( files3) .to eq files
|
240
|
+
expect( File.size( file0 ) ) .to eq fsz
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
it "retrieves the site's public timeline." do
|
245
|
+
ss = Session.new(host: HOST)
|
246
|
+
|
247
|
+
caturday = "caturday"
|
248
|
+
|
249
|
+
# Find the latest 100 toots. This assumes an average distribution
|
250
|
+
# that may not happen on the actual live server, in which case
|
251
|
+
# this test will fail for no good reason.
|
252
|
+
default_tl = ss.each_public_status(limit: 200).to_a
|
253
|
+
|
254
|
+
# Some toots are federated
|
255
|
+
expect( default_tl.select{|st| st.account.acct.include? '@'}.empty? )
|
256
|
+
.to be false
|
257
|
+
|
258
|
+
# Some toots are local
|
259
|
+
expect( default_tl.reject{|st| st.account.acct.include? '@'}.empty? )
|
260
|
+
.to be false
|
261
|
+
|
262
|
+
# Some toots contain media
|
263
|
+
expect( default_tl.reject{|st| st.media_attachments.empty? }.empty? )
|
264
|
+
.to be false
|
265
|
+
|
266
|
+
# All statuses should contain media when selecting only_media
|
267
|
+
media_tl = ss.each_public_status(limit: 20, only_media: true).to_a;
|
268
|
+
expect( media_tl.select{|st| st.media_attachments.empty?} )
|
269
|
+
.to eq []
|
270
|
+
|
271
|
+
# local:true should return no toots from a remote site
|
272
|
+
local_tl = ss.each_public_status(limit: 20, local: true).to_a;
|
273
|
+
expect( local_tl.select{|st| st.account.acct.include? '@' } )
|
274
|
+
.to eq []
|
275
|
+
|
276
|
+
# remote:true should return no toots from the local site
|
277
|
+
remote_tl = ss.each_public_status(limit: 20, remote: true).to_a;
|
278
|
+
expect( remote_tl.reject{|st| st.account.acct.include? '@' } )
|
279
|
+
.to eq []
|
280
|
+
end
|
281
|
+
|
282
|
+
it "retrieves posts by tag." do
|
283
|
+
caturday = "caturday"
|
284
|
+
|
285
|
+
ss = Session.new(host: HOST)
|
286
|
+
|
287
|
+
# Find the latest 100 toots. This assumes an average distribution
|
288
|
+
# that may not happen on the actual live server, in which case
|
289
|
+
# this test will fail for no good reason.
|
290
|
+
default_tl = ss.each_tag_status(caturday, limit: 200).to_a
|
291
|
+
|
292
|
+
# Some toots are federated
|
293
|
+
expect( default_tl.select{|st| st.account.acct.include? '@'}.empty? )
|
294
|
+
.to be false
|
295
|
+
|
296
|
+
# Some toots are local
|
297
|
+
expect( default_tl.reject{|st| st.account.acct.include? '@'}.empty? )
|
298
|
+
.to be false
|
299
|
+
|
300
|
+
# Some toots contain media
|
301
|
+
expect( default_tl.reject{|st| st.media_attachments.empty? }.empty? )
|
302
|
+
.to be false
|
303
|
+
|
304
|
+
# All statuses should contain media when selecting only_media
|
305
|
+
media_tl = ss.each_tag_status(caturday, limit: 20, only_media: true)
|
306
|
+
.to_a
|
307
|
+
expect( media_tl.select{|st| st.media_attachments.empty?} )
|
308
|
+
.to eq []
|
309
|
+
|
310
|
+
# local:true should return no toots from a remote site
|
311
|
+
local_tl = ss.each_tag_status(caturday, limit: 20, local: true).to_a;
|
312
|
+
expect( local_tl.select{|st| st.account.acct.include? '@' } )
|
313
|
+
.to eq []
|
314
|
+
|
315
|
+
# remote:true should return no toots from the local site
|
316
|
+
remote_tl = ss.each_tag_status("caturday", limit: 20, remote: true).to_a
|
317
|
+
expect( remote_tl.reject{|st| st.account.acct.include? '@' } )
|
318
|
+
.to eq []
|
319
|
+
|
320
|
+
|
321
|
+
#
|
322
|
+
# any/all/none
|
323
|
+
#
|
324
|
+
|
325
|
+
# Expect all of the extra tags
|
326
|
+
plus_all = ["cats", "catsofmastodon"]
|
327
|
+
remote_tl = ss.each_tag_status(caturday,
|
328
|
+
limit: 20,
|
329
|
+
all: plus_all
|
330
|
+
).to_a
|
331
|
+
remote_tl.empty? and
|
332
|
+
puts "WARNING: found no posts with tags #{[caturday] + plus_all}"
|
333
|
+
|
334
|
+
remote_tl.each{|rstat|
|
335
|
+
found_tags = rstat.tags.map{|t| t.name.downcase}.to_set
|
336
|
+
expect( found_tags ) .to be >= plus_all.to_set
|
337
|
+
}
|
338
|
+
|
339
|
+
|
340
|
+
# Expect none of the extra tags
|
341
|
+
plus_none = ["cats", "catsofmastodon"]
|
342
|
+
remote_tl = ss.each_tag_status(caturday,
|
343
|
+
limit: 20,
|
344
|
+
none: plus_none
|
345
|
+
).to_a
|
346
|
+
remote_tl.empty? and
|
347
|
+
puts"WARNING: found no posts w/ #{caturday} but not #{plus_none}"
|
348
|
+
|
349
|
+
remote_tl.each{|rstat|
|
350
|
+
found_tags = rstat.tags.map{|t| t.name.downcase}.to_set
|
351
|
+
expect( found_tags & plus_none ) .to be_empty
|
352
|
+
}
|
353
|
+
|
354
|
+
# Expect any of the following
|
355
|
+
plus_any = [caturday, "cats", "catsofmastodon"]
|
356
|
+
remote_tl = ss.each_tag_status(plus_any, limit: 20).to_a
|
357
|
+
remote_tl.empty? and
|
358
|
+
puts"WARNING: found no posts w/ #{caturday} but some of #{plus_any}"
|
359
|
+
|
360
|
+
expected = plus_any + [caturday]
|
361
|
+
remote_tl.each{|rstat|
|
362
|
+
found_tags = rstat.tags.map{|t| t.name.downcase}.to_set
|
363
|
+
expect( found_tags & expected ) .to_not be_empty
|
364
|
+
}
|
365
|
+
end
|
366
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
# Tests requiring authentication
|
3
|
+
|
4
|
+
require_relative 'spec_helper'
|
5
|
+
require_relative '../lib/shep'
|
6
|
+
|
7
|
+
include Shep
|
8
|
+
|
9
|
+
describe Session, :account do
|
10
|
+
it "fetches status sources." do
|
11
|
+
ss = Session.new(host: HOST, token: TOKEN)
|
12
|
+
statuses = ss.each_status(WRITE_ACCOUNT, limit: 1).to_a
|
13
|
+
expect( statuses.empty? ) .to be false
|
14
|
+
stat0 = statuses[0]
|
15
|
+
|
16
|
+
src = ss.fetch_status_src(stat0.id)
|
17
|
+
|
18
|
+
words = src.text.split
|
19
|
+
puts "Couldn't find a word in Entity::StatusSource" if words.empty?
|
20
|
+
expect( words.empty? || stat0.content.include?(words[0]))
|
21
|
+
.to be true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "reads the home timeline" do
|
25
|
+
ss = Session.new(host: HOST, token: TOKEN)
|
26
|
+
statuses = ss.each_home_status(limit: 10).to_a
|
27
|
+
expect( statuses.size ) .to be > 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it "reads notifications" do
|
31
|
+
ss = Session.new(host: HOST, token: TOKEN)
|
32
|
+
|
33
|
+
ntfns = ss.each_notification(limit: 10).to_a
|
34
|
+
expect( ntfns.size ) .to be > 0
|
35
|
+
|
36
|
+
ntfns.each{|ntfn|
|
37
|
+
ntfn2 = ss.fetch_notification(ntfn.id)
|
38
|
+
expect( ntfn2 ) .to eq ntfn
|
39
|
+
}
|
40
|
+
first = ntfns[0]
|
41
|
+
|
42
|
+
# Test 'types' filter
|
43
|
+
proc {
|
44
|
+
ntfns2 = ss
|
45
|
+
.each_notification(limit: 10, types: [first.type])
|
46
|
+
.to_a
|
47
|
+
|
48
|
+
expect( ntfns2[0] ) .to eq first
|
49
|
+
|
50
|
+
expect( ntfns2.select{|n| n.type == first.type} ) .to eq ntfns2
|
51
|
+
}.call
|
52
|
+
|
53
|
+
# Test 'exclude_types' filter
|
54
|
+
proc {
|
55
|
+
ntfns2 = ss
|
56
|
+
.each_notification(limit: 10, exclude_types: [first.type])
|
57
|
+
.to_a
|
58
|
+
expect( ntfns2[0] ) .to_not eq first
|
59
|
+
expect( ntfns2.map{|n| n.type}.include?(first.type) ) .to be false
|
60
|
+
}.call
|
61
|
+
|
62
|
+
# Test 'account_id' filter
|
63
|
+
proc {
|
64
|
+
ntfns2 = ss
|
65
|
+
.each_notification(limit: 10, account_id: first.account.id)
|
66
|
+
.to_a
|
67
|
+
|
68
|
+
expect( ntfns2[0] ) .to eq first
|
69
|
+
|
70
|
+
expect( ntfns2.select{|n| n.account.id == first.account.id} )
|
71
|
+
.to eq ntfns2
|
72
|
+
}.call
|
73
|
+
end
|
74
|
+
|
75
|
+
skip "dismisses notifications" do
|
76
|
+
ss = Session.new(host: HOST, token: TOKEN)
|
77
|
+
|
78
|
+
# We dismiss the first notification, then confirm that it is no
|
79
|
+
# longer there. Since this is not repeatable, we skip it by
|
80
|
+
# default and manually enable it when we want to test it.
|
81
|
+
|
82
|
+
ntfns = ss.each_notification(limit: 10).to_a
|
83
|
+
expect( ntfns.size ) .to be > 0
|
84
|
+
first = ntfns[0]
|
85
|
+
|
86
|
+
ss.dismiss_notification(first.id)
|
87
|
+
|
88
|
+
ntfns2 = ss.each_notification(limit: 10).to_a
|
89
|
+
expect( ntfns2.size ) .to eq ntfns.size - 1
|
90
|
+
expect( ntfns2[0] ) .to_not eq first
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# skip "" do ; end
|
95
|
+
|
96
|
+
end
|