create_spec.rb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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. end
  419. context 'when sender is followed by local users' do
  420. subject { described_class.new(json, sender, delivery: true) }
  421. before do
  422. Fabricate(:account).follow!(sender)
  423. subject.perform
  424. end
  425. let(:object_json) do
  426. {
  427. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  428. type: 'Note',
  429. content: 'Lorem ipsum',
  430. }
  431. end
  432. it 'creates status' do
  433. status = sender.statuses.first
  434. expect(status).to_not be_nil
  435. expect(status.text).to eq 'Lorem ipsum'
  436. end
  437. end
  438. context 'when sender replies to local status' do
  439. let!(:local_status) { Fabricate(:status) }
  440. subject { described_class.new(json, sender, delivery: true) }
  441. before do
  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. inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
  450. }
  451. end
  452. it 'creates status' do
  453. status = sender.statuses.first
  454. expect(status).to_not be_nil
  455. expect(status.text).to eq 'Lorem ipsum'
  456. end
  457. end
  458. context 'when sender targets a local user' do
  459. let!(:local_account) { Fabricate(:account) }
  460. subject { described_class.new(json, sender, delivery: true) }
  461. before do
  462. subject.perform
  463. end
  464. let(:object_json) do
  465. {
  466. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  467. type: 'Note',
  468. content: 'Lorem ipsum',
  469. to: ActivityPub::TagManager.instance.uri_for(local_account),
  470. }
  471. end
  472. it 'creates status' do
  473. status = sender.statuses.first
  474. expect(status).to_not be_nil
  475. expect(status.text).to eq 'Lorem ipsum'
  476. end
  477. end
  478. context 'when sender cc\'s a local user' do
  479. let!(:local_account) { Fabricate(:account) }
  480. subject { described_class.new(json, sender, delivery: true) }
  481. before do
  482. subject.perform
  483. end
  484. let(:object_json) do
  485. {
  486. id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
  487. type: 'Note',
  488. content: 'Lorem ipsum',
  489. cc: ActivityPub::TagManager.instance.uri_for(local_account),
  490. }
  491. end
  492. it 'creates status' do
  493. status = sender.statuses.first
  494. expect(status).to_not be_nil
  495. expect(status.text).to eq 'Lorem ipsum'
  496. end
  497. end
  498. context 'when the sender has no relevance to local activity' do
  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. }
  509. end
  510. it 'does not create anything' do
  511. expect(sender.statuses.count).to eq 0
  512. end
  513. end
  514. end
  515. end