|
70 | 70 | expect(message.role).to eq(Ai::MessageRole::System) |
71 | 71 | end |
72 | 72 | end |
| 73 | + |
| 74 | + describe '.user_message_with_image' do |
| 75 | + it 'creates a user message with text and image parts' do |
| 76 | + text = 'What is in this image?' |
| 77 | + image_data = 'binary image data' |
| 78 | + media_type = 'image/png' |
| 79 | + |
| 80 | + message = Ai.user_message_with_image(text, image_data, media_type) |
| 81 | + |
| 82 | + expect(message).to be_a(Ai::Message) |
| 83 | + expect(message.role).to eq(Ai::MessageRole::User) |
| 84 | + expect(message.content).to be_an(Array) |
| 85 | + expect(message.content.length).to eq(2) |
| 86 | + end |
| 87 | + |
| 88 | + it 'creates proper content parts' do |
| 89 | + text = 'Describe this' |
| 90 | + image_data = 'image bytes' |
| 91 | + media_type = 'image/jpeg' |
| 92 | + |
| 93 | + message = Ai.user_message_with_image(text, image_data, media_type) |
| 94 | + content = message.content |
| 95 | + |
| 96 | + expect(content[0]).to be_a(Ai::TextPart) |
| 97 | + expect(content[0].text).to eq(text) |
| 98 | + |
| 99 | + expect(content[1]).to be_a(Ai::ImagePart) |
| 100 | + expect(content[1].image_data).to eq(image_data) |
| 101 | + expect(content[1].media_type).to eq(media_type) |
| 102 | + end |
| 103 | + |
| 104 | + it 'serializes correctly for API calls' do |
| 105 | + text = 'Analyze this' |
| 106 | + image_data = 'test image' |
| 107 | + media_type = 'image/png' |
| 108 | + |
| 109 | + message = Ai.user_message_with_image(text, image_data, media_type) |
| 110 | + json = message.as_json |
| 111 | + |
| 112 | + expect(json[:role]).to eq('user') |
| 113 | + expect(json[:content]).to be_an(Array) |
| 114 | + expect(json[:content][0][:type]).to eq('text') |
| 115 | + expect(json[:content][0][:text]).to eq(text) |
| 116 | + expect(json[:content][1][:type]).to eq('image') |
| 117 | + expect(json[:content][1][:image]).to start_with('data:image/png;base64,') |
| 118 | + end |
| 119 | + |
| 120 | + it 'handles different image formats' do |
| 121 | + formats = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'] |
| 122 | + |
| 123 | + formats.each do |format| |
| 124 | + message = Ai.user_message_with_image('Test', 'data', format) |
| 125 | + json = message.as_json |
| 126 | + |
| 127 | + expect(json[:content][1][:mediaType]).to eq(format) |
| 128 | + expect(json[:content][1][:image]).to start_with("data:#{format};base64,") |
| 129 | + end |
| 130 | + end |
| 131 | + |
| 132 | + it 'properly encodes binary data to base64' do |
| 133 | + # Simulate actual binary data (PNG header bytes) |
| 134 | + binary_data = [137, 80, 78, 71, 13, 10, 26, 10].pack('C*') |
| 135 | + |
| 136 | + message = Ai.user_message_with_image('What is this?', binary_data, 'image/png') |
| 137 | + json = message.as_json |
| 138 | + |
| 139 | + # Extract and verify the base64 portion |
| 140 | + base64_data = json[:content][1][:image].gsub('data:image/png;base64,', '') |
| 141 | + decoded = Base64.strict_decode64(base64_data) |
| 142 | + |
| 143 | + expect(decoded).to eq(binary_data) |
| 144 | + end |
| 145 | + end |
| 146 | + |
| 147 | + describe '.user_message_with_image_url' do |
| 148 | + it 'creates a user message with text and image URL parts' do |
| 149 | + text = 'What is in this image?' |
| 150 | + image_url = 'https://example.com/photo.jpg' |
| 151 | + media_type = 'image/jpeg' |
| 152 | + |
| 153 | + message = Ai.user_message_with_image_url(text, image_url, media_type) |
| 154 | + |
| 155 | + expect(message).to be_a(Ai::Message) |
| 156 | + expect(message.role).to eq(Ai::MessageRole::User) |
| 157 | + expect(message.content).to be_an(Array) |
| 158 | + expect(message.content.length).to eq(2) |
| 159 | + end |
| 160 | + |
| 161 | + it 'creates proper content parts with URL' do |
| 162 | + text = 'Describe this' |
| 163 | + image_url = 'https://cdn.example.com/image.png' |
| 164 | + media_type = 'image/png' |
| 165 | + |
| 166 | + message = Ai.user_message_with_image_url(text, image_url, media_type) |
| 167 | + content = message.content |
| 168 | + |
| 169 | + expect(content[0]).to be_a(Ai::TextPart) |
| 170 | + expect(content[0].text).to eq(text) |
| 171 | + |
| 172 | + expect(content[1]).to be_a(Ai::ImagePart) |
| 173 | + expect(content[1].image_url).to eq(image_url) |
| 174 | + expect(content[1].image_data).to be_nil |
| 175 | + expect(content[1].media_type).to eq(media_type) |
| 176 | + end |
| 177 | + |
| 178 | + it 'serializes correctly for API calls with URL' do |
| 179 | + text = 'Analyze this' |
| 180 | + image_url = 'https://example.com/test.jpg' |
| 181 | + media_type = 'image/jpeg' |
| 182 | + |
| 183 | + message = Ai.user_message_with_image_url(text, image_url, media_type) |
| 184 | + json = message.as_json |
| 185 | + |
| 186 | + expect(json[:role]).to eq('user') |
| 187 | + expect(json[:content]).to be_an(Array) |
| 188 | + expect(json[:content].length).to eq(2) |
| 189 | + |
| 190 | + # Check text part |
| 191 | + expect(json[:content][0][:type]).to eq('text') |
| 192 | + expect(json[:content][0][:text]).to eq(text) |
| 193 | + |
| 194 | + # Check image part - should be URL, not base64 |
| 195 | + expect(json[:content][1][:type]).to eq('image') |
| 196 | + expect(json[:content][1][:image]).to eq(image_url) |
| 197 | + expect(json[:content][1][:image]).not_to include('base64') |
| 198 | + expect(json[:content][1][:mediaType]).to eq(media_type) |
| 199 | + end |
| 200 | + |
| 201 | + it 'handles different image URL formats' do |
| 202 | + urls = [ |
| 203 | + ['https://example.com/image.png', 'image/png'], |
| 204 | + ['http://test.org/photo.jpg', 'image/jpeg'], |
| 205 | + ['https://cdn.example.com/images/12345.webp', 'image/webp'] |
| 206 | + ] |
| 207 | + |
| 208 | + urls.each do |url, media_type| |
| 209 | + message = Ai.user_message_with_image_url('Test', url, media_type) |
| 210 | + json = message.as_json |
| 211 | + |
| 212 | + expect(json[:content][1][:image]).to eq(url) |
| 213 | + expect(json[:content][1][:mediaType]).to eq(media_type) |
| 214 | + end |
| 215 | + end |
| 216 | + |
| 217 | + it 'works with HTTPS URLs' do |
| 218 | + message = Ai.user_message_with_image_url( |
| 219 | + 'Analyze', |
| 220 | + 'https://secure.example.com/image.jpg', |
| 221 | + 'image/jpeg' |
| 222 | + ) |
| 223 | + json = message.as_json |
| 224 | + |
| 225 | + expect(json[:content][1][:image]).to start_with('https://') |
| 226 | + end |
| 227 | + end |
73 | 228 | end |
0 commit comments