resolve_account_service_spec.rb 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. require 'rails_helper'
  2. RSpec.describe ResolveAccountService, type: :service do
  3. subject { described_class.new }
  4. before do
  5. stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
  6. stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
  7. stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt'))
  8. stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
  9. stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
  10. stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
  11. stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:hoge@example.com').to_return(status: 410)
  12. end
  13. context 'when there is an LRDD endpoint but no resolvable account' do
  14. before do
  15. stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
  16. stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404)
  17. end
  18. it 'returns nil' do
  19. expect(subject.call('catsrgr8@quitter.no')).to be_nil
  20. end
  21. end
  22. context 'when there is no LRDD endpoint nor resolvable account' do
  23. before do
  24. stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com").to_return(status: 404)
  25. end
  26. it 'returns nil' do
  27. expect(subject.call('catsrgr8@example.com')).to be_nil
  28. end
  29. end
  30. context 'when webfinger returns http gone' do
  31. context 'for a previously known account' do
  32. before do
  33. Fabricate(:account, username: 'hoge', domain: 'example.com', last_webfingered_at: nil)
  34. allow(AccountDeletionWorker).to receive(:perform_async)
  35. end
  36. it 'returns nil' do
  37. expect(subject.call('hoge@example.com')).to be_nil
  38. end
  39. it 'queues account deletion worker' do
  40. subject.call('hoge@example.com')
  41. expect(AccountDeletionWorker).to have_received(:perform_async)
  42. end
  43. end
  44. context 'for a previously unknown account' do
  45. it 'returns nil' do
  46. expect(subject.call('hoge@example.com')).to be_nil
  47. end
  48. end
  49. end
  50. context 'with a legitimate webfinger redirection' do
  51. before do
  52. webfinger = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo' }] }
  53. stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
  54. end
  55. it 'returns new remote account' do
  56. account = subject.call('Foo@redirected.example.com')
  57. expect(account.activitypub?).to eq true
  58. expect(account.acct).to eq 'foo@ap.example.com'
  59. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  60. end
  61. end
  62. context 'with too many webfinger redirections' do
  63. before do
  64. webfinger = { subject: 'acct:foo@evil.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo' }] }
  65. stub_request(:get, 'https://redirected.example.com/.well-known/webfinger?resource=acct:Foo@redirected.example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
  66. webfinger2 = { subject: 'acct:foo@ap.example.com', links: [{ rel: 'self', href: 'https://ap.example.com/users/foo' }] }
  67. stub_request(:get, 'https://evil.example.com/.well-known/webfinger?resource=acct:foo@evil.example.com').to_return(body: Oj.dump(webfinger2), headers: { 'Content-Type': 'application/jrd+json' })
  68. end
  69. it 'returns new remote account' do
  70. expect { subject.call('Foo@redirected.example.com') }.to raise_error Webfinger::RedirectError
  71. end
  72. end
  73. context 'with an ActivityPub account' do
  74. it 'returns new remote account' do
  75. account = subject.call('foo@ap.example.com')
  76. expect(account.activitypub?).to eq true
  77. expect(account.domain).to eq 'ap.example.com'
  78. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  79. end
  80. context 'with multiple types' do
  81. before do
  82. stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-individual.txt'))
  83. end
  84. it 'returns new remote account' do
  85. account = subject.call('foo@ap.example.com')
  86. expect(account.activitypub?).to eq true
  87. expect(account.domain).to eq 'ap.example.com'
  88. expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
  89. expect(account.actor_type).to eq 'Person'
  90. end
  91. end
  92. end
  93. it 'processes one remote account at a time using locks' do
  94. wait_for_start = true
  95. fail_occurred = false
  96. return_values = Concurrent::Array.new
  97. # Preload classes that throw circular dependency errors in threads
  98. Account
  99. TagManager
  100. DomainBlock
  101. threads = Array.new(5) do
  102. Thread.new do
  103. true while wait_for_start
  104. begin
  105. return_values << described_class.new.call('foo@ap.example.com')
  106. rescue ActiveRecord::RecordNotUnique
  107. fail_occurred = true
  108. end
  109. end
  110. end
  111. wait_for_start = false
  112. threads.each(&:join)
  113. expect(fail_occurred).to be false
  114. expect(return_values).to_not include(nil)
  115. end
  116. end