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.

514 lines
15 KiB

8 years ago
8 years ago
  1. require 'rails_helper'
  2. RSpec.describe Account, type: :model do
  3. subject { Fabricate(:account, username: 'alice') }
  4. context do
  5. let(:bob) { Fabricate(:account, username: 'bob') }
  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. expect(subject.local?).to be true
  39. end
  40. it 'returns false when the account is on a different domain' do
  41. subject.domain = 'foreign.tld'
  42. expect(subject.local?).to be false
  43. end
  44. end
  45. describe 'Local domain user methods' do
  46. around do |example|
  47. before = Rails.configuration.x.local_domain
  48. example.run
  49. Rails.configuration.x.local_domain = before
  50. end
  51. describe '#to_webfinger_s' do
  52. it 'returns a webfinger string for the account' do
  53. Rails.configuration.x.local_domain = 'example.com'
  54. expect(subject.to_webfinger_s).to eq 'acct:alice@example.com'
  55. end
  56. end
  57. describe '#local_username_and_domain' do
  58. it 'returns the username and local domain for the account' do
  59. Rails.configuration.x.local_domain = 'example.com'
  60. expect(subject.local_username_and_domain).to eq 'alice@example.com'
  61. end
  62. end
  63. end
  64. describe '#acct' do
  65. it 'returns username for local users' do
  66. expect(subject.acct).to eql 'alice'
  67. end
  68. it 'returns username@domain for foreign users' do
  69. subject.domain = 'foreign.tld'
  70. expect(subject.acct).to eql 'alice@foreign.tld'
  71. end
  72. end
  73. describe '#subscribed?' do
  74. it 'returns false when no subscription expiration information is present' do
  75. expect(subject.subscribed?).to be false
  76. end
  77. it 'returns true when subscription expiration has been set' do
  78. subject.subscription_expires_at = 30.days.from_now
  79. expect(subject.subscribed?).to be true
  80. end
  81. end
  82. describe '#keypair' do
  83. it 'returns an RSA key pair' do
  84. expect(subject.keypair).to be_instance_of OpenSSL::PKey::RSA
  85. end
  86. end
  87. describe '#subscription' do
  88. it 'returns an OStatus subscription' do
  89. expect(subject.subscription('')).to be_instance_of OStatus2::Subscription
  90. end
  91. end
  92. describe '#object_type' do
  93. it 'is always a person' do
  94. expect(subject.object_type).to be :person
  95. end
  96. end
  97. describe '#favourited?' do
  98. let(:original_status) do
  99. author = Fabricate(:account, username: 'original')
  100. Fabricate(:status, account: author)
  101. end
  102. context 'when the status is a reblog of another status' do
  103. let(:original_reblog) do
  104. author = Fabricate(:account, username: 'original_reblogger')
  105. Fabricate(:status, reblog: original_status, account: author)
  106. end
  107. it 'is is true when this account has favourited it' do
  108. Fabricate(:favourite, status: original_reblog, account: subject)
  109. expect(subject.favourited?(original_status)).to eq true
  110. end
  111. it 'is false when this account has not favourited it' do
  112. expect(subject.favourited?(original_status)).to eq false
  113. end
  114. end
  115. context 'when the status is an original status' do
  116. it 'is is true when this account has favourited it' do
  117. Fabricate(:favourite, status: original_status, account: subject)
  118. expect(subject.favourited?(original_status)).to eq true
  119. end
  120. it 'is false when this account has not favourited it' do
  121. expect(subject.favourited?(original_status)).to eq false
  122. end
  123. end
  124. end
  125. describe '#reblogged?' do
  126. let(:original_status) do
  127. author = Fabricate(:account, username: 'original')
  128. Fabricate(:status, account: author)
  129. end
  130. context 'when the status is a reblog of another status'do
  131. let(:original_reblog) do
  132. author = Fabricate(:account, username: 'original_reblogger')
  133. Fabricate(:status, reblog: original_status, account: author)
  134. end
  135. it 'is true when this account has reblogged it' do
  136. Fabricate(:status, reblog: original_reblog, account: subject)
  137. expect(subject.reblogged?(original_reblog)).to eq true
  138. end
  139. it 'is false when this account has not reblogged it' do
  140. expect(subject.reblogged?(original_reblog)).to eq false
  141. end
  142. end
  143. context 'when the status is an original status' do
  144. it 'is true when this account has reblogged it' do
  145. Fabricate(:status, reblog: original_status, account: subject)
  146. expect(subject.reblogged?(original_status)).to eq true
  147. end
  148. it 'is false when this account has not reblogged it' do
  149. expect(subject.reblogged?(original_status)).to eq false
  150. end
  151. end
  152. end
  153. describe '#excluded_from_timeline_account_ids' do
  154. it 'includes account ids of blockings, blocked_bys and mutes' do
  155. account = Fabricate(:account)
  156. block = Fabricate(:block, account: account)
  157. mute = Fabricate(:mute, account: account)
  158. block_by = Fabricate(:block, target_account: account)
  159. results = account.excluded_from_timeline_account_ids
  160. expect(results.size).to eq 3
  161. expect(results).to include(block.target_account.id)
  162. expect(results).to include(mute.target_account.id)
  163. expect(results).to include(block_by.account.id)
  164. end
  165. end
  166. describe '.search_for' do
  167. before do
  168. @match = Fabricate(
  169. :account,
  170. display_name: "Display Name",
  171. username: "username",
  172. domain: "example.com"
  173. )
  174. _missing = Fabricate(
  175. :account,
  176. display_name: "Missing",
  177. username: "missing",
  178. domain: "missing.com"
  179. )
  180. end
  181. it 'finds accounts with matching display_name' do
  182. results = Account.search_for("display")
  183. expect(results).to eq [@match]
  184. end
  185. it 'finds accounts with matching username' do
  186. results = Account.search_for("username")
  187. expect(results).to eq [@match]
  188. end
  189. it 'finds accounts with matching domain' do
  190. results = Account.search_for("example")
  191. expect(results).to eq [@match]
  192. end
  193. it 'ranks multiple matches higher' do
  194. account = Fabricate(
  195. :account,
  196. username: "username",
  197. display_name: "username"
  198. )
  199. results = Account.search_for("username")
  200. expect(results).to eq [account, @match]
  201. end
  202. end
  203. describe '.advanced_search_for' do
  204. it 'ranks followed accounts higher' do
  205. account = Fabricate(:account)
  206. match = Fabricate(:account, username: "Matching")
  207. followed_match = Fabricate(:account, username: "Matcher")
  208. Fabricate(:follow, account: account, target_account: followed_match)
  209. results = Account.advanced_search_for("match", account)
  210. expect(results).to eq [followed_match, match]
  211. expect(results.first.rank).to be > results.last.rank
  212. end
  213. end
  214. describe '.triadic_closures' do
  215. subject { described_class.triadic_closures(me) }
  216. let!(:me) { Fabricate(:account) }
  217. let!(:friend) { Fabricate(:account) }
  218. let!(:friends_friend) { Fabricate(:account) }
  219. let!(:both_follow) { Fabricate(:account) }
  220. before do
  221. me.follow!(friend)
  222. friend.follow!(friends_friend)
  223. me.follow!(both_follow)
  224. friend.follow!(both_follow)
  225. end
  226. it 'finds accounts you dont follow which are followed by accounts you do follow' do
  227. is_expected.to eq [friends_friend]
  228. end
  229. context 'when you block account' do
  230. before do
  231. me.block!(friends_friend)
  232. end
  233. it 'rejects blocked accounts' do
  234. is_expected.to be_empty
  235. end
  236. end
  237. context 'when you mute account' do
  238. before do
  239. me.mute!(friends_friend)
  240. end
  241. it 'rejects muted accounts' do
  242. is_expected.to be_empty
  243. end
  244. end
  245. end
  246. describe '.find_local' do
  247. before do
  248. Fabricate(:account, username: 'Alice')
  249. end
  250. it 'returns Alice for alice' do
  251. expect(Account.find_local('alice')).to_not be_nil
  252. end
  253. it 'returns Alice for Alice' do
  254. expect(Account.find_local('Alice')).to_not be_nil
  255. end
  256. it 'does not return anything for a_ice' do
  257. expect(Account.find_local('a_ice')).to be_nil
  258. end
  259. it 'does not return anything for al%' do
  260. expect(Account.find_local('al%')).to be_nil
  261. end
  262. end
  263. describe '.find_remote' do
  264. before do
  265. Fabricate(:account, username: 'Alice', domain: 'mastodon.social')
  266. end
  267. it 'returns Alice for alice@mastodon.social' do
  268. expect(Account.find_remote('alice', 'mastodon.social')).to_not be_nil
  269. end
  270. it 'returns Alice for ALICE@MASTODON.SOCIAL' do
  271. expect(Account.find_remote('ALICE', 'MASTODON.SOCIAL')).to_not be_nil
  272. end
  273. it 'does not return anything for a_ice@mastodon.social' do
  274. expect(Account.find_remote('a_ice', 'mastodon.social')).to be_nil
  275. end
  276. it 'does not return anything for alice@m_stodon.social' do
  277. expect(Account.find_remote('alice', 'm_stodon.social')).to be_nil
  278. end
  279. it 'does not return anything for alice@m%' do
  280. expect(Account.find_remote('alice', 'm%')).to be_nil
  281. end
  282. end
  283. describe '.following_map' do
  284. it 'returns an hash' do
  285. expect(Account.following_map([], 1)).to be_a Hash
  286. end
  287. end
  288. describe '.followed_by_map' do
  289. it 'returns an hash' do
  290. expect(Account.followed_by_map([], 1)).to be_a Hash
  291. end
  292. end
  293. describe '.blocking_map' do
  294. it 'returns an hash' do
  295. expect(Account.blocking_map([], 1)).to be_a Hash
  296. end
  297. end
  298. describe '.requested_map' do
  299. it 'returns an hash' do
  300. expect(Account.requested_map([], 1)).to be_a Hash
  301. end
  302. end
  303. describe 'MENTION_RE' do
  304. subject { Account::MENTION_RE }
  305. it 'matches usernames in the middle of a sentence' do
  306. expect(subject.match('Hello to @alice from me')[1]).to eq 'alice'
  307. end
  308. it 'matches usernames in the beginning of status' do
  309. expect(subject.match('@alice Hey how are you?')[1]).to eq 'alice'
  310. end
  311. it 'matches full usernames' do
  312. expect(subject.match('@alice@example.com')[1]).to eq 'alice@example.com'
  313. end
  314. it 'matches full usernames with a dot at the end' do
  315. expect(subject.match('Hello @alice@example.com.')[1]).to eq 'alice@example.com'
  316. end
  317. it 'matches dot-prepended usernames' do
  318. expect(subject.match('.@alice I want everybody to see this')[1]).to eq 'alice'
  319. end
  320. it 'does not match e-mails' do
  321. expect(subject.match('Drop me an e-mail at alice@example.com')).to be_nil
  322. end
  323. it 'does not match URLs' do
  324. expect(subject.match('Check this out https://medium.com/@alice/some-article#.abcdef123')).to be_nil
  325. end
  326. xit 'does not match URL querystring' do
  327. expect(subject.match('https://example.com/?x=@alice')).to be_nil
  328. end
  329. end
  330. describe 'validations' do
  331. it 'has a valid fabricator' do
  332. account = Fabricate.build(:account)
  333. account.valid?
  334. expect(account).to be_valid
  335. end
  336. it 'is invalid without a username' do
  337. account = Fabricate.build(:account, username: nil)
  338. account.valid?
  339. expect(account).to model_have_error_on_field(:username)
  340. end
  341. it 'is invalid if the username already exists' do
  342. account_1 = Fabricate(:account, username: 'the_doctor')
  343. account_2 = Fabricate.build(:account, username: 'the_doctor')
  344. account_2.valid?
  345. expect(account_2).to model_have_error_on_field(:username)
  346. end
  347. context 'when is local' do
  348. it 'is invalid if the username doesn\'t only contains letters, numbers and underscores' do
  349. account = Fabricate.build(:account, username: 'the-doctor')
  350. account.valid?
  351. expect(account).to model_have_error_on_field(:username)
  352. end
  353. it 'is invalid if the username is longer then 30 characters' do
  354. account = Fabricate.build(:account, username: Faker::Lorem.characters(31))
  355. account.valid?
  356. expect(account).to model_have_error_on_field(:username)
  357. end
  358. end
  359. end
  360. describe 'scopes' do
  361. describe 'remote' do
  362. it 'returns an array of accounts who have a domain' do
  363. account_1 = Fabricate(:account, domain: nil)
  364. account_2 = Fabricate(:account, domain: 'example.com')
  365. expect(Account.remote).to match_array([account_2])
  366. end
  367. end
  368. describe 'by_domain_accounts' do
  369. it 'returns accounts grouped by domain sorted by accounts' do
  370. 2.times { Fabricate(:account, domain: 'example.com') }
  371. Fabricate(:account, domain: 'example2.com')
  372. results = Account.by_domain_accounts
  373. expect(results.length).to eq 2
  374. expect(results.first.domain).to eq 'example.com'
  375. expect(results.first.accounts_count).to eq 2
  376. expect(results.last.domain).to eq 'example2.com'
  377. expect(results.last.accounts_count).to eq 1
  378. end
  379. end
  380. describe 'local' do
  381. it 'returns an array of accounts who do not have a domain' do
  382. account_1 = Fabricate(:account, domain: nil)
  383. account_2 = Fabricate(:account, domain: 'example.com')
  384. expect(Account.local).to match_array([account_1])
  385. end
  386. end
  387. describe 'silenced' do
  388. it 'returns an array of accounts who are silenced' do
  389. account_1 = Fabricate(:account, silenced: true)
  390. account_2 = Fabricate(:account, silenced: false)
  391. expect(Account.silenced).to match_array([account_1])
  392. end
  393. end
  394. describe 'suspended' do
  395. it 'returns an array of accounts who are suspended' do
  396. account_1 = Fabricate(:account, suspended: true)
  397. account_2 = Fabricate(:account, suspended: false)
  398. expect(Account.suspended).to match_array([account_1])
  399. end
  400. end
  401. end
  402. describe 'static avatars' do
  403. describe 'when GIF' do
  404. it 'creates a png static style' do
  405. subject.avatar = attachment_fixture('avatar.gif')
  406. subject.save
  407. expect(subject.avatar_static_url).to_not eq subject.avatar_original_url
  408. end
  409. end
  410. describe 'when non-GIF' do
  411. it 'does not create extra static style' do
  412. subject.avatar = attachment_fixture('attachment.jpg')
  413. subject.save
  414. expect(subject.avatar_static_url).to eq subject.avatar_original_url
  415. end
  416. end
  417. end
  418. end