Previously combined as a single "556-223" caliber. Now tracked separately with dedicated scraper URLs per retailer. Also fixes broken URLs for Lucky Gunner, True Shot Ammo, and Palmetto State Armory.
111 lines
3.7 KiB
Elixir
111 lines
3.7 KiB
Elixir
defmodule Ammoprices.Scraping.Retailers.PalmettoStateArmoryTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias Ammoprices.Scraping.Retailers.PalmettoStateArmory
|
|
|
|
@fixture_path "test/fixtures/palmetto_state_armory/9mm.html"
|
|
|
|
describe "retailer_slug/0" do
|
|
test "returns palmetto-state-armory" do
|
|
assert PalmettoStateArmory.retailer_slug() == "palmetto-state-armory"
|
|
end
|
|
end
|
|
|
|
describe "category_url/1" do
|
|
test "maps 9mm-luger caliber to correct URL" do
|
|
caliber = %{slug: "9mm-luger"}
|
|
assert PalmettoStateArmory.category_url(caliber) == "/9mm-ammo.html"
|
|
end
|
|
|
|
test "maps 223-rem caliber to correct URL" do
|
|
caliber = %{slug: "223-rem"}
|
|
assert PalmettoStateArmory.category_url(caliber) == "/223-ammo.html"
|
|
end
|
|
|
|
test "maps 556-nato caliber to correct URL" do
|
|
caliber = %{slug: "556-nato"}
|
|
assert PalmettoStateArmory.category_url(caliber) == "/5-56-ammo.html"
|
|
end
|
|
|
|
test "returns nil for unmapped caliber" do
|
|
caliber = %{slug: "unknown-caliber"}
|
|
assert PalmettoStateArmory.category_url(caliber) == nil
|
|
end
|
|
end
|
|
|
|
describe "parse_products/1" do
|
|
setup do
|
|
html = File.read!(@fixture_path)
|
|
%{html: html}
|
|
end
|
|
|
|
test "extracts all products from HTML", %{html: html} do
|
|
products = PalmettoStateArmory.parse_products(html)
|
|
assert length(products) == 4
|
|
end
|
|
|
|
test "extracts product title", %{html: html} do
|
|
[first | _] = PalmettoStateArmory.parse_products(html)
|
|
assert first.title == "TROY 9MM 115GR FMJ Ammo, 50rds - YTR9M115FMJ"
|
|
end
|
|
|
|
test "extracts product URL", %{html: html} do
|
|
[first | _] = PalmettoStateArmory.parse_products(html)
|
|
|
|
assert first.url ==
|
|
"https://palmettostatearmory.com/troy-9mm-115gr-fmj-ammo-50rds-ytr9m115fmj.html"
|
|
end
|
|
|
|
test "extracts price in cents from data-price-amount", %{html: html} do
|
|
[first | _] = PalmettoStateArmory.parse_products(html)
|
|
assert first.price_cents == 1199
|
|
end
|
|
|
|
test "extracts price per round in cents from unit_price", %{html: html} do
|
|
[first | _] = PalmettoStateArmory.parse_products(html)
|
|
# 0.2398 * 100 = 23.98 → 24 cents
|
|
assert first.price_per_round_cents == 24
|
|
end
|
|
|
|
test "extracts grain weight from title", %{html: html} do
|
|
[first | _] = PalmettoStateArmory.parse_products(html)
|
|
assert first.grain_weight == 115
|
|
end
|
|
|
|
test "extracts round count from title", %{html: html} do
|
|
[first | _] = PalmettoStateArmory.parse_products(html)
|
|
assert first.round_count == 50
|
|
end
|
|
|
|
test "marks all listed products as in stock", %{html: html} do
|
|
products = PalmettoStateArmory.parse_products(html)
|
|
assert Enum.all?(products, & &1.in_stock)
|
|
end
|
|
|
|
test "detects casing from title", %{html: html} do
|
|
products = PalmettoStateArmory.parse_products(html)
|
|
# Third product: "Steel Case" in title
|
|
assert Enum.at(products, 2).casing == "steel"
|
|
# Second product: "Brass" in title
|
|
assert Enum.at(products, 1).casing == "brass"
|
|
end
|
|
|
|
test "detects subsonic from title", %{html: html} do
|
|
products = PalmettoStateArmory.parse_products(html)
|
|
# Fourth product: "Subsonic" in title
|
|
assert Enum.at(products, 3).subsonic == true
|
|
end
|
|
|
|
test "non-subsonic products return false", %{html: html} do
|
|
[first | _] = PalmettoStateArmory.parse_products(html)
|
|
assert first.subsonic == false
|
|
end
|
|
|
|
test "extracts brand from beginning of title", %{html: html} do
|
|
products = PalmettoStateArmory.parse_products(html)
|
|
assert hd(products).brand == "TROY"
|
|
assert Enum.at(products, 1).brand == "CCI"
|
|
assert Enum.at(products, 2).brand == "Tula"
|
|
end
|
|
end
|
|
end
|