RSpec on the backend, exhaustively covering every role against every permission; static analysis (ESLint + strict TypeScript) as the frontend's quality gate.
# spec/policies/event_policy_spec.rb
require "rails_helper"
RSpec.describe EventPolicy, type: :policy do
let!(:lead) { create(:amigo) }
let!(:assistant) { create(:amigo) }
let!(:participant) { create(:amigo) }
let!(:outsider) { create(:amigo) }
let!(:admin) { create(:amigo, role: :admin) }
let!(:event) { create(:event, lead_coordinator: lead) }
before do
create(:event_amigo_connector, :lead, event: event, amigo: lead)
create(:event_amigo_connector, :assistant, event: event, amigo: assistant)
create(:event_amigo_connector, :participant, event: event, amigo: participant)
end
def policy_for(user, record = event)
described_class.new(user, record)
end
describe "top-level permissions" do
it "update? is allowed for admin, lead, or assistant" do
expect(policy_for(admin).update?).to eq(true)
expect(policy_for(lead).update?).to eq(true)
expect(policy_for(assistant).update?).to eq(true)
expect(policy_for(participant).update?).to eq(false)
expect(policy_for(outsider).update?).to eq(false)
expect(policy_for(nil).update?).to eq(false)
end
it "destroy? is allowed for admin or lead only" do
expect(policy_for(admin).destroy?).to eq(true)
expect(policy_for(lead).destroy?).to eq(true)
expect(policy_for(assistant).destroy?).to eq(false)
expect(policy_for(participant).destroy?).to eq(false)
expect(policy_for(outsider).destroy?).to eq(false)
end
end
describe "role management" do
it "manage_roles? is allowed for admin or lead only (not assistant)" do
expect(policy_for(admin).manage_roles?).to eq(true)
expect(policy_for(lead).manage_roles?).to eq(true)
expect(policy_for(assistant).manage_roles?).to eq(false)
end
end
describe "nil record safety" do
it "does not require a record for create? (record may be nil on create checks)" do
expect(described_class.new(outsider, nil).create?).to eq(true)
expect(described_class.new(nil, nil).create?).to eq(false)
end
end
end
Every predicate gets checked against admin / lead / assistant / participant / outsider / nil — including the edge case of authorizing a not-yet-created record.
The frontend has no automated test suite yet — its npm test script runs eslint + a strict tsc --noEmit typecheck (package.json) rather than unit tests.