Files
redux-scraper/spec/models/http_log_entry_spec.rb
2025-02-25 19:59:41 +00:00

161 lines
4.9 KiB
Ruby

# typed: false
require "rails_helper"
RSpec.describe HttpLogEntry, type: :model do
describe "validations" do
subject { build(:http_log_entry) }
it { should validate_presence_of(:uri_scheme) }
it { should validate_presence_of(:uri_host) }
it { should validate_presence_of(:uri_path) }
it { should validate_presence_of(:verb) }
it { should validate_presence_of(:performed_by) }
it { should validate_presence_of(:status_code) }
it { should validate_presence_of(:response_time_ms) }
it { should validate_presence_of(:content_type) }
it { should validate_presence_of(:requested_at) }
it { should validate_length_of(:response_sha256).is_equal_to(32) }
end
describe "associations" do
it { should belong_to(:response).class_name("::BlobFile") }
it { should belong_to(:request_headers).class_name("::HttpLogEntryHeader") }
it do
should belong_to(:response_headers).class_name("::HttpLogEntryHeader")
end
it do
should belong_to(:caused_by_entry).class_name("::HttpLogEntry").optional
end
it { should have_many(:triggered_entries).class_name("::HttpLogEntry") }
end
describe "immutability" do
let(:entry) { create(:http_log_entry) }
it "prevents updates" do
expect { entry.update!(verb: :post) }.to raise_error(
ActiveRecord::ReadOnlyRecord,
)
end
it "prevents deletion" do
expect { entry.destroy }.to raise_error(ActiveRecord::ReadOnlyRecord)
end
end
describe "#uri=" do
let(:entry) { build(:http_log_entry) }
it "parses and sets URI components" do
entry.uri = "https://example.com/path?query=value#fragment"
expect(entry.uri_scheme).to eq("https")
expect(entry.uri_host).to eq("example.com")
expect(entry.uri_path).to eq("/path")
expect(entry.uri_query).to eq("query=value")
expect(entry.uri_hash).to eq("fragment")
end
it "handles URIs without optional components" do
entry.uri = "https://example.com/path"
expect(entry.uri_scheme).to eq("https")
expect(entry.uri_host).to eq("example.com")
expect(entry.uri_path).to eq("/path")
expect(entry.uri_query).to be_nil
expect(entry.uri_hash).to be_nil
end
it "parses complex URLs with multiple query parameters" do
entry.uri =
"https://www.example.com/big/path/here?and=query&other=query2#smaz"
expect(entry.uri_scheme).to eq("https")
expect(entry.uri_host).to eq("www.example.com")
expect(entry.uri_path).to eq("/big/path/here")
expect(entry.uri_query).to eq("and=query&other=query2")
expect(entry.uri_hash).to eq("smaz")
end
end
describe "#uri" do
let(:entry) { build(:http_log_entry, :with_query, :with_fragment) }
it "reconstructs the full URI" do
expect(entry.uri).to be_a(Addressable::URI)
expect(entry.uri.to_s).to eq(
"https://#{entry.uri_host}/path?foo=bar&baz=qux#section1",
)
end
end
describe "#uri_str" do
it "returns full URI string" do
entry = build(:http_log_entry, :with_query, :with_fragment)
expect(entry.uri_str).to eq(
"https://#{entry.uri_host}/path?foo=bar&baz=qux#section1",
)
end
it "handles URI without optional components" do
entry = build(:http_log_entry)
expect(entry.uri_str).to eq("https://#{entry.uri_host}/path")
end
end
describe "#uri_str_without_host" do
it "returns path and query components" do
entry = build(:http_log_entry, :with_query, :with_fragment)
expect(entry.uri_str_without_host).to eq("/path?foo=bar&baz=qux#section1")
end
it "handles path only" do
entry = build(:http_log_entry)
expect(entry.uri_str_without_host).to eq("/path")
end
end
describe ".find_by_uri_host_path" do
let!(:entry) { create(:http_log_entry) }
it "finds entry by URI string" do
found =
described_class.find_by_uri_host_path("https://#{entry.uri_host}/path")
expect(found).to eq(entry)
end
it "finds entry by Addressable::URI" do
uri = Addressable::URI.parse("https://#{entry.uri_host}/path")
found = described_class.find_by_uri_host_path(uri)
expect(found).to eq(entry)
end
it "returns nil for non-existent URI" do
found =
described_class.find_by_uri_host_path("https://nonexistent.com/path")
expect(found).to be_nil
end
end
describe "#response_size" do
let(:entry) { create(:http_log_entry) }
context "when response association is loaded" do
it "returns size from response object" do
test_content = "test content"
entry.response = build(:blob_file, contents: test_content)
expect(entry.response_size).to eq(test_content.bytesize)
end
end
context "when response association is not loaded" do
it "queries size directly from database" do
size = entry.response.size_bytes
entry.association(:response).reset
expect(entry.response_size).to eq(size)
end
end
end
end