Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: add list_flop and seed list LiveView tests

Context tests for Seed.list_flop/1 covering filtering, sorting, pagination,
and tag preloading. LiveView tests for the seed index page covering rendering,
filter form interaction, sortable headers, pagination visibility, and row
navigation.

SOW-110

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+225
+92
apps/sower/test/sower/seed_test.exs
··· 138 138 end 139 139 end 140 140 141 + describe "list_flop/1" do 142 + test "returns seeds with default ordering" do 143 + seed_a = seed_fixture(%{name: "alpha"}) 144 + seed_b = seed_fixture(%{name: "bravo"}) 145 + 146 + assert {:ok, {seeds, meta}} = Seed.list_flop() 147 + sids = Enum.map(seeds, & &1.sid) 148 + 149 + # Default order is updated_at desc, so most recent first 150 + assert seed_b.sid in sids 151 + assert seed_a.sid in sids 152 + assert meta.total_count == 2 153 + end 154 + 155 + test "filters by name with ilike_and" do 156 + seed_fixture(%{name: "kale-host"}) 157 + seed_fixture(%{name: "bravo-host"}) 158 + 159 + params = %{ 160 + "filters" => [%{"field" => "name", "op" => "ilike_and", "value" => "kale"}] 161 + } 162 + 163 + assert {:ok, {seeds, meta}} = Seed.list_flop(params) 164 + assert length(seeds) == 1 165 + assert hd(seeds).name == "kale-host" 166 + assert meta.total_count == 1 167 + end 168 + 169 + test "filters by seed_type" do 170 + seed_fixture(%{name: "host-a", seed_type: "nixos"}) 171 + seed_fixture(%{name: "user-a", seed_type: "home-manager"}) 172 + 173 + params = %{ 174 + "filters" => [%{"field" => "seed_type", "op" => "==", "value" => "nixos"}] 175 + } 176 + 177 + assert {:ok, {seeds, _meta}} = Seed.list_flop(params) 178 + assert Enum.all?(seeds, &(&1.seed_type == "nixos")) 179 + end 180 + 181 + test "combines multiple filters" do 182 + seed_fixture(%{name: "kale", seed_type: "nixos"}) 183 + seed_fixture(%{name: "kale-hm", seed_type: "home-manager"}) 184 + seed_fixture(%{name: "bravo", seed_type: "nixos"}) 185 + 186 + params = %{ 187 + "filters" => [ 188 + %{"field" => "name", "op" => "ilike_and", "value" => "kale"}, 189 + %{"field" => "seed_type", "op" => "==", "value" => "nixos"} 190 + ] 191 + } 192 + 193 + assert {:ok, {seeds, meta}} = Seed.list_flop(params) 194 + assert length(seeds) == 1 195 + assert hd(seeds).name == "kale" 196 + assert meta.total_count == 1 197 + end 198 + 199 + test "sorts by name ascending" do 200 + seed_fixture(%{name: "charlie"}) 201 + seed_fixture(%{name: "alpha"}) 202 + seed_fixture(%{name: "bravo"}) 203 + 204 + params = %{"order_by" => ["name"], "order_directions" => ["asc"]} 205 + 206 + assert {:ok, {seeds, _meta}} = Seed.list_flop(params) 207 + names = Enum.map(seeds, & &1.name) 208 + assert names == ["alpha", "bravo", "charlie"] 209 + end 210 + 211 + test "paginates results" do 212 + for i <- 1..5, do: seed_fixture(%{name: "seed-#{i}"}) 213 + 214 + params = %{"page" => 1, "page_size" => 2} 215 + assert {:ok, {seeds, meta}} = Seed.list_flop(params) 216 + assert length(seeds) == 2 217 + assert meta.total_count == 5 218 + assert meta.total_pages == 3 219 + 220 + params = %{"page" => 3, "page_size" => 2} 221 + assert {:ok, {seeds, _meta}} = Seed.list_flop(params) 222 + assert length(seeds) == 1 223 + end 224 + 225 + test "preloads tags" do 226 + seed_fixture(%{tags: [%{key: "env", value: "prod"}]}) 227 + 228 + assert {:ok, {[seed], _meta}} = Seed.list_flop() 229 + assert [%{key: "env", value: "prod"}] = seed.tags 230 + end 231 + end 232 + 141 233 describe "extract_info_from_store_path/1" do 142 234 test "extracts the derivation name from a NixOS store path" do 143 235 assert Seed.extract_info_from_store_path("/nix/store/abc123def-nixos-system-myhost-25.11") ==
+133
apps/sower/test/sower_web/live/seed_live_index_test.exs
··· 1 + defmodule SowerWeb.SeedLive.IndexTest do 2 + use SowerWeb.ConnCase, async: true 3 + 4 + import Phoenix.LiveViewTest 5 + import Sower.SeedFixtures 6 + 7 + setup [:register_and_log_in_user] 8 + 9 + setup %{user: user} do 10 + Sower.Repo.put_org_id(user.org_id) 11 + :ok 12 + end 13 + 14 + test "renders seed list with default ordering", %{conn: conn} do 15 + seed_fixture(%{name: "alpha-host", seed_type: "nixos"}) 16 + seed_fixture(%{name: "bravo-host", seed_type: "home-manager"}) 17 + 18 + {:ok, _live, html} = live(conn, ~p"/seeds") 19 + 20 + assert html =~ "Listing Seeds" 21 + assert html =~ "alpha-host" 22 + assert html =~ "bravo-host" 23 + end 24 + 25 + test "filters by seed_type via query params", %{conn: conn} do 26 + seed_fixture(%{name: "nixos-seed", seed_type: "nixos"}) 27 + seed_fixture(%{name: "hm-seed", seed_type: "home-manager"}) 28 + 29 + path = 30 + "/seeds?filters[0][field]=seed_type&filters[0][op]=%3D%3D&filters[0][value]=nixos" 31 + 32 + {:ok, _live, html} = live(conn, path) 33 + 34 + assert html =~ "nixos-seed" 35 + refute html =~ "hm-seed" 36 + end 37 + 38 + test "filters by name via query params", %{conn: conn} do 39 + seed_fixture(%{name: "kale-host", seed_type: "nixos"}) 40 + seed_fixture(%{name: "bravo-host", seed_type: "nixos"}) 41 + 42 + path = 43 + "/seeds?filters[0][field]=name&filters[0][op]=ilike_and&filters[0][value]=kale" 44 + 45 + {:ok, _live, html} = live(conn, path) 46 + 47 + assert html =~ "kale-host" 48 + refute html =~ "bravo-host" 49 + end 50 + 51 + test "filter form triggers filtering", %{conn: conn} do 52 + seed_fixture(%{name: "kale-host", seed_type: "nixos"}) 53 + seed_fixture(%{name: "bravo-host", seed_type: "home-manager"}) 54 + 55 + {:ok, live, _html} = live(conn, ~p"/seeds") 56 + 57 + live 58 + |> element("form") 59 + |> render_change(%{"seed_type" => "nixos", "name" => ""}) 60 + 61 + assert_patch(live) 62 + html = render(live) 63 + 64 + assert html =~ "kale-host" 65 + refute html =~ "bravo-host" 66 + end 67 + 68 + test "name filter narrows results", %{conn: conn} do 69 + seed_fixture(%{name: "kale-host", seed_type: "nixos"}) 70 + seed_fixture(%{name: "bravo-host", seed_type: "nixos"}) 71 + 72 + {:ok, live, _html} = live(conn, ~p"/seeds") 73 + 74 + live 75 + |> element("form") 76 + |> render_change(%{"name" => "kale", "seed_type" => ""}) 77 + 78 + assert_patch(live) 79 + html = render(live) 80 + 81 + assert html =~ "kale-host" 82 + refute html =~ "bravo-host" 83 + end 84 + 85 + test "sortable column headers are links", %{conn: conn} do 86 + seed_fixture() 87 + 88 + {:ok, live, _html} = live(conn, ~p"/seeds") 89 + 90 + assert has_element?(live, "th a", "name") 91 + assert has_element?(live, "th a", "seed_type") 92 + end 93 + 94 + test "clicking sort header updates ordering", %{conn: conn} do 95 + seed_fixture(%{name: "alpha"}) 96 + seed_fixture(%{name: "charlie"}) 97 + seed_fixture(%{name: "bravo"}) 98 + 99 + {:ok, live, _html} = live(conn, ~p"/seeds") 100 + 101 + live |> element("th a", "name") |> render_click() 102 + assert_patch(live) 103 + html = render(live) 104 + 105 + # Should show a sort indicator 106 + assert html =~ "\u25B4" or html =~ "\u25BE" 107 + end 108 + 109 + test "pagination is shown when results exceed page size", %{conn: conn} do 110 + for i <- 1..30, do: seed_fixture(%{name: "seed-#{String.pad_leading("#{i}", 3, "0")}"}) 111 + 112 + {:ok, _live, html} = live(conn, ~p"/seeds") 113 + 114 + assert html =~ "Go to page 2" 115 + end 116 + 117 + test "pagination is not shown when results fit on one page", %{conn: conn} do 118 + seed_fixture() 119 + 120 + {:ok, _live, html} = live(conn, ~p"/seeds") 121 + 122 + refute html =~ "Go to page 2" 123 + end 124 + 125 + test "row click navigates to seed show page", %{conn: conn} do 126 + seed = seed_fixture(%{name: "clickable-seed"}) 127 + 128 + {:ok, live, _html} = live(conn, ~p"/seeds") 129 + 130 + assert has_element?(live, ~s|td[phx-click]|) 131 + assert has_element?(live, ~s|a[href="/seeds/#{seed.sid}"]|, "Show") 132 + end 133 + end