feed_manager_spec.rb 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. # frozen_string_literal: true
  2. require 'rails_helper'
  3. RSpec.describe FeedManager do
  4. before do |example|
  5. unless example.metadata[:skip_stub]
  6. stub_const 'FeedManager::MAX_ITEMS', 10
  7. stub_const 'FeedManager::REBLOG_FALLOFF', 4
  8. end
  9. end
  10. it 'tracks at least as many statuses as reblogs', skip_stub: true do
  11. expect(FeedManager::REBLOG_FALLOFF).to be <= FeedManager::MAX_ITEMS
  12. end
  13. describe '#key' do
  14. subject { described_class.instance.key(:home, 1) }
  15. it 'returns a string' do
  16. expect(subject).to be_a String
  17. end
  18. end
  19. describe '#filter?' do
  20. let(:alice) { Fabricate(:account, username: 'alice') }
  21. let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') }
  22. let(:jeff) { Fabricate(:account, username: 'jeff') }
  23. let(:list) { Fabricate(:list, account: alice) }
  24. context 'with home feed' do
  25. it 'returns false for followee\'s status' do
  26. status = Fabricate(:status, text: 'Hello world', account: alice)
  27. bob.follow!(alice)
  28. expect(described_class.instance.filter?(:home, status, bob)).to be false
  29. end
  30. it 'returns false for reblog by followee' do
  31. status = Fabricate(:status, text: 'Hello world', account: jeff)
  32. reblog = Fabricate(:status, reblog: status, account: alice)
  33. bob.follow!(alice)
  34. expect(described_class.instance.filter?(:home, reblog, bob)).to be false
  35. end
  36. it 'returns true for post from account who blocked me' do
  37. status = Fabricate(:status, text: 'Hello, World', account: alice)
  38. alice.block!(bob)
  39. expect(described_class.instance.filter?(:home, status, bob)).to be true
  40. end
  41. it 'returns true for post from blocked account' do
  42. status = Fabricate(:status, text: 'Hello, World', account: alice)
  43. bob.block!(alice)
  44. expect(described_class.instance.filter?(:home, status, bob)).to be true
  45. end
  46. it 'returns true for reblog by followee of blocked account' do
  47. status = Fabricate(:status, text: 'Hello world', account: jeff)
  48. reblog = Fabricate(:status, reblog: status, account: alice)
  49. bob.follow!(alice)
  50. bob.block!(jeff)
  51. expect(described_class.instance.filter?(:home, reblog, bob)).to be true
  52. end
  53. it 'returns true for reblog by followee of muted account' do
  54. status = Fabricate(:status, text: 'Hello world', account: jeff)
  55. reblog = Fabricate(:status, reblog: status, account: alice)
  56. bob.follow!(alice)
  57. bob.mute!(jeff)
  58. expect(described_class.instance.filter?(:home, reblog, bob)).to be true
  59. end
  60. it 'returns true for reblog by followee of someone who is blocking recipient' do
  61. status = Fabricate(:status, text: 'Hello world', account: jeff)
  62. reblog = Fabricate(:status, reblog: status, account: alice)
  63. bob.follow!(alice)
  64. jeff.block!(bob)
  65. expect(described_class.instance.filter?(:home, reblog, bob)).to be true
  66. end
  67. it 'returns true for reblog from account with reblogs disabled' do
  68. status = Fabricate(:status, text: 'Hello world', account: jeff)
  69. reblog = Fabricate(:status, reblog: status, account: alice)
  70. bob.follow!(alice, reblogs: false)
  71. expect(described_class.instance.filter?(:home, reblog, bob)).to be true
  72. end
  73. it 'returns false for reply by followee to another followee' do
  74. status = Fabricate(:status, text: 'Hello world', account: jeff)
  75. reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
  76. bob.follow!(alice)
  77. bob.follow!(jeff)
  78. expect(described_class.instance.filter?(:home, reply, bob)).to be false
  79. end
  80. it 'returns false for reply by followee to recipient' do
  81. status = Fabricate(:status, text: 'Hello world', account: bob)
  82. reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
  83. bob.follow!(alice)
  84. expect(described_class.instance.filter?(:home, reply, bob)).to be false
  85. end
  86. it 'returns false for reply by followee to self' do
  87. status = Fabricate(:status, text: 'Hello world', account: alice)
  88. reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
  89. bob.follow!(alice)
  90. expect(described_class.instance.filter?(:home, reply, bob)).to be false
  91. end
  92. it 'returns true for reply by followee to non-followed account' do
  93. status = Fabricate(:status, text: 'Hello world', account: jeff)
  94. reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
  95. bob.follow!(alice)
  96. expect(described_class.instance.filter?(:home, reply, bob)).to be true
  97. end
  98. it 'returns true for the second reply by followee to a non-federated status' do
  99. reply = Fabricate(:status, text: 'Reply 1', reply: true, account: alice)
  100. second_reply = Fabricate(:status, text: 'Reply 2', thread: reply, account: alice)
  101. bob.follow!(alice)
  102. expect(described_class.instance.filter?(:home, second_reply, bob)).to be true
  103. end
  104. it 'returns false for status by followee mentioning another account' do
  105. bob.follow!(alice)
  106. jeff.follow!(alice)
  107. status = PostStatusService.new.call(alice, text: 'Hey @jeff')
  108. expect(described_class.instance.filter?(:home, status, bob)).to be false
  109. end
  110. it 'returns true for status by followee mentioning blocked account' do
  111. bob.block!(jeff)
  112. bob.follow!(alice)
  113. status = PostStatusService.new.call(alice, text: 'Hey @jeff')
  114. expect(described_class.instance.filter?(:home, status, bob)).to be true
  115. end
  116. it 'returns true for reblog of a personally blocked domain' do
  117. alice.block_domain!('example.com')
  118. alice.follow!(jeff)
  119. status = Fabricate(:status, text: 'Hello world', account: bob)
  120. reblog = Fabricate(:status, reblog: status, account: jeff)
  121. expect(described_class.instance.filter?(:home, reblog, alice)).to be true
  122. end
  123. it 'returns true for German post when follow is set to English only' do
  124. alice.follow!(bob, languages: %w(en))
  125. status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
  126. expect(described_class.instance.filter?(:home, status, alice)).to be true
  127. end
  128. it 'returns false for German post when follow is set to German' do
  129. alice.follow!(bob, languages: %w(de))
  130. status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
  131. expect(described_class.instance.filter?(:home, status, alice)).to be false
  132. end
  133. it 'returns true for post from followee on exclusive list' do
  134. list.exclusive = true
  135. alice.follow!(bob)
  136. list.accounts << bob
  137. allow(List).to receive(:where).and_return(list)
  138. status = Fabricate(:status, text: 'I post a lot', account: bob)
  139. expect(described_class.instance.filter?(:home, status, alice)).to be true
  140. end
  141. it 'returns true for reblog from followee on exclusive list' do
  142. list.exclusive = true
  143. alice.follow!(jeff)
  144. list.accounts << jeff
  145. allow(List).to receive(:where).and_return(list)
  146. status = Fabricate(:status, text: 'I post a lot', account: bob)
  147. reblog = Fabricate(:status, reblog: status, account: jeff)
  148. expect(described_class.instance.filter?(:home, reblog, alice)).to be true
  149. end
  150. it 'returns false for post from followee on non-exclusive list' do
  151. list.exclusive = false
  152. alice.follow!(bob)
  153. list.accounts << bob
  154. status = Fabricate(:status, text: 'I post a lot', account: bob)
  155. expect(described_class.instance.filter?(:home, status, alice)).to be false
  156. end
  157. it 'returns false for reblog from followee on non-exclusive list' do
  158. list.exclusive = false
  159. alice.follow!(jeff)
  160. list.accounts << jeff
  161. status = Fabricate(:status, text: 'I post a lot', account: bob)
  162. reblog = Fabricate(:status, reblog: status, account: jeff)
  163. expect(described_class.instance.filter?(:home, reblog, alice)).to be false
  164. end
  165. end
  166. context 'with mentions feed' do
  167. it 'returns true for status that mentions blocked account' do
  168. bob.block!(jeff)
  169. status = PostStatusService.new.call(alice, text: 'Hey @jeff')
  170. expect(described_class.instance.filter?(:mentions, status, bob)).to be true
  171. end
  172. it 'returns true for status that replies to a blocked account' do
  173. status = Fabricate(:status, text: 'Hello world', account: jeff)
  174. reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
  175. bob.block!(jeff)
  176. expect(described_class.instance.filter?(:mentions, reply, bob)).to be true
  177. end
  178. it 'returns true for status by silenced account who recipient is not following' do
  179. status = Fabricate(:status, text: 'Hello world', account: alice)
  180. alice.silence!
  181. expect(described_class.instance.filter?(:mentions, status, bob)).to be true
  182. end
  183. it 'returns false for status by followed silenced account' do
  184. status = Fabricate(:status, text: 'Hello world', account: alice)
  185. alice.silence!
  186. bob.follow!(alice)
  187. expect(described_class.instance.filter?(:mentions, status, bob)).to be false
  188. end
  189. end
  190. end
  191. describe '#push_to_home' do
  192. it 'trims timelines if they will have more than FeedManager::MAX_ITEMS' do
  193. account = Fabricate(:account)
  194. status = Fabricate(:status)
  195. members = Array.new(FeedManager::MAX_ITEMS) { |count| [count, count] }
  196. redis.zadd("feed:home:#{account.id}", members)
  197. described_class.instance.push_to_home(account, status)
  198. expect(redis.zcard("feed:home:#{account.id}")).to eq FeedManager::MAX_ITEMS
  199. end
  200. context 'with reblogs' do
  201. it 'saves reblogs of unseen statuses' do
  202. account = Fabricate(:account)
  203. reblogged = Fabricate(:status)
  204. reblog = Fabricate(:status, reblog: reblogged)
  205. expect(described_class.instance.push_to_home(account, reblog)).to be true
  206. end
  207. it 'does not save a new reblog of a recent status' do
  208. account = Fabricate(:account)
  209. reblogged = Fabricate(:status)
  210. reblog = Fabricate(:status, reblog: reblogged)
  211. described_class.instance.push_to_home(account, reblogged)
  212. expect(described_class.instance.push_to_home(account, reblog)).to be false
  213. end
  214. it 'saves a new reblog of an old status' do
  215. account = Fabricate(:account)
  216. reblogged = Fabricate(:status)
  217. reblog = Fabricate(:status, reblog: reblogged)
  218. described_class.instance.push_to_home(account, reblogged)
  219. # Fill the feed with intervening statuses
  220. FeedManager::REBLOG_FALLOFF.times do
  221. described_class.instance.push_to_home(account, Fabricate(:status))
  222. end
  223. expect(described_class.instance.push_to_home(account, reblog)).to be true
  224. end
  225. it 'does not save a new reblog of a recently-reblogged status' do
  226. account = Fabricate(:account)
  227. reblogged = Fabricate(:status)
  228. reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
  229. # The first reblog will be accepted
  230. described_class.instance.push_to_home(account, reblogs.first)
  231. # The second reblog should be ignored
  232. expect(described_class.instance.push_to_home(account, reblogs.last)).to be false
  233. end
  234. it 'saves a new reblog of a recently-reblogged status when previous reblog has been deleted' do
  235. account = Fabricate(:account)
  236. reblogged = Fabricate(:status)
  237. old_reblog = Fabricate(:status, reblog: reblogged)
  238. # The first reblog should be accepted
  239. expect(described_class.instance.push_to_home(account, old_reblog)).to be true
  240. # The first reblog should be successfully removed
  241. expect(described_class.instance.unpush_from_home(account, old_reblog)).to be true
  242. reblog = Fabricate(:status, reblog: reblogged)
  243. # The second reblog should be accepted
  244. expect(described_class.instance.push_to_home(account, reblog)).to be true
  245. end
  246. it 'does not save a new reblog of a multiply-reblogged-then-unreblogged status' do
  247. account = Fabricate(:account)
  248. reblogged = Fabricate(:status)
  249. reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
  250. # Accept the reblogs
  251. described_class.instance.push_to_home(account, reblogs[0])
  252. described_class.instance.push_to_home(account, reblogs[1])
  253. # Unreblog the first one
  254. described_class.instance.unpush_from_home(account, reblogs[0])
  255. # The last reblog should still be ignored
  256. expect(described_class.instance.push_to_home(account, reblogs.last)).to be false
  257. end
  258. it 'saves a new reblog of a long-ago-reblogged status' do
  259. account = Fabricate(:account)
  260. reblogged = Fabricate(:status)
  261. reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
  262. # The first reblog will be accepted
  263. described_class.instance.push_to_home(account, reblogs.first)
  264. # Fill the feed with intervening statuses
  265. FeedManager::REBLOG_FALLOFF.times do
  266. described_class.instance.push_to_home(account, Fabricate(:status))
  267. end
  268. # The second reblog should also be accepted
  269. expect(described_class.instance.push_to_home(account, reblogs.last)).to be true
  270. end
  271. end
  272. it "does not push when the given status's reblog is already inserted" do
  273. account = Fabricate(:account)
  274. reblog = Fabricate(:status)
  275. status = Fabricate(:status, reblog: reblog)
  276. described_class.instance.push_to_home(account, status)
  277. expect(described_class.instance.push_to_home(account, reblog)).to be false
  278. end
  279. end
  280. describe '#push_to_list' do
  281. let(:owner) { Fabricate(:account, username: 'owner') }
  282. let(:alice) { Fabricate(:account, username: 'alice') }
  283. let(:bob) { Fabricate(:account, username: 'bob') }
  284. let(:eve) { Fabricate(:account, username: 'eve') }
  285. let(:list) { Fabricate(:list, account: owner) }
  286. before do
  287. owner.follow!(alice)
  288. owner.follow!(bob)
  289. owner.follow!(eve)
  290. list.accounts << alice
  291. list.accounts << bob
  292. end
  293. it "does not push when the given status's reblog is already inserted" do
  294. reblog = Fabricate(:status)
  295. status = Fabricate(:status, reblog: reblog)
  296. described_class.instance.push_to_list(list, status)
  297. expect(described_class.instance.push_to_list(list, reblog)).to be false
  298. end
  299. context 'when replies policy is set to no replies' do
  300. before do
  301. list.replies_policy = :none
  302. end
  303. it 'pushes statuses that are not replies' do
  304. status = Fabricate(:status, text: 'Hello world', account: bob)
  305. expect(described_class.instance.push_to_list(list, status)).to be true
  306. end
  307. it 'pushes statuses that are replies to list owner' do
  308. status = Fabricate(:status, text: 'Hello world', account: owner)
  309. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  310. expect(described_class.instance.push_to_list(list, reply)).to be true
  311. end
  312. it 'does not push replies to another member of the list' do
  313. status = Fabricate(:status, text: 'Hello world', account: alice)
  314. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  315. expect(described_class.instance.push_to_list(list, reply)).to be false
  316. end
  317. end
  318. context 'when replies policy is set to list-only replies' do
  319. before do
  320. list.replies_policy = :list
  321. end
  322. it 'pushes statuses that are not replies' do
  323. status = Fabricate(:status, text: 'Hello world', account: bob)
  324. expect(described_class.instance.push_to_list(list, status)).to be true
  325. end
  326. it 'pushes statuses that are replies to list owner' do
  327. status = Fabricate(:status, text: 'Hello world', account: owner)
  328. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  329. expect(described_class.instance.push_to_list(list, reply)).to be true
  330. end
  331. it 'pushes replies to another member of the list' do
  332. status = Fabricate(:status, text: 'Hello world', account: alice)
  333. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  334. expect(described_class.instance.push_to_list(list, reply)).to be true
  335. end
  336. it 'does not push replies to someone not a member of the list' do
  337. status = Fabricate(:status, text: 'Hello world', account: eve)
  338. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  339. expect(described_class.instance.push_to_list(list, reply)).to be false
  340. end
  341. end
  342. context 'when replies policy is set to any reply' do
  343. before do
  344. list.replies_policy = :followed
  345. end
  346. it 'pushes statuses that are not replies' do
  347. status = Fabricate(:status, text: 'Hello world', account: bob)
  348. expect(described_class.instance.push_to_list(list, status)).to be true
  349. end
  350. it 'pushes statuses that are replies to list owner' do
  351. status = Fabricate(:status, text: 'Hello world', account: owner)
  352. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  353. expect(described_class.instance.push_to_list(list, reply)).to be true
  354. end
  355. it 'pushes replies to another member of the list' do
  356. status = Fabricate(:status, text: 'Hello world', account: alice)
  357. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  358. expect(described_class.instance.push_to_list(list, reply)).to be true
  359. end
  360. it 'pushes replies to someone not a member of the list' do
  361. status = Fabricate(:status, text: 'Hello world', account: eve)
  362. reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
  363. expect(described_class.instance.push_to_list(list, reply)).to be true
  364. end
  365. end
  366. end
  367. describe '#merge_into_home' do
  368. it "does not push source account's statuses whose reblogs are already inserted" do
  369. account = Fabricate(:account, id: 0)
  370. reblog = Fabricate(:status)
  371. status = Fabricate(:status, reblog: reblog)
  372. described_class.instance.push_to_home(account, status)
  373. described_class.instance.merge_into_home(account, reblog.account)
  374. expect(redis.zscore('feed:home:0', reblog.id)).to be_nil
  375. end
  376. end
  377. describe '#unpush_from_home' do
  378. let(:receiver) { Fabricate(:account) }
  379. it 'leaves a reblogged status if original was on feed' do
  380. reblogged = Fabricate(:status)
  381. status = Fabricate(:status, reblog: reblogged)
  382. described_class.instance.push_to_home(receiver, reblogged)
  383. FeedManager::REBLOG_FALLOFF.times { described_class.instance.push_to_home(receiver, Fabricate(:status)) }
  384. described_class.instance.push_to_home(receiver, status)
  385. # The reblogging status should show up under normal conditions.
  386. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
  387. described_class.instance.unpush_from_home(receiver, status)
  388. # Restore original status
  389. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
  390. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(reblogged.id.to_s)
  391. end
  392. it 'removes a reblogged status if it was only reblogged once' do
  393. reblogged = Fabricate(:status)
  394. status = Fabricate(:status, reblog: reblogged)
  395. described_class.instance.push_to_home(receiver, status)
  396. # The reblogging status should show up under normal conditions.
  397. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [status.id.to_s]
  398. described_class.instance.unpush_from_home(receiver, status)
  399. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to be_empty
  400. end
  401. it 'leaves a multiply-reblogged status if another reblog was in feed' do
  402. reblogged = Fabricate(:status)
  403. reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
  404. reblogs.each do |reblog|
  405. described_class.instance.push_to_home(receiver, reblog)
  406. end
  407. # The reblogging status should show up under normal conditions.
  408. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.first.id.to_s]
  409. reblogs[0...-1].each do |reblog|
  410. described_class.instance.unpush_from_home(receiver, reblog)
  411. end
  412. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.last.id.to_s]
  413. end
  414. it 'sends push updates' do
  415. status = Fabricate(:status)
  416. described_class.instance.push_to_home(receiver, status)
  417. allow(redis).to receive_messages(publish: nil)
  418. described_class.instance.unpush_from_home(receiver, status)
  419. deletion = Oj.dump(event: :delete, payload: status.id.to_s)
  420. expect(redis).to have_received(:publish).with("timeline:#{receiver.id}", deletion)
  421. end
  422. end
  423. describe '#unmerge_tag_from_home' do
  424. let(:receiver) { Fabricate(:account) }
  425. let(:tag) { Fabricate(:tag) }
  426. it 'leaves a tagged status' do
  427. status = Fabricate(:status)
  428. status.tags << tag
  429. described_class.instance.push_to_home(receiver, status)
  430. described_class.instance.unmerge_tag_from_home(tag, receiver)
  431. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
  432. end
  433. it 'remains a tagged status written by receiver\'s followee' do
  434. followee = Fabricate(:account)
  435. receiver.follow!(followee)
  436. status = Fabricate(:status, account: followee)
  437. status.tags << tag
  438. described_class.instance.push_to_home(receiver, status)
  439. described_class.instance.unmerge_tag_from_home(tag, receiver)
  440. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
  441. end
  442. it 'remains a tagged status written by receiver' do
  443. status = Fabricate(:status, account: receiver)
  444. status.tags << tag
  445. described_class.instance.push_to_home(receiver, status)
  446. described_class.instance.unmerge_tag_from_home(tag, receiver)
  447. expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
  448. end
  449. end
  450. describe '#clear_from_home' do
  451. let(:account) { Fabricate(:account) }
  452. let(:followed_account) { Fabricate(:account) }
  453. let(:target_account) { Fabricate(:account) }
  454. let(:status_from_followed_account_first) { Fabricate(:status, account: followed_account) }
  455. let(:status_from_target_account) { Fabricate(:status, account: target_account) }
  456. let(:status_from_followed_account_mentions_target_account) { Fabricate(:status, account: followed_account, mentions: [Fabricate(:mention, account: target_account)]) }
  457. let(:status_mentions_target_account) { Fabricate(:status, mentions: [Fabricate(:mention, account: target_account)]) }
  458. let(:status_from_followed_account_reblogs_status_mentions_target_account) { Fabricate(:status, account: followed_account, reblog: status_mentions_target_account) }
  459. let(:status_from_followed_account_reblogs_status_from_target_account) { Fabricate(:status, account: followed_account, reblog: status_from_target_account) }
  460. let(:status_from_followed_account_next) { Fabricate(:status, account: followed_account) }
  461. before do
  462. [
  463. status_from_followed_account_first,
  464. status_from_followed_account_mentions_target_account,
  465. status_from_followed_account_reblogs_status_mentions_target_account,
  466. status_from_followed_account_reblogs_status_from_target_account,
  467. status_from_followed_account_next,
  468. ].each do |status|
  469. redis.zadd("feed:home:#{account.id}", status.id, status.id)
  470. end
  471. end
  472. it 'correctly cleans the home timeline' do
  473. described_class.instance.clear_from_home(account, target_account)
  474. expect(redis.zrange("feed:home:#{account.id}", 0, -1)).to eq [status_from_followed_account_first.id.to_s, status_from_followed_account_next.id.to_s]
  475. end
  476. end
  477. end