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.

787 lines
22 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. stub_request(:get, 'http://example.com/emojib.png').to_return(body: attachment_fixture('emojo.png'), headers: { 'Content-Type' => 'application/octet-stream' })
  18. end
  19. describe '#perform' do
  20. context 'when fetching' do
  21. subject { described_class.new(json, sender) }
  22. before do
  23. subject.perform
  24. end
  25. context 'unknown object type' do
  26. let(:object_json) do
  27. {
  28. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  29. type: 'Banana',
  30. content: 'Lorem ipsum',
  31. }
  32. end
  33. it 'does not create a status' do
  34. expect(sender.statuses.count).to be_zero
  35. end
  36. end
  37. context 'standalone' do
  38. let(:object_json) do
  39. {
  40. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  41. type: 'Note',
  42. content: 'Lorem ipsum',
  43. }
  44. end
  45. it 'creates status' do
  46. status = sender.statuses.first
  47. expect(status).to_not be_nil
  48. expect(status.text).to eq 'Lorem ipsum'
  49. end
  50. it 'missing to/cc defaults to direct privacy' do
  51. status = sender.statuses.first
  52. expect(status).to_not be_nil
  53. expect(status.visibility).to eq 'direct'
  54. end
  55. end
  56. context 'public' do
  57. let(:object_json) do
  58. {
  59. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  60. type: 'Note',
  61. content: 'Lorem ipsum',
  62. to: 'https://www.w3.org/ns/activitystreams#Public',
  63. }
  64. end
  65. it 'creates status' do
  66. status = sender.statuses.first
  67. expect(status).to_not be_nil
  68. expect(status.visibility).to eq 'public'
  69. end
  70. end
  71. context 'unlisted' do
  72. let(:object_json) do
  73. {
  74. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  75. type: 'Note',
  76. content: 'Lorem ipsum',
  77. cc: 'https://www.w3.org/ns/activitystreams#Public',
  78. }
  79. end
  80. it 'creates status' do
  81. status = sender.statuses.first
  82. expect(status).to_not be_nil
  83. expect(status.visibility).to eq 'unlisted'
  84. end
  85. end
  86. context 'private' do
  87. let(:object_json) do
  88. {
  89. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  90. type: 'Note',
  91. content: 'Lorem ipsum',
  92. to: 'http://example.com/followers',
  93. }
  94. end
  95. it 'creates status' do
  96. status = sender.statuses.first
  97. expect(status).to_not be_nil
  98. expect(status.visibility).to eq 'private'
  99. end
  100. end
  101. context 'limited' do
  102. let(:recipient) { Fabricate(:account) }
  103. let(:object_json) do
  104. {
  105. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  106. type: 'Note',
  107. content: 'Lorem ipsum',
  108. to: ActivityPub::TagManager.instance.uri_for(recipient),
  109. }
  110. end
  111. it 'creates status' do
  112. status = sender.statuses.first
  113. expect(status).to_not be_nil
  114. expect(status.visibility).to eq 'limited'
  115. end
  116. it 'creates silent mention' do
  117. status = sender.statuses.first
  118. expect(status.mentions.first).to be_silent
  119. end
  120. end
  121. context 'direct' do
  122. let(:recipient) { Fabricate(:account) }
  123. let(:object_json) do
  124. {
  125. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  126. type: 'Note',
  127. content: 'Lorem ipsum',
  128. to: ActivityPub::TagManager.instance.uri_for(recipient),
  129. tag: {
  130. type: 'Mention',
  131. href: ActivityPub::TagManager.instance.uri_for(recipient),
  132. },
  133. }
  134. end
  135. it 'creates status' do
  136. status = sender.statuses.first
  137. expect(status).to_not be_nil
  138. expect(status.visibility).to eq 'direct'
  139. end
  140. end
  141. context 'as a reply' do
  142. let(:original_status) { Fabricate(:status) }
  143. let(:object_json) do
  144. {
  145. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  146. type: 'Note',
  147. content: 'Lorem ipsum',
  148. inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
  149. }
  150. end
  151. it 'creates status' do
  152. status = sender.statuses.first
  153. expect(status).to_not be_nil
  154. expect(status.thread).to eq original_status
  155. expect(status.reply?).to be true
  156. expect(status.in_reply_to_account).to eq original_status.account
  157. expect(status.conversation).to eq original_status.conversation
  158. end
  159. end
  160. context 'with mentions' do
  161. let(:recipient) { Fabricate(:account) }
  162. let(:object_json) do
  163. {
  164. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  165. type: 'Note',
  166. content: 'Lorem ipsum',
  167. tag: [
  168. {
  169. type: 'Mention',
  170. href: ActivityPub::TagManager.instance.uri_for(recipient),
  171. },
  172. ],
  173. }
  174. end
  175. it 'creates status' do
  176. status = sender.statuses.first
  177. expect(status).to_not be_nil
  178. expect(status.mentions.map(&:account)).to include(recipient)
  179. end
  180. end
  181. context 'with mentions missing href' do
  182. let(:object_json) do
  183. {
  184. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  185. type: 'Note',
  186. content: 'Lorem ipsum',
  187. tag: [
  188. {
  189. type: 'Mention',
  190. },
  191. ],
  192. }
  193. end
  194. it 'creates status' do
  195. status = sender.statuses.first
  196. expect(status).to_not be_nil
  197. end
  198. end
  199. context 'with media attachments' do
  200. let(:object_json) do
  201. {
  202. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  203. type: 'Note',
  204. content: 'Lorem ipsum',
  205. attachment: [
  206. {
  207. type: 'Document',
  208. mediaType: 'image/png',
  209. url: 'http://example.com/attachment.png',
  210. },
  211. ],
  212. }
  213. end
  214. it 'creates status' do
  215. status = sender.statuses.first
  216. expect(status).to_not be_nil
  217. expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
  218. end
  219. end
  220. context 'with media attachments with long description' do
  221. let(:object_json) do
  222. {
  223. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  224. type: 'Note',
  225. content: 'Lorem ipsum',
  226. attachment: [
  227. {
  228. type: 'Document',
  229. mediaType: 'image/png',
  230. url: 'http://example.com/attachment.png',
  231. name: '*' * 1500,
  232. },
  233. ],
  234. }
  235. end
  236. it 'creates status' do
  237. status = sender.statuses.first
  238. expect(status).to_not be_nil
  239. expect(status.media_attachments.map(&:description)).to include('*' * 1500)
  240. end
  241. end
  242. context 'with media attachments with long description as summary' do
  243. let(:object_json) do
  244. {
  245. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  246. type: 'Note',
  247. content: 'Lorem ipsum',
  248. attachment: [
  249. {
  250. type: 'Document',
  251. mediaType: 'image/png',
  252. url: 'http://example.com/attachment.png',
  253. summary: '*' * 1500,
  254. },
  255. ],
  256. }
  257. end
  258. it 'creates status' do
  259. status = sender.statuses.first
  260. expect(status).to_not be_nil
  261. expect(status.media_attachments.map(&:description)).to include('*' * 1500)
  262. end
  263. end
  264. context 'with media attachments with focal points' do
  265. let(:object_json) do
  266. {
  267. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  268. type: 'Note',
  269. content: 'Lorem ipsum',
  270. attachment: [
  271. {
  272. type: 'Document',
  273. mediaType: 'image/png',
  274. url: 'http://example.com/attachment.png',
  275. focalPoint: [0.5, -0.7],
  276. },
  277. ],
  278. }
  279. end
  280. it 'creates status' do
  281. status = sender.statuses.first
  282. expect(status).to_not be_nil
  283. expect(status.media_attachments.map(&:focus)).to include('0.5,-0.7')
  284. end
  285. end
  286. context 'with media attachments missing url' do
  287. let(:object_json) do
  288. {
  289. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  290. type: 'Note',
  291. content: 'Lorem ipsum',
  292. attachment: [
  293. {
  294. type: 'Document',
  295. mediaType: 'image/png',
  296. },
  297. ],
  298. }
  299. end
  300. it 'creates status' do
  301. status = sender.statuses.first
  302. expect(status).to_not be_nil
  303. end
  304. end
  305. context 'with hashtags' do
  306. let(:object_json) do
  307. {
  308. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  309. type: 'Note',
  310. content: 'Lorem ipsum',
  311. tag: [
  312. {
  313. type: 'Hashtag',
  314. href: 'http://example.com/blah',
  315. name: '#test',
  316. },
  317. ],
  318. }
  319. end
  320. it 'creates status' do
  321. status = sender.statuses.first
  322. expect(status).to_not be_nil
  323. expect(status.tags.map(&:name)).to include('test')
  324. end
  325. end
  326. context 'with hashtags missing name' do
  327. let(:object_json) do
  328. {
  329. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  330. type: 'Note',
  331. content: 'Lorem ipsum',
  332. tag: [
  333. {
  334. type: 'Hashtag',
  335. href: 'http://example.com/blah',
  336. },
  337. ],
  338. }
  339. end
  340. it 'creates status' do
  341. status = sender.statuses.first
  342. expect(status).to_not be_nil
  343. end
  344. end
  345. context 'with hashtags invalid name' do
  346. let(:object_json) do
  347. {
  348. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  349. type: 'Note',
  350. content: 'Lorem ipsum',
  351. tag: [
  352. {
  353. type: 'Hashtag',
  354. href: 'http://example.com/blah',
  355. name: 'foo, #eh !',
  356. },
  357. ],
  358. }
  359. end
  360. it 'creates status' do
  361. status = sender.statuses.first
  362. expect(status).to_not be_nil
  363. end
  364. end
  365. context 'with emojis' do
  366. let(:object_json) do
  367. {
  368. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  369. type: 'Note',
  370. content: 'Lorem ipsum :tinking:',
  371. tag: [
  372. {
  373. type: 'Emoji',
  374. icon: {
  375. url: 'http://example.com/emoji.png',
  376. },
  377. name: 'tinking',
  378. },
  379. ],
  380. }
  381. end
  382. it 'creates status' do
  383. status = sender.statuses.first
  384. expect(status).to_not be_nil
  385. expect(status.emojis.map(&:shortcode)).to include('tinking')
  386. end
  387. end
  388. context 'with emojis served with invalid content-type' do
  389. let(:object_json) do
  390. {
  391. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  392. type: 'Note',
  393. content: 'Lorem ipsum :tinkong:',
  394. tag: [
  395. {
  396. type: 'Emoji',
  397. icon: {
  398. url: 'http://example.com/emojib.png',
  399. },
  400. name: 'tinkong',
  401. },
  402. ],
  403. }
  404. end
  405. it 'creates status' do
  406. status = sender.statuses.first
  407. expect(status).to_not be_nil
  408. expect(status.emojis.map(&:shortcode)).to include('tinkong')
  409. end
  410. end
  411. context 'with emojis missing name' do
  412. let(:object_json) do
  413. {
  414. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  415. type: 'Note',
  416. content: 'Lorem ipsum :tinking:',
  417. tag: [
  418. {
  419. type: 'Emoji',
  420. icon: {
  421. url: 'http://example.com/emoji.png',
  422. },
  423. },
  424. ],
  425. }
  426. end
  427. it 'creates status' do
  428. status = sender.statuses.first
  429. expect(status).to_not be_nil
  430. end
  431. end
  432. context 'with emojis missing icon' do
  433. let(:object_json) do
  434. {
  435. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  436. type: 'Note',
  437. content: 'Lorem ipsum :tinking:',
  438. tag: [
  439. {
  440. type: 'Emoji',
  441. name: 'tinking',
  442. },
  443. ],
  444. }
  445. end
  446. it 'creates status' do
  447. status = sender.statuses.first
  448. expect(status).to_not be_nil
  449. end
  450. end
  451. context 'with poll' do
  452. let(:object_json) do
  453. {
  454. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  455. type: 'Question',
  456. content: 'Which color was the submarine?',
  457. oneOf: [
  458. {
  459. name: 'Yellow',
  460. replies: {
  461. type: 'Collection',
  462. totalItems: 10,
  463. },
  464. },
  465. {
  466. name: 'Blue',
  467. replies: {
  468. type: 'Collection',
  469. totalItems: 3,
  470. }
  471. },
  472. ],
  473. }
  474. end
  475. it 'creates status' do
  476. status = sender.statuses.first
  477. expect(status).to_not be_nil
  478. expect(status.poll).to_not be_nil
  479. end
  480. it 'creates a poll' do
  481. poll = sender.polls.first
  482. expect(poll).to_not be_nil
  483. expect(poll.status).to_not be_nil
  484. expect(poll.options).to eq %w(Yellow Blue)
  485. expect(poll.cached_tallies).to eq [10, 3]
  486. end
  487. end
  488. context 'when a vote to a local poll' do
  489. let(:poll) { Fabricate(:poll, options: %w(Yellow Blue)) }
  490. let!(:local_status) { Fabricate(:status, poll: poll) }
  491. let(:object_json) do
  492. {
  493. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  494. type: 'Note',
  495. name: 'Yellow',
  496. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
  497. }
  498. end
  499. it 'adds a vote to the poll with correct uri' do
  500. vote = poll.votes.first
  501. expect(vote).to_not be_nil
  502. expect(vote.uri).to eq object_json[:id]
  503. expect(poll.reload.cached_tallies).to eq [1, 0]
  504. end
  505. end
  506. context 'when a vote to an expired local poll' do
  507. let(:poll) do
  508. poll = Fabricate.build(:poll, options: %w(Yellow Blue), expires_at: 1.day.ago)
  509. poll.save(validate: false)
  510. poll
  511. end
  512. let!(:local_status) { Fabricate(:status, poll: poll) }
  513. let(:object_json) do
  514. {
  515. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  516. type: 'Note',
  517. name: 'Yellow',
  518. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
  519. }
  520. end
  521. it 'does not add a vote to the poll' do
  522. expect(poll.votes.first).to be_nil
  523. end
  524. end
  525. end
  526. context 'with an encrypted message' do
  527. let(:recipient) { Fabricate(:account) }
  528. let(:target_device) { Fabricate(:device, account: recipient) }
  529. subject { described_class.new(json, sender, delivery: true, delivered_to_account_id: recipient.id) }
  530. let(:object_json) do
  531. {
  532. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  533. type: 'EncryptedMessage',
  534. attributedTo: {
  535. type: 'Device',
  536. deviceId: '1234',
  537. },
  538. to: {
  539. type: 'Device',
  540. deviceId: target_device.device_id,
  541. },
  542. messageType: 1,
  543. cipherText: 'Foo',
  544. messageFranking: 'Baz678',
  545. digest: {
  546. digestAlgorithm: 'Bar456',
  547. digestValue: 'Foo123',
  548. },
  549. }
  550. end
  551. before do
  552. subject.perform
  553. end
  554. it 'creates an encrypted message' do
  555. encrypted_message = target_device.encrypted_messages.reload.first
  556. expect(encrypted_message).to_not be_nil
  557. expect(encrypted_message.from_device_id).to eq '1234'
  558. expect(encrypted_message.from_account).to eq sender
  559. expect(encrypted_message.type).to eq 1
  560. expect(encrypted_message.body).to eq 'Foo'
  561. expect(encrypted_message.digest).to eq 'Foo123'
  562. end
  563. it 'creates a message franking' do
  564. encrypted_message = target_device.encrypted_messages.reload.first
  565. message_franking = encrypted_message.message_franking
  566. crypt = ActiveSupport::MessageEncryptor.new(SystemKey.current_key, serializer: Oj)
  567. json = crypt.decrypt_and_verify(message_franking)
  568. expect(json['source_account_id']).to eq sender.id
  569. expect(json['target_account_id']).to eq recipient.id
  570. expect(json['original_franking']).to eq 'Baz678'
  571. end
  572. end
  573. context 'when sender is followed by local users' do
  574. subject { described_class.new(json, sender, delivery: true) }
  575. before do
  576. Fabricate(:account).follow!(sender)
  577. subject.perform
  578. end
  579. let(:object_json) do
  580. {
  581. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  582. type: 'Note',
  583. content: 'Lorem ipsum',
  584. }
  585. end
  586. it 'creates status' do
  587. status = sender.statuses.first
  588. expect(status).to_not be_nil
  589. expect(status.text).to eq 'Lorem ipsum'
  590. end
  591. end
  592. context 'when sender replies to local status' do
  593. let!(:local_status) { Fabricate(:status) }
  594. subject { described_class.new(json, sender, delivery: true) }
  595. before do
  596. subject.perform
  597. end
  598. let(:object_json) do
  599. {
  600. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  601. type: 'Note',
  602. content: 'Lorem ipsum',
  603. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
  604. }
  605. end
  606. it 'creates status' do
  607. status = sender.statuses.first
  608. expect(status).to_not be_nil
  609. expect(status.text).to eq 'Lorem ipsum'
  610. end
  611. end
  612. context 'when sender targets a local user' do
  613. let!(:local_account) { Fabricate(:account) }
  614. subject { described_class.new(json, sender, delivery: true) }
  615. before do
  616. subject.perform
  617. end
  618. let(:object_json) do
  619. {
  620. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  621. type: 'Note',
  622. content: 'Lorem ipsum',
  623. to: ActivityPub::TagManager.instance.uri_for(local_account),
  624. }
  625. end
  626. it 'creates status' do
  627. status = sender.statuses.first
  628. expect(status).to_not be_nil
  629. expect(status.text).to eq 'Lorem ipsum'
  630. end
  631. end
  632. context 'when sender cc\'s a local user' do
  633. let!(:local_account) { Fabricate(:account) }
  634. subject { described_class.new(json, sender, delivery: true) }
  635. before do
  636. subject.perform
  637. end
  638. let(:object_json) do
  639. {
  640. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  641. type: 'Note',
  642. content: 'Lorem ipsum',
  643. cc: ActivityPub::TagManager.instance.uri_for(local_account),
  644. }
  645. end
  646. it 'creates status' do
  647. status = sender.statuses.first
  648. expect(status).to_not be_nil
  649. expect(status.text).to eq 'Lorem ipsum'
  650. end
  651. end
  652. context 'when the sender has no relevance to local activity' do
  653. subject { described_class.new(json, sender, delivery: true) }
  654. before do
  655. subject.perform
  656. end
  657. let(:object_json) do
  658. {
  659. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  660. type: 'Note',
  661. content: 'Lorem ipsum',
  662. }
  663. end
  664. it 'does not create anything' do
  665. expect(sender.statuses.count).to eq 0
  666. end
  667. end
  668. end
  669. end