2022-08-17 20:15:32 +02:00
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
@author : Adolfo Gómez , dkmaster at dkmon dot com
"""
import typing
import logging
from django . test import TestCase , TransactionTestCase
2022-12-08 14:44:47 +01:00
from django . test . client import Client , AsyncClient # type: ignore # Pylance does not know about AsyncClient, but it is there
2022-08-25 17:55:51 +02:00
from django . http . response import HttpResponse
2022-08-17 20:15:32 +02:00
from django . conf import settings
from uds . core . managers . crypto import CryptoManager
logger = logging . getLogger ( __name__ )
2022-08-25 22:28:57 +02:00
2022-08-25 17:55:51 +02:00
class UDSHttpResponse ( HttpResponse ) :
"""
Custom response class to be able to access the response content
"""
2022-08-25 22:28:57 +02:00
2022-08-25 17:55:51 +02:00
def __init__ ( self , content , * args , * * kwargs ) :
super ( ) . __init__ ( content , * args , * * kwargs )
self . content = content
def json ( self ) - > typing . Any :
return super ( ) . json ( ) # type: ignore
2022-08-25 22:28:57 +02:00
2022-12-08 14:44:47 +01:00
class UDSClientMixin :
2022-08-19 00:46:38 +02:00
headers : typing . Dict [ str , str ] = {
' HTTP_USER_AGENT ' : ' Testing user agent ' ,
}
2022-12-09 02:58:17 +01:00
ip_version : int = 4
2022-08-19 00:46:38 +02:00
2022-12-08 14:44:47 +01:00
def initialize ( self ) :
2022-08-17 20:15:32 +02:00
# Ensure only basic middleware are enabled.
settings . MIDDLEWARE = [
' django.contrib.sessions.middleware.SessionMiddleware ' ,
' django.middleware.locale.LocaleMiddleware ' ,
' django.middleware.common.CommonMiddleware ' ,
' django.middleware.csrf.CsrfViewMiddleware ' ,
' django.contrib.messages.middleware.MessageMiddleware ' ,
' uds.core.util.middleware.request.GlobalRequestMiddleware ' ,
]
2022-08-25 22:28:57 +02:00
# Update settings security options
settings . RSA_KEY = ' -----BEGIN PRIVATE KEY----- \n MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcANi/08cnpn04 \n jKW/o2G1k4SIa6dJks8DmT4MQHOWqYC46YSIIPzqGoBPcvkbDSPSFBnByo3HhMY+ \n k4JHc9SUwEmHSJWDCHjt7XSXX/ryqH0QQIJtSjk9Bc+GkOU24mnbITiw7ORjp7VN \n vgFdFhjVsZM/NjX/6Y9DoCPC1mGj0O9Dd4MfCsNwUxRhhR6LdrEnpRUSVW0Ksxbz \n cTfpQjdFr86+1BeUbzqN2HDcEGhvioj+0lGPXcOZoRNYU16H7kjLNP+o+rC7f/q/ \n foOYLzDSkmzePbcG+g0Hv7K7fuLus05ZWjupOmJA9hytB1BIF4p5f4ewl05Fx2Zj \n G2LneO2fAgMBAAECggEBANDimOnh2TkDceeMWx+OsAooC3E/zbEkjBudl3UoiNcn \n D0oCpkxeDeT0zpkgz/ZoTnd7kE0Y1e73WQc3JT5UcyXdQLMLLrIgDDnT+Jx1jB5z \n 7XLN3UiJbblL2BOrZYbsCJf/fgU2l08rgBBVdJP+lAvps6YUAcd+6gDKfsnSpRhU \n WBHLZde7l6vUJ2OK9ZmHaghF5E8Xx918OSUKFJfGTYL5JLTb/scdl8vQse1quWC1 \n k48PPXK10vOFvYWonQpRb2cOK/PPjPXPNWzcQyQY9D1iOeFvRyLqOXYE/ZY+qDe2 \n HdPGrkl67yz01nzepkWWg/ZNbMXeZZyOnZm0aXtOxtkCgYEA/Qz3mescgwrt67yh \n FrbXjUqiVf2IpbNt88CUcbY0r1EdTA9OMtOtPYNvfpyRIRfDaZJ1zAdh3CZ2/hTm \n g+VUtseKnUDCi0xIBKX3V2O8sryWt2KStTnTo6JP0T47yXvmaRu5cutgoaD9SK+r \n N5vg1D2gNLmsT8uJh1Bl/yWGC4sCgYEA3pFGgAmiywsvmsddkI+LujoQVTiqkfFg \n MHHsJFOZlhYO83g49Q11pcQ70ukT6e89Ggwy///+z19p8jJ+wGqQWQLsM6eO1utg \n nJ8wMTwk8tOEm9MnWnnWhtG9KWcgkmwOVQiesJdWa1xOqsBKGchUkugmFycKNsiG \n HUbogbJ0OL0CgYBVLIcuxKdNKGGaxlwGVDbLdQKdJQBYncN1ly2f9K9ZD1loH4K3 \n su4N1W6y1Co5VFFO+KAzs4xp2HyW2xwX6xoPh6yNb53L2zombmKJhKWgF8A3K7Or \n 0jH9UwXArUzcbZrJaC6MktNss85tJ8vepNYROkjxVkm8dgrtg89BCTVMLwKBgQCW \n SSh+uoL3cdUyQV63h4ZFOIHg2cOrin52F+bpXJ3/z2NHGa30IqOHTGtM7l+o/geX \n OBeT72tC4d2rUlduXEaeJDAUbRcxnnx9JayoAkG8ygDoK3uOR2kJXkTJ2T4QQPCo \n kIp/GaGcGxdviyo+IJyjGijmR1FJTrvotwG22iZKTQKBgQCIh50Dz0/rqZB4Om5g \n LLdZn1C8/lOR8hdK9WUyPHZfJKpQaDOlNdiy9x6xD6+uIQlbNsJhlDbOudHDurfI \n ghGbJ1sy1FUloP+V3JAFS88zIwrddcGEso8YMFMCE1fH2/q35XGwZEnUq7ttDaxx \n HmTQ2w37WASIUgCl2GhM25np0Q== \n -----END PRIVATE KEY----- \n '
settings . CERTIFICATE = ' -----BEGIN CERTIFICATE----- \n MIICzTCCAjYCCQCOUQEWpuEa3jANBgkqhkiG9w0BAQUFADCBqjELMAkGA1UEBhMC \n RVMxDzANBgNVBAgMBk1hZHJpZDEUMBIGA1UEBwwLQWxjb3Jjw4PCs24xHTAbBgNV \n BAoMFFZpcnR1YWwgQ2FibGUgUy5MLlUuMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEY \n MBYGA1UEAwwPQWRvbGZvIEfDg8KzbWV6MSUwIwYJKoZIhvcNAQkBFhZhZ29tZXpA \n dmlydHVhbGNhYmxlLmVzMB4XDTEyMDYyNTA0MjM0MloXDTEzMDYyNTA0MjM0Mlow \n gaoxCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxFDASBgNVBAcMC0FsY29y \n Y8ODwrNuMR0wGwYDVQQKDBRWaXJ0dWFsIENhYmxlIFMuTC5VLjEUMBIGA1UECwwL \n RGV2ZWxvcG1lbnQxGDAWBgNVBAMMD0Fkb2xmbyBHw4PCs21lejElMCMGCSqGSIb3 \n DQEJARYWYWdvbWV6QHZpcnR1YWxjYWJsZS5lczCBnzANBgkqhkiG9w0BAQEFAAOB \n jQAwgYkCgYEA35iGyHS/GVdWk3n9kQ+wsCLR++jd9Vez/s407/natm8YDteKksA0 \n MwIvDAX722blm8PUya2NOlnum8KdyUPDOq825XERDlsIA+sTd6lb1c7w44qZ/pb+ \n 68mhXoRx2VJsu//+zhBkaQ1/KcugeHa4WLRIH35YLxdQDxrXS1eQWccCAwEAATAN \n BgkqhkiG9w0BAQUFAAOBgQAk+fJPpY+XvUsxR2A4SaQ8TGnE2x4PtpwCrCVzKEU9 \n W2ugdXvysxkHbib3+JdA6s+lJjHs5HiMZPo/ak8adEKke+d10EU5YcUaJRRUpStY \n qQHziaqOl5Hgi75Kjskq6+tCU0Iui+s9pBg0V6y1AQsCmH2xFs7t1oEOGRFVarfF \n 4Q== \n -----END CERTIFICATE----- '
2022-12-08 14:44:47 +01:00
def add_header ( self , name : str , value : str ) :
self . headers [ name ] = value
2022-12-09 02:58:17 +01:00
def set_user_agent ( self , user_agent : typing . Optional [ str ] = None ) :
user_agent = user_agent or ' '
# Add 'HTTP_USER_AGENT' header
self . headers [ ' HTTP_USER_AGENT ' ] = user_agent
def enable_ipv4 ( self ) :
self . ip_version = 4
def enable_ipv6 ( self ) :
self . ip_version = 6
2022-12-08 14:44:47 +01:00
2022-12-09 02:58:17 +01:00
def append_remote_addr ( self , kwargs : typing . Dict [ str , typing . Any ] ) - > None :
if self . ip_version == 4 :
kwargs [ ' REMOTE_ADDR ' ] = ' 127.0.0.1 '
elif self . ip_version == 6 :
kwargs [ ' REMOTE_ADDR ' ] = ' ::1 '
2022-12-08 14:44:47 +01:00
class UDSClient ( UDSClientMixin , Client ) :
def __init__ (
self ,
enforce_csrf_checks : bool = False ,
raise_request_exception : bool = True ,
* * defaults : typing . Any
) :
UDSClientMixin . initialize ( self )
2022-08-17 20:15:32 +02:00
# Instantiate the client and add basic user agent
2022-08-19 00:46:38 +02:00
super ( ) . __init__ ( enforce_csrf_checks , raise_request_exception )
2022-08-25 22:28:57 +02:00
2022-08-17 20:15:32 +02:00
# and required UDS cookie
self . cookies [ ' uds ' ] = CryptoManager ( ) . randomString ( 48 )
2022-08-19 00:46:38 +02:00
def request ( self , * * request : typing . Any ) :
# Copy request dict
request = request . copy ( )
# Add headers
request . update ( self . headers )
return super ( ) . request ( * * request )
2022-08-25 17:55:51 +02:00
def get ( self , * args , * * kwargs ) - > ' UDSHttpResponse ' :
2022-12-09 02:58:17 +01:00
self . append_remote_addr ( kwargs )
2022-08-25 17:55:51 +02:00
return typing . cast ( ' UDSHttpResponse ' , super ( ) . get ( * args , * * kwargs ) )
2022-08-24 22:16:47 +02:00
2022-08-25 17:55:51 +02:00
def post ( self , * args , * * kwargs ) - > ' UDSHttpResponse ' :
2022-12-09 02:58:17 +01:00
self . append_remote_addr ( kwargs )
2022-08-25 17:55:51 +02:00
return typing . cast ( ' UDSHttpResponse ' , super ( ) . post ( * args , * * kwargs ) )
2022-08-24 22:16:47 +02:00
2022-12-08 14:44:47 +01:00
class UDSAsyncClient ( UDSClientMixin , AsyncClient ) :
def __init__ (
self ,
enforce_csrf_checks : bool = False ,
raise_request_exception : bool = True ,
* * defaults : typing . Any
) :
UDSClientMixin . initialize ( self )
# Instantiate the client and add basic user agent
super ( ) . __init__ ( enforce_csrf_checks , raise_request_exception ) # type: ignore # Coplains, but this is ok
# and required UDS cookie
self . cookies [ ' uds ' ] = CryptoManager ( ) . randomString ( 48 )
async def request ( self , * * request : typing . Any ) :
# Copy request dict
request = request . copy ( )
# Add headers
request . update ( self . headers )
return await super ( ) . request ( * * request )
async def get ( self , * args , * * kwargs ) - > ' UDSHttpResponse ' :
2022-12-09 02:58:17 +01:00
self . append_remote_addr ( kwargs )
2022-12-08 14:44:47 +01:00
return typing . cast ( ' UDSHttpResponse ' , await super ( ) . get ( * args , * * kwargs ) )
async def post ( self , * args , * * kwargs ) - > ' UDSHttpResponse ' :
2022-12-09 02:58:17 +01:00
self . append_remote_addr ( kwargs )
2022-12-08 14:44:47 +01:00
return typing . cast ( ' UDSHttpResponse ' , await super ( ) . post ( * args , * * kwargs ) )
2022-12-09 02:58:17 +01:00
class UDSTestCaseMixin :
2022-08-17 20:15:32 +02:00
client_class : typing . Type = UDSClient
2022-12-08 14:44:47 +01:00
async_client_class : typing . Type = UDSAsyncClient
2022-08-17 20:15:32 +02:00
2022-08-19 00:46:38 +02:00
client : UDSClient
2022-12-08 14:44:47 +01:00
async_client : UDSAsyncClient
2022-08-19 00:46:38 +02:00
2022-12-09 02:58:17 +01:00
@staticmethod
def add_middleware ( middleware : str ) - > None :
if middleware not in settings . MIDDLEWARE :
settings . MIDDLEWARE . append ( middleware )
@staticmethod
def remove_middleware ( middleware : str ) - > None :
# Remove middleware from settings, if present
try :
settings . MIDDLEWARE . remove ( middleware )
except ValueError :
pass # Not present
class UDSTestCase ( UDSTestCaseMixin , TestCase ) :
2022-08-29 15:21:12 +02:00
@classmethod
def setUpClass ( cls ) - > None :
super ( ) . setUpClass ( )
2022-12-08 14:44:47 +01:00
setupClass ( cls ) # The one local to this module
2022-08-25 22:28:57 +02:00
2022-12-09 02:58:17 +01:00
class UDSTransactionTestCase ( UDSTestCaseMixin , TransactionTestCase ) :
2022-08-29 15:21:12 +02:00
@classmethod
def setUpClass ( cls ) - > None :
super ( ) . setUpClass ( )
setupClass ( cls )
2022-12-08 14:44:47 +01:00
def setupClass (
cls : typing . Union [ typing . Type [ UDSTestCase ] , typing . Type [ UDSTransactionTestCase ] ]
) - > None :
2022-08-29 15:21:12 +02:00
# Nothing right now
pass