I am new to unit testing using script. I tried to verify login with arguments in post data, but I am getting login page as response and not get logged in.Because of #tornado.web.authenticated i can't access other functions without login and it responding to login page
import tornado
from tornado.testing import AsyncTestCase
from tornado.web import Application, RequestHandler
import app
import urllib
class MyTestCase(AsyncTestCase):
#tornado.testing.gen_test
def test_http_fetch_login(self):
data = urllib.urlencode(dict(username='admin', password=''))
client = AsyncHTTPClient(self.io_loop)
response = yield client.fetch("http://localhost:8888/console/login/?", method="POST",body=data)
# Test contents of response
self.assertIn("Automaton web console", response.body)
#tornado.testing.gen_test
def test_http_fetch_config(self):
client = AsyncHTTPClient(self.io_loop)
response = yield client.fetch("http://localhost:8888/console/configuration/?")
self.assertIn("server-version",response.body)
To test code that uses #authenticated (unless you are testing the redirection to the login page itself), you need to pass a cookie (or whatever form of authentication you're using) that will be accepted by your get_current_user method. The details of this will vary depending on how exactly you are doing your authentication, but if you're using Tornado's secure cookies you'll probably use the create_signed_value function to encode a cookie.
From documentation:
If you decorate post() methods with the authenticated decorator, and the user is not logged in, the server will send a 403 response. The #authenticated decorator is simply shorthand for if not self.current_user: self.redirect() and may not be appropriate for non-browser-based login schemes.
So you can use mock do like pointed in this answer: https://stackoverflow.com/a/18286742/1090700
Another 403 status will be thrown on POST calls if you have secure_cookies=True app param. If you want to test the code in a non debug fashion, you can also using mock POST actions while keeping the secure_cookies=True application parameter and then just mock the check_xsrf_cookie Handler method as well, like this:
# Some previous stuff...
with mock.patch.object(YourSecuredHandler, 'get_secure_cookie') as mget:
mget.return_value = 'user_email'
response = self.fetch('/', method='GET')
with mock.patch.object(
YourSecuredHandler, 'check_xsrf_cookie') as mpost:
mpost.return_value = None
response = self.fetch(
url, method="POST", body=parse.urlencode(body1),
headers=headers)
self.assertEqual(response.code, 201)
Related
I'm writing a web-app that has to support Single-Sign-On through Saml. The back-end is made with FastAPI and the front end is made in react. Nginx is used to proxy requests to the backend. I have an endpoint for logins that returns a redirect request to an identity provider. When accessing the endpoint directly through the browser everything works as expected and the user is redirected to an external login page.
I want to be able to use a similar redirect when a user who isn't authorized tries to access the page. My attempt at this consists of returning a redirect response, when a http exception is raised, to my login endpoint which should then redirect to the external login page. Doing this prompts a 404 response from the idp.
The login endpoint is defined like this in FastAPI:
#app.get("/saml/login")
async def sso(request: Request):
req = await prepare_from_fastapi_request(request)
auth = Saml2_Auth_patched(req, saml_settings)
callback_url = auth.login(return_to="https://website.com/")
response = RedirectResponse(url=callback_url)
return response
And the exception handler like this:
#app.exception_handler(StarletteHTTPException)
async def unauthorized_handler(request: Request, exc):
response = RedirectResponse(url="/api/saml/login")
return response
This looks more like a front-end issue since as you are saying everything works as expected when you access the endpoint in the browser.
I think the issue here is that the library you are using to make API requests from your front end application does not follow redirect. For example axios does not do that out of the box. You can see the conversation here: https://github.com/axios/axios/issues/1350
In the same conversation they mention another dependency you can use to address this issue: https://www.npmjs.com/package/follow-redirects
How can I submit a POST request with Django test Client? I just want to test the login function of my application. It's strange that I put
response = self.client.post(reverse('rbac:login'), data=data) in my testcase, and I want to validate it so I print response.status_code.Actually it should return 302 coz it should turn to another page. but it return 200. This situation happens when I use "python manage.py test testmodel" to test. However, if I use "python manage.py shell" to make an InteractiveConsole and write exactly the same code, it return 302 at last.
I have nearly tried all the methods provided from stack overflow, e.g. change the url, offer the content-type when I post....but these are meaningless.
from django.test import TestCase
from django.test.utils import setup_test_environment,teardown_test_environment
from django.test import Client
from django.urls import reverse
import django
from rbac.models import RbacUser, RbacRole
from system_module.models import Redeploy
from urllib import urlencode
class CreateUser(TestCase):
#classmethod
def setUp(self):
self.client = Client()
def test_create_user(self):
data = {"Username": "admin_xy", "Password": "aaa"}
response = self.client.post(reverse('rbac:login'), data=data)
print response
and it shows me 200.....
However, in interactive console:
>>> response = client.post(reverse('rbac:login'), data=data)
>>> print response.status_code
302
it shows the right result.
I want to know how to cope with this annoying problem. Thx
I expect when I use test file to test my test case, it works fine....like show 302 code.
Before sending the POST or GET request by using Client, first you need to create the user make the user active and give necessary permissions and login through client and then send the post request.
Ex: self.client.login(username='username', 'password'='password)
It's been long time that this question is here, I hope you found the answer, but for other people I'll suggest to add this to this code to be redirected:
response = self.client.post(reverse('rbac:login'), data=data, follow=True)
The follow=True will follow the redirect .
I'm using YouTube data API v3.
Is it possible to make a big BatchHttpRequest (e.g., see here) and also to use ETags for local caching at the httplib2 level (e.g., see here)?
ETags work fine for single queries, I don't understand if they are useful also for batch requests.
TL;DR:
BatchHttpRequest cannot be used with caching
HERE IT IS:
First lets see the way to initialize BatchHttpRequest:
from apiclient.http import BatchHttpRequest
def list_animals(request_id, response, exception):
if exception is not None:
# Do something with the exception
pass
else:
# Do something with the response
pass
def list_farmers(request_id, response):
"""Do something with the farmers list response."""
pass
service = build('farm', 'v2')
batch = service.new_batch_http_request()
batch.add(service.animals().list(), callback=list_animals)
batch.add(service.farmers().list(), callback=list_farmers)
batch.execute(http=http)
Second lets see how ETags are used:
from google.appengine.api import memcache
http = httplib2.Http(cache=memcache)
Now lets analyze:
Observe the last line of BatchHttpRequest example: batch.execute(http=http), and now checking the source code for execute, it calls _refresh_and_apply_credentials, which applies the http object we pass it.
def _refresh_and_apply_credentials(self, request, http):
"""Refresh the credentials and apply to the request.
Args:
request: HttpRequest, the request.
http: httplib2.Http, the global http object for the batch.
"""
# For the credentials to refresh, but only once per refresh_token
# If there is no http per the request then refresh the http passed in
# via execute()
Which means, execute call which takes in http, can be passed the ETag http you would have created as:
http = httplib2.Http(cache=memcache)
# This would mean we would get the ETags cached http
batch.execute(http=http)
Update 1:
Could try with a custom object as well:
from googleapiclient.discovery_cache import DISCOVERY_DOC_MAX_AGE
from googleapiclient.discovery_cache.base import Cache
from googleapiclient.discovery_cache.file_cache import Cache as FileCache
custCache = FileCache(max_age=DISCOVERY_DOC_MAX_AGE)
http = httplib2.Http(cache=custCache)
# This would mean we would get the ETags cached http
batch.execute(http=http)
Because, this is just a hunch on the comment in http2 lib:
"""If 'cache' is a string then it is used as a directory name for
a disk cache. Otherwise it must be an object that supports the
same interface as FileCache.
Conclusion Update 2:
After again verifying the google-api-python source code, I see that, BatchHttpRequest is fixed with 'POST' request and has a content-type of multipart/mixed;.. - source code.
Giving a clue about the fact that, BatchHttpRequest is useful in order to POST data which is then processed down the later.
Now, keeping that in mind, observing what httplib2 request method uses: _updateCache only when following criteria are met:
Request is in ["GET", "HEAD"] or response.status == 303 or is a redirect request
ElSE -- response.status in [200, 203] and method in ["GET", "HEAD"]
OR -- if response.status == 304 and method == "GET"
This means, BatchHttpRequest cannot be used with caching.
I need to have a proxy that acts as an intermediary to fetch images. An example would be, my server requests domain1.com/?url=domain2.com/image.png and domain1.com server will respond with the data at domain2.com/image.png via domain1.com server.
Essentially I want to pass to the proxy the URL I want fetched, and have the proxy server respond with that resource.
Any suggestions on where to start on this?
I need something very easy to use or implement as I'm very much a beginner at all of this.
Most solutions I have found in python and/or django have the proxy acts as a "translater" i.e. domain1.com/image.png translates to domain2.com/image.png, which is obviously not the same.
I currently have the following code, but fetching images results in garbled data:
import httplib2
from django.conf.urls.defaults import *
from django.http import HttpResponse
def proxy(request, url):
conn = httplib2.Http()
if request.method == "GET":
url = request.GET['url']
resp, content = conn.request(url, request.method)
return HttpResponse(content)
Old question but for future googlers, I think this is what you want:
# proxies the google logo
def test(request):
url = "http://www.google.com/logos/classicplus.png"
req = urllib2.Request(url)
response = urllib2.urlopen(req)
return HttpResponse(response.read(), mimetype="image/png")
A very simple Django proxy view with requests and StreamingHttpResponse:
import requests
from django.http import StreamingHttpResponse
def my_proxy_view(request):
url = request.GET['url']
response = requests.get(url, stream=True)
return StreamingHttpResponse(
response.raw,
content_type=response.headers.get('content-type'),
status=response.status_code,
reason=response.reason)
The advantage of this approach is that you don't need to load the complete file in memory before streaming the content to the client.
As you can see, it forwards some response headers. Depending on your needs, you may want to forward the request headers as well; for example:
response = requests.get(url, stream=True,
headers={'user-agent': request.headers.get('user-agent')})
If you need something more complete than my previous answer, you can use this class:
import requests
from django.http import StreamingHttpResponse
class ProxyHttpResponse(StreamingHttpResponse):
def __init__(self, url, headers=None, **kwargs):
upstream = requests.get(url, stream=True, headers=headers)
kwargs.setdefault('content_type', upstream.headers.get('content-type'))
kwargs.setdefault('status', upstream.status_code)
kwargs.setdefault('reason', upstream.reason)
super().__init__(upstream.raw, **kwargs)
for name, value in upstream.headers.items():
self[name] = value
You can use this class like so:
def my_proxy_view(request):
url = request.GET['url']
return ProxyHttpResponse(url, headers=request.headers)
The advantage of this version is that you can reuse it in multiple views. Also, it forwards all headers, and you can easily extend it to add or exclude some other headers.
If the file you're fetching and returning is an image, you'll need to change the mimetype of your HttpResponse Object.
Use mechanize, it allow you to choose a proxy and act like a browser, making it easy to change the user agent, to go back and forth in the history and to handle authentification or cookies.
My Django site is set up with some middleware that on each request, checks for a cookie, and if it is not set, forwards the user elsewhere.
I now want to run some tests on the site. This is my code:
def test_contactform(self):
response = self.client.get('/contact/')
self.assertEqual(response.status_code, 200)
print response
self.assertTrue('Contact me' in response.content)
Unfortunately, this fails with:
Vary: Cookie
Content-Type: text/html; charset=utf-8
Location: http://testserver/ldap/?next=/contact/
Traceback (most recent call last):
File "tests.py", line 43, in test_contactform
self.assertEqual(response.status_code, 200)
AssertionError: 302 != 200
Can I either (i) set a cookie on the Django test client (and if so how) or (ii) require the Django test client to follow the redirect and test against the final page?
None of the above worked for me (Django1.9, Python3.4). Found this solution here:
from django.test import TestCase
from http.cookies import SimpleCookie
class TestViewWithCookies(TestCase):
def test_votes(self):
self.client.cookies = SimpleCookie({'name': 'bla'})
response = self.client.get('/vote/2')
self.assertEqual(response.status_code, 200)
While the accepted answer is the right approach for this problem, I just want to point out that you can set cookies directly (i.e. approach number (i) as you call it), but not via the test client. Instead you need to use a RequestFactory to construct a request which you can set the cookie on, then pass that directly to the view in question.
So instead of:
response = self.client.get('/contact/')
you do:
request = RequestFactory().get('/contact/')
request.COOKIES['thing'] = 'whatever'
response = contact_view(request)
where contact_view is the view serving /contact/.
You can set cookies for test client by calling load on cookies attribute which is SimpleCookie object.
from django.core import signing
self.client.cookies.load({
'example': '123',
'signed_example': signing.get_cookie_signer('signed_example').sign('123')
})
Django's test client is stateful - will persist cookies between tests and will ignore expire dates. For removal, you need to manually remove cookie or create a new client. - See docs
--- For Python 3 and Django 2+
The client.get method takes a follow argument which allows it to follow redirects:
response = self.client.get('/contact/', follow=True)
This is an old question but maybe this is handy for someone:
from http.cookies import SimpleCookie
from django.test import TestCase, Client
class CookieClientTests(TestCase):
def test_cookie(self):
cookies = SimpleCookie()
cookies["cookie_key"] = "something"
client = Client(HTTP_COOKIE=cookies.output(header='', sep='; '))
resp = client.get("/")
self.assertEqual(200, resp.status_code)