media_attachment_spec.rb 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. # frozen_string_literal: true
  2. require 'rails_helper'
  3. RSpec.describe MediaAttachment, :paperclip_processing do
  4. describe 'local?' do
  5. subject { media_attachment.local? }
  6. let(:media_attachment) { described_class.new(remote_url: remote_url) }
  7. context 'when remote_url is blank' do
  8. let(:remote_url) { '' }
  9. it 'returns true' do
  10. expect(subject).to be true
  11. end
  12. end
  13. context 'when remote_url is present' do
  14. let(:remote_url) { 'remote_url' }
  15. it 'returns false' do
  16. expect(subject).to be false
  17. end
  18. end
  19. end
  20. describe 'needs_redownload?' do
  21. subject { media_attachment.needs_redownload? }
  22. let(:media_attachment) { described_class.new(remote_url: remote_url, file: file) }
  23. context 'when file is blank' do
  24. let(:file) { nil }
  25. context 'when remote_url is present' do
  26. let(:remote_url) { 'remote_url' }
  27. it 'returns true' do
  28. expect(subject).to be true
  29. end
  30. end
  31. end
  32. context 'when file is present' do
  33. let(:file) { attachment_fixture('avatar.gif') }
  34. context 'when remote_url is blank' do
  35. let(:remote_url) { '' }
  36. it 'returns false' do
  37. expect(subject).to be false
  38. end
  39. end
  40. context 'when remote_url is present' do
  41. let(:remote_url) { 'remote_url' }
  42. it 'returns true' do
  43. expect(subject).to be false
  44. end
  45. end
  46. end
  47. end
  48. describe '#to_param' do
  49. let(:media_attachment) { Fabricate.build(:media_attachment, shortcode: shortcode, id: id) }
  50. context 'when media attachment has a shortcode' do
  51. let(:shortcode) { 'foo' }
  52. let(:id) { 123 }
  53. it 'returns shortcode' do
  54. expect(media_attachment.to_param).to eq shortcode
  55. end
  56. end
  57. context 'when media attachment does not have a shortcode' do
  58. let(:shortcode) { nil }
  59. let(:id) { 123 }
  60. it 'returns string representation of id' do
  61. expect(media_attachment.to_param).to eq id.to_s
  62. end
  63. end
  64. end
  65. shared_examples 'static 600x400 image' do |content_type, extension|
  66. after do
  67. media.destroy
  68. end
  69. it 'saves media attachment with correct file metadata' do
  70. expect(media)
  71. .to be_persisted
  72. .and be_processing_complete
  73. .and have_attributes(
  74. file: be_present,
  75. type: eq('image'),
  76. file_content_type: eq(content_type),
  77. file_file_name: end_with(extension)
  78. )
  79. # Rack::Mime (used by PublicFileServerMiddleware) recognizes file extension
  80. expect(Rack::Mime.mime_type(extension, nil)).to eq content_type
  81. end
  82. it 'saves media attachment with correct size metadata' do
  83. # strips original file name
  84. expect(media.file_file_name)
  85. .to_not start_with '600x400'
  86. # sets meta for original and thumbnail
  87. expect(media.file.meta.deep_symbolize_keys)
  88. .to include(
  89. original: include(
  90. width: eq(600),
  91. height: eq(400),
  92. aspect: eq(1.5)
  93. ),
  94. small: include(
  95. width: eq(588),
  96. height: eq(392),
  97. aspect: eq(1.5)
  98. )
  99. )
  100. end
  101. end
  102. describe 'jpeg' do
  103. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.jpeg')) }
  104. it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
  105. end
  106. describe 'png' do
  107. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.png')) }
  108. it_behaves_like 'static 600x400 image', 'image/png', '.png'
  109. end
  110. describe 'webp' do
  111. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.webp')) }
  112. it_behaves_like 'static 600x400 image', 'image/webp', '.webp'
  113. end
  114. describe 'avif' do
  115. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.avif')) }
  116. it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
  117. end
  118. describe 'heic' do
  119. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.heic')) }
  120. it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
  121. end
  122. describe 'base64-encoded image' do
  123. let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('600x400.jpeg').read)}" }
  124. let(:media) { Fabricate(:media_attachment, file: base64_attachment) }
  125. it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg'
  126. end
  127. describe 'animated gif' do
  128. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('avatar.gif')) }
  129. it 'sets correct file metadata' do
  130. expect(media.type).to eq 'gifv'
  131. expect(media.file_content_type).to eq 'video/mp4'
  132. expect(media.file.meta['original']['width']).to eq 128
  133. expect(media.file.meta['original']['height']).to eq 128
  134. end
  135. end
  136. describe 'static gif' do
  137. fixtures = [
  138. { filename: 'attachment.gif', width: 600, height: 400, aspect: 1.5 },
  139. { filename: 'mini-static.gif', width: 32, height: 32, aspect: 1.0 },
  140. ]
  141. fixtures.each do |fixture|
  142. context fixture[:filename] do
  143. let(:media) { Fabricate(:media_attachment, file: attachment_fixture(fixture[:filename])) }
  144. it 'sets correct file metadata' do
  145. expect(media.type).to eq 'image'
  146. expect(media.file_content_type).to eq 'image/gif'
  147. expect(media.file.meta['original']['width']).to eq fixture[:width]
  148. expect(media.file.meta['original']['height']).to eq fixture[:height]
  149. expect(media.file.meta['original']['aspect']).to eq fixture[:aspect]
  150. end
  151. end
  152. end
  153. end
  154. describe 'ogg with cover art' do
  155. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('boop.ogg')) }
  156. it 'sets correct file metadata' do
  157. expect(media.type).to eq 'audio'
  158. expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102)
  159. expect(media.thumbnail.present?).to be true
  160. expect(media.file.meta['colors']['background']).to eq '#3088d4'
  161. expect(media.file_file_name).to_not eq 'boop.ogg'
  162. end
  163. end
  164. describe 'mp3 with large cover art' do
  165. let(:media) { Fabricate(:media_attachment, file: attachment_fixture('boop.mp3')) }
  166. it 'detects it as an audio file' do
  167. expect(media.type).to eq 'audio'
  168. end
  169. it 'sets meta for the duration' do
  170. expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102)
  171. end
  172. it 'extracts thumbnail' do
  173. expect(media.thumbnail.present?).to be true
  174. end
  175. it 'gives the file a random name' do
  176. expect(media.file_file_name).to_not eq 'boop.mp3'
  177. end
  178. end
  179. it 'is invalid without file' do
  180. media = described_class.new
  181. expect(media.valid?).to be false
  182. expect(media).to model_have_error_on_field(:file)
  183. end
  184. describe 'size limit validation' do
  185. it 'rejects video files that are too large' do
  186. stub_const 'MediaAttachment::IMAGE_LIMIT', 100.megabytes
  187. stub_const 'MediaAttachment::VIDEO_LIMIT', 1.kilobyte
  188. expect { Fabricate(:media_attachment, file: attachment_fixture('attachment.webm')) }.to raise_error(ActiveRecord::RecordInvalid)
  189. end
  190. it 'accepts video files that are small enough' do
  191. stub_const 'MediaAttachment::IMAGE_LIMIT', 1.kilobyte
  192. stub_const 'MediaAttachment::VIDEO_LIMIT', 100.megabytes
  193. media = Fabricate(:media_attachment, file: attachment_fixture('attachment.webm'))
  194. expect(media.valid?).to be true
  195. end
  196. it 'rejects image files that are too large' do
  197. stub_const 'MediaAttachment::IMAGE_LIMIT', 1.kilobyte
  198. stub_const 'MediaAttachment::VIDEO_LIMIT', 100.megabytes
  199. expect { Fabricate(:media_attachment, file: attachment_fixture('attachment.jpg')) }.to raise_error(ActiveRecord::RecordInvalid)
  200. end
  201. it 'accepts image files that are small enough' do
  202. stub_const 'MediaAttachment::IMAGE_LIMIT', 100.megabytes
  203. stub_const 'MediaAttachment::VIDEO_LIMIT', 1.kilobyte
  204. media = Fabricate(:media_attachment, file: attachment_fixture('attachment.jpg'))
  205. expect(media.valid?).to be true
  206. end
  207. end
  208. end