Merge branch 'master' into skeb
This commit is contained in:
@@ -46,8 +46,8 @@ class PostNavbarComponentTest < ViewComponent::TestCase
|
||||
context "for a post with favgroups" do
|
||||
setup do
|
||||
as(@user) do
|
||||
@favgroup1 = create(:favorite_group, creator: @user)
|
||||
@favgroup2 = create(:favorite_group, creator: @user)
|
||||
@favgroup1 = create(:favorite_group, creator: @user, is_public: true)
|
||||
@favgroup2 = create(:favorite_group, creator: @user, is_public: false)
|
||||
@post.update(tag_string: "favgroup:#{@favgroup1.id} favgroup:#{@favgroup2.id}")
|
||||
end
|
||||
end
|
||||
@@ -65,6 +65,18 @@ class PostNavbarComponentTest < ViewComponent::TestCase
|
||||
assert_css(".favgroup-navbar[data-selected=true] .favgroup-name", text: "Favgroup: #{@favgroup1.pretty_name}")
|
||||
assert_css(".favgroup-navbar[data-selected=false] .favgroup-name", text: "Favgroup: #{@favgroup2.pretty_name}")
|
||||
end
|
||||
|
||||
should "show public favgroups that belong to another user when doing a favgroup:<id> search" do
|
||||
render_post_navbar(@post, current_user: create(:user), search: "favgroup:#{@favgroup1.id}")
|
||||
|
||||
assert_css(".favgroup-navbar[data-selected=true] .favgroup-name", text: "Favgroup: #{@favgroup1.pretty_name}")
|
||||
end
|
||||
|
||||
should "not show private favgroups that belong to another user when doing a favgroup:<id> search" do
|
||||
render_post_navbar(@post, current_user: create(:user), search: "favgroup:#{@favgroup2.id}")
|
||||
|
||||
assert_css(".favgroup-navbar .favgroup-name", count: 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
8
test/factories/rate_limit.rb
Normal file
8
test/factories/rate_limit.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
FactoryBot.define do
|
||||
factory(:rate_limit) do
|
||||
limited { false }
|
||||
points { 0 }
|
||||
action { "test" }
|
||||
key { "1234" }
|
||||
end
|
||||
end
|
||||
@@ -227,7 +227,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
||||
should "fail with a 429 error" do
|
||||
user = create(:user)
|
||||
post = create(:post, rating: "s")
|
||||
TokenBucket.any_instance.stubs(:throttled?).returns(true)
|
||||
RateLimit.any_instance.stubs(:limited?).returns(true)
|
||||
|
||||
put_auth post_path(post), user, params: { post: { rating: "e" } }
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@ class BansControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
should "not raise an exception on a blank username" do
|
||||
post_auth bans_path, @mod, params: {}
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
context "update action" do
|
||||
|
||||
@@ -142,14 +142,14 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
context "when updating another user's comment" do
|
||||
should "succeed if updater is a moderator" do
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {body: "abc"}}
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {body: "abc"}}, xhr: true
|
||||
assert_equal("abc", @comment.reload.body)
|
||||
assert_redirected_to post_path(@comment.post)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "fail if updater is not a moderator" do
|
||||
@mod_comment = as(@mod) { create(:comment, post: @post) }
|
||||
put_auth comment_path(@mod_comment.id), @user, params: {comment: {body: "abc"}}
|
||||
put_auth comment_path(@mod_comment.id), @user, params: {comment: {body: "abc"}}, xhr: true
|
||||
assert_not_equal("abc", @mod_comment.reload.body)
|
||||
assert_response 403
|
||||
end
|
||||
@@ -157,13 +157,13 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
context "when stickying a comment" do
|
||||
should "succeed if updater is a moderator" do
|
||||
put_auth comment_path(@comment.id), @mod, params: {comment: {is_sticky: true}}
|
||||
put_auth comment_path(@comment.id), @mod, params: {comment: {is_sticky: true}}, xhr: true
|
||||
assert_equal(true, @comment.reload.is_sticky)
|
||||
assert_redirected_to @comment.post
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "fail if updater is not a moderator" do
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {is_sticky: true}}
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {is_sticky: true}}, xhr: true
|
||||
assert_response 403
|
||||
assert_equal(false, @comment.reload.is_sticky)
|
||||
end
|
||||
@@ -172,7 +172,7 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
|
||||
context "for a deleted comment" do
|
||||
should "not allow the creator to edit the comment" do
|
||||
@comment.update!(is_deleted: true)
|
||||
put_auth comment_path(@comment.id), @user, params: { comment: { body: "blah" }}
|
||||
put_auth comment_path(@comment.id), @user, params: { comment: { body: "blah" }}, xhr: true
|
||||
|
||||
assert_response 403
|
||||
assert_not_equal("blah", @comment.reload.body)
|
||||
@@ -180,16 +180,16 @@ class CommentsControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
should "update the body" do
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {body: "abc"}}
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {body: "abc"}}, xhr: true
|
||||
assert_equal("abc", @comment.reload.body)
|
||||
assert_redirected_to post_path(@comment.post)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "allow changing the body and is_deleted" do
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {body: "herp derp", is_deleted: true}}
|
||||
put_auth comment_path(@comment.id), @user, params: {comment: {body: "herp derp", is_deleted: true}}, xhr: true
|
||||
assert_equal("herp derp", @comment.reload.body)
|
||||
assert_equal(true, @comment.is_deleted)
|
||||
assert_redirected_to post_path(@post)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "not allow changing do_not_bump_post or post_id" do
|
||||
|
||||
33
test/functional/rate_limits_controller_test.rb
Normal file
33
test/functional/rate_limits_controller_test.rb
Normal file
@@ -0,0 +1,33 @@
|
||||
require 'test_helper'
|
||||
|
||||
class RateLimitsControllerTest < ActionDispatch::IntegrationTest
|
||||
context "The rate limits controller" do
|
||||
context "index action" do
|
||||
setup do
|
||||
@user = create(:user)
|
||||
create(:rate_limit, key: @user.cache_key)
|
||||
end
|
||||
|
||||
should "show all rate limits to the owner" do
|
||||
get_auth rate_limits_path, create(:owner_user)
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 2 # 2 because the login action creates a second rate limit.
|
||||
end
|
||||
|
||||
should "show the user their own rate limits" do
|
||||
get_auth rate_limits_path, @user
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 1
|
||||
end
|
||||
|
||||
should "not show users rate limits belonging to other users" do
|
||||
get_auth rate_limits_path, create(:user)
|
||||
|
||||
assert_response :success
|
||||
assert_select "tbody tr", count: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -72,6 +72,31 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal(0, @ip_ban.reload.hit_count)
|
||||
assert_nil(@ip_ban.last_hit_at)
|
||||
end
|
||||
|
||||
should "rate limit logins to 10 per minute per IP" do
|
||||
freeze_time
|
||||
|
||||
11.times do
|
||||
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
||||
assert_redirected_to posts_path
|
||||
assert_equal(@user.id, session[:user_id])
|
||||
delete_auth session_path, @user
|
||||
end
|
||||
|
||||
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
||||
assert_response 429
|
||||
assert_not_equal(@user.id, session[:user_id])
|
||||
|
||||
travel 59.seconds
|
||||
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
||||
assert_response 429
|
||||
assert_not_equal(@user.id, session[:user_id])
|
||||
|
||||
travel 10.seconds
|
||||
post session_path, params: { name: @user.name, password: "password" }, headers: { REMOTE_ADDR: "1.2.3.4" }
|
||||
assert_redirected_to posts_path
|
||||
assert_equal(@user.id, session[:user_id])
|
||||
end
|
||||
end
|
||||
|
||||
context "destroy action" do
|
||||
|
||||
@@ -48,11 +48,5 @@ class ApiKeyTest < ActiveSupport::TestCase
|
||||
should "not authenticate with the wrong name" do
|
||||
assert_equal(false, create(:user).authenticate_api_key(@api_key.key))
|
||||
end
|
||||
|
||||
should "have the same limits whether or not they have an api key" do
|
||||
assert_no_difference(["@user.reload.api_regen_multiplier", "@user.reload.api_burst_limit"]) do
|
||||
@api_key.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,7 +33,7 @@ class IpGeolocationTest < ActiveSupport::TestCase
|
||||
should "work for a residential IP" do
|
||||
@ip = IpGeolocation.create_or_update!("2a01:0e35:2f22:e3d0::1")
|
||||
|
||||
assert_equal(26, @ip.network.prefix)
|
||||
assert_equal(28, @ip.network.prefix)
|
||||
assert_equal(false, @ip.is_proxy?)
|
||||
assert_equal(49, @ip.latitude.round(0))
|
||||
assert_equal(2, @ip.longitude.round(0))
|
||||
|
||||
72
test/unit/rate_limit_test.rb
Normal file
72
test/unit/rate_limit_test.rb
Normal file
@@ -0,0 +1,72 @@
|
||||
require 'test_helper'
|
||||
|
||||
class RateLimitTest < ActiveSupport::TestCase
|
||||
context "RateLimit: " do
|
||||
context "#limit! method" do
|
||||
should "create a new rate limit object if none exists, or update it if it already exists" do
|
||||
assert_difference("RateLimit.count", 1) do
|
||||
RateLimiter.new("write", ["users/1"]).limited?
|
||||
end
|
||||
|
||||
assert_difference("RateLimit.count", 0) do
|
||||
RateLimiter.new("write", ["users/1"]).limited?
|
||||
end
|
||||
|
||||
assert_difference("RateLimit.count", 1) do
|
||||
RateLimiter.new("write", ["users/1", "ip/1.2.3.4"]).limited?
|
||||
end
|
||||
|
||||
assert_difference("RateLimit.count", 0) do
|
||||
RateLimiter.new("write", ["users/1", "ip/1.2.3.4"]).limited?
|
||||
end
|
||||
end
|
||||
|
||||
should "include the cost of the first action when initializing the limit" do
|
||||
limiter = RateLimiter.new("write", ["users/1"], burst: 10, cost: 1)
|
||||
assert_equal(9, limiter.rate_limits.first.points)
|
||||
end
|
||||
|
||||
should "be limited and penalize the caller 1 second if the point count is negative" do
|
||||
freeze_time
|
||||
create(:rate_limit, action: "write", key: "users/1", points: -1)
|
||||
limiter = RateLimiter.new("write", ["users/1"], cost: 1)
|
||||
|
||||
assert_equal(true, limiter.limited?)
|
||||
assert_equal(-2, limiter.rate_limits.first.points)
|
||||
end
|
||||
|
||||
should "not be limited if the point count was positive before the action" do
|
||||
freeze_time
|
||||
create(:rate_limit, action: "write", key: "users/1", points: 0.01)
|
||||
limiter = RateLimiter.new("write", ["users/1"], cost: 1)
|
||||
|
||||
assert_equal(false, limiter.limited?)
|
||||
assert_equal(-0.99, limiter.rate_limits.first.points)
|
||||
end
|
||||
|
||||
should "refill the points at the correct rate" do
|
||||
freeze_time
|
||||
create(:rate_limit, action: "write", key: "users/1", points: -2)
|
||||
|
||||
limiter = RateLimiter.new("write", ["users/1"], cost: 1, rate: 1, burst: 10)
|
||||
assert_equal(true, limiter.limited?)
|
||||
assert_equal(-3, limiter.rate_limits.first.points)
|
||||
|
||||
travel 1.second
|
||||
limiter = RateLimiter.new("write", ["users/1"], cost: 1, rate: 1, burst: 10)
|
||||
assert_equal(true, limiter.limited?)
|
||||
assert_equal(-3, limiter.rate_limits.first.points)
|
||||
|
||||
travel 5.second
|
||||
limiter = RateLimiter.new("write", ["users/1"], cost: 1, rate: 1, burst: 10)
|
||||
assert_equal(false, limiter.limited?)
|
||||
assert_equal(1, limiter.rate_limits.first.points)
|
||||
|
||||
travel 60.second
|
||||
limiter = RateLimiter.new("write", ["users/1"], cost: 1, rate: 1, burst: 10)
|
||||
assert_equal(false, limiter.limited?)
|
||||
assert_equal(9, limiter.rate_limits.first.points)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -95,20 +95,30 @@ module Sources
|
||||
should "fetch the source data" do
|
||||
assert_equal("evazion", @site.artist_name)
|
||||
end
|
||||
|
||||
should "correctly get the page url" do
|
||||
assert_equal(@ref, @site.page_url)
|
||||
end
|
||||
end
|
||||
|
||||
context "A baraag url" do
|
||||
setup do
|
||||
skip "Baraag keys not set" unless Danbooru.config.baraag_client_id
|
||||
@url = "https://baraag.net/@bardbot/105732813175612920"
|
||||
@site = Sources::Strategies.find(@url)
|
||||
@site1 = Sources::Strategies.find(@url)
|
||||
|
||||
@img = "https://baraag.net/system/media_attachments/files/105/803/948/862/719/091/original/54e1cb7ca33ec449.png"
|
||||
@ref = "https://baraag.net/@Nakamura/105803949565505009"
|
||||
@site2 = Sources::Strategies.find(@img, @ref)
|
||||
end
|
||||
|
||||
should "work" do
|
||||
assert_equal("https://baraag.net/@bardbot", @site.profile_url)
|
||||
assert_equal(["https://baraag.net/system/media_attachments/files/105/732/803/241/495/700/original/556e1eb7f5ca610f.png"], @site.image_urls)
|
||||
assert_equal("bardbot", @site.artist_name)
|
||||
assert_equal("🍌", @site.dtext_artist_commentary_desc)
|
||||
assert_equal("https://baraag.net/@bardbot", @site1.profile_url)
|
||||
assert_equal(["https://baraag.net/system/media_attachments/files/105/732/803/241/495/700/original/556e1eb7f5ca610f.png"], @site1.image_urls)
|
||||
assert_equal("bardbot", @site1.artist_name)
|
||||
assert_equal("🍌", @site1.dtext_artist_commentary_desc)
|
||||
|
||||
assert_equal([@img], @site2.image_urls)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -305,6 +305,18 @@ module Sources
|
||||
end
|
||||
end
|
||||
|
||||
context "when the cached session cookie is invalid" do
|
||||
should "clear the cached cookie after failing to fetch the data" do
|
||||
site = Sources::Strategies.find("https://nijie.info/view.php?id=203688")
|
||||
|
||||
Cache.put("nijie-session-cookie", HTTP::Cookie.new(name: "NIJIEIJIEID", value: "fake", domain: "nijie.info", path: "/"))
|
||||
assert_equal("fake", site.cached_session_cookie.value)
|
||||
|
||||
assert_equal([], site.image_urls)
|
||||
assert_nil(Cache.get("nijie-session-cookie"))
|
||||
end
|
||||
end
|
||||
|
||||
context "a doujin post" do
|
||||
should "work" do
|
||||
image = "https://pic.nijie.net/01/dojin_main/dojin_sam/20120213044700%E3%82%B3%E3%83%94%E3%83%BC%20%EF%BD%9E%200011%E3%81%AE%E3%82%B3%E3%83%94%E3%83%BC.jpg"
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
require 'test_helper'
|
||||
|
||||
class TokenBucketTest < ActiveSupport::TestCase
|
||||
context "#add!" do
|
||||
setup do
|
||||
@user = FactoryBot.create(:user)
|
||||
TokenBucket.create(user_id: @user.id, last_touched_at: 1.minute.ago, token_count: 0)
|
||||
end
|
||||
|
||||
should "work" do
|
||||
@user.token_bucket.add!
|
||||
assert_operator(@user.token_bucket.token_count, :>, 0)
|
||||
@user.reload
|
||||
assert_operator(@user.token_bucket.token_count, :>, 0)
|
||||
end
|
||||
end
|
||||
|
||||
context "#consume!" do
|
||||
setup do
|
||||
@user = FactoryBot.create(:user)
|
||||
TokenBucket.create(user_id: @user.id, last_touched_at: 1.minute.ago, token_count: 1)
|
||||
end
|
||||
|
||||
should "work" do
|
||||
@user.token_bucket.consume!
|
||||
assert_operator(@user.token_bucket.token_count, :<, 1)
|
||||
@user.reload
|
||||
assert_operator(@user.token_bucket.token_count, :<, 1)
|
||||
end
|
||||
end
|
||||
|
||||
context "#throttled?" do
|
||||
setup do
|
||||
@user = FactoryBot.create(:user)
|
||||
TokenBucket.create(user_id: @user.id, last_touched_at: 1.minute.ago, token_count: 0)
|
||||
end
|
||||
|
||||
should "work" do
|
||||
assert(!@user.token_bucket.throttled?)
|
||||
assert_operator(@user.token_bucket.token_count, :<, 60)
|
||||
@user.reload
|
||||
assert_operator(@user.token_bucket.token_count, :<, 60)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user