···44 require Logger
5566 alias Garden.Scheduler
77+ alias Garden.Socket.State
78 alias Garden.Storage
89 alias SowerClient.Orchestration.DeploymentStatus
910 alias SowerClient.Orchestration.SeedDeploymentStatus
···15161617 @impl Slipstream
1718 def handle_cast({:deployment_request, %{sid: sid}, force?}, socket) do
1818- request_payload =
1919- if force? do
2020- %{subscription_sids: [sid], force: true}
2121- else
2222- %{subscription_sids: [sid]}
2323- end
2424-2525- {:ok, upgrade_request} =
2626- SowerClient.Orchestration.DeploymentRequest.new(request_payload)
2727-1919+ {:ok, upgrade_request} = State.build_deployment_request(sid, force?)
2820 {:ok, _ref} = push_message(socket, upgrade_request)
29213022 {:noreply, socket}
···32243325 @impl Slipstream
3426 def handle_cast(:report_seeds, socket) do
3535- storage = Storage.read()
3636- subscriptions = Map.get(storage, :subscriptions, [])
3737-3838- report = Garden.Profile.collect_profiles_for_subscriptions(subscriptions)
3939-4040- if not Enum.empty?(subscriptions) and Enum.empty?(report.profiles) do
4141- Logger.debug(
4242- msg: "No profiles found for any targets",
4343- subscription_count: length(subscriptions)
4444- )
2727+ subscriptions = Map.get(Storage.read(), :subscriptions, [])
45284646- {:noreply, socket}
4747- else
4848- Logger.debug(
4949- msg: "Reporting seed profiles",
5050- profile_count: length(report.profiles),
5151- subscription_count: length(subscriptions)
5252- )
2929+ case State.build_seed_report(subscriptions) do
3030+ :no_profiles ->
3131+ Logger.debug(
3232+ msg: "No profiles found for any targets",
3333+ subscription_count: length(subscriptions)
3434+ )
53355454- topic = private_channel(socket)
5555- {:ok, _ref} = push(socket, topic, "garden:seeds:report", report)
3636+ {:report, report} ->
3737+ Logger.debug(
3838+ msg: "Reporting seed profiles",
3939+ profile_count: length(report.profiles),
4040+ subscription_count: length(subscriptions)
4141+ )
56425757- {:noreply, socket}
4343+ {:ok, _ref} = push(socket, private_channel(socket), "garden:seeds:report", report)
5844 end
4545+4646+ {:noreply, socket}
5947 end
60486149 @impl Slipstream
+37
apps/garden/lib/garden/socket/state.ex
···11+defmodule Garden.Socket.State do
22+ @moduledoc """
33+ Pure state transition functions for Garden.Socket.
44+55+ Each function takes relevant state and returns a result without
66+ performing side effects. The socket callbacks are thin wrappers
77+ that call these functions and execute the returned effects.
88+ """
99+1010+ alias SowerClient.Orchestration.DeploymentRequest
1111+1212+ def build_seed_report(
1313+ subscriptions,
1414+ collect_profiles_fun \\ &Garden.Profile.collect_profiles_for_subscriptions/1
1515+ ) do
1616+ report = collect_profiles_fun.(subscriptions)
1717+1818+ if not Enum.empty?(subscriptions) and Enum.empty?(report.profiles) do
1919+ :no_profiles
2020+ else
2121+ {:report, report}
2222+ end
2323+ end
2424+2525+ def build_deployment_request(sid, force?) do
2626+ payload = %{subscription_sids: [sid]}
2727+2828+ payload =
2929+ if force? do
3030+ Map.put(payload, :force, true)
3131+ else
3232+ payload
3333+ end
3434+3535+ DeploymentRequest.new(payload)
3636+ end
3737+end
+55
apps/garden/test/garden/socket/state_test.exs
···11+defmodule Garden.Socket.StateTest do
22+ use ExUnit.Case, async: true
33+44+ alias Garden.Socket.State
55+ alias SowerClient.Orchestration.DeploymentRequest
66+ alias SowerClient.Orchestration.GardenSeedsReport
77+88+ describe "build_deployment_request/2" do
99+ test "builds request with subscription sid" do
1010+ {:ok, %DeploymentRequest{} = request} = State.build_deployment_request("sub_123", false)
1111+1212+ assert request.subscription_sids == ["sub_123"]
1313+ assert request.request_id != nil
1414+ end
1515+1616+ test "sets force flag when true" do
1717+ {:ok, %DeploymentRequest{} = request} = State.build_deployment_request("sub_123", true)
1818+1919+ assert request.force == true
2020+ end
2121+2222+ test "omits force flag when false" do
2323+ {:ok, %DeploymentRequest{} = request} = State.build_deployment_request("sub_123", false)
2424+2525+ assert request.force == false
2626+ end
2727+ end
2828+2929+ describe "build_seed_report/1" do
3030+ test "returns report when profiles are found" do
3131+ subscriptions = [%{seed_type: "nixos", seed_name: "host", rules: []}]
3232+3333+ report =
3434+ GardenSeedsReport.cast!(%{
3535+ profiles: [%{profile_path: "/nix/var/nix/profiles/system", tags: %{}, generations: []}]
3636+ })
3737+3838+ assert {:report, ^report} =
3939+ State.build_seed_report(subscriptions, fn _subs -> report end)
4040+ end
4141+4242+ test "returns no_profiles when subscriptions exist but no profiles found" do
4343+ subscriptions = [%{seed_type: "nixos", seed_name: "host", rules: []}]
4444+ report = GardenSeedsReport.cast!(%{profiles: []})
4545+4646+ assert :no_profiles = State.build_seed_report(subscriptions, fn _subs -> report end)
4747+ end
4848+4949+ test "returns report when subscriptions are empty" do
5050+ report = GardenSeedsReport.cast!(%{profiles: []})
5151+5252+ assert {:report, ^report} = State.build_seed_report([], fn _subs -> report end)
5353+ end
5454+ end
5555+end