account_spec.rb 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. require 'rails_helper'
  2. RSpec.describe Account, type: :model do
  3. context do
  4. let(:bob) { Fabricate(:account, username: 'bob') }
  5. subject { Fabricate(:account) }
  6. describe '#follow!' do
  7. it 'creates a follow' do
  8. follow = subject.follow!(bob)
  9. expect(follow).to be_instance_of Follow
  10. expect(follow.account).to eq subject
  11. expect(follow.target_account).to eq bob
  12. end
  13. end
  14. describe '#unfollow!' do
  15. before do
  16. subject.follow!(bob)
  17. end
  18. it 'destroys a follow' do
  19. unfollow = subject.unfollow!(bob)
  20. expect(unfollow).to be_instance_of Follow
  21. expect(unfollow.account).to eq subject
  22. expect(unfollow.target_account).to eq bob
  23. expect(unfollow.destroyed?).to be true
  24. end
  25. end
  26. describe '#following?' do
  27. it 'returns true when the target is followed' do
  28. subject.follow!(bob)
  29. expect(subject.following?(bob)).to be true
  30. end
  31. it 'returns false if the target is not followed' do
  32. expect(subject.following?(bob)).to be false
  33. end
  34. end
  35. end
  36. describe '#local?' do
  37. it 'returns true when the account is local' do
  38. account = Fabricate(:account, domain: nil)
  39. expect(account.local?).to be true
  40. end
  41. it 'returns false when the account is on a different domain' do
  42. account = Fabricate(:account, domain: 'foreign.tld')
  43. expect(account.local?).to be false
  44. end
  45. end
  46. describe 'Local domain user methods' do
  47. around do |example|
  48. before = Rails.configuration.x.local_domain
  49. example.run
  50. Rails.configuration.x.local_domain = before
  51. end
  52. subject { Fabricate(:account, domain: nil, username: 'alice') }
  53. describe '#to_webfinger_s' do
  54. it 'returns a webfinger string for the account' do
  55. Rails.configuration.x.local_domain = 'example.com'
  56. expect(subject.to_webfinger_s).to eq 'acct:alice@example.com'
  57. end
  58. end
  59. describe '#local_username_and_domain' do
  60. it 'returns the username and local domain for the account' do
  61. Rails.configuration.x.local_domain = 'example.com'
  62. expect(subject.local_username_and_domain).to eq 'alice@example.com'
  63. end
  64. end
  65. end
  66. describe '#acct' do
  67. it 'returns username for local users' do
  68. account = Fabricate(:account, domain: nil, username: 'alice')
  69. expect(account.acct).to eql 'alice'
  70. end
  71. it 'returns username@domain for foreign users' do
  72. account = Fabricate(:account, domain: 'foreign.tld', username: 'alice')
  73. expect(account.acct).to eql 'alice@foreign.tld'
  74. end
  75. end
  76. describe '#save_with_optional_media!' do
  77. before do
  78. stub_request(:get, 'https://remote.test/valid_avatar').to_return(request_fixture('avatar.txt'))
  79. stub_request(:get, 'https://remote.test/invalid_avatar').to_return(request_fixture('feed.txt'))
  80. end
  81. let(:account) do
  82. Fabricate(:account,
  83. avatar_remote_url: 'https://remote.test/valid_avatar',
  84. header_remote_url: 'https://remote.test/valid_avatar')
  85. end
  86. let!(:expectation) { account.dup }
  87. context 'with valid properties' do
  88. before do
  89. account.save_with_optional_media!
  90. end
  91. it 'unchanges avatar, header, avatar_remote_url, and header_remote_url' do
  92. expect(account.avatar_remote_url).to eq expectation.avatar_remote_url
  93. expect(account.header_remote_url).to eq expectation.header_remote_url
  94. expect(account.avatar_file_name).to eq expectation.avatar_file_name
  95. expect(account.header_file_name).to eq expectation.header_file_name
  96. end
  97. end
  98. context 'with invalid properties' do
  99. before do
  100. account.avatar_remote_url = 'https://remote.test/invalid_avatar'
  101. account.save_with_optional_media!
  102. end
  103. it 'sets default avatar, header, avatar_remote_url, and header_remote_url' do
  104. expect(account.avatar_remote_url).to eq 'https://remote.test/invalid_avatar'
  105. expect(account.header_remote_url).to eq expectation.header_remote_url
  106. expect(account.avatar_file_name).to eq nil
  107. expect(account.header_file_name).to eq nil
  108. end
  109. end
  110. end
  111. describe '#subscribed?' do
  112. it 'returns false when no subscription expiration information is present' do
  113. account = Fabricate(:account, subscription_expires_at: nil)
  114. expect(account.subscribed?).to be false
  115. end
  116. it 'returns true when subscription expiration has been set' do
  117. account = Fabricate(:account, subscription_expires_at: 30.days.from_now)
  118. expect(account.subscribed?).to be true
  119. end
  120. end
  121. describe '#possibly_stale?' do
  122. let(:account) { Fabricate(:account, last_webfingered_at: last_webfingered_at) }
  123. context 'last_webfingered_at is nil' do
  124. let(:last_webfingered_at) { nil }
  125. it 'returns true' do
  126. expect(account.possibly_stale?).to be true
  127. end
  128. end
  129. context 'last_webfingered_at is more than 24 hours before' do
  130. let(:last_webfingered_at) { 25.hours.ago }
  131. it 'returns true' do
  132. expect(account.possibly_stale?).to be true
  133. end
  134. end
  135. context 'last_webfingered_at is less than 24 hours before' do
  136. let(:last_webfingered_at) { 23.hours.ago }
  137. it 'returns false' do
  138. expect(account.possibly_stale?).to be false
  139. end
  140. end
  141. end
  142. describe '#refresh!' do
  143. let(:account) { Fabricate(:account, domain: domain) }
  144. let(:acct) { account.acct }
  145. context 'domain is nil' do
  146. let(:domain) { nil }
  147. it 'returns nil' do
  148. expect(account.refresh!).to be_nil
  149. end
  150. it 'calls not ResolveAccountService#call' do
  151. expect_any_instance_of(ResolveAccountService).not_to receive(:call).with(acct)
  152. account.refresh!
  153. end
  154. end
  155. context 'domain is present' do
  156. let(:domain) { 'example.com' }
  157. it 'calls ResolveAccountService#call' do
  158. expect_any_instance_of(ResolveAccountService).to receive(:call).with(acct).once
  159. account.refresh!
  160. end
  161. end
  162. end
  163. describe '#to_param' do
  164. it 'returns username' do
  165. account = Fabricate(:account, username: 'alice')
  166. expect(account.to_param).to eq 'alice'
  167. end
  168. end
  169. describe '#keypair' do
  170. it 'returns an RSA key pair' do
  171. account = Fabricate(:account)
  172. expect(account.keypair).to be_instance_of OpenSSL::PKey::RSA
  173. end
  174. end
  175. describe '#object_type' do
  176. it 'is always a person' do
  177. account = Fabricate(:account)
  178. expect(account.object_type).to be :person
  179. end
  180. end
  181. describe '#favourited?' do
  182. let(:original_status) do
  183. author = Fabricate(:account, username: 'original')
  184. Fabricate(:status, account: author)
  185. end
  186. subject { Fabricate(:account) }
  187. context 'when the status is a reblog of another status' do
  188. let(:original_reblog) do
  189. author = Fabricate(:account, username: 'original_reblogger')
  190. Fabricate(:status, reblog: original_status, account: author)
  191. end
  192. it 'is is true when this account has favourited it' do
  193. Fabricate(:favourite, status: original_reblog, account: subject)
  194. expect(subject.favourited?(original_status)).to eq true
  195. end
  196. it 'is false when this account has not favourited it' do
  197. expect(subject.favourited?(original_status)).to eq false
  198. end
  199. end
  200. context 'when the status is an original status' do
  201. it 'is is true when this account has favourited it' do
  202. Fabricate(:favourite, status: original_status, account: subject)
  203. expect(subject.favourited?(original_status)).to eq true
  204. end
  205. it 'is false when this account has not favourited it' do
  206. expect(subject.favourited?(original_status)).to eq false
  207. end
  208. end
  209. end
  210. describe '#reblogged?' do
  211. let(:original_status) do
  212. author = Fabricate(:account, username: 'original')
  213. Fabricate(:status, account: author)
  214. end
  215. subject { Fabricate(:account) }
  216. context 'when the status is a reblog of another status' do
  217. let(:original_reblog) do
  218. author = Fabricate(:account, username: 'original_reblogger')
  219. Fabricate(:status, reblog: original_status, account: author)
  220. end
  221. it 'is true when this account has reblogged it' do
  222. Fabricate(:status, reblog: original_reblog, account: subject)
  223. expect(subject.reblogged?(original_reblog)).to eq true
  224. end
  225. it 'is false when this account has not reblogged it' do
  226. expect(subject.reblogged?(original_reblog)).to eq false
  227. end
  228. end
  229. context 'when the status is an original status' do
  230. it 'is true when this account has reblogged it' do
  231. Fabricate(:status, reblog: original_status, account: subject)
  232. expect(subject.reblogged?(original_status)).to eq true
  233. end
  234. it 'is false when this account has not reblogged it' do
  235. expect(subject.reblogged?(original_status)).to eq false
  236. end
  237. end
  238. end
  239. describe '#excluded_from_timeline_account_ids' do
  240. it 'includes account ids of blockings, blocked_bys and mutes' do
  241. account = Fabricate(:account)
  242. block = Fabricate(:block, account: account)
  243. mute = Fabricate(:mute, account: account)
  244. block_by = Fabricate(:block, target_account: account)
  245. results = account.excluded_from_timeline_account_ids
  246. expect(results.size).to eq 3
  247. expect(results).to include(block.target_account.id)
  248. expect(results).to include(mute.target_account.id)
  249. expect(results).to include(block_by.account.id)
  250. end
  251. end
  252. describe '#excluded_from_timeline_domains' do
  253. it 'returns the domains blocked by the account' do
  254. account = Fabricate(:account)
  255. account.block_domain!('domain')
  256. expect(account.excluded_from_timeline_domains).to match_array ['domain']
  257. end
  258. end
  259. describe '.search_for' do
  260. before do
  261. _missing = Fabricate(
  262. :account,
  263. display_name: "Missing",
  264. username: "missing",
  265. domain: "missing.com"
  266. )
  267. end
  268. it 'accepts ?, \, : and space as delimiter' do
  269. match = Fabricate(
  270. :account,
  271. display_name: 'A & l & i & c & e',
  272. username: 'username',
  273. domain: 'example.com'
  274. )
  275. results = Account.search_for('A?l\i:c e')
  276. expect(results).to eq [match]
  277. end
  278. it 'finds accounts with matching display_name' do
  279. match = Fabricate(
  280. :account,
  281. display_name: "Display Name",
  282. username: "username",
  283. domain: "example.com"
  284. )
  285. results = Account.search_for("display")
  286. expect(results).to eq [match]
  287. end
  288. it 'finds accounts with matching username' do
  289. match = Fabricate(
  290. :account,
  291. display_name: "Display Name",
  292. username: "username",
  293. domain: "example.com"
  294. )
  295. results = Account.search_for("username")
  296. expect(results).to eq [match]
  297. end
  298. it 'finds accounts with matching domain' do
  299. match = Fabricate(
  300. :account,
  301. display_name: "Display Name",
  302. username: "username",
  303. domain: "example.com"
  304. )
  305. results = Account.search_for("example")
  306. expect(results).to eq [match]
  307. end
  308. it 'limits by 10 by default' do
  309. 11.times.each { Fabricate(:account, display_name: "Display Name") }
  310. results = Account.search_for("display")
  311. expect(results.size).to eq 10
  312. end
  313. it 'accepts arbitrary limits' do
  314. 2.times.each { Fabricate(:account, display_name: "Display Name") }
  315. results = Account.search_for("display", 1)
  316. expect(results.size).to eq 1
  317. end
  318. it 'ranks multiple matches higher' do
  319. matches = [
  320. { username: "username", display_name: "username" },
  321. { display_name: "Display Name", username: "username", domain: "example.com" },
  322. ].map(&method(:Fabricate).curry(2).call(:account))
  323. results = Account.search_for("username")
  324. expect(results).to eq matches
  325. end
  326. end
  327. describe '.advanced_search_for' do
  328. it 'accepts ?, \, : and space as delimiter' do
  329. account = Fabricate(:account)
  330. match = Fabricate(
  331. :account,
  332. display_name: 'A & l & i & c & e',
  333. username: 'username',
  334. domain: 'example.com'
  335. )
  336. results = Account.advanced_search_for('A?l\i:c e', account)
  337. expect(results).to eq [match]
  338. end
  339. it 'limits by 10 by default' do
  340. 11.times { Fabricate(:account, display_name: "Display Name") }
  341. results = Account.search_for("display")
  342. expect(results.size).to eq 10
  343. end
  344. it 'accepts arbitrary limits' do
  345. 2.times { Fabricate(:account, display_name: "Display Name") }
  346. results = Account.search_for("display", 1)
  347. expect(results.size).to eq 1
  348. end
  349. it 'ranks followed accounts higher' do
  350. account = Fabricate(:account)
  351. match = Fabricate(:account, username: "Matching")
  352. followed_match = Fabricate(:account, username: "Matcher")
  353. Fabricate(:follow, account: account, target_account: followed_match)
  354. results = Account.advanced_search_for("match", account)
  355. expect(results).to eq [followed_match, match]
  356. expect(results.first.rank).to be > results.last.rank
  357. end
  358. end
  359. describe '.domains' do
  360. it 'returns domains' do
  361. Fabricate(:account, domain: 'domain')
  362. expect(Account.remote.domains).to match_array(['domain'])
  363. end
  364. end
  365. describe '#statuses_count' do
  366. subject { Fabricate(:account) }
  367. it 'counts statuses' do
  368. Fabricate(:status, account: subject)
  369. Fabricate(:status, account: subject)
  370. expect(subject.statuses_count).to eq 2
  371. end
  372. it 'does not count direct statuses' do
  373. Fabricate(:status, account: subject, visibility: :direct)
  374. expect(subject.statuses_count).to eq 0
  375. end
  376. it 'is decremented when status is removed' do
  377. status = Fabricate(:status, account: subject)
  378. expect(subject.statuses_count).to eq 1
  379. status.destroy
  380. expect(subject.statuses_count).to eq 0
  381. end
  382. it 'is decremented when status is removed when account is not preloaded' do
  383. status = Fabricate(:status, account: subject)
  384. expect(subject.reload.statuses_count).to eq 1
  385. clean_status = Status.find(status.id)
  386. expect(clean_status.association(:account).loaded?).to be false
  387. clean_status.destroy
  388. expect(subject.reload.statuses_count).to eq 0
  389. end
  390. end
  391. describe '.following_map' do
  392. it 'returns an hash' do
  393. expect(Account.following_map([], 1)).to be_a Hash
  394. end
  395. end
  396. describe '.followed_by_map' do
  397. it 'returns an hash' do
  398. expect(Account.followed_by_map([], 1)).to be_a Hash
  399. end
  400. end
  401. describe '.blocking_map' do
  402. it 'returns an hash' do
  403. expect(Account.blocking_map([], 1)).to be_a Hash
  404. end
  405. end
  406. describe '.requested_map' do
  407. it 'returns an hash' do
  408. expect(Account.requested_map([], 1)).to be_a Hash
  409. end
  410. end
  411. describe 'MENTION_RE' do
  412. subject { Account::MENTION_RE }
  413. it 'matches usernames in the middle of a sentence' do
  414. expect(subject.match('Hello to @alice from me')[1]).to eq 'alice'
  415. end
  416. it 'matches usernames in the beginning of status' do
  417. expect(subject.match('@alice Hey how are you?')[1]).to eq 'alice'
  418. end
  419. it 'matches full usernames' do
  420. expect(subject.match('@alice@example.com')[1]).to eq 'alice@example.com'
  421. end
  422. it 'matches full usernames with a dot at the end' do
  423. expect(subject.match('Hello @alice@example.com.')[1]).to eq 'alice@example.com'
  424. end
  425. it 'matches dot-prepended usernames' do
  426. expect(subject.match('.@alice I want everybody to see this')[1]).to eq 'alice'
  427. end
  428. it 'does not match e-mails' do
  429. expect(subject.match('Drop me an e-mail at alice@example.com')).to be_nil
  430. end
  431. it 'does not match URLs' do
  432. expect(subject.match('Check this out https://medium.com/@alice/some-article#.abcdef123')).to be_nil
  433. end
  434. xit 'does not match URL querystring' do
  435. expect(subject.match('https://example.com/?x=@alice')).to be_nil
  436. end
  437. end
  438. describe 'validations' do
  439. it 'has a valid fabricator' do
  440. account = Fabricate.build(:account)
  441. account.valid?
  442. expect(account).to be_valid
  443. end
  444. it 'is invalid without a username' do
  445. account = Fabricate.build(:account, username: nil)
  446. account.valid?
  447. expect(account).to model_have_error_on_field(:username)
  448. end
  449. it 'squishes the username before validation' do
  450. account = Fabricate(:account, domain: nil, username: " \u3000bob \t \u00a0 \n ")
  451. expect(account.username).to eq 'bob'
  452. end
  453. context 'when is local' do
  454. it 'is invalid if the username is not unique in case-insensitive comparison among local accounts' do
  455. account_1 = Fabricate(:account, username: 'the_doctor')
  456. account_2 = Fabricate.build(:account, username: 'the_Doctor')
  457. account_2.valid?
  458. expect(account_2).to model_have_error_on_field(:username)
  459. end
  460. it 'is invalid if the username is reserved' do
  461. account = Fabricate.build(:account, username: 'support')
  462. account.valid?
  463. expect(account).to model_have_error_on_field(:username)
  464. end
  465. it 'is valid when username is reserved but record has already been created' do
  466. account = Fabricate.build(:account, username: 'support')
  467. account.save(validate: false)
  468. expect(account.valid?).to be true
  469. end
  470. it 'is valid if we are creating an instance actor account with a period' do
  471. account = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com')
  472. expect(account.valid?).to be true
  473. end
  474. it 'is valid if we are creating a possibly-conflicting instance actor account' do
  475. account_1 = Fabricate(:account, username: 'examplecom')
  476. account_2 = Fabricate.build(:account, id: -99, actor_type: 'Application', locked: true, username: 'example.com')
  477. expect(account_2.valid?).to be true
  478. end
  479. it 'is invalid if the username doesn\'t only contains letters, numbers and underscores' do
  480. account = Fabricate.build(:account, username: 'the-doctor')
  481. account.valid?
  482. expect(account).to model_have_error_on_field(:username)
  483. end
  484. it 'is invalid if the username contains a period' do
  485. account = Fabricate.build(:account, username: 'the.doctor')
  486. account.valid?
  487. expect(account).to model_have_error_on_field(:username)
  488. end
  489. it 'is invalid if the username is longer then 30 characters' do
  490. account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31))
  491. account.valid?
  492. expect(account).to model_have_error_on_field(:username)
  493. end
  494. it 'is invalid if the display name is longer than 30 characters' do
  495. account = Fabricate.build(:account, display_name: Faker::Lorem.characters(number: 31))
  496. account.valid?
  497. expect(account).to model_have_error_on_field(:display_name)
  498. end
  499. it 'is invalid if the note is longer than 500 characters' do
  500. account = Fabricate.build(:account, note: Faker::Lorem.characters(number: 501))
  501. account.valid?
  502. expect(account).to model_have_error_on_field(:note)
  503. end
  504. end
  505. context 'when is remote' do
  506. it 'is invalid if the username is same among accounts in the same normalized domain' do
  507. Fabricate(:account, domain: 'にゃん', username: 'username')
  508. account = Fabricate.build(:account, domain: 'xn--r9j5b5b', username: 'username')
  509. account.valid?
  510. expect(account).to model_have_error_on_field(:username)
  511. end
  512. it 'is invalid if the username is not unique in case-insensitive comparison among accounts in the same normalized domain' do
  513. Fabricate(:account, domain: 'にゃん', username: 'username')
  514. account = Fabricate.build(:account, domain: 'xn--r9j5b5b', username: 'Username')
  515. account.valid?
  516. expect(account).to model_have_error_on_field(:username)
  517. end
  518. it 'is valid even if the username contains hyphens' do
  519. account = Fabricate.build(:account, domain: 'domain', username: 'the-doctor')
  520. account.valid?
  521. expect(account).to_not model_have_error_on_field(:username)
  522. end
  523. it 'is invalid if the username doesn\'t only contains letters, numbers, underscores and hyphens' do
  524. account = Fabricate.build(:account, domain: 'domain', username: 'the doctor')
  525. account.valid?
  526. expect(account).to model_have_error_on_field(:username)
  527. end
  528. it 'is valid even if the username is longer then 30 characters' do
  529. account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31))
  530. account.valid?
  531. expect(account).not_to model_have_error_on_field(:username)
  532. end
  533. it 'is valid even if the display name is longer than 30 characters' do
  534. account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31))
  535. account.valid?
  536. expect(account).not_to model_have_error_on_field(:display_name)
  537. end
  538. it 'is valid even if the note is longer than 500 characters' do
  539. account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501))
  540. account.valid?
  541. expect(account).not_to model_have_error_on_field(:note)
  542. end
  543. end
  544. end
  545. describe 'scopes' do
  546. describe 'alphabetic' do
  547. it 'sorts by alphabetic order of domain and username' do
  548. matches = [
  549. { username: 'a', domain: 'a' },
  550. { username: 'b', domain: 'a' },
  551. { username: 'a', domain: 'b' },
  552. { username: 'b', domain: 'b' },
  553. ].map(&method(:Fabricate).curry(2).call(:account))
  554. expect(Account.where('id > 0').alphabetic).to eq matches
  555. end
  556. end
  557. describe 'matches_display_name' do
  558. it 'matches display name which starts with the given string' do
  559. match = Fabricate(:account, display_name: 'pattern and suffix')
  560. Fabricate(:account, display_name: 'prefix and pattern')
  561. expect(Account.matches_display_name('pattern')).to eq [match]
  562. end
  563. end
  564. describe 'matches_username' do
  565. it 'matches display name which starts with the given string' do
  566. match = Fabricate(:account, username: 'pattern_and_suffix')
  567. Fabricate(:account, username: 'prefix_and_pattern')
  568. expect(Account.matches_username('pattern')).to eq [match]
  569. end
  570. end
  571. describe 'by_domain_and_subdomains' do
  572. it 'returns exact domain matches' do
  573. account = Fabricate(:account, domain: 'example.com')
  574. expect(Account.by_domain_and_subdomains('example.com')).to eq [account]
  575. end
  576. it 'returns subdomains' do
  577. account = Fabricate(:account, domain: 'foo.example.com')
  578. expect(Account.by_domain_and_subdomains('example.com')).to eq [account]
  579. end
  580. it 'does not return partially matching domains' do
  581. account = Fabricate(:account, domain: 'grexample.com')
  582. expect(Account.by_domain_and_subdomains('example.com')).to_not eq [account]
  583. end
  584. end
  585. describe 'expiring' do
  586. it 'returns remote accounts with followers whose subscription expiration date is past or not given' do
  587. local = Fabricate(:account, domain: nil)
  588. matches = [
  589. { domain: 'remote', subscription_expires_at: '2000-01-01T00:00:00Z' },
  590. ].map(&method(:Fabricate).curry(2).call(:account))
  591. matches.each(&local.method(:follow!))
  592. Fabricate(:account, domain: 'remote', subscription_expires_at: nil)
  593. local.follow!(Fabricate(:account, domain: 'remote', subscription_expires_at: '2000-01-03T00:00:00Z'))
  594. local.follow!(Fabricate(:account, domain: nil, subscription_expires_at: nil))
  595. expect(Account.expiring('2000-01-02T00:00:00Z').recent).to eq matches.reverse
  596. end
  597. end
  598. describe 'remote' do
  599. it 'returns an array of accounts who have a domain' do
  600. account_1 = Fabricate(:account, domain: nil)
  601. account_2 = Fabricate(:account, domain: 'example.com')
  602. expect(Account.remote).to match_array([account_2])
  603. end
  604. end
  605. describe 'by_domain_accounts' do
  606. it 'returns accounts grouped by domain sorted by accounts' do
  607. 2.times { Fabricate(:account, domain: 'example.com') }
  608. Fabricate(:account, domain: 'example2.com')
  609. results = Account.where('id > 0').by_domain_accounts
  610. expect(results.length).to eq 2
  611. expect(results.first.domain).to eq 'example.com'
  612. expect(results.first.accounts_count).to eq 2
  613. expect(results.last.domain).to eq 'example2.com'
  614. expect(results.last.accounts_count).to eq 1
  615. end
  616. end
  617. describe 'local' do
  618. it 'returns an array of accounts who do not have a domain' do
  619. account_1 = Fabricate(:account, domain: nil)
  620. account_2 = Fabricate(:account, domain: 'example.com')
  621. expect(Account.where('id > 0').local).to match_array([account_1])
  622. end
  623. end
  624. describe 'partitioned' do
  625. it 'returns a relation of accounts partitioned by domain' do
  626. matches = ['a', 'b', 'a', 'b']
  627. matches.size.times.to_a.shuffle.each do |index|
  628. matches[index] = Fabricate(:account, domain: matches[index])
  629. end
  630. expect(Account.where('id > 0').partitioned).to match_array(matches)
  631. end
  632. end
  633. describe 'recent' do
  634. it 'returns a relation of accounts sorted by recent creation' do
  635. matches = 2.times.map { Fabricate(:account) }
  636. expect(Account.where('id > 0').recent).to match_array(matches)
  637. end
  638. end
  639. describe 'silenced' do
  640. it 'returns an array of accounts who are silenced' do
  641. account_1 = Fabricate(:account, silenced: true)
  642. account_2 = Fabricate(:account, silenced: false)
  643. expect(Account.silenced).to match_array([account_1])
  644. end
  645. end
  646. describe 'suspended' do
  647. it 'returns an array of accounts who are suspended' do
  648. account_1 = Fabricate(:account, suspended: true)
  649. account_2 = Fabricate(:account, suspended: false)
  650. expect(Account.suspended).to match_array([account_1])
  651. end
  652. end
  653. end
  654. context 'when is local' do
  655. # Test disabled because test environment omits autogenerating keys for performance
  656. xit 'generates keys' do
  657. account = Account.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_']))
  658. expect(account.keypair.private?).to eq true
  659. end
  660. end
  661. context 'when is remote' do
  662. it 'does not generate keys' do
  663. key = OpenSSL::PKey::RSA.new(1024).public_key
  664. account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem)
  665. expect(account.keypair.params).to eq key.params
  666. end
  667. it 'normalizes domain' do
  668. account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(separators: ['_']))
  669. expect(account.domain).to eq 'xn--r9j5b5b'
  670. end
  671. end
  672. include_examples 'AccountAvatar', :account
  673. include_examples 'AccountHeader', :account
  674. describe '#increment_count!' do
  675. subject { Fabricate(:account) }
  676. it 'increments the count in multi-threaded an environment when account_stat is not yet initialized' do
  677. subject
  678. increment_by = 15
  679. wait_for_start = true
  680. threads = Array.new(increment_by) do
  681. Thread.new do
  682. true while wait_for_start
  683. Account.find(subject.id).increment_count!(:followers_count)
  684. end
  685. end
  686. wait_for_start = false
  687. threads.each(&:join)
  688. expect(subject.reload.followers_count).to eq 15
  689. end
  690. end
  691. end