require "rails_helper" RSpec.describe Podcast, type: :model do let(:podcast) { create(:podcast) } it "has a creator" do user = build(:user) pod = create(:podcast, creator: user) expect(pod.creator).to eq(user) end context "when callbacks are triggered after save" do it "triggers cache busting on save" do sidekiq_assert_enqueued_with(job: Podcasts::BustCacheWorker, args: [podcast.path]) do podcast.save end end end describe "validations" do describe "builtin validations" do subject { podcast } it { is_expected.to belong_to(:creator).class_name("User").inverse_of(:created_podcasts).optional } it { is_expected.to have_many(:owners).through(:podcast_ownerships) } it { is_expected.to have_many(:podcast_episodes).dependent(:destroy) } it { is_expected.to have_many(:podcast_ownerships).dependent(:destroy) } it { is_expected.to validate_presence_of(:feed_url) } it { is_expected.to validate_presence_of(:image) } it { is_expected.to validate_presence_of(:main_color_hex) } it { is_expected.to validate_presence_of(:slug) } it { is_expected.to validate_presence_of(:title) } end # Couldn't use shoulda uniqueness matchers for these tests because: # Shoulda uses `save(validate: false)` which skips validations # So an invalid record is trying to be saved but fails because of the db constraints # https://git.io/fjg2g it "validates slug uniqueness" do podcast2 = build(:podcast, slug: podcast.slug) expect(podcast2).not_to be_valid expect(podcast2.errors[:slug]).to be_present end it "validates feed_url uniqueness" do podcast2 = build(:podcast, feed_url: podcast.feed_url) expect(podcast2).not_to be_valid expect(podcast2.errors[:feed_url]).to be_present end it "validates feed_url format" do podcast2 = build(:podcast, feed_url: "example.com") expect(podcast2).not_to be_valid expect(podcast2.errors[:feed_url]).to be_present end it "validates main_color_hex" do podcast2 = build(:podcast, main_color_hex: "#FFFFFF") expect(podcast2).not_to be_valid expect(podcast2.errors[:main_color_hex]).to be_present end it "doesn't allow to create a podcast with a reserved word slug" do enter_podcast = build(:podcast, slug: "enter") expect(enter_podcast).not_to be_valid expect(enter_podcast.errors[:slug]).to be_present end it "is invalid when a user with a username equal to the podcast slug exists" do create(:user, username: "annabu") user_podcast = build(:podcast, slug: "annabu") expect(user_podcast).not_to be_valid expect(user_podcast.errors[:slug]).to be_present end it "is invalid when a page with a slug equal to the podcast slug exists" do create(:page, slug: "superpage") user_podcast = build(:podcast, slug: "superpage") expect(user_podcast).not_to be_valid expect(user_podcast.errors[:slug]).to be_present end it "is invalid when an org with a slug equal to the podcast slug exists" do create(:organization, slug: "superorganization") user_podcast = build(:podcast, slug: "superorganization") expect(user_podcast).not_to be_valid expect(user_podcast.errors[:slug]).to be_present end end describe ".reachable" do it "is reachable when it has a reachable episode" do expect do create(:podcast_episode, reachable: true) create(:podcast, published: true) end.to change(described_class.reachable, :count).by(1) end it "is reachable when it has a reachable episode even if it is unpublished" do expect do create(:podcast_episode, reachable: true) create(:podcast, published: false) end.to change(described_class.reachable, :count).by(1) end it "is not reachable when it has an unreachable episode" do expect do create(:podcast_episode, reachable: false) create(:podcast, published: true) end.to change(described_class.reachable, :count).by(0) end end describe ".available" do it "is available when it has a reachable episode and it is published" do expect do create(:podcast_episode, reachable: true) create(:podcast, published: true) end.to change(described_class.available, :count).by(1) end it "is not available when it has an unreachable episode" do expect do create(:podcast_episode, reachable: false) create(:podcast, published: true) end.to change(described_class.available, :count).by(0) end it "is not available when it is not published" do expect do create(:podcast, published: false) end.to change(described_class.available, :count).by(0) end end describe "#existing_episode" do let(:guid) { "http://podcast.example/file.mp3" } let(:item_attributes) do { pubDate: "2019-06-19", enclosure_url: "https://audio.simplecast.com/2330f132.mp3", description: "yet another podcast", title: "lightalloy's podcast", guid: guid, itunes_subtitle: "hello", content_encoded: nil, itunes_summary: "world" } end let(:item) do build(:podcast_episode_rss_item, item_attributes.merge(link: "https://litealloy.ru")) end it "determines existing episode by media_url" do episode = create(:podcast_episode, podcast: podcast, media_url: "https://audio.simplecast.com/2330f132.mp3") expect(podcast.existing_episode(item)).to eq(episode) end it "determines existing episode by title" do episode = create(:podcast_episode, podcast: podcast, title: "lightalloy's podcast") expect(podcast.existing_episode(item)).to eq(episode) end it "determines existing episode by guid" do episode = create(:podcast_episode, podcast: podcast, guid: guid) expect(podcast.existing_episode(item)).to eq(episode) end it "determines existing episode by website_url" do episode = create(:podcast_episode, podcast: podcast, website_url: "https://litealloy.ru") expect(podcast.existing_episode(item)).to eq(episode) end it "doesn't determine existing episode if episode link is empty" do create(:podcast_episode, podcast: podcast, website_url: "") no_link_item = build(:podcast_episode_rss_item, item_attributes.merge(link: "")) expect(podcast.existing_episode(no_link_item)).to eq(nil) end it "doesn't determine existing episode by non-unique website_url" do podcast.update_attribute(:unique_website_url?, false) create(:podcast_episode, podcast: podcast, website_url: "https://litealloy.ru") expect(podcast.existing_episode(item)).to eq(nil) end end describe "#admins" do let(:user) { create(:user) } it "returns podcast admins" do user.add_role(:podcast_admin, podcast) expect(podcast.admins).to include(user) end it "does not return admins for other podcasts" do other_podcast = create(:podcast) user.add_role(:podcast_admin, other_podcast) expect(podcast.admins).to be_empty end end end