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