Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retry asset wait file processing timeouts #1150

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/models/apple/config.rb
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ def self.build_apple_config(podcast, key)
def self.mark_as_delivered!(apple_publisher)
apple_publisher.episodes_to_sync.each do |episode|
if episode.podcast_container&.needs_delivery? == false
episode.feeder_episode.apple_has_delivery!
episode.feeder_episode.apple_mark_as_delivered!
end
end
end
25 changes: 18 additions & 7 deletions app/models/apple/episode.rb
Original file line number Diff line number Diff line change
@@ -15,10 +15,8 @@ class Episode
EPISODE_ASSET_WAIT_TIMEOUT = 15.minutes.freeze
EPISODE_ASSET_WAIT_INTERVAL = 10.seconds.freeze

# Cleans up old delivery/delivery files iff the episode is to be delivered
# Cleans up old delivery/delivery files iff the episode is to be uploaded
def self.prepare_for_delivery(episodes)
episodes = episodes.select { |ep| ep.needs_delivery? }

episodes.map do |ep|
Rails.logger.info("Preparing episode #{ep.feeder_id} for delivery", {episode_id: ep.feeder_id})
ep.feeder_episode.apple_prepare_for_delivery!
@@ -499,9 +497,9 @@ def audio_asset_state_success?
end

def has_media_version?
return false unless delivery_status.present? && delivery_status.source_media_version_id.present?
return false unless apple_status.present? && apple_status.source_media_version_id.present?

delivery_status.source_media_version_id == feeder_episode.media_version_id
apple_status.source_media_version_id == feeder_episode.media_version_id
end

def needs_media_version?
@@ -573,7 +571,20 @@ def apple_episode_delivery_statuses
alias_method :container, :podcast_container
alias_method :deliveries, :podcast_deliveries
alias_method :delivery_files, :podcast_delivery_files
alias_method :delivery_status, :apple_episode_delivery_status
alias_method :delivery_statuses, :apple_episode_delivery_statuses
alias_method :apple_status, :apple_episode_delivery_status
alias_method :apple_statuses, :apple_episode_delivery_statuses

# Delegate methods to feeder_episode
def method_missing(method_name, *arguments, &block)
if feeder_episode.respond_to?(method_name)
feeder_episode.send(method_name, *arguments, &block)
else
super
end
end

def respond_to_missing?(method_name, include_private = false)
feeder_episode.respond_to?(method_name) || super
end
end
end
23 changes: 21 additions & 2 deletions app/models/apple/episode_delivery_status.rb
Original file line number Diff line number Diff line change
@@ -15,10 +15,10 @@ def self.measure_asset_processing_duration(apple_episode_delivery_statuses)
end

def self.update_status(episode, attrs)
new_status = (episode.apple_episode_delivery_status&.dup || default_status(episode))
new_status = episode.apple_status&.dup || default_status(episode)
new_status.assign_attributes(attrs)
new_status.save!
episode.apple_episode_delivery_statuses.reset
episode.apple_statuses.reset
new_status
end

@@ -33,5 +33,24 @@ def increment_asset_wait
def reset_asset_wait
self.class.update_status(episode, asset_processing_attempts: 0)
end

def mark_as_uploaded!
self.class.update_status(episode, uploaded: true)
end

def mark_as_not_uploaded!
Rails.logger.warn("Manually marking episode #{episode.id} as not uploaded")
self.class.update_status(episode, uploaded: false)
end

# Whether the media file has been uploaded to Apple
# is a subset of whether the episode has been delivered
def mark_as_delivered!
self.class.update_status(episode, delivered: true, uploaded: true)
end

def mark_as_not_delivered!
self.class.update_status(episode, delivered: false, uploaded: false)
end
end
end
81 changes: 56 additions & 25 deletions app/models/apple/publisher.rb
Original file line number Diff line number Diff line change
@@ -131,33 +131,49 @@ def publish!
def deliver_and_publish!(eps)
Rails.logger.tagged("Apple::Publisher#deliver_and_publish!") do
eps.each_slice(PUBLISH_CHUNK_LEN) do |eps|
# Soft delete any existing delivery and delivery files
prepare_for_delivery!(eps)
eps.filter(&:apple_needs_upload?).tap do |eps|
upload_media!(eps)
end

# only create if needed
sync_episodes!(eps)
sync_podcast_containers!(eps)
process_and_deliver!(eps)

wait_for_versioned_source_metadata(eps)
raise_delivery_processing_errors(eps)
end
end
end

