neiam
/apollos_crib
It's Apollo's Crib
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
Plani: a plani only takes the owner's foci, ankyra and sources
b4aa1b1 · Gabe Morell · 2026-11-04 22:43
Message
{commit_body(@commit)}
Files changed
modified
apps/room_sanctum/lib/room_sanctum/configuration/plani.ex
+9
−0
@@ -14,6 +14,9 @@ defmodule RoomSanctum.Configuration.Plani do
| 14 | 14 | use Ecto.Schema |
| 15 | 15 | import Ecto.Changeset |
| 16 | 16 | |
| 17 | + alias RoomSanctum.Accounts.RabbitUser | |
| 18 | + alias RoomSanctum.Configuration.{Foci, Ownership, Source} | |
| 19 | + | |
| 17 | 20 | schema "cfg_plani" do |
| 18 | 21 | field :name, :string |
| 19 | 22 | belongs_to :user, RoomSanctum.Accounts.User |
@@ -102,6 +105,12 @@ defmodule RoomSanctum.Configuration.Plani do
| 102 | 105 | |> validate_home_tint() |
| 103 | 106 | |> foreign_key_constraint(:user_id) |
| 104 | 107 | |> foreign_key_constraint(:home_foci_id) |
| 108 | + # The form only offers the owner's own. Anything else would ask another | |
| 109 | + # user's sources, follow their client's position, or call their foci home. | |
| 110 | + |> Ownership.validate_owned(:home_foci_id, Foci) | |
| 111 | + |> Ownership.validate_owned(:home_foci_ids, Foci) | |
| 112 | + |> Ownership.validate_owned(:ankyra_id, RabbitUser) | |
| 113 | + |> Ownership.validate_owned(:sources, Source) | |
| 105 | 114 | end |
| 106 | 115 | |
| 107 | 116 | # A tint that is not one of the offered colours renders as an unstyled dot |
added
apps/room_sanctum/test/room_sanctum_web/live/plani_owned_ids_test.exs
+77
−0
@@ -0,0 +1,77 @@
| 1 | +defmodule RoomSanctumWeb.PlaniOwnedIdsTest do | |
| 2 | + @moduledoc """ | |
| 3 | + A Plani asks its owner's sources around wherever their client is, falling | |
| 4 | + back to their foci. The form only offers those; a forged id of anyone | |
| 5 | + else's is refused with a changeset error and nothing is saved. | |
| 6 | + """ | |
| 7 | + use RoomSanctumWeb.ConnCase | |
| 8 | + | |
| 9 | + import Phoenix.LiveViewTest | |
| 10 | + import RoomSanctum.DataCase, only: [errors_on: 1] | |
| 11 | + import RoomSanctum.OwnershipFixtures | |
| 12 | + | |
| 13 | + alias RoomSanctum.{Configuration, Repo} | |
| 14 | + alias RoomSanctum.Configuration.Plani | |
| 15 | + | |
| 16 | + setup :users_rabbit! | |
| 17 | + | |
| 18 | + setup %{conn: conn} do | |
| 19 | + me = owner() | |
| 20 | + %{conn: log_in_user(conn, me), me: me, them: owner()} | |
| 21 | + end | |
| 22 | + | |
| 23 | + defp submit(conn, fields) do | |
| 24 | + {:ok, live, _html} = live(conn, Routes.plani_index_path(conn, :new)) | |
| 25 | + | |
| 26 | + live | |
| 27 | + |> form("#plani-form") | |
| 28 | + |> render_submit(%{"plani" => Map.put(fields, "name", "Pocket")}) | |
| 29 | + end | |
| 30 | + | |
| 31 | + defp mine(ctx) do | |
| 32 | + foci = foci_for(ctx.me) | |
| 33 | + | |
| 34 | + %{ | |
| 35 | + "home_foci_id" => to_string(foci.id), | |
| 36 | + "home_foci_ids" => [to_string(foci.id)], | |
| 37 | + "ankyra_id" => to_string(ankyra_for(ctx.me).id), | |
| 38 | + "sources" => [to_string(source_for(ctx.me).id)] | |
| 39 | + } | |
| 40 | + end | |
| 41 | + | |
| 42 | + test "the form saves the owner's own foci, ankyra and sources", ctx do | |
| 43 | + own = mine(ctx) | |
| 44 | + submit(ctx.conn, own) | |
| 45 | + | |
| 46 | + assert [%Plani{} = saved] = Repo.all(Plani) | |
| 47 | + assert to_string(saved.ankyra_id) == own["ankyra_id"] | |
| 48 | + assert Enum.map(saved.sources, &to_string/1) == own["sources"] | |
| 49 | + end | |
| 50 | + | |
| 51 | + test "the form refuses each of another user's records", ctx do | |
| 52 | + own = mine(ctx) | |
| 53 | + their_foci = to_string(foci_for(ctx.them).id) | |
| 54 | + | |
| 55 | + forged = [ | |
| 56 | + %{"home_foci_id" => their_foci}, | |
| 57 | + %{"home_foci_ids" => own["home_foci_ids"] ++ [their_foci]}, | |
| 58 | + %{"ankyra_id" => to_string(ankyra_for(ctx.them).id)}, | |
| 59 | + %{"sources" => own["sources"] ++ [to_string(source_for(ctx.them).id)]} | |
| 60 | + ] | |
| 61 | + | |
| 62 | + for fields <- forged do | |
| 63 | + submit(ctx.conn, Map.merge(own, fields)) | |
| 64 | + assert Repo.all(Plani) == [], "saved with #{inspect(fields)}" | |
| 65 | + end | |
| 66 | + end | |
| 67 | + | |
| 68 | + test "an existing plani cannot be pointed at another user's source", ctx do | |
| 69 | + plani = plani_for(ctx.me) | |
| 70 | + | |
| 71 | + assert {:error, changeset} = | |
| 72 | + Configuration.update_plani(plani, %{sources: [source_for(ctx.them).id]}) | |
| 73 | + | |
| 74 | + assert %{sources: [_]} = errors_on(changeset) | |
| 75 | + assert {:ok, _} = Configuration.update_plani(plani, %{sources: [source_for(ctx.me).id]}) | |
| 76 | + end | |
| 77 | +end |
Parents: e70ed4d