I would like to request an S3 image and then serve it using Phoenix.
def getImage(conn, %{"id" => uuid}) do
file = ExAws.S3.get_object("bucket", "images/image.jpg")
|> ExAws.request
conn
|> put_resp_content_type("image/jpg")
|> put_resp_header(
"content-disposition",
"attachment; filename=\"file.jpg\""
)
|> send_resp(200, file)
end
I have found endless tutorials on how to upload to S3 but nothing on retrieving. Thank you in advance!
You have to pattern match against get_object function and extract image content from that.
def getImage(conn, %{"id" => uuid}) do
{:ok, %{body: image_content}} = ExAws.S3.get_object("bucket", "images/image.jpg")
|> ExAws.request
conn
|> put_resp_content_type("image/jpg")
|> put_resp_header(
"content-disposition",
"attachment; filename=\"file.jpg\""
)
|> send_resp(200, image_content)
end
Related
I have been trying to run a process when testing my browser app. The process is one of my fixture for pytest testing, and it should be running when i start my test. However, it showed error when i run it. I am quite confused because it should be a simple process. this is my code:
#pytest.fixture(scope='class')
def chrome_driver(request):
""" Selenium webdriver with options to support running in GitHub actions
Note:
For CI: `headless` and `disable-gpu` not commented out
For running on your computer: `headless` and `disable-gpu` to be commented out
"""
options = ChromeOptions()
#options.add_argument("--headless") # use for GitHub Actions CI
#options.add_argument('--disable-gpu') # use for GitHub Actions CI
options.add_argument("--window-size=1920,1080")
chrome_driver = Chrome(options=options)
request.cls.driver = chrome_driver
yield
chrome_driver.close()
#pytest.fixture(scope='class')
def run_app(app):
"""
Fixture to run the Flask app for Selenium browser tests
"""
multiprocessing.set_start_method("spawn") # Needed in Python 3.8 and later
#process = multiprocessing.Process(target=app.run, args=())
#process.start()
process = multiprocessing.Process(target=app.run, args=())
process.start()
process.join()
yield process
process.terminate()
above are the two fixtures for my testing, and they will be run the same time when i start my test. Below is the errors:
test setup failed
app = <Flask 'flask_app'>
#pytest.fixture(scope='class')
def run_app(app):
"""
Fixture to run the Flask app for Selenium browser tests
"""
multiprocessing.set_start_method("spawn") # Needed in Python 3.8 and later
#process = multiprocessing.Process(target=app.run, args=())
#process.start()
process = multiprocessing.Process(target=app.run, args=())
> process.start()
..\conftest.py:138:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\..\..\AppData\Local\Programs\Python\Python310\lib\multiprocessing\process.py:121: in start
self._popen = self._Popen(self)
..\..\..\..\AppData\Local\Programs\Python\Python310\lib\multiprocessing\context.py:224: in _Popen
return _default_context.get_context().Process._Popen(process_obj)
..\..\..\..\AppData\Local\Programs\Python\Python310\lib\multiprocessing\context.py:327: in _Popen
return Popen(process_obj)
..\..\..\..\AppData\Local\Programs\Python\Python310\lib\multiprocessing\popen_spawn_win32.py:93: in __init__
reduction.dump(process_obj, to_child)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
obj = <Process name='Process-1' parent=26100 initial>
file = <_io.BufferedWriter name=11>, protocol = None
def dump(obj, file, protocol=None):
'''Replacement for pickle.dump() using ForkingPickler.'''
> ForkingPickler(file, protocol).dump(obj)
E AttributeError: Can't pickle local object 'Flask.__init__.<locals>.<lambda>'
I was able to get my similar test to run if I instead set multiprocessing.set_start_method("fork") and running the single test inside the IDE. As soon as I run it in a test suite I get the 'pickle' error though.
As Henry pointed out, forking will avoid the pickle error. Set in a session scoped fixture to executed once in pytest session.
#pytest.fixture(scope='session')
def init_multiprocessing():
multiprocessing.set_start_method("fork")
#pytest.fixture
def run_app(app, init_multiprocessing):
process = multiprocessing.Process(target=app.run)
process.start()
yield process
process.terminate()
process.join()
I hope all is well. At this point, I plan on reading a file, and then writing it to another file. (The equivalent of cp in OCaml, before I go ahead and manipulate text.) Currently in my code, I am making using the extlib to read a file, and then output it. I am also using dune to build file as an executable file. My file looks something like the following:
(* example.ml file *)
let read_filename = "example_1.ts"
let filename = "example_2.ts"
let () =
let text read_filename =
let chan = open_in read_filename in
Std.input_list chan
let filename = filename in
let text = text in
Std.output_file ~filename ~text
(* normal exit: all channels are flushed and closed *)
When I build the file using dune build example.bc I receive the following error:
File "example.ml", line 11, characters 2-5:
Error: Syntax error
I am trying to figure out what I am doing wrong, but to no avail. Any help would be more than appreciated. Thank you.
When I have readed you code I remark these lines.
let filename = filename in
let text = text in
Why have you writting this line ? (* You could delete it safely *)
And then I have found your syntax error was a line above you have forgotten a in
(* example.ml file *)
let read_filename = "example_1.ts"
let filename = "example_2.ts"
let () =
let text read_filename =
let chan = open_in read_filename in
Std.input_list chan
in
Std.output_file ~filename ~text
(* normal exit: all channels are flushed and closed *)
The rule for each local definition you must have a in keyword
(* a local definition is a let inside a let expression *)
I was trying to check that all links work on a Yesod website home page. I wrote this hSpec test.
module Handler.HomeSpec (spec) where
import Data.Either (fromRight)
import qualified Data.Text as T
import Network.Wai.Test (simpleBody)
import TestImport
import Yesod.Test.TransversingCSS (findAttributeBySelector)
getAllLinks :: YesodExample site [Text]
getAllLinks = withResponse $ \res -> do
let links = fromRight [] findAttributeBySelector (simpleBody res) "a" "href"
return $ T.concat <$> links
spec :: Spec
spec = withApp $
describe "Homepage" $ do
it "checks all links" $ do
get HomeR
statusIs 200
links <- getAllLinks
forM_ links $ \oneLink -> do
get HomeR
statusIs 200
get oneLink
statusIs 200
Everything compiles ok but the get function gets rid of the host part of the URLs you feed it. For example, when you give it https://github.com/zigazou/bazasso, it will try to fetch /zigazou/bazasso which returns a 404 code.
Is there a way to make it work like I want ?
Should I add a function that removes external links from the tests ?
Is it simply not the right place to do it ?
The simpler, the better: I've removed everything that starts with a protocol from the links that will be checked. Thanks to #ncaq for your comments.
module Handler.HomeSpec (spec) where
import Data.Either (fromRight)
import qualified Data.Text as T
import Network.Wai.Test (simpleBody)
import TestImport
import Yesod.Test.TransversingCSS (findAttributeBySelector)
isRelative :: Text -> Bool
isRelative url
| T.take 7 url == "http://" = False
| T.take 8 url == "https://" = False
| T.take 7 url == "mailto:" = False
| T.take 4 url == "tel:" = False
| otherwise = True
getAllLinks :: YesodExample site [Text]
getAllLinks = withResponse $ \res -> do
let currentHtml = simpleBody res
links = fromRight [] $ findAttributeBySelector currentHtml "a" "href"
return $ filter isRelative $ T.concat <$> links
spec :: Spec
spec = withApp $
describe "Homepage" $ do
it "checks all links" $ do
get HomeR
statusIs 200
links <- getAllLinks
forM_ links $ \oneLink -> do
get HomeR
statusIs 200
get oneLink
statusIs 200
I need to explicitly delete a fixture after it is used. I know that pytest-django by default drops all objects on teardown but in this particular case I need to do it manually. However, although my tests are marked as pytest.mark.django_db, I am able to create a fixture, but unable to delete it after a yield line:
import pytest
from tgapps.models import TelegramApp
#pytest.fixture(scope='module')
def some_fixture():
app = TelegramApp.objects.create(
session_data=b'\xa2\x8f#',
app_owner_phone=79856235474,
app_id=182475,
app_hash='aad9ab4384fea1af0342b77b606d13b0'
)
yield app
print('deleting object...')
app.delete()
class TestTelegramServiceObject(object):
#pytest.mark.django_db
def test1(self, some_fixture):
print('Fixture created:')
print(some_fixture)
this is my test output:
============================= test session starts ==============================
platform darwin -- Python 3.6.4, pytest-3.4.0, py-1.5.2, pluggy-0.6.0
Django settings: inviter.settings.staging (from ini file)
rootdir: /Users/1111/_projects/fasttrack/inviter, inifile: pytest.ini
plugins: mock-1.7.1, dotenv-0.1.0, django-3.1.2
collected 1 item
test_example.py E.Fixture created:
<79856235474 - 182475>
deleting object...
tests/api/test_example.py:25 (TestTelegramServiceObject.test1)
#pytest.fixture(scope='module')
def some_fixture():
app = TelegramApp.objects.create(
session_data=b'\xa2\x8f#',
app_owner_phone=79856235474,
app_id=182475,
app_hash='aad9ab4384fea1af0342b77b606d13b0'
)
yield app
print('deleting object...')
> app.delete()
test_example.py:21:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/base.py:890: in delete
collector.collect([self], keep_parents=keep_parents)
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/deletion.py:221: in collect
elif sub_objs:
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/query.py:276: in __bool__
self._fetch_all()
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/query.py:1179: in _fetch_all
self._result_cache = list(self._iterable_class(self))
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/query.py:53: in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/sql/compiler.py:1062: in execute_sql
cursor = self.connection.cursor()
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/backends/base/base.py:255: in cursor
return self._cursor()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <django.db.backends.postgresql.base.DatabaseWrapper object at 0x1048bf080>
name = None
def _cursor(self, name=None):
> self.ensure_connection()
E Failed: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.
Why is this? and how do I enable DB assess on fixture teardown?
The db fixture as mentioned by Andreas Profous is function scoped so that wouldn't work here.
What you need to do is:
#pytest.fixture(scope='module')
def some_fixture(django_db_setup, django_db_blocker):
with django_db_blocker.unblock():
app = TelegramApp.objects.create(
session_data=b'\xa2\x8f#',
app_owner_phone=79856235474,
app_id=182475,
app_hash='aad9ab4384fea1af0342b77b606d13b0'
)
yield app
with django_db_blocker.unblock():
print('deleting object...')
app.delete()
django_db_setup ensures the test database is setup (and ready) for additional records to be added. django_db_blocker is what the db fixture uses to allow function scoped modifications. The reason why it's function scoped is so that it will behave how TestCase methods behave in the default unittest framework where the records are rolled back after each test. This is a good thing, it ensures a modification of a record in one test won't change the outcome in other test.
So be very careful when creating fixtures that aren't function scoped as your modifications won't by default be wrapped in a transaction and rolled back. Using the django_db_blocker gives you hooks into the django_db_blocker.unblock() method which removes the block from modifying the database. This is required in your non-function scoped fixtures.
You need to require the db fixture in some_fixture:
def some_fixture(db):
...
The VCR Cucumber documents show many examples using a tiny Sinatra app to simulate a remote server, using a function called start_sinatra_app loaded from vcr_cucumber_helpers.rb.
I'd like use something like that for my Rails / RSpec / VCR testing, but haven't figured out how to get start_sinatra_app (or equivalent) into my testing framework. My naive approach doesn't work since -- not surprisingly -- it can't find vcr_cucumber_helpers.rb.
What do I need to add to the following to make it work under RSpec? Or am I off in the weeds and doing this all wrong?
# file: spec/app/models/sinatra_test_spec.rb
require 'spec_helper'
start_sinatra_app(:port => 7777) do
get("/") { "Hello" }
end
describe "sinatra rspec test" do
it 'calls the sinatra app' do
VCR.use_cassette("sinatra_rspec_test") do
res = Net::HTTP.get_response('localhost', "/", 7777)
res.body.should == 'Hello'
end
end
end
Here's the code you're looking for:
def start_sinatra_app(options, &block)
raise ArgumentError.new("You must pass a port") unless options[:port]
require 'sinatra'
require 'support/vcr_localhost_server'
klass = Class.new(Sinatra::Base)
klass.disable :protection
klass.class_eval(&block)
VCR::LocalhostServer.new(klass.new, options[:port])
end
That in turn uses VCR::LocalhostServer:
require 'rack'
require 'rack/handler/webrick'
require 'net/http'
# The code for this is inspired by Capybara's server:
# http://github.com/jnicklas/capybara/blob/0.3.9/lib/capybara/server.rb
module VCR
class LocalhostServer
READY_MESSAGE = "VCR server ready"
class Identify
def initialize(app)
#app = app
end
def call(env)
if env["PATH_INFO"] == "/__identify__"
[200, {}, [VCR::LocalhostServer::READY_MESSAGE]]
else
#app.call(env)
end
end
end
attr_reader :port
def initialize(rack_app, port = nil)
#port = port || find_available_port
#rack_app = rack_app
concurrently { boot }
wait_until(10, "Boot failed.") { booted? }
end
private
def find_available_port
server = TCPServer.new('127.0.0.1', 0)
server.addr[1]
ensure
server.close if server
end
def boot
# Use WEBrick since it's part of the ruby standard library and is available on all ruby interpreters.
options = { :Port => port }
options.merge!(:AccessLog => [], :Logger => WEBrick::BasicLog.new(StringIO.new)) unless ENV['VERBOSE_SERVER']
Rack::Handler::WEBrick.run(Identify.new(#rack_app), options)
end
def booted?
res = ::Net::HTTP.get_response("localhost", '/__identify__', port)
if res.is_a?(::Net::HTTPSuccess) or res.is_a?(::Net::HTTPRedirection)
return res.body == READY_MESSAGE
end
rescue Errno::ECONNREFUSED, Errno::EBADF
return false
end
def concurrently
# JRuby doesn't support forking.
# Rubinius does, but there's a weird issue with the booted? check not working,
# so we're just using a thread for now.
Thread.new { yield }
end
def wait_until(timeout, error_message, &block)
start_time = Time.now
while true
return if yield
raise TimeoutError.new(error_message) if (Time.now - start_time) > timeout
sleep(0.05)
end
end
end
end
Webmock does this quite nicely.
Allow connections to localhost:
# spec/spec_helper.rb
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
And then point your URL to the app:
# spec/spec_helper.rb
RSpec.configure do |config|
config.before(:each) do
stub_request(:any, /api.github.com/).to_rack(SinatraApp)
end
end
For a clearer example, see:
http://robots.thoughtbot.com/how-to-stub-external-services-in-tests