# frozen_string_literal: true require "rails_helper" RSpec.describe Streams::NodeRegistry do def with_env(vars) previous = vars.keys.index_with { |k| ENV[k] } vars.each { |k, v| ENV[k] = v } yield ensure previous.each { |k, v| v.nil? ? ENV.delete(k) : ENV[k] = v } end let(:home_env) do { "MEDIAMTX_API_URL" => "http://mtx-home:9997", "MEDIAMTX_RTMP_URL" => "rtmp://ingest-home.example:1935", "HLS_PUBLIC_URL" => "https://ingest-home.example/hls", "MEDIAMTX_INTERNAL_RTMP_URL" => "rtmp://mtx-home:1935", "MEDIAMTX_HLS_URL" => "http://mtx-home:8888", "STREAM_NODE_HOME_MAX_PUBLISHERS" => "2" } end it "creates home node from ENV" do with_env(home_env) do node = described_class.ensure_home_from_env! expect(node.slug).to eq("home") expect(node.rtmp_base_url).to eq("rtmp://ingest-home.example:1935") expect(node.api_base_url).to eq("http://mtx-home:9997") expect(node.max_publishers).to eq(2) expect(node.status).to eq("ready") end end it "allocates the least loaded ready node" do with_env(home_env) do home = described_class.ensure_home_from_env! cloud = StreamNode.create!( slug: "ingest-01", hostname: "ingest-01.mltv-stream.net", role: "cloud", status: "ready", provider: "hetzner", rtmp_base_url: "rtmp://ingest-01.mltv-stream.net:1935", hls_base_url: "https://ingest-01.mltv-stream.net/hls", api_base_url: "http://10.0.0.2:9997", max_publishers: 2, max_relays: 2 ) user = User.create!(email: "n@example.com", name: "N", password: "Password123", role: "coach") club = Club.create!(name: "C", sport: "volleyball") team = club.teams.create!(name: "T", sport: "volleyball", slug: "t-node") match = team.matches.create!(opponent_name: "X", scheduled_at: 1.hour.from_now) StreamSession.create!( match: match, user: user, platform: "matchlivetv", status: "live", stream_node: home ) expect(described_class.allocate!).to eq(cloud) end end it "raises when no capacity remains" do with_env(home_env.merge("STREAM_NODE_HOME_MAX_PUBLISHERS" => "1")) do home = described_class.ensure_home_from_env! user = User.create!(email: "full@example.com", name: "F", password: "Password123", role: "coach") club = Club.create!(name: "Full", sport: "volleyball") team = club.teams.create!(name: "T", sport: "volleyball", slug: "t-full") match = team.matches.create!(opponent_name: "X", scheduled_at: 1.hour.from_now) StreamSession.create!( match: match, user: user, platform: "matchlivetv", status: "live", stream_node: home ) expect { described_class.allocate! }.to raise_error(Streams::NodeRegistry::NoCapacityError) end end end