require "test_helper" class FamilyExportTest < ActiveSupport::TestCase setup do @family = families(:dylan_family) @export = @family.family_exports.create! end test "belongs to family" do assert_equal @family, @export.family end test "has default status of pending" do assert_equal "pending", @export.status end test "can have export file attached" do @export.export_file.attach( io: StringIO.new("test content"), filename: "test.zip", content_type: "application/zip" ) assert @export.export_file.attached? assert_equal "test.zip", @export.export_file.filename.to_s assert_equal "application/zip", @export.export_file.content_type end test "filename is generated correctly" do travel_to Time.zone.local(2024, 1, 15, 14, 30, 0) do export = @family.family_exports.create! expected_filename = "sure_export_20240115_143000.zip" assert_equal expected_filename, export.filename end end test "downloadable? returns true for completed export with file" do @export.update!(status: "completed") @export.export_file.attach( io: StringIO.new("test content"), filename: "test.zip", content_type: "application/zip" ) assert @export.downloadable? end test "downloadable? returns false for pending export" do @export.update!(status: "pending") @export.export_file.attach( io: StringIO.new("test content"), filename: "test.zip", content_type: "application/zip" ) assert_not @export.downloadable? end test "downloadable? returns false for completed export without file" do @export.update!(status: "completed") assert_not @export.downloadable? end test "downloadable? returns false for failed export with file" do @export.update!(status: "failed") @export.export_file.attach( io: StringIO.new("test content"), filename: "test.zip", content_type: "application/zip" ) assert_not @export.downloadable? end test "export file is purged when export is destroyed" do @export.export_file.attach( io: StringIO.new("test content"), filename: "test.zip", content_type: "application/zip" ) # Verify file is attached assert @export.export_file.attached? file_id = @export.export_file.id blob_id = @export.export_file.blob.id # Destroy the export @export.destroy! # Verify the export record is gone assert_not FamilyExport.exists?(@export.id) # Verify the Active Storage attachment is gone assert_not ActiveStorage::Attachment.exists?(file_id) # Note: Active Storage purges blobs asynchronously with dependent: :purge_later # In tests, we can verify the attachment is gone, which is the immediate effect # The blob will be purged in the background end test "can transition through statuses" do assert_equal "pending", @export.status @export.processing! assert_equal "processing", @export.status @export.completed! assert_equal "completed", @export.status @export.failed! assert_equal "failed", @export.status end test "ordered scope returns exports in descending order" do # Clear existing exports to avoid interference @family.family_exports.destroy_all # Create exports with specific timestamps old_export = @family.family_exports.create! old_export.update_column(:created_at, 2.days.ago) new_export = @family.family_exports.create! new_export.update_column(:created_at, 1.day.ago) ordered_exports = @family.family_exports.ordered.to_a assert_equal 2, ordered_exports.length assert_equal new_export.id, ordered_exports.first.id assert_equal old_export.id, ordered_exports.last.id end end