sync_podcast_deliveries!(eps)
sync_podcast_delivery_files!(eps)
def upload_media!(eps)
# Soft delete any existing delivery and delivery files
prepare_for_delivery!(eps)

# upload and mark as uploaded
execute_upload_operations!(eps)
mark_delivery_files_uploaded!(eps)
# only create if needed
sync_episodes!(eps)
sync_podcast_containers!(eps)

wait_for_upload_processing(eps)
wait_for_versioned_source_metadata(eps)

increment_asset_wait!(eps)
wait_for_asset_state(eps)
sync_podcast_deliveries!(eps)
sync_podcast_delivery_files!(eps)

publish_drafting!(eps)
reset_asset_wait!(eps)
# upload and mark as uploaded, then update the audio container reference
execute_upload_operations!(eps)
mark_delivery_files_uploaded!(eps)
update_audio_container_reference!(eps)

raise_delivery_processing_errors(eps)
end
end
# finally mark the episode as uploaded
mark_as_uploaded!(eps)
end

def process_and_deliver!(eps)
increment_asset_wait!(eps)

wait_for_upload_processing(eps)
wait_for_asset_state(eps)

mark_as_delivered!(eps)

publish_drafting!(eps)
reset_asset_wait!(eps)
end

def prepare_for_delivery!(eps)
@@ -231,9 +247,7 @@ def wait_for_upload_processing(eps)

def increment_asset_wait!(eps)
Rails.logger.tagged("##{__method__}") do
eps = eps.filter { |e| e.podcast_delivery_files.any?(&:api_marked_as_uploaded?) }

# Mark the episodes as waiting again for asset processing
eps = eps.filter { |e| e.feeder_episode.apple_status.uploaded? }
eps.each { |ep| ep.apple_episode_delivery_status.increment_asset_wait }
end
end
@@ -361,16 +375,33 @@ def mark_delivery_files_uploaded!(eps)
Rails.logger.tagged("##{__method__}") do
pdfs = eps.map(&:podcast_delivery_files).flatten
::Apple::PodcastDeliveryFile.mark_uploaded(api, pdfs)
end
end

def update_audio_container_reference!(eps)
Rails.logger.tagged("##{__method__}") do
# link the podcast container with the audio to the episode
res = Apple::Episode.update_audio_container_reference(api, eps)
# update the feeder episode to indicate that delivery is no longer needed

Rails.logger.info("Updated remote container references for episodes.", {count: res.length})
end
end

def mark_as_delivered!(eps)
Rails.logger.tagged("##{__method__}") do
eps.each do |ep|
Rails.logger.info("Marking episode as no longer needing delivery", {episode_id: ep.feeder_episode.id})
ep.feeder_episode.apple_has_delivery!
ep.feeder_episode.apple_mark_as_delivered!
end
end
end

Rails.logger.info("Updated remote container references for episodes.", {count: res.length})
def mark_as_uploaded!(eps)
Rails.logger.tagged("##{__method__}") do
eps.each do |ep|
Rails.logger.info("Marking episode as no longer needing delivery", {episode_id: ep.feeder_episode.id})
ep.feeder_episode.apple_mark_as_uploaded!
end
end
end

36 changes: 24 additions & 12 deletions app/models/concerns/apple_delivery.rb
Original file line number Diff line number Diff line change
@@ -11,14 +11,16 @@ module AppleDelivery
class_name: "Apple::PodcastDelivery"
has_many :apple_podcast_delivery_files, through: :apple_podcast_deliveries, source: :podcast_delivery_files,
class_name: "Apple::PodcastDeliveryFile"
has_many :apple_episode_delivery_statuses, -> { order(created_at: :desc) }, dependent: :destroy, class_name: "Apple::EpisodeDeliveryStatus"
has_many :apple_episode_delivery_statuses, -> { order(created_at: :desc) }, class_name: "Apple::EpisodeDeliveryStatus"

alias_method :podcast_container, :apple_podcast_container
alias_method :apple_status, :apple_episode_delivery_status
alias_method :apple_update_status, :apple_update_delivery_status
alias_method :apple_statuses, :apple_episode_delivery_statuses
end

def publish_to_apple?
podcast.apple_config&.publish_to_apple?
podcast.apple_config&.publish_to_apple? || false
end

def apple_update_delivery_status(attrs)
@@ -30,25 +32,31 @@ def build_initial_delivery_status
end

