1
0

feed_manager_spec.rb 23 KB

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