discourse/spec/models/topic_link_click_spec.rb
Ted Johansson fb087b7ff6
DEV: Convert min_trust_to_post_links to groups ()
We're changing the implementation of trust levels to use groups. Part of this is to have site settings that reference trust levels use groups instead. It converts the min_trust_to_post_links  site setting to post_links_allowed_groups.

This isn't used by any of our plugins or themes, so very little fallout.
2024-01-18 14:08:40 +08:00

355 lines
11 KiB
Ruby

# frozen_string_literal: true
RSpec.describe TopicLinkClick do
it { is_expected.to belong_to :topic_link }
it { is_expected.to belong_to :user }
it { is_expected.to validate_presence_of :topic_link_id }
def test_uri
URI.parse("http://test.host")
end
describe "topic_links" do
before do
@topic = Fabricate(:topic, user: Fabricate(:user, refresh_auto_groups: true))
@post = Fabricate(:post_with_external_links, user: @topic.user, topic: @topic)
TopicLink.extract_from(@post)
@topic_link = @topic.topic_links.first
end
it "has 0 clicks at first" do
expect(@topic_link.clicks).to eq(0)
end
describe ".create" do
before { TopicLinkClick.create(topic_link: @topic_link, ip_address: "192.168.1.1") }
it "creates the forum topic link click" do
expect(TopicLinkClick.count).to eq(1)
@topic_link.reload
expect(@topic_link.clicks).to eq(1)
expect(TopicLinkClick.first.ip_address.to_s).to eq("192.168.1.1")
end
end
describe ".create_from" do
it "works correctly" do
# returns nil to prevent exploits
click =
TopicLinkClick.create_from(
url: "http://url-that-doesnt-exist.com",
post_id: @post.id,
ip: "127.0.0.1",
)
expect(click).to eq(nil)
# redirects if allowlisted
click =
TopicLinkClick.create_from(
url: "https://www.youtube.com/watch?v=jYd_5aggzd4",
post_id: @post.id,
ip: "127.0.0.1",
)
expect(click).to eq("https://www.youtube.com/watch?v=jYd_5aggzd4")
# does not change own link
expect {
TopicLinkClick.create_from(
url: @topic_link.url,
post_id: @post.id,
ip: "127.0.0.0",
user_id: @post.user_id,
)
}.not_to change(TopicLinkClick, :count)
# can handle double # in a url
# NOTE: this is not compliant but exists in the wild
click =
TopicLinkClick.create_from(
url: "http://discourse.org#a#b",
post_id: @post.id,
ip: "127.0.0.1",
)
expect(click).to eq("http://discourse.org#a#b")
end
context "with a valid url and post_id" do
before do
@url =
TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: "127.0.0.1")
@click = TopicLinkClick.last
end
it "creates a click" do
expect(@click).to be_present
expect(@click.topic_link).to eq(@topic_link)
expect(@url).to eq(@topic_link.url)
# second click should not record
expect {
TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: "127.0.0.1")
}.not_to change(TopicLinkClick, :count)
end
end
context "while logged in" do
fab!(:other_user) { Fabricate(:user) }
before do
@url =
TopicLinkClick.create_from(
url: @topic_link.url,
post_id: @post.id,
ip: "127.0.0.1",
user_id: other_user.id,
)
@click = TopicLinkClick.last
end
it "creates a click without an IP" do
expect(@click).to be_present
expect(@click.topic_link).to eq(@topic_link)
expect(@click.user_id).to eq(other_user.id)
expect(@click.ip_address).to eq(nil)
end
end
context "with relative urls" do
let(:host) { URI.parse(Discourse.base_url).host }
it "returns the url" do
url = TopicLinkClick.create_from(url: "/relative-url", post_id: @post.id, ip: "127.0.0.1")
expect(url).to eq("/relative-url")
end
it "finds a protocol relative urls with a host" do
url = "//#{host}/relative-url"
redirect = TopicLinkClick.create_from(url: url)
expect(redirect).to eq(url)
end
it "returns the url if it's on our host" do
url = "http://#{host}/relative-url"
redirect = TopicLinkClick.create_from(url: url)
expect(redirect).to eq(url)
end
context "with cdn links" do
before do
Rails.configuration.action_controller.asset_host = "https://cdn.discourse.org/stuff"
end
after { Rails.configuration.action_controller.asset_host = nil }
it "correctly handles cdn links" do
url =
TopicLinkClick.create_from(
url: "https://cdn.discourse.org/stuff/my_link",
topic_id: @topic.id,
ip: "127.0.0.3",
)
expect(url).to eq("https://cdn.discourse.org/stuff/my_link")
# cdn exploit
url =
TopicLinkClick.create_from(
url: "https://cdn.discourse.org/bad/my_link",
topic_id: @topic.id,
ip: "127.0.0.3",
)
expect(url).to eq(nil)
# cdn better link track
path = "/uploads/site/29/5b585f848d8761d5.xls"
post = Fabricate(:post, topic: @topic, raw: "[test](#{path})")
TopicLink.extract_from(post)
url =
TopicLinkClick.create_from(
url: "https://cdn.discourse.org/stuff#{path}",
topic_id: post.topic_id,
post_id: post.id,
ip: "127.0.0.3",
)
expect(url).to eq("https://cdn.discourse.org/stuff#{path}")
click = TopicLinkClick.order("id desc").first
expect(click.topic_link_id).to eq(TopicLink.order("id desc").first.id)
end
end
context "with s3 cdns" do
it "works with s3 urls" do
setup_s3
SiteSetting.s3_cdn_url = "https://discourse-s3-cdn.global.ssl.fastly.net"
post =
Fabricate(
:post,
topic: @topic,
raw: "[test](//test.localhost/uploads/default/my-test-link)",
)
TopicLink.extract_from(post)
url =
TopicLinkClick.create_from(
url: "https://discourse-s3-cdn.global.ssl.fastly.net/my-test-link",
topic_id: @topic.id,
ip: "127.0.0.3",
)
expect(url).to be_present
end
end
end
context "with a HTTPS version of the same URL" do
before do
@url =
TopicLinkClick.create_from(
url: "https://twitter.com",
topic_id: @topic.id,
ip: "127.0.0.3",
)
@click = TopicLinkClick.last
end
it "creates a click" do
expect(@click).to be_present
expect(@click.topic_link).to eq(@topic_link)
expect(@url).to eq("https://twitter.com")
end
end
context "with a google analytics tracking code" do
before do
@url =
TopicLinkClick.create_from(
url: "http://twitter.com?_ga=1.16846778.221554446.1071987018",
topic_id: @topic.id,
ip: "127.0.0.3",
)
@click = TopicLinkClick.last
end
it "creates a click" do
expect(@click).to be_present
expect(@click.topic_link).to eq(@topic_link)
expect(@url).to eq("http://twitter.com?_ga=1.16846778.221554446.1071987018")
end
end
context "with a query param and google analytics" do
before do
@topic = Fabricate(:topic, user: Fabricate(:user, refresh_auto_groups: true))
@post =
Fabricate(
:post,
topic: @topic,
user: @topic.user,
raw: "Here's a link to twitter: http://twitter.com?ref=forum",
)
TopicLink.extract_from(@post)
@topic_link = @topic.topic_links.first
end
it "creates a click" do
url =
TopicLinkClick.create_from(
url: "http://twitter.com?ref=forum&_ga=1.16846778.221554446.1071987018",
topic_id: @topic.id,
post_id: @post.id,
ip: "127.0.0.3",
)
click = TopicLinkClick.last
expect(click).to be_present
expect(click.topic_link).to eq(@topic_link)
expect(url).to eq("http://twitter.com?ref=forum&_ga=1.16846778.221554446.1071987018")
end
end
context "with same base URL with different query" do
it "are handled differently" do
post = Fabricate(:post, raw: <<~RAW)
no query param: http://example.com/a
with query param: http://example.com/a?b=c
with two query params: http://example.com/a?b=c&d=e
RAW
TopicLink.extract_from(post)
TopicLinkClick.create_from(
url: "http://example.com/a",
post_id: post.id,
ip: "127.0.0.1",
user: Fabricate(:user),
)
TopicLinkClick.create_from(
url: "http://example.com/a?b=c",
post_id: post.id,
ip: "127.0.0.2",
user: Fabricate(:user),
)
TopicLinkClick.create_from(
url: "http://example.com/a?b=c&d=e",
post_id: post.id,
ip: "127.0.0.3",
user: Fabricate(:user),
)
TopicLinkClick.create_from(
url: "http://example.com/a?b=c",
post_id: post.id,
ip: "127.0.0.4",
user: Fabricate(:user),
)
expect(
TopicLink.where("url LIKE '%example.com%'").pluck(:url, :clicks),
).to contain_exactly(
["http://example.com/a", 1],
["http://example.com/a?b=c", 2],
["http://example.com/a?b=c&d=e", 1],
)
end
end
context "with a google analytics tracking code and a hash" do
before do
@url =
TopicLinkClick.create_from(
url: "http://discourse.org?_ga=1.16846778.221554446.1071987018#faq",
topic_id: @topic.id,
ip: "127.0.0.3",
)
@click = TopicLinkClick.last
end
it "creates a click" do
expect(@click).to be_present
expect(@url).to eq("http://discourse.org?_ga=1.16846778.221554446.1071987018#faq")
end
end
context "with a valid url and topic_id" do
before do
@url =
TopicLinkClick.create_from(url: @topic_link.url, topic_id: @topic.id, ip: "127.0.0.3")
@click = TopicLinkClick.last
end
it "creates a click" do
expect(@click).to be_present
expect(@click.topic_link).to eq(@topic_link)
expect(@url).to eq(@topic_link.url)
end
end
end
end
end