Limita una diretta attiva per account e valorizza la copertina Premium Full.

Blocca la seconda diretta sullo stesso utente, registra gli abusi in admin e presenta la copertina come grafica caricata dalla società, con demo Team MLTV.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-01 12:52:26 +02:00
co-authored by Cursor
parent 356aa28fad
commit d52434fb2e
62 changed files with 1509 additions and 109 deletions
@@ -0,0 +1,47 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "Admin stream concurrency violations", type: :request do
let!(:admin) { AdminAccount.create!(username: "ops-abuse", password: "Password123") }
let!(:user) { User.create!(email: "abuse@test.it", name: "Abuser", password: "Password123", role: "coach") }
let!(:club) { Club.create!(name: "Abuse Club", sport: "volleyball") }
let!(:team) { club.teams.create!(name: "U16", sport: "volleyball") }
let!(:match_a) { team.matches.create!(opponent_name: "Rival A", sport: "volleyball") }
let!(:match_b) { team.matches.create!(opponent_name: "Rival B", sport: "volleyball") }
let!(:live) do
StreamSession.create!(match: match_a, user: user, platform: "matchlivetv", status: "live")
end
let!(:idle) do
StreamSession.create!(match: match_b, user: user, platform: "matchlivetv", status: "idle")
end
let!(:violation) do
StreamConcurrencyViolation.record!(attempted: idle, occupying: live, action: "start")
end
before do
post admin_login_path, params: { username: admin.username, password: "Password123" }
end
it "mostra l'elenco abusi" do
get admin_stream_concurrency_violations_path
expect(response).to have_http_status(:ok)
expect(response.body).to include("abuse@test.it")
expect(response.body).to include("Abuse Club")
expect(response.body).to include("Rival A")
expect(response.body).to include("Rival B")
end
it "mostra il badge in dashboard" do
get admin_root_path
expect(response).to have_http_status(:ok)
expect(response.body).to include("abuse@test.it")
end
it "mostra i tentativi nella scheda società" do
get admin_club_path(club)
expect(response).to have_http_status(:ok)
expect(response.body).to include("abuse@test.it")
expect(response.body).to include("Rival B")
end
end
@@ -0,0 +1,61 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "API start seconda diretta stesso account", type: :request do
let!(:user) { User.create!(email: "conc@test.it", name: "C", password: "Password123", role: "coach") }
let!(:club) { Club.create!(name: "Conc Club", sport: "volleyball") }
let!(:membership) { club.club_memberships.create!(user: user, role: "owner") }
let!(:team) { club.teams.create!(name: "Tigers", sport: "volleyball") }
let!(:match_a) { team.matches.create!(opponent_name: "A", sport: "volleyball") }
let!(:match_b) { team.matches.create!(opponent_name: "B", sport: "volleyball") }
let!(:live) do
StreamSession.create!(
match: match_a,
user: user,
platform: "matchlivetv",
status: "live",
started_at: 5.minutes.ago,
device_model: "Pixel 8",
client_os: "android"
)
end
let!(:idle) do
StreamSession.create!(
match: match_b,
user: user,
platform: "matchlivetv",
status: "idle",
device_model: "iPhone 15",
client_os: "ios"
)
end
def auth_headers
post "/api/v1/auth/login", params: { email: user.email, password: "Password123" }
token = response.parsed_body["access_token"]
{ "Authorization" => "Bearer #{token}", "Content-Type" => "application/json" }
end
before do
plan = Plan.find_or_initialize_by(slug: "premium_full")
plan.name ||= "Premium Full"
plan.features = (plan.features || {}).merge(
"platforms" => %w[matchlivetv],
"concurrent_streams_limit" => 10
)
plan.save!
club.create_subscription!(plan: plan, status: "active") if club.subscription.blank?
allow(SessionChannel).to receive(:broadcast_message)
end
it "restituisce 403 user_concurrent_stream e salva l'evidenza" do
patch "/api/v1/sessions/#{idle.id}/start", headers: auth_headers
expect(response).to have_http_status(:forbidden)
body = response.parsed_body
expect(body["error_code"]).to eq("user_concurrent_stream")
expect(body["error"]).to be_present
expect(StreamConcurrencyViolation.where(attempted_session_id: idle.id, occupying_session_id: live.id)).to exist
end
end
@@ -16,7 +16,17 @@ RSpec.describe "Public features page", type: :request do
expect(response.body).to include("Chi filma, filma")
expect(response.body).to include("Accesso con link")
expect(response.body).to include("La diretta resta della tua società")
expect(response.body).to include("La partita finisce. Il ricordo resta.")
expect(response.body).to include("Dai visibilità ai tuoi sponsor")
expect(response.body).to include("Copertina caricata per ogni partita")
expect(response.body).to include("Scopri Premium Full")
expect(response.body).to include("Team MLTV")
expect(response.body).to include("Squadra ospite")
expect(response.body).to include("Copertina personalizzata")
expect(response.body).not_to include("Lupi Santa Croce")
expect(response.body).not_to include("Rossi vs Neri")
expect(response.body).not_to include("Tigers Volley")
expect(response.body).not_to include("MAIN SPONSOR")
expect(response.body).not_to include("CON IL SUPPORTO DI")
expect(response.body).to include("Inizia gratis")
expect(response.body).to include("Confronta i piani")
expect(response.body).to include(public_signup_path)
@@ -10,6 +10,10 @@ RSpec.describe "Public home page", type: :request do
expect(response.body).to include(I18n.t("home.stores_label", locale: :it))
expect(response.body).to include(I18n.t("home.store_app_store_name", locale: :it))
expect(response.body).to include(I18n.t("home.store_play_name", locale: :it))
expect(response.body).to include("Dai visibilità ai tuoi sponsor")
expect(response.body).to include("Team MLTV")
expect(response.body).not_to include("Lupi Santa Croce")
expect(response.body).to include("Scopri Premium Full")
expect(response.body.scan(MatchLiveTv.app_store_url).size).to eq(3)
expect(response.body.scan(MatchLiveTv.play_store_url).size).to eq(3)
end
@@ -26,6 +26,14 @@ RSpec.describe "Public pricing page", type: :request do
expect(response.body).to include("Account trasmettitori")
expect(response.body).to include("Niente password condivise")
expect(response.body).to include("plan-card--featured")
expect(response.body).to include("Copertina personalizzabile")
expect(response.body).to include("con sponsor")
expect(response.body).to include("Copertina personalizzata")
expect(response.body).to include("Dai visibilità ai tuoi sponsor")
expect(response.body).to include("Team MLTV")
expect(response.body).not_to include("Lupi Santa Croce")
expect(response.body).not_to include("MAIN SPONSOR")
expect(response.body).not_to include("CON IL SUPPORTO DI")
expect(response.body).to include("Soddisfatti o rimborsati")
expect(response.body).to include("30 giorni soddisfatti o rimborsati")
expect(response.body).to include("ti rimborsiamo il 100%")
+11 -11
View File
@@ -1,12 +1,12 @@
require "rails_helper"
RSpec.describe "Public team page", type: :request do
let(:club) { Club.create!(name: "ASD Eagles", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
let(:club) { Club.create!(name: "Team MLTV", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
let!(:team) do
club.teams.create!(
name: "Tigers Volley U17",
name: "Team MLTV U15",
sport_key: "pallavolo",
description: "Squadra giovanile under 17.",
description: "Squadra giovanile under 15.",
roster_public: true
)
end
@@ -15,9 +15,9 @@ RSpec.describe "Public team page", type: :request do
get public_team_page_path(team.slug)
expect(response).to have_http_status(:ok)
expect(response.body).to include("Tigers Volley U17")
expect(response.body).to include("ASD Eagles")
expect(response.body).to include("Squadra giovanile under 17")
expect(response.body).to include("Team MLTV U15")
expect(response.body).to include("Team MLTV")
expect(response.body).to include("Squadra giovanile under 15")
end
it "restituisce 404 per slug inesistente" do
@@ -41,13 +41,13 @@ RSpec.describe "Public team page", type: :request do
end
RSpec.describe "Public team directory", type: :request do
let(:club) { Club.create!(name: "ASD Eagles", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
let(:club) { Club.create!(name: "Team MLTV", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
let(:user) { User.create!(email: "dir2@test.com", name: "Coach", password: "Password123", role: "coach") }
let!(:team) { club.teams.create!(name: "Tigers Volley U17", sport_key: "pallavolo") }
let!(:team) { club.teams.create!(name: "Team MLTV U15", sport_key: "pallavolo") }
before do
club.club_memberships.create!(user: user, role: "owner")
match = team.matches.create!(opponent_name: "Rival", sport_key: "pallavolo", scheduled_at: 1.day.from_now)
match = team.matches.create!(opponent_name: MatchLiveTv::Demo::AWAY_TEAM, sport_key: "pallavolo", scheduled_at: 1.day.from_now)
StreamSession.create!(
match: match, user: user, status: "live", platform: "matchlivetv",
privacy_status: "public", publish_token: "tok-dir", started_at: Time.current
@@ -58,13 +58,13 @@ RSpec.describe "Public team directory", type: :request do
get public_team_pages_path
expect(response).to have_http_status(:ok)
expect(response.body).to include("Tigers Volley U17")
expect(response.body).to include("Team MLTV U15")
expect(response.body).to include("In diretta")
end
it "filtra per sport via query string" do
get public_team_pages_path(sport: "basket")
expect(response.body).not_to include("Tigers Volley U17")
expect(response.body).not_to include("Team MLTV U15")
end
end