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.

809 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 'private with inlined Collection in audience' do
  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: {
  108. 'type': 'OrderedCollection',
  109. 'id': 'http://example.com/followers',
  110. 'first': 'http://example.com/followers?page=true',
  111. }
  112. }
  113. end
  114. it 'creates status' do
  115. status = sender.statuses.first
  116. expect(status).to_not be_nil
  117. expect(status.visibility).to eq 'private'
  118. end
  119. end
  120. context 'limited' 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. }
  129. end
  130. it 'creates status' do
  131. status = sender.statuses.first
  132. expect(status).to_not be_nil
  133. expect(status.visibility).to eq 'limited'
  134. end
  135. it 'creates silent mention' do
  136. status = sender.statuses.first
  137. expect(status.mentions.first).to be_silent
  138. end
  139. end
  140. context 'direct' do
  141. let(:recipient) { Fabricate(:account) }
  142. let(:object_json) do
  143. {
  144. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  145. type: 'Note',
  146. content: 'Lorem ipsum',
  147. to: ActivityPub::TagManager.instance.uri_for(recipient),
  148. tag: {
  149. type: 'Mention',
  150. href: ActivityPub::TagManager.instance.uri_for(recipient),
  151. },
  152. }
  153. end
  154. it 'creates status' do
  155. status = sender.statuses.first
  156. expect(status).to_not be_nil
  157. expect(status.visibility).to eq 'direct'
  158. end
  159. end
  160. context 'as a reply' do
  161. let(:original_status) { Fabricate(:status) }
  162. let(:object_json) do
  163. {
  164. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  165. type: 'Note',
  166. content: 'Lorem ipsum',
  167. inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
  168. }
  169. end
  170. it 'creates status' do
  171. status = sender.statuses.first
  172. expect(status).to_not be_nil
  173. expect(status.thread).to eq original_status
  174. expect(status.reply?).to be true
  175. expect(status.in_reply_to_account).to eq original_status.account
  176. expect(status.conversation).to eq original_status.conversation
  177. end
  178. end
  179. context 'with mentions' do
  180. let(:recipient) { Fabricate(:account) }
  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. href: ActivityPub::TagManager.instance.uri_for(recipient),
  190. },
  191. ],
  192. }
  193. end
  194. it 'creates status' do
  195. status = sender.statuses.first
  196. expect(status).to_not be_nil
  197. expect(status.mentions.map(&:account)).to include(recipient)
  198. end
  199. end
  200. context 'with mentions missing href' do
  201. let(:object_json) do
  202. {
  203. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  204. type: 'Note',
  205. content: 'Lorem ipsum',
  206. tag: [
  207. {
  208. type: 'Mention',
  209. },
  210. ],
  211. }
  212. end
  213. it 'creates status' do
  214. status = sender.statuses.first
  215. expect(status).to_not be_nil
  216. end
  217. end
  218. context 'with media attachments' do
  219. let(:object_json) do
  220. {
  221. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  222. type: 'Note',
  223. content: 'Lorem ipsum',
  224. attachment: [
  225. {
  226. type: 'Document',
  227. mediaType: 'image/png',
  228. url: 'http://example.com/attachment.png',
  229. },
  230. ],
  231. }
  232. end
  233. it 'creates status' do
  234. status = sender.statuses.first
  235. expect(status).to_not be_nil
  236. expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
  237. end
  238. end
  239. context 'with media attachments with long description' do
  240. let(:object_json) do
  241. {
  242. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  243. type: 'Note',
  244. content: 'Lorem ipsum',
  245. attachment: [
  246. {
  247. type: 'Document',
  248. mediaType: 'image/png',
  249. url: 'http://example.com/attachment.png',
  250. name: '*' * 1500,
  251. },
  252. ],
  253. }
  254. end
  255. it 'creates status' do
  256. status = sender.statuses.first
  257. expect(status).to_not be_nil
  258. expect(status.media_attachments.map(&:description)).to include('*' * 1500)
  259. end
  260. end
  261. context 'with media attachments with long description as summary' do
  262. let(:object_json) do
  263. {
  264. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  265. type: 'Note',
  266. content: 'Lorem ipsum',
  267. attachment: [
  268. {
  269. type: 'Document',
  270. mediaType: 'image/png',
  271. url: 'http://example.com/attachment.png',
  272. summary: '*' * 1500,
  273. },
  274. ],
  275. }
  276. end
  277. it 'creates status' do
  278. status = sender.statuses.first
  279. expect(status).to_not be_nil
  280. expect(status.media_attachments.map(&:description)).to include('*' * 1500)
  281. end
  282. end
  283. context 'with media attachments with focal points' do
  284. let(:object_json) do
  285. {
  286. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  287. type: 'Note',
  288. content: 'Lorem ipsum',
  289. attachment: [
  290. {
  291. type: 'Document',
  292. mediaType: 'image/png',
  293. url: 'http://example.com/attachment.png',
  294. focalPoint: [0.5, -0.7],
  295. },
  296. ],
  297. }
  298. end
  299. it 'creates status' do
  300. status = sender.statuses.first
  301. expect(status).to_not be_nil
  302. expect(status.media_attachments.map(&:focus)).to include('0.5,-0.7')
  303. end
  304. end
  305. context 'with media attachments missing url' 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. attachment: [
  312. {
  313. type: 'Document',
  314. mediaType: 'image/png',
  315. },
  316. ],
  317. }
  318. end
  319. it 'creates status' do
  320. status = sender.statuses.first
  321. expect(status).to_not be_nil
  322. end
  323. end
  324. context 'with hashtags' do
  325. let(:object_json) do
  326. {
  327. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  328. type: 'Note',
  329. content: 'Lorem ipsum',
  330. tag: [
  331. {
  332. type: 'Hashtag',
  333. href: 'http://example.com/blah',
  334. name: '#test',
  335. },
  336. ],
  337. }
  338. end
  339. it 'creates status' do
  340. status = sender.statuses.first
  341. expect(status).to_not be_nil
  342. expect(status.tags.map(&:name)).to include('test')
  343. end
  344. end
  345. context 'with hashtags missing 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. },
  356. ],
  357. }
  358. end
  359. it 'creates status' do
  360. status = sender.statuses.first
  361. expect(status).to_not be_nil
  362. end
  363. end
  364. context 'with hashtags invalid name' do
  365. let(:object_json) do
  366. {
  367. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  368. type: 'Note',
  369. content: 'Lorem ipsum',
  370. tag: [
  371. {
  372. type: 'Hashtag',
  373. href: 'http://example.com/blah',
  374. name: 'foo, #eh !',
  375. },
  376. ],
  377. }
  378. end
  379. it 'creates status' do
  380. status = sender.statuses.first
  381. expect(status).to_not be_nil
  382. end
  383. end
  384. context 'with emojis' do
  385. let(:object_json) do
  386. {
  387. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  388. type: 'Note',
  389. content: 'Lorem ipsum :tinking:',
  390. tag: [
  391. {
  392. type: 'Emoji',
  393. icon: {
  394. url: 'http://example.com/emoji.png',
  395. },
  396. name: 'tinking',
  397. },
  398. ],
  399. }
  400. end
  401. it 'creates status' do
  402. status = sender.statuses.first
  403. expect(status).to_not be_nil
  404. expect(status.emojis.map(&:shortcode)).to include('tinking')
  405. end
  406. end
  407. context 'with emojis served with invalid content-type' do
  408. let(:object_json) do
  409. {
  410. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  411. type: 'Note',
  412. content: 'Lorem ipsum :tinkong:',
  413. tag: [
  414. {
  415. type: 'Emoji',
  416. icon: {
  417. url: 'http://example.com/emojib.png',
  418. },
  419. name: 'tinkong',
  420. },
  421. ],
  422. }
  423. end
  424. it 'creates status' do
  425. status = sender.statuses.first
  426. expect(status).to_not be_nil
  427. expect(status.emojis.map(&:shortcode)).to include('tinkong')
  428. end
  429. end
  430. context 'with emojis missing name' do
  431. let(:object_json) do
  432. {
  433. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  434. type: 'Note',
  435. content: 'Lorem ipsum :tinking:',
  436. tag: [
  437. {
  438. type: 'Emoji',
  439. icon: {
  440. url: 'http://example.com/emoji.png',
  441. },
  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 emojis missing icon' do
  452. let(:object_json) do
  453. {
  454. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  455. type: 'Note',
  456. content: 'Lorem ipsum :tinking:',
  457. tag: [
  458. {
  459. type: 'Emoji',
  460. name: 'tinking',
  461. },
  462. ],
  463. }
  464. end
  465. it 'creates status' do
  466. status = sender.statuses.first
  467. expect(status).to_not be_nil
  468. end
  469. end
  470. context 'with poll' do
  471. let(:object_json) do
  472. {
  473. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  474. type: 'Question',
  475. content: 'Which color was the submarine?',
  476. oneOf: [
  477. {
  478. name: 'Yellow',
  479. replies: {
  480. type: 'Collection',
  481. totalItems: 10,
  482. },
  483. },
  484. {
  485. name: 'Blue',
  486. replies: {
  487. type: 'Collection',
  488. totalItems: 3,
  489. }
  490. },
  491. ],
  492. }
  493. end
  494. it 'creates status' do
  495. status = sender.statuses.first
  496. expect(status).to_not be_nil
  497. expect(status.poll).to_not be_nil
  498. end
  499. it 'creates a poll' do
  500. poll = sender.polls.first
  501. expect(poll).to_not be_nil
  502. expect(poll.status).to_not be_nil
  503. expect(poll.options).to eq %w(Yellow Blue)
  504. expect(poll.cached_tallies).to eq [10, 3]
  505. end
  506. end
  507. context 'when a vote to a local poll' do
  508. let(:poll) { Fabricate(:poll, options: %w(Yellow Blue)) }
  509. let!(:local_status) { Fabricate(:status, poll: poll) }
  510. let(:object_json) do
  511. {
  512. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  513. type: 'Note',
  514. name: 'Yellow',
  515. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
  516. }
  517. end
  518. it 'adds a vote to the poll with correct uri' do
  519. vote = poll.votes.first
  520. expect(vote).to_not be_nil
  521. expect(vote.uri).to eq object_json[:id]
  522. expect(poll.reload.cached_tallies).to eq [1, 0]
  523. end
  524. end
  525. context 'when a vote to an expired local poll' do
  526. let(:poll) do
  527. poll = Fabricate.build(:poll, options: %w(Yellow Blue), expires_at: 1.day.ago)
  528. poll.save(validate: false)
  529. poll
  530. end
  531. let!(:local_status) { Fabricate(:status, poll: poll) }
  532. let(:object_json) do
  533. {
  534. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  535. type: 'Note',
  536. name: 'Yellow',
  537. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
  538. }
  539. end
  540. it 'does not add a vote to the poll' do
  541. expect(poll.votes.first).to be_nil
  542. end
  543. end
  544. end
  545. context 'with an encrypted message' do
  546. let(:recipient) { Fabricate(:account) }
  547. let(:target_device) { Fabricate(:device, account: recipient) }
  548. subject { described_class.new(json, sender, delivery: true, delivered_to_account_id: recipient.id) }
  549. let(:object_json) do
  550. {
  551. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  552. type: 'EncryptedMessage',
  553. attributedTo: {
  554. type: 'Device',
  555. deviceId: '1234',
  556. },
  557. to: {
  558. type: 'Device',
  559. deviceId: target_device.device_id,
  560. },
  561. messageType: 1,
  562. cipherText: 'Foo',
  563. messageFranking: 'Baz678',
  564. digest: {
  565. digestAlgorithm: 'Bar456',
  566. digestValue: 'Foo123',
  567. },
  568. }
  569. end
  570. before do
  571. subject.perform
  572. end
  573. it 'creates an encrypted message' do
  574. encrypted_message = target_device.encrypted_messages.reload.first
  575. expect(encrypted_message).to_not be_nil
  576. expect(encrypted_message.from_device_id).to eq '1234'
  577. expect(encrypted_message.from_account).to eq sender
  578. expect(encrypted_message.type).to eq 1
  579. expect(encrypted_message.body).to eq 'Foo'
  580. expect(encrypted_message.digest).to eq 'Foo123'
  581. end
  582. it 'creates a message franking' do
  583. encrypted_message = target_device.encrypted_messages.reload.first
  584. message_franking = encrypted_message.message_franking
  585. crypt = ActiveSupport::MessageEncryptor.new(SystemKey.current_key, serializer: Oj)
  586. json = crypt.decrypt_and_verify(message_franking)
  587. expect(json['source_account_id']).to eq sender.id
  588. expect(json['target_account_id']).to eq recipient.id
  589. expect(json['original_franking']).to eq 'Baz678'
  590. end
  591. end
  592. context 'when sender is followed by local users' do
  593. subject { described_class.new(json, sender, delivery: true) }
  594. before do
  595. Fabricate(:account).follow!(sender)
  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. }
  604. end
  605. it 'creates status' do
  606. status = sender.statuses.first
  607. expect(status).to_not be_nil
  608. expect(status.text).to eq 'Lorem ipsum'
  609. end
  610. end
  611. context 'when sender replies to local status' do
  612. let!(:local_status) { Fabricate(:status) }
  613. subject { described_class.new(json, sender, delivery: true) }
  614. before do
  615. subject.perform
  616. end
  617. let(:object_json) do
  618. {
  619. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  620. type: 'Note',
  621. content: 'Lorem ipsum',
  622. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
  623. }
  624. end
  625. it 'creates status' do
  626. status = sender.statuses.first
  627. expect(status).to_not be_nil
  628. expect(status.text).to eq 'Lorem ipsum'
  629. end
  630. end
  631. context 'when sender targets a local user' do
  632. let!(:local_account) { Fabricate(:account) }
  633. subject { described_class.new(json, sender, delivery: true) }
  634. before do
  635. subject.perform
  636. end
  637. let(:object_json) do
  638. {
  639. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  640. type: 'Note',
  641. content: 'Lorem ipsum',
  642. to: ActivityPub::TagManager.instance.uri_for(local_account),
  643. }
  644. end
  645. it 'creates status' do
  646. status = sender.statuses.first
  647. expect(status).to_not be_nil
  648. expect(status.text).to eq 'Lorem ipsum'
  649. end
  650. end
  651. context 'when sender cc\'s a local user' do
  652. let!(:local_account) { Fabricate(:account) }
  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. cc: ActivityPub::TagManager.instance.uri_for(local_account),
  663. }
  664. end
  665. it 'creates status' do
  666. status = sender.statuses.first
  667. expect(status).to_not be_nil
  668. expect(status.text).to eq 'Lorem ipsum'
  669. end
  670. end
  671. context 'when the sender has no relevance to local activity' do
  672. subject { described_class.new(json, sender, delivery: true) }
  673. before do
  674. subject.perform
  675. end
  676. let(:object_json) do
  677. {
  678. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  679. type: 'Note',
  680. content: 'Lorem ipsum',
  681. }
  682. end
  683. it 'does not create anything' do
  684. expect(sender.statuses.count).to eq 0
  685. end
  686. end
  687. end
  688. end