2017-10-10 07:28:28 +09:00
|
|
|
require 'rails_helper'
|
|
|
|
|
2017-10-22 04:47:17 +09:00
|
|
|
RSpec.describe Glitch::KeywordMute, type: :model do
|
2017-10-15 10:36:53 +09:00
|
|
|
let(:alice) { Fabricate(:account, username: 'alice').tap(&:save!) }
|
|
|
|
let(:bob) { Fabricate(:account, username: 'bob').tap(&:save!) }
|
2017-10-14 16:28:20 +09:00
|
|
|
|
2017-11-16 08:26:29 +09:00
|
|
|
describe '.text_matcher_for' do
|
|
|
|
let(:matcher) { Glitch::KeywordMute.text_matcher_for(alice.id) }
|
2017-10-14 16:28:20 +09:00
|
|
|
|
2017-10-22 14:24:32 +09:00
|
|
|
describe 'with no mutes' do
|
2017-10-15 10:36:53 +09:00
|
|
|
before do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.delete_all
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not match' do
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a hot take')).to be_falsy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
2017-10-14 16:28:20 +09:00
|
|
|
end
|
|
|
|
|
2017-10-22 14:24:32 +09:00
|
|
|
describe 'with mutes' do
|
2017-10-15 10:36:53 +09:00
|
|
|
it 'does not match keywords set by a different account' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: bob, keyword: 'take')
|
2017-10-15 10:36:53 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a hot take')).to be_falsy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not match if no keywords match the status text' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'cold')
|
2017-10-15 10:36:53 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a hot take')).to be_falsy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
Allow keywords to match either substrings or whole words.
Word-boundary matching only works as intended in English and languages
that use similar word-breaking characters; it doesn't work so well in
(say) Japanese, Chinese, or Thai. It's unacceptable to have a feature
that doesn't work as intended for some languages. (Moreso especially
considering that it's likely that the largest contingent on the Mastodon
bit of the fediverse speaks Japanese.)
There are rules specified in Unicode TR29[1] for word-breaking across
all languages supported by Unicode, but the rules deliberately do not
cover all cases. In fact, TR29 states
For example, reliable detection of word boundaries in languages such
as Thai, Lao, Chinese, or Japanese requires the use of dictionary
lookup, analogous to English hyphenation.
So we aren't going to be able to make word detection work with regexes
within Mastodon (or glitchsoc). However, for a first pass (even if it's
kind of punting) we can allow the user to choose whether they want word
or substring detection and warn about the limitations of this
implementation in, say, docs.
[1]: https://unicode.org/reports/tr29/
https://web.archive.org/web/20171001005125/https://unicode.org/reports/tr29/
2017-10-16 09:49:22 +09:00
|
|
|
it 'considers word boundaries when matching' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'bob', whole_word: true)
|
Allow keywords to match either substrings or whole words.
Word-boundary matching only works as intended in English and languages
that use similar word-breaking characters; it doesn't work so well in
(say) Japanese, Chinese, or Thai. It's unacceptable to have a feature
that doesn't work as intended for some languages. (Moreso especially
considering that it's likely that the largest contingent on the Mastodon
bit of the fediverse speaks Japanese.)
There are rules specified in Unicode TR29[1] for word-breaking across
all languages supported by Unicode, but the rules deliberately do not
cover all cases. In fact, TR29 states
For example, reliable detection of word boundaries in languages such
as Thai, Lao, Chinese, or Japanese requires the use of dictionary
lookup, analogous to English hyphenation.
So we aren't going to be able to make word detection work with regexes
within Mastodon (or glitchsoc). However, for a first pass (even if it's
kind of punting) we can allow the user to choose whether they want word
or substring detection and warn about the limitations of this
implementation in, say, docs.
[1]: https://unicode.org/reports/tr29/
https://web.archive.org/web/20171001005125/https://unicode.org/reports/tr29/
2017-10-16 09:49:22 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('bobcats')).to be_falsy
|
Allow keywords to match either substrings or whole words.
Word-boundary matching only works as intended in English and languages
that use similar word-breaking characters; it doesn't work so well in
(say) Japanese, Chinese, or Thai. It's unacceptable to have a feature
that doesn't work as intended for some languages. (Moreso especially
considering that it's likely that the largest contingent on the Mastodon
bit of the fediverse speaks Japanese.)
There are rules specified in Unicode TR29[1] for word-breaking across
all languages supported by Unicode, but the rules deliberately do not
cover all cases. In fact, TR29 states
For example, reliable detection of word boundaries in languages such
as Thai, Lao, Chinese, or Japanese requires the use of dictionary
lookup, analogous to English hyphenation.
So we aren't going to be able to make word detection work with regexes
within Mastodon (or glitchsoc). However, for a first pass (even if it's
kind of punting) we can allow the user to choose whether they want word
or substring detection and warn about the limitations of this
implementation in, say, docs.
[1]: https://unicode.org/reports/tr29/
https://web.archive.org/web/20171001005125/https://unicode.org/reports/tr29/
2017-10-16 09:49:22 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches substrings if whole_word is false' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'take', whole_word: false)
|
2017-10-15 10:36:53 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a shiitake mushroom')).to be_truthy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches keywords at the beginning of the text' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'take')
|
2017-10-15 10:36:53 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('Take this')).to be_truthy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
2017-10-25 08:33:02 +09:00
|
|
|
it 'matches keywords at the end of the text' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'take')
|
2017-10-15 10:36:53 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a hot take')).to be_truthy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches if at least one keyword case-insensitively matches the text' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'hot')
|
2017-10-15 10:36:53 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a HOT take')).to be_truthy
|
2017-10-15 10:41:52 +09:00
|
|
|
end
|
|
|
|
|
2017-11-14 02:06:02 +09:00
|
|
|
it 'maintains case-insensitivity when combining keywords into a single matcher' do
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'hot')
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'cold')
|
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a HOT take')).to be_truthy
|
2017-11-14 02:06:02 +09:00
|
|
|
end
|
|
|
|
|
2017-10-15 10:41:52 +09:00
|
|
|
it 'matches keywords surrounded by non-alphanumeric ornamentation' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'hot')
|
2017-10-15 10:41:52 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('(hot take)')).to be_truthy
|
2017-10-22 14:24:32 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'escapes metacharacters in keywords' do
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: '(hot take)')
|
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('(hot take)')).to be_truthy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'uses case-folding rules appropriate for more than just English' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'großeltern')
|
2017-10-15 10:36:53 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('besuch der grosseltern')).to be_truthy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches keywords that are composed of multiple words' do
|
2017-10-22 04:47:17 +09:00
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'a shiitake')
|
2017-10-14 16:28:20 +09:00
|
|
|
|
2017-11-16 09:26:21 +09:00
|
|
|
expect(matcher.matches?('This is a shiitake')).to be_truthy
|
|
|
|
expect(matcher.matches?('This is shiitake')).to_not be_truthy
|
2017-10-15 10:36:53 +09:00
|
|
|
end
|
2017-10-14 16:28:20 +09:00
|
|
|
end
|
|
|
|
end
|
2017-11-16 14:31:49 +09:00
|
|
|
|
|
|
|
describe '.tag_matcher_for' do
|
|
|
|
let(:matcher) { Glitch::KeywordMute.tag_matcher_for(alice.id) }
|
|
|
|
let(:status) { Fabricate(:status) }
|
|
|
|
|
|
|
|
describe 'with no mutes' do
|
|
|
|
before do
|
|
|
|
Glitch::KeywordMute.delete_all
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not match' do
|
|
|
|
status.tags << Fabricate(:tag, name: 'xyzzy')
|
|
|
|
|
|
|
|
expect(matcher.matches?(status.tags)).to be false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'with mutes' do
|
|
|
|
it 'does not match keywords set by a different account' do
|
|
|
|
status.tags << Fabricate(:tag, name: 'xyzzy')
|
|
|
|
Glitch::KeywordMute.create!(account: bob, keyword: 'take')
|
|
|
|
|
|
|
|
expect(matcher.matches?(status.tags)).to be false
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches #xyzzy when given the mute "#xyzzy"' do
|
|
|
|
status.tags << Fabricate(:tag, name: 'xyzzy')
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: '#xyzzy')
|
|
|
|
|
|
|
|
expect(matcher.matches?(status.tags)).to be true
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches #thingiverse when given the non-whole-word mute "#thing"' do
|
|
|
|
status.tags << Fabricate(:tag, name: 'thingiverse')
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: '#thing', whole_word: false)
|
|
|
|
|
|
|
|
expect(matcher.matches?(status.tags)).to be true
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches #hashtag when given the mute "##hashtag""' do
|
|
|
|
status.tags << Fabricate(:tag, name: 'hashtag')
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: '##hashtag')
|
|
|
|
|
|
|
|
expect(matcher.matches?(status.tags)).to be true
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'matches #oatmeal when given the non-whole-word mute "oat"' do
|
|
|
|
status.tags << Fabricate(:tag, name: 'oatmeal')
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'oat', whole_word: false)
|
|
|
|
|
|
|
|
expect(matcher.matches?(status.tags)).to be true
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not match #oatmeal when given the mute "#oat"' do
|
|
|
|
status.tags << Fabricate(:tag, name: 'oatmeal')
|
|
|
|
Glitch::KeywordMute.create!(account: alice, keyword: 'oat')
|
|
|
|
|
|
|
|
expect(matcher.matches?(status.tags)).to be false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-10-10 07:28:28 +09:00
|
|
|
end
|