account_statuses_cleanup_policy_spec.rb 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. # frozen_string_literal: true
  2. require 'rails_helper'
  3. RSpec.describe AccountStatusesCleanupPolicy do
  4. let(:account) { Fabricate(:account, username: 'alice', domain: nil) }
  5. describe 'validation' do
  6. it 'disallow remote accounts' do
  7. account.update(domain: 'example.com')
  8. account_statuses_cleanup_policy = Fabricate.build(:account_statuses_cleanup_policy, account: account)
  9. account_statuses_cleanup_policy.valid?
  10. expect(account_statuses_cleanup_policy).to model_have_error_on_field(:account)
  11. end
  12. end
  13. describe 'save hooks' do
  14. context 'when widening a policy' do
  15. let!(:account_statuses_cleanup_policy) do
  16. Fabricate(:account_statuses_cleanup_policy,
  17. account: account,
  18. keep_direct: true,
  19. keep_pinned: true,
  20. keep_polls: true,
  21. keep_media: true,
  22. keep_self_fav: true,
  23. keep_self_bookmark: true,
  24. min_favs: 1,
  25. min_reblogs: 1)
  26. end
  27. before do
  28. account_statuses_cleanup_policy.record_last_inspected(42)
  29. end
  30. it 'invalidates last_inspected when widened because of keep_direct' do
  31. account_statuses_cleanup_policy.keep_direct = false
  32. account_statuses_cleanup_policy.save
  33. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  34. end
  35. it 'invalidates last_inspected when widened because of keep_pinned' do
  36. account_statuses_cleanup_policy.keep_pinned = false
  37. account_statuses_cleanup_policy.save
  38. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  39. end
  40. it 'invalidates last_inspected when widened because of keep_polls' do
  41. account_statuses_cleanup_policy.keep_polls = false
  42. account_statuses_cleanup_policy.save
  43. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  44. end
  45. it 'invalidates last_inspected when widened because of keep_media' do
  46. account_statuses_cleanup_policy.keep_media = false
  47. account_statuses_cleanup_policy.save
  48. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  49. end
  50. it 'invalidates last_inspected when widened because of keep_self_fav' do
  51. account_statuses_cleanup_policy.keep_self_fav = false
  52. account_statuses_cleanup_policy.save
  53. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  54. end
  55. it 'invalidates last_inspected when widened because of keep_self_bookmark' do
  56. account_statuses_cleanup_policy.keep_self_bookmark = false
  57. account_statuses_cleanup_policy.save
  58. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  59. end
  60. it 'invalidates last_inspected when widened because of higher min_favs' do
  61. account_statuses_cleanup_policy.min_favs = 5
  62. account_statuses_cleanup_policy.save
  63. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  64. end
  65. it 'invalidates last_inspected when widened because of disabled min_favs' do
  66. account_statuses_cleanup_policy.min_favs = nil
  67. account_statuses_cleanup_policy.save
  68. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  69. end
  70. it 'invalidates last_inspected when widened because of higher min_reblogs' do
  71. account_statuses_cleanup_policy.min_reblogs = 5
  72. account_statuses_cleanup_policy.save
  73. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  74. end
  75. it 'invalidates last_inspected when widened because of disable min_reblogs' do
  76. account_statuses_cleanup_policy.min_reblogs = nil
  77. account_statuses_cleanup_policy.save
  78. expect(account_statuses_cleanup_policy.last_inspected).to be_nil
  79. end
  80. end
  81. context 'when narrowing a policy' do
  82. let!(:account_statuses_cleanup_policy) do
  83. Fabricate(:account_statuses_cleanup_policy,
  84. account: account,
  85. keep_direct: false,
  86. keep_pinned: false,
  87. keep_polls: false,
  88. keep_media: false,
  89. keep_self_fav: false,
  90. keep_self_bookmark: false,
  91. min_favs: nil,
  92. min_reblogs: nil)
  93. end
  94. it 'does not unnecessarily invalidate last_inspected' do
  95. account_statuses_cleanup_policy.record_last_inspected(42)
  96. account_statuses_cleanup_policy.keep_direct = true
  97. account_statuses_cleanup_policy.keep_pinned = true
  98. account_statuses_cleanup_policy.keep_polls = true
  99. account_statuses_cleanup_policy.keep_media = true
  100. account_statuses_cleanup_policy.keep_self_fav = true
  101. account_statuses_cleanup_policy.keep_self_bookmark = true
  102. account_statuses_cleanup_policy.min_favs = 5
  103. account_statuses_cleanup_policy.min_reblogs = 5
  104. account_statuses_cleanup_policy.save
  105. expect(account_statuses_cleanup_policy.last_inspected).to eq 42
  106. end
  107. end
  108. end
  109. describe '#record_last_inspected' do
  110. let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
  111. it 'records the given id' do
  112. account_statuses_cleanup_policy.record_last_inspected(42)
  113. expect(account_statuses_cleanup_policy.last_inspected).to eq 42
  114. end
  115. end
  116. describe '#invalidate_last_inspected' do
  117. subject { account_statuses_cleanup_policy.invalidate_last_inspected(status, action) }
  118. let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
  119. let(:status) { Fabricate(:status, id: 10, account: account) }
  120. before do
  121. account_statuses_cleanup_policy.record_last_inspected(42)
  122. end
  123. context 'when the action is :unbookmark' do
  124. let(:action) { :unbookmark }
  125. context 'when the policy is not to keep self-bookmarked toots' do
  126. before do
  127. account_statuses_cleanup_policy.keep_self_bookmark = false
  128. end
  129. it 'does not change the recorded id' do
  130. subject
  131. expect(account_statuses_cleanup_policy.last_inspected).to eq 42
  132. end
  133. end
  134. context 'when the policy is to keep self-bookmarked toots' do
  135. before do
  136. account_statuses_cleanup_policy.keep_self_bookmark = true
  137. end
  138. it 'records the older id' do
  139. subject
  140. expect(account_statuses_cleanup_policy.last_inspected).to eq 10
  141. end
  142. end
  143. end
  144. context 'when the action is :unfav' do
  145. let(:action) { :unfav }
  146. context 'when the policy is not to keep self-favourited toots' do
  147. before do
  148. account_statuses_cleanup_policy.keep_self_fav = false
  149. end
  150. it 'does not change the recorded id' do
  151. subject
  152. expect(account_statuses_cleanup_policy.last_inspected).to eq 42
  153. end
  154. end
  155. context 'when the policy is to keep self-favourited toots' do
  156. before do
  157. account_statuses_cleanup_policy.keep_self_fav = true
  158. end
  159. it 'records the older id' do
  160. subject
  161. expect(account_statuses_cleanup_policy.last_inspected).to eq 10
  162. end
  163. end
  164. end
  165. context 'when the action is :unpin' do
  166. let(:action) { :unpin }
  167. context 'when the policy is not to keep pinned toots' do
  168. before do
  169. account_statuses_cleanup_policy.keep_pinned = false
  170. end
  171. it 'does not change the recorded id' do
  172. subject
  173. expect(account_statuses_cleanup_policy.last_inspected).to eq 42
  174. end
  175. end
  176. context 'when the policy is to keep pinned toots' do
  177. before do
  178. account_statuses_cleanup_policy.keep_pinned = true
  179. end
  180. it 'records the older id' do
  181. subject
  182. expect(account_statuses_cleanup_policy.last_inspected).to eq 10
  183. end
  184. end
  185. end
  186. context 'when the status is more recent than the recorded inspected id' do
  187. let(:action) { :unfav }
  188. let(:status) { Fabricate(:status, account: account) }
  189. it 'does not change the recorded id' do
  190. subject
  191. expect(account_statuses_cleanup_policy.last_inspected).to eq 42
  192. end
  193. end
  194. end
  195. describe '#compute_cutoff_id' do
  196. subject { account_statuses_cleanup_policy.compute_cutoff_id }
  197. let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) }
  198. let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
  199. context 'when the account has posted multiple toots' do
  200. let!(:very_old_status) { Fabricate(:status, created_at: 3.years.ago, account: account) }
  201. let!(:old_status) { Fabricate(:status, created_at: 3.weeks.ago, account: account) }
  202. let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) }
  203. it 'returns the most recent id that is still below policy age' do
  204. expect(subject).to eq old_status.id
  205. end
  206. end
  207. context 'when the account has not posted anything' do
  208. it 'returns nil' do
  209. expect(subject).to be_nil
  210. end
  211. end
  212. end
  213. describe '#statuses_to_delete' do
  214. subject { account_statuses_cleanup_policy.statuses_to_delete }
  215. let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) }
  216. let!(:very_old_status) { Fabricate(:status, created_at: 3.years.ago, account: account) }
  217. let!(:pinned_status) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  218. let!(:direct_message) { Fabricate(:status, created_at: 1.year.ago, account: account, visibility: :direct) }
  219. let!(:self_faved) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  220. let!(:self_bookmarked) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  221. let!(:status_with_poll) { Fabricate(:status, created_at: 1.year.ago, account: account, poll_attributes: { account: account, voters_count: 0, options: %w(a b), expires_in: 2.days }) }
  222. let!(:status_with_media) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  223. let!(:faved_primary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  224. let!(:faved_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  225. let!(:reblogged_primary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  226. let!(:reblogged_secondary) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  227. let!(:recent_status) { Fabricate(:status, created_at: 2.days.ago, account: account) }
  228. let!(:media_attachment) { Fabricate(:media_attachment, account: account, status: status_with_media) }
  229. let!(:status_pin) { Fabricate(:status_pin, account: account, status: pinned_status) }
  230. let!(:favourite) { Fabricate(:favourite, account: account, status: self_faved) }
  231. let!(:bookmark) { Fabricate(:bookmark, account: account, status: self_bookmarked) }
  232. let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
  233. before do
  234. 4.times { faved_primary.increment_count!(:favourites_count) }
  235. 5.times { faved_secondary.increment_count!(:favourites_count) }
  236. 4.times { reblogged_primary.increment_count!(:reblogs_count) }
  237. 5.times { reblogged_secondary.increment_count!(:reblogs_count) }
  238. end
  239. context 'when passed a max_id' do
  240. subject { account_statuses_cleanup_policy.statuses_to_delete(50, old_status.id).pluck(:id) }
  241. let!(:old_status) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  242. let!(:slightly_less_old_status) { Fabricate(:status, created_at: 6.months.ago, account: account) }
  243. it 'returns statuses including max_id' do
  244. expect(subject).to include(old_status.id)
  245. end
  246. it 'returns statuses including older than max_id' do
  247. expect(subject).to include(very_old_status.id)
  248. end
  249. it 'does not return statuses newer than max_id' do
  250. expect(subject).to_not include(slightly_less_old_status.id)
  251. end
  252. end
  253. context 'when passed a min_id' do
  254. subject { account_statuses_cleanup_policy.statuses_to_delete(50, recent_status.id, old_status.id).pluck(:id) }
  255. let!(:old_status) { Fabricate(:status, created_at: 1.year.ago, account: account) }
  256. let!(:slightly_less_old_status) { Fabricate(:status, created_at: 6.months.ago, account: account) }
  257. it 'returns statuses including min_id' do
  258. expect(subject).to include(old_status.id)
  259. end
  260. it 'returns statuses including newer than max_id' do
  261. expect(subject).to include(slightly_less_old_status.id)
  262. end
  263. it 'does not return statuses older than min_id' do
  264. expect(subject).to_not include(very_old_status.id)
  265. end
  266. end
  267. context 'when passed a low limit' do
  268. it 'only returns the limited number of items' do
  269. expect(account_statuses_cleanup_policy.statuses_to_delete(1).count).to eq 1
  270. end
  271. end
  272. context 'when policy is set to keep statuses more recent than 2 years' do
  273. before do
  274. account_statuses_cleanup_policy.min_status_age = 2.years.seconds
  275. end
  276. it 'does not return unrelated old status' do
  277. expect(subject.pluck(:id)).to_not include(unrelated_status.id)
  278. end
  279. it 'returns only oldest status for deletion' do
  280. expect(subject.pluck(:id)).to eq [very_old_status.id]
  281. end
  282. end
  283. context 'when policy is set to keep DMs and reject everything else' do
  284. before do
  285. account_statuses_cleanup_policy.keep_direct = true
  286. account_statuses_cleanup_policy.keep_pinned = false
  287. account_statuses_cleanup_policy.keep_polls = false
  288. account_statuses_cleanup_policy.keep_media = false
  289. account_statuses_cleanup_policy.keep_self_fav = false
  290. account_statuses_cleanup_policy.keep_self_bookmark = false
  291. end
  292. it 'does not return the old direct message for deletion' do
  293. expect(subject.pluck(:id)).to_not include(direct_message.id)
  294. end
  295. it 'returns every other old status for deletion' do
  296. expect(subject.pluck(:id)).to include(very_old_status.id, pinned_status.id, self_faved.id, self_bookmarked.id, status_with_poll.id, status_with_media.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  297. end
  298. end
  299. context 'when policy is set to keep self-bookmarked toots and reject everything else' do
  300. before do
  301. account_statuses_cleanup_policy.keep_direct = false
  302. account_statuses_cleanup_policy.keep_pinned = false
  303. account_statuses_cleanup_policy.keep_polls = false
  304. account_statuses_cleanup_policy.keep_media = false
  305. account_statuses_cleanup_policy.keep_self_fav = false
  306. account_statuses_cleanup_policy.keep_self_bookmark = true
  307. end
  308. it 'does not return the old self-bookmarked message for deletion' do
  309. expect(subject.pluck(:id)).to_not include(self_bookmarked.id)
  310. end
  311. it 'returns every other old status for deletion' do
  312. expect(subject.pluck(:id)).to include(direct_message.id, very_old_status.id, pinned_status.id, self_faved.id, status_with_poll.id, status_with_media.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  313. end
  314. end
  315. context 'when policy is set to keep self-faved toots and reject everything else' do
  316. before do
  317. account_statuses_cleanup_policy.keep_direct = false
  318. account_statuses_cleanup_policy.keep_pinned = false
  319. account_statuses_cleanup_policy.keep_polls = false
  320. account_statuses_cleanup_policy.keep_media = false
  321. account_statuses_cleanup_policy.keep_self_fav = true
  322. account_statuses_cleanup_policy.keep_self_bookmark = false
  323. end
  324. it 'does not return the old self-bookmarked message for deletion' do
  325. expect(subject.pluck(:id)).to_not include(self_faved.id)
  326. end
  327. it 'returns every other old status for deletion' do
  328. expect(subject.pluck(:id)).to include(direct_message.id, very_old_status.id, pinned_status.id, self_bookmarked.id, status_with_poll.id, status_with_media.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  329. end
  330. end
  331. context 'when policy is set to keep toots with media and reject everything else' do
  332. before do
  333. account_statuses_cleanup_policy.keep_direct = false
  334. account_statuses_cleanup_policy.keep_pinned = false
  335. account_statuses_cleanup_policy.keep_polls = false
  336. account_statuses_cleanup_policy.keep_media = true
  337. account_statuses_cleanup_policy.keep_self_fav = false
  338. account_statuses_cleanup_policy.keep_self_bookmark = false
  339. end
  340. it 'does not return the old message with media for deletion' do
  341. expect(subject.pluck(:id)).to_not include(status_with_media.id)
  342. end
  343. it 'returns every other old status for deletion' do
  344. expect(subject.pluck(:id)).to include(direct_message.id, very_old_status.id, pinned_status.id, self_faved.id, self_bookmarked.id, status_with_poll.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  345. end
  346. end
  347. context 'when policy is set to keep toots with polls and reject everything else' do
  348. before do
  349. account_statuses_cleanup_policy.keep_direct = false
  350. account_statuses_cleanup_policy.keep_pinned = false
  351. account_statuses_cleanup_policy.keep_polls = true
  352. account_statuses_cleanup_policy.keep_media = false
  353. account_statuses_cleanup_policy.keep_self_fav = false
  354. account_statuses_cleanup_policy.keep_self_bookmark = false
  355. end
  356. it 'does not return the old poll message for deletion' do
  357. expect(subject.pluck(:id)).to_not include(status_with_poll.id)
  358. end
  359. it 'returns every other old status for deletion' do
  360. expect(subject.pluck(:id)).to include(direct_message.id, very_old_status.id, pinned_status.id, self_faved.id, self_bookmarked.id, status_with_media.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  361. end
  362. end
  363. context 'when policy is set to keep pinned toots and reject everything else' do
  364. before do
  365. account_statuses_cleanup_policy.keep_direct = false
  366. account_statuses_cleanup_policy.keep_pinned = true
  367. account_statuses_cleanup_policy.keep_polls = false
  368. account_statuses_cleanup_policy.keep_media = false
  369. account_statuses_cleanup_policy.keep_self_fav = false
  370. account_statuses_cleanup_policy.keep_self_bookmark = false
  371. end
  372. it 'does not return the old pinned message for deletion' do
  373. expect(subject.pluck(:id)).to_not include(pinned_status.id)
  374. end
  375. it 'returns every other old status for deletion' do
  376. expect(subject.pluck(:id)).to include(direct_message.id, very_old_status.id, self_faved.id, self_bookmarked.id, status_with_poll.id, status_with_media.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  377. end
  378. end
  379. context 'when policy is to not keep any special messages' do
  380. before do
  381. account_statuses_cleanup_policy.keep_direct = false
  382. account_statuses_cleanup_policy.keep_pinned = false
  383. account_statuses_cleanup_policy.keep_polls = false
  384. account_statuses_cleanup_policy.keep_media = false
  385. account_statuses_cleanup_policy.keep_self_fav = false
  386. account_statuses_cleanup_policy.keep_self_bookmark = false
  387. end
  388. it 'does not return the recent toot' do
  389. expect(subject.pluck(:id)).to_not include(recent_status.id)
  390. end
  391. it 'does not return the unrelated toot' do
  392. expect(subject.pluck(:id)).to_not include(unrelated_status.id)
  393. end
  394. it 'returns every other old status for deletion' do
  395. expect(subject.pluck(:id)).to include(direct_message.id, very_old_status.id, pinned_status.id, self_faved.id, self_bookmarked.id, status_with_poll.id, status_with_media.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  396. end
  397. end
  398. context 'when policy is set to keep every category of toots' do
  399. before do
  400. account_statuses_cleanup_policy.keep_direct = true
  401. account_statuses_cleanup_policy.keep_pinned = true
  402. account_statuses_cleanup_policy.keep_polls = true
  403. account_statuses_cleanup_policy.keep_media = true
  404. account_statuses_cleanup_policy.keep_self_fav = true
  405. account_statuses_cleanup_policy.keep_self_bookmark = true
  406. end
  407. it 'does not return unrelated old status' do
  408. expect(subject.pluck(:id)).to_not include(unrelated_status.id)
  409. end
  410. it 'returns only normal statuses for deletion' do
  411. expect(subject.pluck(:id)).to contain_exactly(very_old_status.id, faved_primary.id, faved_secondary.id, reblogged_primary.id, reblogged_secondary.id)
  412. end
  413. end
  414. context 'when policy is to keep statuses with at least 5 boosts' do
  415. before do
  416. account_statuses_cleanup_policy.min_reblogs = 5
  417. end
  418. it 'does not return the recent toot' do
  419. expect(subject.pluck(:id)).to_not include(recent_status.id)
  420. end
  421. it 'does not return the toot reblogged 5 times' do
  422. expect(subject.pluck(:id)).to_not include(reblogged_secondary.id)
  423. end
  424. it 'does not return the unrelated toot' do
  425. expect(subject.pluck(:id)).to_not include(unrelated_status.id)
  426. end
  427. it 'returns old statuses not reblogged as much' do
  428. expect(subject.pluck(:id)).to include(very_old_status.id, faved_primary.id, faved_secondary.id, reblogged_primary.id)
  429. end
  430. end
  431. context 'when policy is to keep statuses with at least 5 favs' do
  432. before do
  433. account_statuses_cleanup_policy.min_favs = 5
  434. end
  435. it 'does not return the recent toot' do
  436. expect(subject.pluck(:id)).to_not include(recent_status.id)
  437. end
  438. it 'does not return the toot faved 5 times' do
  439. expect(subject.pluck(:id)).to_not include(faved_secondary.id)
  440. end
  441. it 'does not return the unrelated toot' do
  442. expect(subject.pluck(:id)).to_not include(unrelated_status.id)
  443. end
  444. it 'returns old statuses not faved as much' do
  445. expect(subject.pluck(:id)).to include(very_old_status.id, faved_primary.id, reblogged_primary.id, reblogged_secondary.id)
  446. end
  447. end
  448. end
  449. end