defmodule Microwaveprop.Weather.SolarClientTest do use ExUnit.Case, async: true alias Microwaveprop.Weather.SolarClient @sample_gfz_file """ # Comment line 1 # Comment line 2 # YYYY MM DD days days_m Bsr dB Kp1 Kp2 Kp3 Kp4 Kp5 Kp6 Kp7 Kp8 ap1 ap2 ap3 ap4 ap5 ap6 ap7 ap8 Ap SN F10.7obs F10.7adj D 2024 06 15 33769 33769.5 2589 18 2.000 1.667 2.333 3.000 2.667 1.333 1.000 1.667 7 6 9 15 12 5 4 6 8 120 150.2 148.5 2 2024 06 16 33770 33770.5 2589 19 3.333 4.000 2.667 1.000 0.667 1.333 2.000 1.667 18 27 12 4 3 5 7 6 10 115 145.0 143.2 2 """ @sample_row_with_missing "1932 01 01 0 0.5 1352 10 3.333 2.667 2.333 2.667 3.333 2.667 3.333 3.333 18 12 9 12 18 12 18 18 15 22 -1.0 -1.0 2" @sample_row_all_missing "1940 05 01 3043 3043.5 1385 23 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1.0 -1.0 0" describe "parse_gfz_row/1" do test "parses a complete data row" do row = "2024 06 15 33769 33769.5 2589 18 2.000 1.667 2.333 3.000 2.667 1.333 1.000 1.667 7 6 9 15 12 5 4 6 8 120 150.2 148.5 2" assert {:ok, parsed} = SolarClient.parse_gfz_row(row) assert parsed.date == ~D[2024-06-15] assert parsed.sfi == 150.2 assert parsed.sfi_adjusted == 148.5 assert parsed.sunspot_number == 120 assert parsed.ap_index == 8 assert parsed.kp_values == [2.0, 1.667, 2.333, 3.0, 2.667, 1.333, 1.0, 1.667] end test "maps -1 values to nil for F10.7" do assert {:ok, parsed} = SolarClient.parse_gfz_row(@sample_row_with_missing) assert parsed.date == ~D[1932-01-01] assert is_nil(parsed.sfi) assert is_nil(parsed.sfi_adjusted) assert parsed.sunspot_number == 22 assert parsed.ap_index == 15 end test "maps all -1 values to nil" do assert {:ok, parsed} = SolarClient.parse_gfz_row(@sample_row_all_missing) assert parsed.date == ~D[1940-05-01] assert is_nil(parsed.sfi) assert is_nil(parsed.sfi_adjusted) assert is_nil(parsed.sunspot_number) assert is_nil(parsed.ap_index) assert parsed.kp_values == [nil, nil, nil, nil, nil, nil, nil, nil] end end describe "parse_gfz_file/1" do test "skips comment lines and parses data rows" do result = SolarClient.parse_gfz_file(@sample_gfz_file) assert length(result) == 2 [first, second] = result assert first.date == ~D[2024-06-15] assert first.sfi == 150.2 assert second.date == ~D[2024-06-16] assert second.sfi == 145.0 end test "returns empty list for empty input" do assert SolarClient.parse_gfz_file("") == [] end test "returns empty list for comments-only input" do assert SolarClient.parse_gfz_file("# just a comment\n# another") == [] end end describe "fetch_solar_indices_since/1" do test "filters records to those on or after the given date" do records = SolarClient.parse_gfz_file(@sample_gfz_file) # Records are 2024-06-15 and 2024-06-16 filtered = SolarClient.filter_since(records, ~D[2024-06-16]) assert length(filtered) == 1 assert hd(filtered).date == ~D[2024-06-16] end test "returns all records when since_date is before all records" do records = SolarClient.parse_gfz_file(@sample_gfz_file) filtered = SolarClient.filter_since(records, ~D[2020-01-01]) assert length(filtered) == 2 end test "returns empty list when since_date is after all records" do records = SolarClient.parse_gfz_file(@sample_gfz_file) filtered = SolarClient.filter_since(records, ~D[2025-01-01]) assert filtered == [] end end describe "data_url/0" do test "returns the GFZ data URL" do assert SolarClient.data_url() == "https://kp.gfz.de/app/files/Kp_ap_Ap_SN_F107_since_1932.txt" end end end