account_statuses_cleanup_policy_spec.rb 18 KB

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