def apple_episode_delivery_status
apple_episode_delivery_statuses.order(created_at: :desc).first || build_initial_delivery_status
apple_statuses.order(created_at: :desc).first || build_initial_delivery_status
end

def apple_needs_delivery?
return true if apple_episode_delivery_status.nil?
!apple_status.delivered?
end

apple_episode_delivery_status.delivered == false
def apple_needs_upload?
!apple_status.uploaded?
end

def apple_needs_delivery!
apple_update_delivery_status(delivered: false, asset_processing_attempts: 0)
def apple_mark_as_delivered!
apple_status.mark_as_delivered!
end

def apple_has_delivery!
apple_update_delivery_status(delivered: true)
def apple_mark_as_not_delivered!
apple_update_status(delivered: false, asset_processing_attempts: 0)
end

def measure_asset_processing_duration
Apple::EpisodeDeliveryStatus.measure_asset_processing_duration(apple_episode_delivery_statuses)
def apple_mark_as_uploaded!
apple_status.mark_as_uploaded!
end

def apple_mark_as_not_uploaded!
apple_status.mark_as_not_uploaded!
end

def apple_prepare_for_delivery!
@@ -60,7 +68,11 @@ def apple_prepare_for_delivery!
end

def apple_mark_for_reupload!
apple_needs_delivery!
apple_mark_as_not_delivered!
end

def measure_asset_processing_duration
Apple::EpisodeDeliveryStatus.measure_asset_processing_duration(apple_statuses)
end

def apple_episode
Original file line number Diff line number Diff line change
@@ -20,10 +20,10 @@ def change
needs_delivery_episodes = []
apple_episodes.each do |apple_episode|
if (apple_episode.podcast_container.nil? || apple_episode.podcast_container.needs_delivery?) || apple_episode.apple_hosted_audio_asset_container_id.blank?
apple_episode.feeder_episode.apple_needs_delivery!
apple_episode.feeder_episode.apple_mark_as_not_delivered!
needs_delivery_episodes << apple_episode.feeder_episode
else
apple_episode.feeder_episode.apple_has_delivery!
apple_episode.feeder_episode.apple_mark_as_delivered!
end
end

12 changes: 12 additions & 0 deletions db/migrate/20241029181938_new_file_uploaded_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class NewFileUploadedState < ActiveRecord::Migration[7.2]
def change
add_column :apple_episode_delivery_statuses, :uploaded, :boolean, default: false

# Set the new column to match the delivered column
execute(<<~SQL
UPDATE apple_episode_delivery_statuses
SET uploaded = delivered
SQL
)
end
end
1 change: 1 addition & 0 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion test/factories/apple_episode_api_response.rb
Original file line number Diff line number Diff line change
@@ -15,13 +15,14 @@

