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.

631 lines
17 KiB

  1. require 'rails_helper'
  2. RSpec.describe ActivityPub::Activity::Create do
  3. let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor') }
  4. let(:json) do
  5. {
  6. '@context': 'https://www.w3.org/ns/activitystreams',
  7. id: [ActivityPub::TagManager.instance.uri_for(sender), '#foo'].join,
  8. type: 'Create',
  9. actor: ActivityPub::TagManager.instance.uri_for(sender),
  10. object: object_json,
  11. }.with_indifferent_access
  12. end
  13. before do
  14. sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender))
  15. stub_request(:get, 'http://example.com/attachment.png').to_return(request_fixture('avatar.txt'))
  16. stub_request(:get, 'http://example.com/emoji.png').to_return(body: attachment_fixture('emojo.png'))
  17. end
  18. describe '#perform' do
  19. context 'when fetching' do
  20. subject { described_class.new(json, sender) }
  21. before do
  22. subject.perform
  23. end
  24. context 'unknown object type' do
  25. let(:object_json) do
  26. {
  27. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  28. type: 'Banana',
  29. content: 'Lorem ipsum',
  30. }
  31. end
  32. it 'does not create a status' do
  33. expect(sender.statuses.count).to be_zero
  34. end
  35. end
  36. context 'standalone' do
  37. let(:object_json) do
  38. {
  39. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  40. type: 'Note',
  41. content: 'Lorem ipsum',
  42. }
  43. end
  44. it 'creates status' do
  45. status = sender.statuses.first
  46. expect(status).to_not be_nil
  47. expect(status.text).to eq 'Lorem ipsum'
  48. end
  49. it 'missing to/cc defaults to direct privacy' do
  50. status = sender.statuses.first
  51. expect(status).to_not be_nil
  52. expect(status.visibility).to eq 'direct'
  53. end
  54. end
  55. context 'public' do
  56. let(:object_json) do
  57. {
  58. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  59. type: 'Note',
  60. content: 'Lorem ipsum',
  61. to: 'https://www.w3.org/ns/activitystreams#Public',
  62. }
  63. end
  64. it 'creates status' do
  65. status = sender.statuses.first
  66. expect(status).to_not be_nil
  67. expect(status.visibility).to eq 'public'
  68. end
  69. end
  70. context 'unlisted' do
  71. let(:object_json) do
  72. {
  73. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  74. type: 'Note',
  75. content: 'Lorem ipsum',
  76. cc: 'https://www.w3.org/ns/activitystreams#Public',
  77. }
  78. end
  79. it 'creates status' do
  80. status = sender.statuses.first
  81. expect(status).to_not be_nil
  82. expect(status.visibility).to eq 'unlisted'
  83. end
  84. end
  85. context 'private' do
  86. let(:object_json) do
  87. {
  88. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  89. type: 'Note',
  90. content: 'Lorem ipsum',
  91. to: 'http://example.com/followers',
  92. }
  93. end
  94. it 'creates status' do
  95. status = sender.statuses.first
  96. expect(status).to_not be_nil
  97. expect(status.visibility).to eq 'private'
  98. end
  99. end
  100. context 'limited' do
  101. let(:recipient) { Fabricate(:account) }
  102. let(:object_json) do
  103. {
  104. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  105. type: 'Note',
  106. content: 'Lorem ipsum',
  107. to: ActivityPub::TagManager.instance.uri_for(recipient),
  108. }
  109. end
  110. it 'creates status' do
  111. status = sender.statuses.first
  112. expect(status).to_not be_nil
  113. expect(status.visibility).to eq 'limited'
  114. end
  115. it 'creates silent mention' do
  116. status = sender.statuses.first
  117. expect(status.mentions.first).to be_silent
  118. end
  119. end
  120. context 'direct' do
  121. let(:recipient) { Fabricate(:account) }
  122. let(:object_json) do
  123. {
  124. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  125. type: 'Note',
  126. content: 'Lorem ipsum',
  127. to: ActivityPub::TagManager.instance.uri_for(recipient),
  128. tag: {
  129. type: 'Mention',
  130. href: ActivityPub::TagManager.instance.uri_for(recipient),
  131. },
  132. }
  133. end
  134. it 'creates status' do
  135. status = sender.statuses.first
  136. expect(status).to_not be_nil
  137. expect(status.visibility).to eq 'direct'
  138. end
  139. end
  140. context 'as a reply' do
  141. let(:original_status) { Fabricate(:status) }
  142. let(:object_json) do
  143. {
  144. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  145. type: 'Note',
  146. content: 'Lorem ipsum',
  147. inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
  148. }
  149. end
  150. it 'creates status' do
  151. status = sender.statuses.first
  152. expect(status).to_not be_nil
  153. expect(status.thread).to eq original_status
  154. expect(status.reply?).to be true
  155. expect(status.in_reply_to_account).to eq original_status.account
  156. expect(status.conversation).to eq original_status.conversation
  157. end
  158. end
  159. context 'with mentions' do
  160. let(:recipient) { Fabricate(:account) }
  161. let(:object_json) do
  162. {
  163. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  164. type: 'Note',
  165. content: 'Lorem ipsum',
  166. tag: [
  167. {
  168. type: 'Mention',
  169. href: ActivityPub::TagManager.instance.uri_for(recipient),
  170. },
  171. ],
  172. }
  173. end
  174. it 'creates status' do
  175. status = sender.statuses.first
  176. expect(status).to_not be_nil
  177. expect(status.mentions.map(&:account)).to include(recipient)
  178. end
  179. end
  180. context 'with mentions missing href' do
  181. let(:object_json) do
  182. {
  183. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  184. type: 'Note',
  185. content: 'Lorem ipsum',
  186. tag: [
  187. {
  188. type: 'Mention',
  189. },
  190. ],
  191. }
  192. end
  193. it 'creates status' do
  194. status = sender.statuses.first
  195. expect(status).to_not be_nil
  196. end
  197. end
  198. context 'with media attachments' do
  199. let(:object_json) do
  200. {
  201. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  202. type: 'Note',
  203. content: 'Lorem ipsum',
  204. attachment: [
  205. {
  206. type: 'Document',
  207. mediaType: 'image/png',
  208. url: 'http://example.com/attachment.png',
  209. },
  210. ],
  211. }
  212. end
  213. it 'creates status' do
  214. status = sender.statuses.first
  215. expect(status).to_not be_nil
  216. expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
  217. end
  218. end
  219. context 'with media attachments with focal points' do
  220. let(:object_json) do
  221. {
  222. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  223. type: 'Note',
  224. content: 'Lorem ipsum',
  225. attachment: [
  226. {
  227. type: 'Document',
  228. mediaType: 'image/png',
  229. url: 'http://example.com/attachment.png',
  230. focalPoint: [0.5, -0.7],
  231. },
  232. ],
  233. }
  234. end
  235. it 'creates status' do
  236. status = sender.statuses.first
  237. expect(status).to_not be_nil
  238. expect(status.media_attachments.map(&:focus)).to include('0.5,-0.7')
  239. end
  240. end
  241. context 'with media attachments missing url' do
  242. let(:object_json) do
  243. {
  244. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  245. type: 'Note',
  246. content: 'Lorem ipsum',
  247. attachment: [
  248. {
  249. type: 'Document',
  250. mediaType: 'image/png',
  251. },
  252. ],
  253. }
  254. end
  255. it 'creates status' do
  256. status = sender.statuses.first
  257. expect(status).to_not be_nil
  258. end
  259. end
  260. context 'with hashtags' do
  261. let(:object_json) do
  262. {
  263. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  264. type: 'Note',
  265. content: 'Lorem ipsum',
  266. tag: [
  267. {
  268. type: 'Hashtag',
  269. href: 'http://example.com/blah',
  270. name: '#test',
  271. },
  272. ],
  273. }
  274. end
  275. it 'creates status' do
  276. status = sender.statuses.first
  277. expect(status).to_not be_nil
  278. expect(status.tags.map(&:name)).to include('test')
  279. end
  280. end
  281. context 'with hashtags missing name' do
  282. let(:object_json) do
  283. {
  284. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  285. type: 'Note',
  286. content: 'Lorem ipsum',
  287. tag: [
  288. {
  289. type: 'Hashtag',
  290. href: 'http://example.com/blah',
  291. },
  292. ],
  293. }
  294. end
  295. it 'creates status' do
  296. status = sender.statuses.first
  297. expect(status).to_not be_nil
  298. end
  299. end
  300. context 'with emojis' do
  301. let(:object_json) do
  302. {
  303. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  304. type: 'Note',
  305. content: 'Lorem ipsum :tinking:',
  306. tag: [
  307. {
  308. type: 'Emoji',
  309. icon: {
  310. url: 'http://example.com/emoji.png',
  311. },
  312. name: 'tinking',
  313. },
  314. ],
  315. }
  316. end
  317. it 'creates status' do
  318. status = sender.statuses.first
  319. expect(status).to_not be_nil
  320. expect(status.emojis.map(&:shortcode)).to include('tinking')
  321. end
  322. end
  323. context 'with emojis missing name' do
  324. let(:object_json) do
  325. {
  326. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  327. type: 'Note',
  328. content: 'Lorem ipsum :tinking:',
  329. tag: [
  330. {
  331. type: 'Emoji',
  332. icon: {
  333. url: 'http://example.com/emoji.png',
  334. },
  335. },
  336. ],
  337. }
  338. end
  339. it 'creates status' do
  340. status = sender.statuses.first
  341. expect(status).to_not be_nil
  342. end
  343. end
  344. context 'with emojis missing icon' do
  345. let(:object_json) do
  346. {
  347. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  348. type: 'Note',
  349. content: 'Lorem ipsum :tinking:',
  350. tag: [
  351. {
  352. type: 'Emoji',
  353. name: 'tinking',
  354. },
  355. ],
  356. }
  357. end
  358. it 'creates status' do
  359. status = sender.statuses.first
  360. expect(status).to_not be_nil
  361. end
  362. end
  363. context 'with poll' do
  364. let(:object_json) do
  365. {
  366. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  367. type: 'Question',
  368. content: 'Which color was the submarine?',
  369. oneOf: [
  370. {
  371. name: 'Yellow',
  372. replies: {
  373. type: 'Collection',
  374. totalItems: 10,
  375. },
  376. },
  377. {
  378. name: 'Blue',
  379. replies: {
  380. type: 'Collection',
  381. totalItems: 3,
  382. }
  383. },
  384. ],
  385. }
  386. end
  387. it 'creates status' do
  388. status = sender.statuses.first
  389. expect(status).to_not be_nil
  390. expect(status.poll).to_not be_nil
  391. end
  392. it 'creates a poll' do
  393. poll = sender.polls.first
  394. expect(poll).to_not be_nil
  395. expect(poll.status).to_not be_nil
  396. expect(poll.options).to eq %w(Yellow Blue)
  397. expect(poll.cached_tallies).to eq [10, 3]
  398. end
  399. end
  400. context 'when a vote to a local poll' do
  401. let(:poll) { Fabricate(:poll, options: %w(Yellow Blue)) }
  402. let!(:local_status) { Fabricate(:status, owned_poll: poll) }
  403. let(:object_json) do
  404. {
  405. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  406. type: 'Note',
  407. name: 'Yellow',
  408. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
  409. }
  410. end
  411. it 'adds a vote to the poll with correct uri' do
  412. vote = poll.votes.first
  413. expect(vote).to_not be_nil
  414. expect(vote.uri).to eq object_json[:id]
  415. expect(poll.reload.cached_tallies).to eq [1, 0]
  416. end
  417. end
  418. context 'when a vote to an expired local poll' do
  419. let(:poll) do
  420. poll = Fabricate.build(:poll, options: %w(Yellow Blue), expires_at: 1.day.ago)
  421. poll.save(validate: false)
  422. poll
  423. end
  424. let!(:local_status) { Fabricate(:status, owned_poll: poll) }
  425. let(:object_json) do
  426. {
  427. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  428. type: 'Note',
  429. name: 'Yellow',
  430. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
  431. }
  432. end
  433. it 'does not add a vote to the poll' do
  434. expect(poll.votes.first).to be_nil
  435. end
  436. end
  437. end
  438. context 'when sender is followed by local users' do
  439. subject { described_class.new(json, sender, delivery: true) }
  440. before do
  441. Fabricate(:account).follow!(sender)
  442. subject.perform
  443. end
  444. let(:object_json) do
  445. {
  446. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  447. type: 'Note',
  448. content: 'Lorem ipsum',
  449. }
  450. end
  451. it 'creates status' do
  452. status = sender.statuses.first
  453. expect(status).to_not be_nil
  454. expect(status.text).to eq 'Lorem ipsum'
  455. end
  456. end
  457. context 'when sender replies to local status' do
  458. let!(:local_status) { Fabricate(:status) }
  459. subject { described_class.new(json, sender, delivery: true) }
  460. before do
  461. subject.perform
  462. end
  463. let(:object_json) do
  464. {
  465. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  466. type: 'Note',
  467. content: 'Lorem ipsum',
  468. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
  469. }
  470. end
  471. it 'creates status' do
  472. status = sender.statuses.first
  473. expect(status).to_not be_nil
  474. expect(status.text).to eq 'Lorem ipsum'
  475. end
  476. end
  477. context 'when sender targets a local user' do
  478. let!(:local_account) { Fabricate(:account) }
  479. subject { described_class.new(json, sender, delivery: true) }
  480. before do
  481. subject.perform
  482. end
  483. let(:object_json) do
  484. {
  485. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  486. type: 'Note',
  487. content: 'Lorem ipsum',
  488. to: ActivityPub::TagManager.instance.uri_for(local_account),
  489. }
  490. end
  491. it 'creates status' do
  492. status = sender.statuses.first
  493. expect(status).to_not be_nil
  494. expect(status.text).to eq 'Lorem ipsum'
  495. end
  496. end
  497. context 'when sender cc\'s a local user' do
  498. let!(:local_account) { Fabricate(:account) }
  499. subject { described_class.new(json, sender, delivery: true) }
  500. before do
  501. subject.perform
  502. end
  503. let(:object_json) do
  504. {
  505. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  506. type: 'Note',
  507. content: 'Lorem ipsum',
  508. cc: ActivityPub::TagManager.instance.uri_for(local_account),
  509. }
  510. end
  511. it 'creates status' do
  512. status = sender.statuses.first
  513. expect(status).to_not be_nil
  514. expect(status.text).to eq 'Lorem ipsum'
  515. end
  516. end
  517. context 'when the sender has no relevance to local activity' do
  518. subject { described_class.new(json, sender, delivery: true) }
  519. before do
  520. subject.perform
  521. end
  522. let(:object_json) do
  523. {
  524. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  525. type: 'Note',
  526. content: 'Lorem ipsum',
  527. }
  528. end
  529. it 'does not create anything' do
  530. expect(sender.statuses.count).to eq 0
  531. end
  532. end
  533. end
  534. end