logo

mastofe

My custom branche(s) on git.pleroma.social/pleroma/mastofe git clone https://hacktivis.me/git/mastofe.git

status_spec.rb (19345B)


  1. require 'rails_helper'
  2. RSpec.describe Status, type: :model do
  3. let(:alice) { Fabricate(:account, username: 'alice') }
  4. let(:bob) { Fabricate(:account, username: 'bob') }
  5. let(:other) { Fabricate(:status, account: bob, text: 'Skulls for the skull god! The enemy\'s gates are sideways!') }
  6. subject { Fabricate(:status, account: alice) }
  7. describe '#local?' do
  8. it 'returns true when no remote URI is set' do
  9. expect(subject.local?).to be true
  10. end
  11. it 'returns false if a remote URI is set' do
  12. alice.update(domain: 'example.com')
  13. subject.save
  14. expect(subject.local?).to be false
  15. end
  16. it 'returns true if a URI is set and `local` is true' do
  17. subject.update(uri: 'example.com', local: true)
  18. expect(subject.local?).to be true
  19. end
  20. end
  21. describe '#reblog?' do
  22. it 'returns true when the status reblogs another status' do
  23. subject.reblog = other
  24. expect(subject.reblog?).to be true
  25. end
  26. it 'returns false if the status is self-contained' do
  27. expect(subject.reblog?).to be false
  28. end
  29. end
  30. describe '#reply?' do
  31. it 'returns true if the status references another' do
  32. subject.thread = other
  33. expect(subject.reply?).to be true
  34. end
  35. it 'returns false if the status is self-contained' do
  36. expect(subject.reply?).to be false
  37. end
  38. end
  39. describe '#verb' do
  40. context 'if destroyed?' do
  41. it 'returns :delete' do
  42. subject.destroy!
  43. expect(subject.verb).to be :delete
  44. end
  45. end
  46. context 'unless destroyed?' do
  47. context 'if reblog?' do
  48. it 'returns :share' do
  49. subject.reblog = other
  50. expect(subject.verb).to be :share
  51. end
  52. end
  53. context 'unless reblog?' do
  54. it 'returns :post' do
  55. subject.reblog = nil
  56. expect(subject.verb).to be :post
  57. end
  58. end
  59. end
  60. end
  61. describe '#object_type' do
  62. it 'is note when the status is self-contained' do
  63. expect(subject.object_type).to be :note
  64. end
  65. it 'is comment when the status replies to another' do
  66. subject.thread = other
  67. expect(subject.object_type).to be :comment
  68. end
  69. end
  70. describe '#title' do
  71. # rubocop:disable Style/InterpolationCheck
  72. let(:account) { subject.account }
  73. context 'if destroyed?' do
  74. it 'returns "#{account.acct} deleted status"' do
  75. subject.destroy!
  76. expect(subject.title).to eq "#{account.acct} deleted status"
  77. end
  78. end
  79. context 'unless destroyed?' do
  80. context 'if reblog?' do
  81. it 'returns "#{account.acct} shared a status by #{reblog.account.acct}"' do
  82. reblog = subject.reblog = other
  83. expect(subject.title).to eq "#{account.acct} shared a status by #{reblog.account.acct}"
  84. end
  85. end
  86. context 'unless reblog?' do
  87. it 'returns "New status by #{account.acct}"' do
  88. subject.reblog = nil
  89. expect(subject.title).to eq "New status by #{account.acct}"
  90. end
  91. end
  92. end
  93. end
  94. describe '#hidden?' do
  95. context 'if private_visibility?' do
  96. it 'returns true' do
  97. subject.visibility = :private
  98. expect(subject.hidden?).to be true
  99. end
  100. end
  101. context 'if direct_visibility?' do
  102. it 'returns true' do
  103. subject.visibility = :direct
  104. expect(subject.hidden?).to be true
  105. end
  106. end
  107. context 'if public_visibility?' do
  108. it 'returns false' do
  109. subject.visibility = :public
  110. expect(subject.hidden?).to be false
  111. end
  112. end
  113. context 'if unlisted_visibility?' do
  114. it 'returns false' do
  115. subject.visibility = :unlisted
  116. expect(subject.hidden?).to be false
  117. end
  118. end
  119. end
  120. describe '#content' do
  121. it 'returns the text of the status if it is not a reblog' do
  122. expect(subject.content).to eql subject.text
  123. end
  124. it 'returns the text of the reblogged status' do
  125. subject.reblog = other
  126. expect(subject.content).to eql other.text
  127. end
  128. end
  129. describe '#target' do
  130. it 'returns nil if the status is self-contained' do
  131. expect(subject.target).to be_nil
  132. end
  133. it 'returns nil if the status is a reply' do
  134. subject.thread = other
  135. expect(subject.target).to be_nil
  136. end
  137. it 'returns the reblogged status' do
  138. subject.reblog = other
  139. expect(subject.target).to eq other
  140. end
  141. end
  142. describe '#reblogs_count' do
  143. it 'is the number of reblogs' do
  144. Fabricate(:status, account: bob, reblog: subject)
  145. Fabricate(:status, account: alice, reblog: subject)
  146. expect(subject.reblogs_count).to eq 2
  147. end
  148. end
  149. describe '#favourites_count' do
  150. it 'is the number of favorites' do
  151. Fabricate(:favourite, account: bob, status: subject)
  152. Fabricate(:favourite, account: alice, status: subject)
  153. expect(subject.favourites_count).to eq 2
  154. end
  155. end
  156. describe '#proper' do
  157. it 'is itself for original statuses' do
  158. expect(subject.proper).to eq subject
  159. end
  160. it 'is the source status for reblogs' do
  161. subject.reblog = other
  162. expect(subject.proper).to eq other
  163. end
  164. end
  165. describe '.mutes_map' do
  166. let(:status) { Fabricate(:status) }
  167. let(:account) { Fabricate(:account) }
  168. subject { Status.mutes_map([status.conversation.id], account) }
  169. it 'returns a hash' do
  170. expect(subject).to be_a Hash
  171. end
  172. it 'contains true value' do
  173. account.mute_conversation!(status.conversation)
  174. expect(subject[status.conversation.id]).to be true
  175. end
  176. end
  177. describe '.favourites_map' do
  178. let(:status) { Fabricate(:status) }
  179. let(:account) { Fabricate(:account) }
  180. subject { Status.favourites_map([status], account) }
  181. it 'returns a hash' do
  182. expect(subject).to be_a Hash
  183. end
  184. it 'contains true value' do
  185. Fabricate(:favourite, status: status, account: account)
  186. expect(subject[status.id]).to be true
  187. end
  188. end
  189. describe '.reblogs_map' do
  190. let(:status) { Fabricate(:status) }
  191. let(:account) { Fabricate(:account) }
  192. subject { Status.reblogs_map([status], account) }
  193. it 'returns a hash' do
  194. expect(subject).to be_a Hash
  195. end
  196. it 'contains true value' do
  197. Fabricate(:status, account: account, reblog: status)
  198. expect(subject[status.id]).to be true
  199. end
  200. end
  201. describe '.not_in_filtered_languages' do
  202. context 'for accounts with language filters' do
  203. let(:user) { Fabricate(:user, filtered_languages: ['en']) }
  204. it 'does not include statuses in filtered languages' do
  205. status = Fabricate(:status, language: 'en')
  206. expect(Status.not_in_filtered_languages(user.account)).not_to include status
  207. end
  208. it 'includes status with unknown language' do
  209. status = Fabricate(:status, language: nil)
  210. expect(Status.not_in_filtered_languages(user.account)).to include status
  211. end
  212. end
  213. end
  214. describe '.as_home_timeline' do
  215. let(:account) { Fabricate(:account) }
  216. let(:followed) { Fabricate(:account) }
  217. let(:not_followed) { Fabricate(:account) }
  218. before do
  219. Fabricate(:follow, account: account, target_account: followed)
  220. @self_status = Fabricate(:status, account: account, visibility: :public)
  221. @self_direct_status = Fabricate(:status, account: account, visibility: :direct)
  222. @followed_status = Fabricate(:status, account: followed, visibility: :public)
  223. @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
  224. @not_followed_status = Fabricate(:status, account: not_followed, visibility: :public)
  225. @results = Status.as_home_timeline(account)
  226. end
  227. it 'includes statuses from self' do
  228. expect(@results).to include(@self_status)
  229. end
  230. it 'does not include direct statuses from self' do
  231. expect(@results).to_not include(@self_direct_status)
  232. end
  233. it 'includes statuses from followed' do
  234. expect(@results).to include(@followed_status)
  235. end
  236. it 'does not include direct statuses mentioning recipient from followed' do
  237. Fabricate(:mention, account: account, status: @followed_direct_status)
  238. expect(@results).to_not include(@followed_direct_status)
  239. end
  240. it 'does not include direct statuses not mentioning recipient from followed' do
  241. expect(@results).not_to include(@followed_direct_status)
  242. end
  243. it 'does not include statuses from non-followed' do
  244. expect(@results).not_to include(@not_followed_status)
  245. end
  246. end
  247. describe '.as_public_timeline' do
  248. it 'only includes statuses with public visibility' do
  249. public_status = Fabricate(:status, visibility: :public)
  250. private_status = Fabricate(:status, visibility: :private)
  251. results = Status.as_public_timeline
  252. expect(results).to include(public_status)
  253. expect(results).not_to include(private_status)
  254. end
  255. it 'does not include replies' do
  256. status = Fabricate(:status)
  257. reply = Fabricate(:status, in_reply_to_id: status.id)
  258. results = Status.as_public_timeline
  259. expect(results).to include(status)
  260. expect(results).not_to include(reply)
  261. end
  262. it 'does not include boosts' do
  263. status = Fabricate(:status)
  264. boost = Fabricate(:status, reblog_of_id: status.id)
  265. results = Status.as_public_timeline
  266. expect(results).to include(status)
  267. expect(results).not_to include(boost)
  268. end
  269. it 'filters out silenced accounts' do
  270. account = Fabricate(:account)
  271. silenced_account = Fabricate(:account, silenced: true)
  272. status = Fabricate(:status, account: account)
  273. silenced_status = Fabricate(:status, account: silenced_account)
  274. results = Status.as_public_timeline
  275. expect(results).to include(status)
  276. expect(results).not_to include(silenced_status)
  277. end
  278. context 'without local_only option' do
  279. let(:viewer) { nil }
  280. let!(:local_account) { Fabricate(:account, domain: nil) }
  281. let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
  282. let!(:local_status) { Fabricate(:status, account: local_account) }
  283. let!(:remote_status) { Fabricate(:status, account: remote_account) }
  284. subject { Status.as_public_timeline(viewer, false) }
  285. context 'without a viewer' do
  286. let(:viewer) { nil }
  287. it 'includes remote instances statuses' do
  288. expect(subject).to include(remote_status)
  289. end
  290. it 'includes local statuses' do
  291. expect(subject).to include(local_status)
  292. end
  293. end
  294. context 'with a viewer' do
  295. let(:viewer) { Fabricate(:account, username: 'viewer') }
  296. it 'includes remote instances statuses' do
  297. expect(subject).to include(remote_status)
  298. end
  299. it 'includes local statuses' do
  300. expect(subject).to include(local_status)
  301. end
  302. end
  303. end
  304. context 'with a local_only option set' do
  305. let!(:local_account) { Fabricate(:account, domain: nil) }
  306. let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
  307. let!(:local_status) { Fabricate(:status, account: local_account) }
  308. let!(:remote_status) { Fabricate(:status, account: remote_account) }
  309. subject { Status.as_public_timeline(viewer, true) }
  310. context 'without a viewer' do
  311. let(:viewer) { nil }
  312. it 'does not include remote instances statuses' do
  313. expect(subject).to include(local_status)
  314. expect(subject).not_to include(remote_status)
  315. end
  316. end
  317. context 'with a viewer' do
  318. let(:viewer) { Fabricate(:account, username: 'viewer') }
  319. it 'does not include remote instances statuses' do
  320. expect(subject).to include(local_status)
  321. expect(subject).not_to include(remote_status)
  322. end
  323. it 'is not affected by personal domain blocks' do
  324. viewer.block_domain!('test.com')
  325. expect(subject).to include(local_status)
  326. expect(subject).not_to include(remote_status)
  327. end
  328. end
  329. end
  330. describe 'with an account passed in' do
  331. before do
  332. @account = Fabricate(:account)
  333. end
  334. it 'excludes statuses from accounts blocked by the account' do
  335. blocked = Fabricate(:account)
  336. Fabricate(:block, account: @account, target_account: blocked)
  337. blocked_status = Fabricate(:status, account: blocked)
  338. results = Status.as_public_timeline(@account)
  339. expect(results).not_to include(blocked_status)
  340. end
  341. it 'excludes statuses from accounts who have blocked the account' do
  342. blocked = Fabricate(:account)
  343. Fabricate(:block, account: blocked, target_account: @account)
  344. blocked_status = Fabricate(:status, account: blocked)
  345. results = Status.as_public_timeline(@account)
  346. expect(results).not_to include(blocked_status)
  347. end
  348. it 'excludes statuses from accounts muted by the account' do
  349. muted = Fabricate(:account)
  350. Fabricate(:mute, account: @account, target_account: muted)
  351. muted_status = Fabricate(:status, account: muted)
  352. results = Status.as_public_timeline(@account)
  353. expect(results).not_to include(muted_status)
  354. end
  355. it 'excludes statuses from accounts from personally blocked domains' do
  356. blocked = Fabricate(:account, domain: 'example.com')
  357. @account.block_domain!(blocked.domain)
  358. blocked_status = Fabricate(:status, account: blocked)
  359. results = Status.as_public_timeline(@account)
  360. expect(results).not_to include(blocked_status)
  361. end
  362. context 'with language preferences' do
  363. it 'excludes statuses in languages not allowed by the account user' do
  364. user = Fabricate(:user, filtered_languages: [:fr])
  365. @account.update(user: user)
  366. en_status = Fabricate(:status, language: 'en')
  367. es_status = Fabricate(:status, language: 'es')
  368. fr_status = Fabricate(:status, language: 'fr')
  369. results = Status.as_public_timeline(@account)
  370. expect(results).to include(en_status)
  371. expect(results).to include(es_status)
  372. expect(results).not_to include(fr_status)
  373. end
  374. it 'includes all languages when user does not have a setting' do
  375. user = Fabricate(:user, filtered_languages: [])
  376. @account.update(user: user)
  377. en_status = Fabricate(:status, language: 'en')
  378. es_status = Fabricate(:status, language: 'es')
  379. results = Status.as_public_timeline(@account)
  380. expect(results).to include(en_status)
  381. expect(results).to include(es_status)
  382. end
  383. it 'includes all languages when account does not have a user' do
  384. expect(@account.user).to be_nil
  385. en_status = Fabricate(:status, language: 'en')
  386. es_status = Fabricate(:status, language: 'es')
  387. results = Status.as_public_timeline(@account)
  388. expect(results).to include(en_status)
  389. expect(results).to include(es_status)
  390. end
  391. end
  392. context 'where that account is silenced' do
  393. it 'includes statuses from other accounts that are silenced' do
  394. @account.update(silenced: true)
  395. other_silenced_account = Fabricate(:account, silenced: true)
  396. other_status = Fabricate(:status, account: other_silenced_account)
  397. results = Status.as_public_timeline(@account)
  398. expect(results).to include(other_status)
  399. end
  400. end
  401. end
  402. end
  403. describe '.as_tag_timeline' do
  404. it 'includes statuses with a tag' do
  405. tag = Fabricate(:tag)
  406. status = Fabricate(:status, tags: [tag])
  407. other = Fabricate(:status)
  408. results = Status.as_tag_timeline(tag)
  409. expect(results).to include(status)
  410. expect(results).not_to include(other)
  411. end
  412. it 'allows replies to be included' do
  413. original = Fabricate(:status)
  414. tag = Fabricate(:tag)
  415. status = Fabricate(:status, tags: [tag], in_reply_to_id: original.id)
  416. results = Status.as_tag_timeline(tag)
  417. expect(results).to include(status)
  418. end
  419. end
  420. describe '.permitted_for' do
  421. subject { described_class.permitted_for(target_account, account).pluck(:visibility) }
  422. let(:target_account) { alice }
  423. let(:account) { bob }
  424. let!(:public_status) { Fabricate(:status, account: target_account, visibility: 'public') }
  425. let!(:unlisted_status) { Fabricate(:status, account: target_account, visibility: 'unlisted') }
  426. let!(:private_status) { Fabricate(:status, account: target_account, visibility: 'private') }
  427. let!(:direct_status) do
  428. Fabricate(:status, account: target_account, visibility: 'direct').tap do |status|
  429. Fabricate(:mention, status: status, account: account)
  430. end
  431. end
  432. let!(:other_direct_status) do
  433. Fabricate(:status, account: target_account, visibility: 'direct').tap do |status|
  434. Fabricate(:mention, status: status)
  435. end
  436. end
  437. context 'given nil' do
  438. let(:account) { nil }
  439. let(:direct_status) { nil }
  440. it { is_expected.to eq(%w(unlisted public)) }
  441. end
  442. context 'given blocked account' do
  443. before do
  444. target_account.block!(account)
  445. end
  446. it { is_expected.to be_empty }
  447. end
  448. context 'given same account' do
  449. let(:account) { target_account }
  450. it { is_expected.to eq(%w(direct direct private unlisted public)) }
  451. end
  452. context 'given followed account' do
  453. before do
  454. account.follow!(target_account)
  455. end
  456. it { is_expected.to eq(%w(direct private unlisted public)) }
  457. end
  458. context 'given unfollowed account' do
  459. it { is_expected.to eq(%w(direct unlisted public)) }
  460. end
  461. end
  462. describe 'before_validation' do
  463. it 'sets account being replied to correctly over intermediary nodes' do
  464. first_status = Fabricate(:status, account: bob)
  465. intermediary = Fabricate(:status, thread: first_status, account: alice)
  466. final = Fabricate(:status, thread: intermediary, account: alice)
  467. expect(final.in_reply_to_account_id).to eq bob.id
  468. end
  469. it 'creates new conversation for stand-alone status' do
  470. expect(Status.create(account: alice, text: 'First').conversation_id).to_not be_nil
  471. end
  472. it 'keeps conversation of parent node' do
  473. parent = Fabricate(:status, text: 'First')
  474. expect(Status.create(account: alice, thread: parent, text: 'Response').conversation_id).to eq parent.conversation_id
  475. end
  476. it 'sets `local` to true for status by local account' do
  477. expect(Status.create(account: alice, text: 'foo').local).to be true
  478. end
  479. it 'sets `local` to false for status by remote account' do
  480. alice.update(domain: 'example.com')
  481. expect(Status.create(account: alice, text: 'foo').local).to be false
  482. end
  483. end
  484. describe 'validation' do
  485. it 'disallow empty uri for remote status' do
  486. alice.update(domain: 'example.com')
  487. status = Fabricate.build(:status, uri: '', account: alice)
  488. expect(status).to model_have_error_on_field(:uri)
  489. end
  490. end
  491. describe 'after_create' do
  492. it 'saves ActivityPub uri as uri for local status' do
  493. status = Status.create(account: alice, text: 'foo')
  494. status.reload
  495. expect(status.uri).to start_with('https://')
  496. end
  497. end
  498. end