after(:build) do |response_container, evaluator|
response_container["api_response"] =
{"request_metadata" => {"apple_episode_id" => evaluator.apple_episode_id, "item_guid" => evaluator.item_guid},
{"request_metadata" => {"apple_episode_id" => evaluator.apple_episode_id, "guid" => evaluator.item_guid},
"api_url" => evaluator.api_url,
"api_parameters" => {},
"api_response" => {"ok" => evaluator.ok,
"err" => evaluator.err,
"val" => {"data" => {"id" => "123",
"attributes" => {
"appleHostedAudioAssetContainerId" => nil,
"appleHostedAudioAssetVendorId" => evaluator.apple_hosted_audio_asset_container_id,
"publishingState" => evaluator.publishing_state,
"guid" => evaluator.item_guid,
32 changes: 22 additions & 10 deletions test/factories/apple_episode_factory.rb
Original file line number Diff line number Diff line change
@@ -6,20 +6,20 @@
# set up transient api_response
transient do
feeder_episode { create(:episode) }
api_response { build(:apple_episode_api_response) }
api_response { build(:apple_episode_api_response, item_guid: feeder_episode.item_guid) }
apple_hosted_audio_asset_container_id { "456" }
end

# set a complete episode factory varient
factory :uploaded_apple_episode do
feeder_episode do
ep = create(:episode)
ep.apple_has_delivery!
ep
end
feeder_episode { create(:episode) }
transient do
apple_hosted_audio_asset_container_id { "456" }
api_response do
build(:apple_episode_api_response,
publishing_state: "PUBLISH",
item_guid: feeder_episode.item_guid,
apple_hosted_audio_asset_container_id: apple_hosted_audio_asset_container_id,
apple_hosted_audio_state: Apple::Episode::AUDIO_ASSET_SUCCESS)
end
end
@@ -32,11 +32,23 @@
api_marked_as_uploaded: true,
upload_operations_complete: true)

create(:content, episode: apple_episode.feeder_episode, position: 1, status: "complete")
create(:content, episode: apple_episode.feeder_episode, position: 2, status: "complete")
v1 = apple_episode.feeder_episode.cut_media_version!
feeder_episode = apple_episode.feeder_episode

# The content model calls Episode#publish!
# and that triggers a call to Episode#apple_mark_for_reupload!
# This modifies state to indicate that the episode needs to be reuploaded
create(:content, episode: feeder_episode, position: 1, status: "complete")
create(:content, episode: feeder_episode, position: 2, status: "complete")
v1 = feeder_episode.cut_media_version!

# Now model the case where the episode is uploaded.
# First we've gathered file metadata from the CDN
feeder_episode.apple_update_delivery_status(source_size: 1.megabyte,
source_url: "https://cdn.example.com/episode.mp3",
source_media_version_id: v1.id)

apple_episode.delivery_status.update!(delivered: true, source_media_version_id: v1.id)
# Then we've delivered (and necessarily uploaded)
feeder_episode.apple_mark_as_delivered!
end
end

14 changes: 7 additions & 7 deletions test/models/apple/episode_delivery_status_test.rb
Original file line number Diff line number Diff line change
@@ -14,9 +14,9 @@ class Apple::EpisodeDeliveryStatusTest < ActiveSupport::TestCase
episode.destroy
assert_equal episode, delivery_status.episode
assert_difference "Apple::EpisodeDeliveryStatus.count", +1 do
episode.apple_needs_delivery!
episode.apple_mark_as_not_delivered!
end
assert_equal episode, episode.apple_episode_delivery_statuses.first.episode
assert_equal episode, episode.apple_statuses.first.episode
end
end

@@ -25,7 +25,7 @@ class Apple::EpisodeDeliveryStatusTest < ActiveSupport::TestCase
old_status = create(:apple_episode_delivery_status, episode: episode, created_at: 2.days.ago)
new_status = create(:apple_episode_delivery_status, episode: episode, created_at: 1.day.ago)

assert_equal [new_status, old_status], episode.apple_episode_delivery_statuses.to_a
assert_equal [new_status, old_status], episode.apple_statuses.to_a
end
end

@@ -38,7 +38,7 @@ class Apple::EpisodeDeliveryStatusTest < ActiveSupport::TestCase

describe ".update_status" do
it "creates a new status when none exists" do
episode.apple_episode_delivery_statuses.destroy_all
episode.apple_statuses.destroy_all
assert_difference "Apple::EpisodeDeliveryStatus.count", 1 do
Apple::EpisodeDeliveryStatus.update_status(episode, delivered: true)
end
@@ -64,10 +64,10 @@ class Apple::EpisodeDeliveryStatusTest < ActiveSupport::TestCase
assert_equal "audio.mp3", new_status.source_filename
end

it "resets the episode's apple_episode_delivery_statuses association" do
episode.apple_episode_delivery_statuses.load
it "resets the episode's apple_statuses association" do
episode.apple_statuses.load
Apple::EpisodeDeliveryStatus.update_status(episode, delivered: true)
refute episode.apple_episode_delivery_statuses.loaded?
refute episode.apple_statuses.loaded?
end
end

17 changes: 1 addition & 16 deletions test/models/apple/episode_test.rb
Original file line number Diff line number Diff line change
@@ -274,22 +274,7 @@

describe ".prepare_for_delivery" do
it "should filter for episodes that need delivery" do
mock = Minitest::Mock.new
mock.expect(:call, true, [])

apple_episode.feeder_episode.stub(:apple_prepare_for_delivery!, mock) do
apple_episode.stub(:needs_delivery?, true) do
assert_equal [apple_episode], Apple::Episode.prepare_for_delivery([apple_episode])
end
end

mock.verify
end

it "should reject delivered episodes" do
apple_episode.stub(:needs_delivery?, false) do
assert_equal [], Apple::Episode.prepare_for_delivery([apple_episode])
end
assert_equal [apple_episode], Apple::Episode.prepare_for_delivery([apple_episode])
end

describe "soft deleting the delivery files" do
105 changes: 97 additions & 8 deletions test/models/apple/publisher_test.rb
Original file line number Diff line number Diff line change
@@ -406,13 +406,14 @@
end

describe "#increment_asset_wait!" do
let(:episode1) { build(:uploaded_apple_episode, show: apple_publisher.show) }
let(:episode2) { build(:uploaded_apple_episode, show: apple_publisher.show) }
let(:episode1) { build(:apple_episode, show: apple_publisher.show) }
let(:episode2) { build(:apple_episode, show: apple_publisher.show) }
let(:episodes) { [episode1, episode2] }

it "should increment asset wait count for each episode" do
episodes.each do |ep|
assert_equal 0, ep.apple_episode_delivery_status.asset_processing_attempts
ep.feeder_episode.apple_episode_delivery_status.mark_as_uploaded!
end

apple_publisher.increment_asset_wait!(episodes)
@@ -422,15 +423,12 @@
end
end

it "should only increment the episodes that are still waiting" do
it "only increments the episodes that are still waiting" do
assert 1, episode1.podcast_delivery_files.length
assert 1, episode2.podcast_delivery_files.length

episode2.podcast_delivery_files.first.stub(:api_marked_as_uploaded?, false) do
episode1.podcast_delivery_files.first.stub(:api_marked_as_uploaded?, true) do
apple_publisher.increment_asset_wait!(episodes)
end
end
episode1.feeder_episode.apple_mark_as_uploaded!
apple_publisher.increment_asset_wait!(episodes)

assert_equal [1, 0], [episode1, episode2].map { |ep| ep.apple_episode_delivery_status.asset_processing_attempts }
end
@@ -527,4 +525,95 @@
end
end
end

describe "#mark_as_uploaded!" do
let(:episode1) { build(:apple_episode, show: apple_publisher.show) }
let(:episode2) { build(:apple_episode, show: apple_publisher.show) }
let(:episodes) { [episode1, episode2] }

it "marks episodes as uploaded" do
episodes.each do |ep|
refute ep.delivery_status.uploaded
refute ep.delivery_status.delivered
end

apple_publisher.mark_as_uploaded!(episodes)

episodes.each do |ep|
assert ep.delivery_status.uploaded
refute ep.delivery_status.delivered
end
end
end

describe "#mark_as_delivered!" do
let(:episode1) { build(:apple_episode, show: apple_publisher.show) }
let(:episode2) { build(:apple_episode, show: apple_publisher.show) }
let(:episodes) { [episode1, episode2] }

it "marks episodes as delivered" do
episodes.each do |ep|
refute ep.delivery_status.uploaded
refute ep.delivery_status.delivered
end

apple_publisher.mark_as_delivered!(episodes)

episodes.each do |ep|
assert ep.delivery_status.uploaded
assert ep.delivery_status.delivered
end
end
end

describe "#update_audio_container_reference!" do
let(:episode) { build(:uploaded_apple_episode, show: apple_publisher.show, apple_hosted_audio_asset_container_id: nil) }

it "updates container references for episodes" do
assert episode.has_unlinked_container?

mock_result = episode.apple_sync_log.api_response.deep_dup
mock_result["api_response"]["val"]["data"]["attributes"]["appleHostedAudioAssetContainerId"] = "456"

apple_publisher.api.stub(:bridge_remote_and_retry, [[mock_result], []]) do
apple_publisher.update_audio_container_reference!([episode])
end

refute episode.has_unlinked_container?
end
end

describe "#deliver_and_publish!" do
let(:episode) { build(:apple_episode, show: apple_publisher.show) }

it "skips upload for already uploaded episodes" do
episode.feeder_episode.apple_mark_as_uploaded!

mock = Minitest::Mock.new
episode.feeder_episode.stub(:apple_prepare_for_delivery!, ->(*) { raise "Should not be called" }) do
mock.expect(:call, nil, [[]])
apple_publisher.stub(:upload_media!, mock) do
apple_publisher.stub(:process_and_deliver!, ->(*) {}) do
apple_publisher.deliver_and_publish!([episode])
end
end
end

assert mock.verify
end

it "processes uploads for non-uploaded episodes" do
refute episode.delivery_status.uploaded

mock = Minitest::Mock.new
mock.expect(:call, nil, [[episode]])
apple_publisher.stub(:upload_media!, mock) do
apple_publisher.stub(:process_and_deliver!, ->(*) {}) do
apple_publisher.deliver_and_publish!([episode])
end
end

mock.verify
end
end
end
99 changes: 92 additions & 7 deletions test/models/concerns/apple_delivery_test.rb
Original file line number Diff line number Diff line change
@@ -18,16 +18,56 @@ class AppleDeliveryTest < ActiveSupport::TestCase
end

it "can be set to false" do
episode.apple_has_delivery!
episode.apple_mark_as_delivered!
refute episode.apple_needs_delivery?
end

it "can be set to true" do
episode.apple_has_delivery!
episode.apple_mark_as_delivered!
refute episode.apple_needs_delivery?

# now set it to true
episode.apple_needs_delivery!
episode.apple_mark_as_not_delivered!
assert episode.apple_needs_delivery?
end

it "resets the apple delivery asset_processing_attempts" do
episode.apple_mark_as_delivered!
refute episode.apple_needs_delivery?

episode.apple_update_delivery_status(asset_processing_attempts: 1)
assert_equal 1, episode.apple_status.asset_processing_attempts

episode.apple_mark_as_not_delivered!
assert_equal 0, episode.apple_status.asset_processing_attempts
end
end

describe "#apple_mark_as_delivered!" do
let(:episode) { create(:episode) }

it "supercedes the uploaded status" do
episode.apple_mark_as_not_delivered!

assert episode.apple_needs_upload?
assert episode.apple_needs_delivery?

episode.apple_mark_as_delivered!

refute episode.apple_needs_upload?
refute episode.apple_needs_delivery?
end
end

describe "#apple_mark_as_uploaded!" do
it "sets the uploaded status" do
episode.apple_mark_as_uploaded!
assert episode.apple_status.uploaded?
refute episode.apple_needs_upload?
end

it "does not interact with the delivery status" do
episode.apple_mark_as_uploaded!
assert episode.apple_needs_delivery?
end
end
@@ -36,7 +76,7 @@ class AppleDeliveryTest < ActiveSupport::TestCase
let(:episode) { create(:episode) }

it "creates a new status with incremented asset_processing_attempts" do
assert_difference -> { episode.apple_episode_delivery_statuses.count }, 1 do
assert_difference -> { episode.apple_statuses.count }, 1 do
new_status = episode.apple_status.increment_asset_wait
assert_equal 1, new_status.asset_processing_attempts
end
@@ -62,8 +102,8 @@ class AppleDeliveryTest < ActiveSupport::TestCase
end

it "creates a new status with asset_processing_attempts set to 1 if no previous status exists" do
episode.apple_episode_delivery_statuses.destroy_all
assert_difference -> { episode.apple_episode_delivery_statuses.count }, 1 do
episode.apple_statuses.destroy_all
assert_difference -> { episode.apple_statuses.count }, 1 do
new_status = episode.apple_status.increment_asset_wait
assert_equal 1, new_status.asset_processing_attempts
end
@@ -72,7 +112,52 @@ class AppleDeliveryTest < ActiveSupport::TestCase
it "returns the new status" do
result = episode.apple_status.increment_asset_wait
assert_instance_of Apple::EpisodeDeliveryStatus, result
assert_equal episode.apple_episode_delivery_statuses.last, result
assert_equal episode.apple_statuses.last, result
end
end

describe "#publish_to_apple?" do
let(:episode) { create(:episode) }

it "returns false when podcast has no apple config" do
refute episode.publish_to_apple?
end

it "returns false when apple config exists but publishing disabled" do
create(:apple_config, feed: create(:private_feed, podcast: episode.podcast), publish_enabled: false)
refute episode.publish_to_apple?
end

it "returns true when apple config exists and publishing enabled" do
assert episode.publish_to_apple? == false
create(:apple_config, feed: create(:private_feed, podcast: episode.podcast), publish_enabled: true)
episode.podcast.reload
assert episode.publish_to_apple?
end
end

describe "#apple_prepare_for_delivery!" do
let(:episode) { create(:episode) }
let(:container) { create(:apple_podcast_container, episode: episode) }
let(:delivery) { create(:apple_podcast_delivery, episode: episode, podcast_container: container) }
let(:delivery_file) { create(:apple_podcast_delivery_file, episode: episode, podcast_delivery: delivery) }

before do
delivery_file # Create the delivery file
end

it "soft deletes existing deliveries" do
assert_equal 1, episode.apple_podcast_deliveries.count
episode.apple_prepare_for_delivery!
assert_equal 0, episode.apple_podcast_deliveries.count
assert_equal 1, episode.apple_podcast_deliveries.with_deleted.count
end

it "resets associations" do
episode.apple_prepare_for_delivery!
refute episode.apple_podcast_deliveries.loaded?
refute episode.apple_podcast_delivery_files.loaded?
refute episode.apple_podcast_container.podcast_deliveries.loaded?
end
end
end