You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

546 lines
22 KiB

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