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.
Pythiae: a pythiae only takes the owner's visions, foci, plani and ankyra
e70ed4d · Gabe Morell · 2026-11-04 22:43
Message
{commit_body(@commit)}
Files changed
modified
apps/room_sanctum/lib/room_sanctum/configuration/pythiae.ex
+10
−0
@@ -39,6 +39,9 @@ defmodule RoomSanctum.Configuration.Pythiae do
| 39 | 39 | use Ecto.Schema |
| 40 | 40 | import Ecto.Changeset |
| 41 | 41 | |
| 42 | + alias RoomSanctum.Accounts.RabbitUser | |
| 43 | + alias RoomSanctum.Configuration.{Foci, Ownership, Plani, Vision} | |
| 44 | + | |
| 42 | 45 | schema "cfg_pythiae" do |
| 43 | 46 | field :name, :string |
| 44 | 47 | field :ankyra, {:array, :integer} |
@@ -79,5 +82,12 @@ defmodule RoomSanctum.Configuration.Pythiae do
| 79 | 82 | |> foreign_key_constraint(:curr_foci) |
| 80 | 83 | |> foreign_key_constraint(:curr_plani) |
| 81 | 84 | |> validate_required([:visions, :ankyra, :name]) |
| 85 | + # The form only offers the owner's own; a forged id of someone else's | |
| 86 | + # would show their vision, at their foci, on their Ankyra. | |
| 87 | + |> Ownership.validate_owned(:visions, Vision) | |
| 88 | + |> Ownership.validate_owned(:ankyra, RabbitUser) | |
| 89 | + |> Ownership.validate_owned(:curr_vision, Vision) | |
| 90 | + |> Ownership.validate_owned(:curr_foci, Foci) | |
| 91 | + |> Ownership.validate_owned(:curr_plani, Plani) | |
| 82 | 92 | end |
| 83 | 93 | end |
modified
apps/room_sanctum/test/room_sanctum_web/live/ownership_test.exs
+11
−1
@@ -136,7 +136,17 @@ defmodule RoomSanctumWeb.OwnershipTest do
| 136 | 136 | |
| 137 | 137 | test "neither lists nor edits another user's pythiae that holds the vision", ctx do |
| 138 | 138 | mine = vision_for(ctx.me, "My dash") |
| 139 | − their_screen = pythiae_for(ctx.them, [mine.id]) | |
| 139 | + # Planted past the changeset, which no longer lets their pythiae take | |
| 140 | + # someone else's vision -- as one saved before it checked could have. | |
| 141 | + their_screen = | |
| 142 | + Repo.insert!(%Configuration.Pythiae{ | |
| 143 | + name: "screen-#{System.unique_integer([:positive])}", | |
| 144 | + user_id: ctx.them.id, | |
| 145 | + ankyra: [], | |
| 146 | + visions: [mine.id], | |
| 147 | + curr_vision: mine.id | |
| 148 | + }) | |
| 149 | + | |
| 140 | 150 | {:ok, live, _html} = live(ctx.conn, Routes.vision_show_path(ctx.conn, :show, mine)) |
| 141 | 151 | send(live.pid, :update_sec) |
| 142 | 152 | render_click(live, "toggle-pythiae-sel", %{}) |
added
apps/room_sanctum/test/room_sanctum_web/live/pythiae_owned_ids_test.exs
+85
−0
@@ -0,0 +1,85 @@
| 1 | +defmodule RoomSanctumWeb.PythiaeOwnedIdsTest do | |
| 2 | + @moduledoc """ | |
| 3 | + A pythiae shows its owner's visions, at their foci or Plani, on their | |
| 4 | + Ankyra. The form only offers those; a forged id of anyone else's is | |
| 5 | + 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.Pythiae | |
| 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.pythiae_index_path(conn, :new)) | |
| 25 | + | |
| 26 | + live | |
| 27 | + |> form("#pythiae-form") | |
| 28 | + |> render_submit(%{"pythiae" => Map.put(fields, "name", "screen")}) | |
| 29 | + end | |
| 30 | + | |
| 31 | + defp ids(records), do: Enum.map(records, &to_string(&1.id)) | |
| 32 | + | |
| 33 | + test "the form saves the owner's own visions, ankyra and foci", ctx do | |
| 34 | + vision = vision_for(ctx.me) | |
| 35 | + ankyra = ankyra_for(ctx.me) | |
| 36 | + foci = foci_for(ctx.me) | |
| 37 | + | |
| 38 | + submit(ctx.conn, %{ | |
| 39 | + "visions" => ids([vision]), | |
| 40 | + "ankyra" => ids([ankyra]), | |
| 41 | + "curr_vision" => to_string(vision.id), | |
| 42 | + "curr_foci" => to_string(foci.id) | |
| 43 | + }) | |
| 44 | + | |
| 45 | + assert [%Pythiae{} = saved] = Repo.all(Pythiae) | |
| 46 | + assert saved.visions == [vision.id] | |
| 47 | + assert saved.ankyra == [ankyra.id] | |
| 48 | + assert saved.curr_foci == foci.id | |
| 49 | + end | |
| 50 | + | |
| 51 | + test "the form refuses each of another user's records", ctx do | |
| 52 | + vision = vision_for(ctx.me) | |
| 53 | + ankyra = ankyra_for(ctx.me) | |
| 54 | + | |
| 55 | + own = %{ | |
| 56 | + "visions" => ids([vision]), | |
| 57 | + "ankyra" => ids([ankyra]), | |
| 58 | + "curr_vision" => to_string(vision.id) | |
| 59 | + } | |
| 60 | + | |
| 61 | + forged = [ | |
| 62 | + %{"visions" => ids([vision, vision_for(ctx.them)])}, | |
| 63 | + %{"ankyra" => ids([ankyra, ankyra_for(ctx.them)])}, | |
| 64 | + %{"curr_vision" => to_string(vision_for(ctx.them).id)}, | |
| 65 | + %{"curr_foci" => to_string(foci_for(ctx.them).id)}, | |
| 66 | + %{"curr_plani" => to_string(plani_for(ctx.them).id)} | |
| 67 | + ] | |
| 68 | + | |
| 69 | + for fields <- forged do | |
| 70 | + submit(ctx.conn, Map.merge(own, fields)) | |
| 71 | + assert Repo.all(Pythiae) == [], "saved with #{inspect(fields)}" | |
| 72 | + end | |
| 73 | + end | |
| 74 | + | |
| 75 | + test "an existing pythiae cannot be moved onto another user's plani", ctx do | |
| 76 | + {:ok, screen} = | |
| 77 | + Configuration.create_pythiae(%{name: "screen", user_id: ctx.me.id, visions: [], ankyra: []}) | |
| 78 | + | |
| 79 | + assert {:error, changeset} = | |
| 80 | + Configuration.update_pythiae(screen, %{curr_plani: plani_for(ctx.them).id}) | |
| 81 | + | |
| 82 | + assert %{curr_plani: [_]} = errors_on(changeset) | |
| 83 | + assert {:ok, _} = Configuration.update_pythiae(screen, %{curr_plani: plani_for(ctx.me).id}) | |
| 84 | + end | |
| 85 | +end |
Parents: 2d73edf