I am trying to run the following test:
tests.py
from rest_framework.test import APITestCase
from myapp.routing import application
from channels.testing import WebsocketCommunicator
from account.models import User
from rest_framework.authtoken.models import Token
class Tests(APITestCase):
def setUp(self):
self.user = User.objects.create(email='test#test.test',
password='a password')
self.token, created = Token.objects.get_or_create(user=self.user)
async def test_connect(self):
communicator = WebsocketCommunicator(application, f"/ws/user/{self.token}/")
connected, subprotocol = await communicator.connect()
self.assertTrue(connected)
await communicator.disconnect()
application is a boilerplate instance of channels.routing.ProtocolTypeRouter (like in here: https://channels.readthedocs.io/en/latest/topics/routing.html). Everything works fine in production. The test exits with the following error:
Traceback (most recent call last):
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/testing.py", line 74, in receive_output
return await self.output_queue.get()
File "/usr/lib/python3.7/asyncio/queues.py", line 159, in get
await getter
concurrent.futures._base.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/sync.py", line 223, in __call__
return call_result.result()
File "/usr/lib/python3.7/concurrent/futures/_base.py", line 428, in result
return self.__get_result()
File "/usr/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/sync.py", line 292, in main_wrap
result = await self.awaitable(*args, **kwargs)
File "/home/projects/myapp/myapp-api/app/tests.py", line 35, in test_connect
connected, subprotocol = await communicator.connect()
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/channels/testing/websocket.py", line 36, in connect
response = await self.receive_output(timeout)
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/testing.py", line 85, in receive_output
raise e
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/testing.py", line 74, in receive_output
return await self.output_queue.get()
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/timeout.py", line 66, in __aexit__
self._do_exit(exc_type)
File "/home/projects/myapp/myapp-env/lib/python3.7/site-packages/asgiref/timeout.py", line 103, in _do_exit
raise asyncio.TimeoutError
concurrent.futures._base.TimeoutError
----------------------------------------------------------------------
Ran 1 test in 1.026s
I have tried python versions 3.7.5, 3.8.0 and 3.9.9 using channels 3.0.4 with django 3.2.10 and channels-redis 3.3.1 ('BACKEND': 'channels_redis.core.RedisChannelLayer' in settings.py). The error persists. What am I doing wrong?
I had the same problem. APITestCase or TestCase dont allow transactions, you have to use SimpleTestCase from django test, and set databases to all. Just with that difference i think it will work.
Note that the transactions will be saved between test, and not rolled back after the test.
from django.test import SimpleTestCase
from myapp.routing import application
from channels.testing import WebsocketCommunicator
from account.models import User
from rest_framework.authtoken.models import Token
class Tests(SimpleTestCase):
databases = '__all__'
def setUp(self):
self.user = User.objects.create(email='test#test.test', password='a password')
self.token, created = Token.objects.get_or_create(user=self.user)
async def test_connect(self):
communicator = WebsocketCommunicator(application, f"/ws/user/{self.token}/")
connected, subprotocol = await communicator.connect()
self.assertTrue(connected)
await communicator.disconnect()
here are the information of SimpleTestCase
https://docs.djangoproject.com/en/4.0/topics/testing/tools/
I faced a similar issue and the solution is usually to mimic your production router for the tests too, i.e whatever middleware or additional component used in production should also be added when imstantiating your Communicator. for example in my asgi.py I have:
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": AllowedHostsOriginValidator(
jwt_auth_middleware_stack(URLRouter(chat.routing.websocket_urlpatterns)),
),
}
)
My communicator is instantiated as follows:
communicator = WebsocketCommunicator(jwt_auth_middleware_stack(URLRouter(websocket_urlpatterns)),
f"/ws/chat/{chat.id}/?token={token}")
And my url is:
websocket_urlpatterns = [
path("ws/chat/<str:chat_id>/", consumers.AsyncChatConsumer.as_asgi())
]
Related
I've checked all the related questions none of them helped me. I'm making a simple application where I will get JSON responses on the websocketking.com site. No matter how many times I make changes none of the tries are working.
here every detail
asgi.py file
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from django.urls import path
from home.consumers import *
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
ws_patterns = [
path('ws/test/', TestConsumer),
]
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(URLRouter(ws_patterns)),
})
consumers.py file
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
import json
class TestConsumer(WebsocketConsumer):
def connect(self):
self.room_name = "test_consumer"
self.room_group_name = "test_consumer_group"
async_to_sync(self.channel_layer.group_add)(self.room_name, self.room_group_name)
self.accept()
self.send(text_data=json.dumps({'status' : 'connected'}))
def receive(self):
pass
def disconnect(self):
pass
These are the things I added in my settings.py file
ASGI_APPLICATION = 'core.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("localhost", 6379)],
},
},
}
And here is the error I got in console
WebSocket HANDSHAKING /ws/test/ [127.0.0.1:2795]
Exception inside application: object.__init__() takes exactly one argument (the instance to initialize)
Traceback (most recent call last):
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\sessions.py", line 254, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\auth.py", line 181, in __call__
return await super().__call__(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\routing.py", line 150, in __call__
return await application(
File "D:\PythonProjects\channels_project\env\lib\site-packages\asgiref\compatibility.py", line 33, in new_application
instance = application(scope)
File "D:\PythonProjects\channels_project\env\lib\site-packages\channels\generic\websocket.py", line 23, in __init__
super().__init__(*args, **kwargs)
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
WebSocket DISCONNECT /ws/test/ [127.0.0.1:2795]
Maybe you've already solved this issue but here's some things I've noticed with your code which might help you:
Change WebsocketConsumer to AsyncWebsocketConsumer- Then change your consumer methods to async, e.g.
async def connect(self):
...
Change
async_to_sync(self.channel_layer.group_add)(self.room_name,
self.room_group_name)
to
await self.channel_layer.group_add(
self.room_name,
self.room_group_name)
As a side note:
You don't need to do self.send(msg) to check if you are connected. This is handled in self.accept() and will return to
client.onopen = () => {
console.log("connected");
}
If none of these sort your problem it may be an issue with your routing and/or the middleware stack. I have had issues with authentication recently. Try using a different browser if you're using Chrome and see if you can tag onto the onerror event
I use Django and Graphene in my project. I wrote tests with GraphQLTestCase. When i try to authenticate users using JWT, i usually get errors.
Here is my code:
from django.test import TestCase
import json
from graphene_django.utils.testing import GraphQLTestCase
from resume.graph.schema import schema
from .models import Post
from django.contrib.auth import get_user_model
from graphql_jwt.shortcuts import get_token
User = get_user_model()
class PostTestCase(GraphQLTestCase):
GRAPHQL_SCHEMA = schema
def test_post_list(self):
token = get_token(User.objects.get(pk=1))
headers = {"HTTP_AUTHORIZATION": f"JWT {token}"}
response = self.query(
'''
query {
post{
user
text
}
}
''',
op_name = 'post',
headers=headers,
)
content = json.loads(response.content)
self.assertResponseNoErrors(response)
Here are the errors I get after running python manage.py test.
Traceback (most recent call last):
File "C:\Users\Udemezue\Desktop\resume\post\tests.py", line 25, in test_post_list
token = get_token(User.objects.get(pk=9))
File "C:\Users\Udemezue\Desktop\resume\env\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\Udemezue\Desktop\resume\env\lib\site-packages\django\db\models\query.py", line 415, in get
raise self.model.DoesNotExist(
accounts.models.User.DoesNotExist: User matching query does not exist.
Here is the errors i get.
======================================================================
FAIL: test_post_list (post.tests.PostTestCase)
Traceback (most recent call last):
File "C:\Users\Udemezue\Desktop\resume\post\tests.py", line 62, in test_post_list
self.assertResponseNoErrors(response)
File "C:\Users\Udemezue\Desktop\resume\env\lib\site-packages\graphene_django\utils\testing.py", line 75, in assertResponseNoErrors
self.assertEqual(resp.status_code, 200)
AssertionError: 400 != 200
Ran 1 test in 0.137s
FAILED (failures=1)
Destroying test database for alias 'default'...
This works for me.
from django.contrib.auth import get_user_model
from graphql_jwt.shortcuts import get_token
User = get_user_model()
import json
class PostTestCase(GraphQLTestCase):
def test_post_list(self):
user = get_user_model().objects.create(username='myuser')
token = get_token(user)
headers = {"HTTP_AUTHORIZATION": f"JWT {token}"}
response = self.query(
'''
query GetUser($username: String!) {
user(username: $username) {
id
}
}
''',
headers=headers,
)
content = json.loads(response.content)
self.assertResponseNoErrors(response)
I'm having trouble decoding a token that I receive through the header of my request.
Application:
from flask import Flask
from flask import jsonify
from flask_restplus import Resource, Api
from helpers.load import get_env as _
from middleware.environment_middleware import EnvironmentMiddleware
from flask_jwt_extended import JWTManager
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = _('DATABASE_URI')
app.config['SQLALCHEMY_DATABASE_URI'] = _('DATABASE_URI')
app.config['SECRET_KEY'] = _('SECRET_KEY')
app.config['JWT_SECRET_KEY'] = _('JWT_SECRET')
app.wsgi_app = EnvironmentMiddleware(app.wsgi_app)
jwt = JWTManager(app)
api = Api(app)
jwt._set_error_handler_callbacks(api)
Middleware Class:
from werkzeug.wrappers import Request, Response, ResponseStream
from helpers.load import load_db_env
from flask_jwt_extended import get_jwt_identity, jwt_required, verify_jwt_in_request
import jwt
class EnvironmentMiddleware():
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
request = Request(environ)
if request.headers:
params = load_db_env(request.headers.get('Whitelabel'))
jwt.decode(request.headers.get('Authorization').replace('Bearer ', ''), params['JWT_SECRET'], algorithm='HS256')
return self.app(environ, start_response)
res = Response(u'Unauthorized.', mimetype='application/json', status=401)
return res(environ, start_response)
load_db_env brings a dict with all params from my database according with the 'whitelabel' parameter including the JWT_SECRET and my environ brings all Response data, headers, etc that I need for authentication.
But for some reason I can't decode and find the informations inside the Bearer Token from the request for validate and identify the user.
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/home/bela/dev/bela/lib/python3.8/site-packages/jwt/api_jwt.py", line 63, in decode
decoded = super(PyJWT, self).decode(jwt, key, verify, algorithms,
File "/home/bela/dev/bela/lib/python3.8/site-packages/jwt/api_jws.py", line 115, in decode
self._verify_signature(payload, signing_input, header, signature,
File "/home/bela/dev/bela/lib/python3.8/site-packages/jwt/api_jws.py", line 186, in _verify_signature
raise DecodeError('Signature verification failed')
jwt.exceptions.DecodeError: Signature verification failed
I hope I was clear, I'm from Brazil and my english is not the best.
Obrigada! :*
Same code deployed on development system works fine but once deployed to production fails with following error:
Traceback (most recent call last):
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 240, in Handle
handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 299, in _LoadHandler
handler, path, err = LoadObject(self._handler)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 85, in LoadObject
obj = __import__(path[0])
ImportError: No module named urlhandlers
Following is the URL handler code:
import traceback
import datetime
from google.appengine.api import taskqueue
import re
import webapp2
from utilities.logger import logThis, AEL_LEVEL_INFO, AEL_LEVEL_CRITICAL
from constants.constants import GAET_MASTERTASK_NAME, GAEQ_FOR_MASTERTASK
import sys
class QueueAMasterTask(webapp2.RequestHandler):
def get(self):
try:
dt =datetime.datetime.now()
logThis(AEL_LEVEL_INFO, 'Master Task added to its Q at[%s]' %(dt))
task_name = GAET_MASTERTASK_NAME + str(datetime.datetime.now())
task_name = re.sub('[^a-zA-Z0-9_-]', '_', task_name)
taskqueue.add(queue_name=GAEQ_FOR_MASTERTASK,name=task_name)
logThis(AEL_LEVEL_INFO, "OK-MASTER TASK ADD")
except taskqueue.DuplicateTaskNameError:
logThis(AEL_LEVEL_CRITICAL, "EXCEPTION on QueueAMasterTask-" + traceback.format_exc())
except:
logThis(AEL_LEVEL_CRITICAL, "EXP on QueueAMasterTask-" + traceback.format_exc())
app = webapp2.WSGIApplication([('/QueueAMasterTask', QueueAMasterTask)
], debug=True)
Cron job error log
I'm using Django==1.9.2 and djangorestframework==3.3.2, and django.test.Client to make some tests. The problem is that when I execute my tests I'm gettting this error:
ERROR: test_view (main.tests.test_http.TestMainViewSet)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/vladir/work/all/project-django1.9/saxo-publish/publish/main/tests/test_http.py", line 111, in test_view
content_type='application/json'
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 515, in post
secure=secure, **extra)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 314, in post
secure=secure, **extra)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 380, in generic
return self.request(**r)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 449, in request
response = self.handler(environ)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 123, in __call__
response = self.get_response(request)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 245, in get_response
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 296, in handle_uncaught_exception
return callback(request, **param_dict)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 166, in _wrapped_view
return middleware.process_response(request, response)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/middleware/csrf.py", line 230, in process_response
request.META["CSRF_COOKIE"],
KeyError: u'CSRF_COOKIE'
My test code looks like this:
import json
from django.test import Client
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
class TestMainViewSet(TestCase):
def setUp(self):
self.client = Client(
HTTP_HOST='example.com' # I have also tried removing this
)
self.create_read_url = reverse('books-list')
User.objects.create_user(
username="username",
email="username#zunzun.se",
password="123"
)
def test_create(self):
self.client.login(username='username', password="123")
# In this case I'm doing a POST, but it is the same with a GET
response = self.client.post(
self.create_read_url,
data=json.dumps({'title': "Create"}), # I have also tried without the json.dumps
content_type='application/json'
)
data = json.loads(response.content)
print data
self.assertEqual(response.status_code, 201)
self.assertEquals(data['title'], "Create")
my view code is:
from django.contrib.auth.mixins import LoginRequiredMixin
from rest_framework import viewsets
from .serialiazers import (
BookSerializerRead,
BookSerializerWrite,
)
class MainViewSet(LoginRequiredMixin, viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class_read = BookSerializerRead
serializer_class_write = BookSerializerWrite
on the urls.py:
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'books', MainViewSet, 'books')
urlpatterns = [
url(r'^api/', include(router.urls)),
]
According with the Django doc about it, I should not need anything additional to avoid the CSRF checks,
because as textually said there: "By default, the test client will disable any CSRF checks performed by your site.", and I also know that enforce_csrf_checks=False by default
on the Client.
I have found one detail though, if I create an instance of the client that way self.client = Client(HTTP_HOST='example.com', CSRF_COOKIE='xxxxxx') then it works, but is that
actually needed? It is not what the documentation says, so I suppose I'm doing something wrong. Could someone help me with that please? I will appreciate any help about.
Thanks in advance
Try to use DRF's APITestCase as base class for test cases:
from rest_framework.testing import APITestCase
class TestMainViewSet(APITestCase):
...