From 9ae3537a79e61349cb8fd431d8b2495383a998a1 Mon Sep 17 00:00:00 2001 From: LeandroRoberto Date: Fri, 7 Oct 2016 09:43:37 -0300 Subject: [PATCH] =?UTF-8?q?Adiciona=20teste=20de=20padroniza=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20urls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/test_general.py | 256 ------------------------------ sapl/test_urls.py | 365 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 365 insertions(+), 256 deletions(-) create mode 100644 sapl/test_urls.py diff --git a/sapl/test_general.py b/sapl/test_general.py index 15b589a80..baaaf18d6 100644 --- a/sapl/test_general.py +++ b/sapl/test_general.py @@ -1,81 +1,12 @@ from django.apps import apps -from django.contrib.auth import get_user_model -from django.contrib.auth.management import _get_all_permissions -from django.contrib.auth.models import Permission, User -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ObjectDoesNotExist -from django.core.urlresolvers import reverse from django.db.models import CharField, TextField -from django.http.response import HttpResponseNotFound -from django.utils.translation import string_concat -from django.utils.translation import ugettext_lazy as _ from model_mommy import mommy import pytest - -from sapl.crud.base import PermissionRequiredForAppCrudMixin, CrudAux -from scripts.inicializa_grupos_autorizacoes import cria_grupos_permissoes -from scripts.lista_urls import lista_urls - from .settings import SAPL_APPS - pytestmark = pytest.mark.django_db sapl_appconfs = [apps.get_app_config(n[5:]) for n in SAPL_APPS] -_lista_urls = lista_urls() - - -def create_perms_post_migrate(app): - - searched_perms = list() - # The codenames and ctypes that should exist. - ctypes = set() - - for klass in list(app.get_models()): - opts = klass._meta - permissions = ( - ("list_" + opts.model_name, - string_concat( - _('Visualizaçao da lista de'), ' ', - opts.verbose_name_plural)), - ("detail_" + opts.model_name, - string_concat( - _('Visualização dos detalhes de'), ' ', - opts.verbose_name_plural)), - ) - opts.permissions = tuple( - set(list(permissions) + list(opts.permissions))) - - if opts.proxy: - # Force looking up the content types in the current database - # before creating foreign keys to them. - app_label, model = opts.app_label, opts.model_name - - try: - ctype = ContentType.objects.get_by_natural_key( - app_label, model) - except: - ctype = ContentType.objects.create( - app_label=app_label, model=model) - else: - ctype = ContentType.objects.get_for_model(klass) - - ctypes.add(ctype) - for perm in _get_all_permissions(klass._meta, ctype): - searched_perms.append((ctype, perm)) - - all_perms = set(Permission.objects.filter( - content_type__in=ctypes, - ).values_list( - "content_type", "codename" - )) - - perms = [ - Permission(codename=codename, name=name, content_type=ct) - for ct, (codename, name) in searched_perms - if (ct.pk, codename) not in all_perms - ] - Permission.objects.bulk_create(perms) def test_charfield_textfield(): @@ -102,190 +33,3 @@ def test_str_sanity(): msg = '%s.%s.__str__ is broken.' % ( model.__module__, model.__name__) raise AssertionError(msg, exc) - -btn_login = ('') - - -@pytest.mark.parametrize('url_item', _lista_urls) -def test_crudaux_formato_inicio_urls_associadas(url_item): - - # Verifica se um crud é do tipo CrudAux, se sim, sua url deve começar - # com /sistema/ - key, url, var, app_name = url_item - url = '/' + (url % {v: 1 for v in var}) - - view_class = None - if hasattr(key, 'view_class'): - view_class = key.view_class - - # se não tem view_class, possivelmente é não é uma classed base view - if not view_class: - return - - # se não tem atributo crud, não é será nenhum tipo de crud - if not hasattr(view_class, 'crud'): - return - - # se o crud da view_class relativa a url a ser testada, - # implementa a classe CrudAux, seu link deve iniciar com /sistema - for string_class in list(map(str, type.mro(view_class.crud))): - - if 'CrudAux' in string_class: - assert url.startswith('/sistema'), """ - A url (%s) foi gerada a partir de um CrudAux, - o que diz que está é uma implementação de uma - tabela auxiliar, porém a url em questão, está fora - do padrão, que é iniciar com /sistema. - """ % (url) - - -@pytest.mark.parametrize('url_item', _lista_urls) -def test_crudaux_list_do_crud_esta_na_pagina_sistema(url_item, admin_client): - - # Verifica se um crud é do tipo CrudAux, se sim, sua url deve começar - # com /sistema/ - key, url, var, app_name = url_item - url = '/' + (url % {v: 1 for v in var}) - - view_class = None - if hasattr(key, 'view_class'): - view_class = key.view_class - - # se não tem view_class, possivelmente não é uma classed base view - if not view_class: - return - - # se não tem atributo crud, não é será nenhum tipo de crud - if not hasattr(view_class, 'crud'): - return - - herancas_crud = list(map(str, type.mro(view_class.crud))) - for string_class in herancas_crud: - if 'CrudAux' in string_class: - - herancas_view = list(map(str, type.mro(view_class))) - - for string_view_class in herancas_view: - # verifica se o link para manutenção do crud está em /sistema - if 'ListView' in string_view_class: - response = admin_client.get('/sistema', {}, follow=True) - assert url in str(response.content), """ - A url (%s) não consta nas Tabelas Auxiliares, - porem é uma implementação de ListView de CrudAux. - Se encontra em %s.urls - """ % (url, app_name) - - -@pytest.mark.parametrize('urls_app', _lista_urls) -def em_construcao_crud_permissions_urls(urls_app, client): - if not get_user_model().objects.exists(): - for app in sapl_appconfs: - # readequa permissões dos models adicionando - # list e detail permissions - create_perms_post_migrate(app) - # cria usuários de perfil do sapl - cria_grupos_permissoes() - users = get_user_model().objects.values_list('username', flat=True) - - for url_item in _lista_urls[urls_app]: - - key, url, var, app_name = url_item - url = '/' + (url % {v: 1 for v in var}) - - app_labels = app_name.split('.')[1] - - view_class = None - if hasattr(key, 'view_class'): - view_class = key.view_class - - """ - A classe PermissionRequiredForAppCrudMixin pode ser usada em uma - app mas envolver permissoes para outras - como é o caso de PainelView que está na app 'sessao' - mas é um redirecionamento para 'painel'... aqui é feita - a troca a urls_app a ser testada, por essas outras possíveis - """ - if PermissionRequiredForAppCrudMixin in type.mro(view_class): - # essa classe deve informar app_label - assert hasattr(view_class, 'app_label') - # app_label deve ter conteudo - assert view_class.app_label - app_labels = view_class.app_label - - if isinstance(app_labels, str): - app_labels = app_labels, - - for app in app_labels: - - # monta o username correspondente de a app da url a ser testada - user_for_url_atual_app = 'operador_%s' - if app in ['base', 'parlamentares']: - user_for_url_atual_app = user_for_url_atual_app % 'geral' - elif app in 'protocoloadm': - user_for_url_atual_app = user_for_url_atual_app % 'administrativo' - elif app in ['compilacao']: - return # TODO implementar teste para compilacao - else: - user_for_url_atual_app = user_for_url_atual_app % app - - for username in users: - print(username, user_for_url_atual_app, url) - - client.login(username=username, password='interlegis') - - rg = None - try: - rg = client.get(url, {}, follow=True) - except: - pass - - rp = None - try: - rp = client.post(url, {}, follow=True) - except: - pass - - """ - devido às urls serem incompletas ou com pks e outras valores - inexistentes na base, iniciar a execução da view, seja por get, - post ou qualquer outro método pode causar o erro... - por isso o "try ... except" acima. - No entanto, o objetivo do teste é validar o acesso de toda url. - Independente do erro que vá acontecer, esse erro não ocorrerá - se o user não tiver permissão de acesso pelo fato de que "AS - VIEWS BEM FORMADAS PARA VALIDAÇÃO DE ACESSO DEVEM SEMPRE REDIRECIONAR PARA - LOGIN ANTES DE SUA EXECUÇÃO", desta forma nunca gerando erro - interno dada qualquer incoerência de parâmetros nas urls - """ - if rg: - """ - Se o usuário a ser testado é o usuário da app da url de get - espera-se que não tenha recebido uma tela de login - """ - if username == user_for_url_atual_app and\ - not url.startswith('/sistema/'): - assert btn_login not in str(rg.content) - elif username != 'operador_geral' and\ - url.startswith('/sistema/'): - assert btn_login in str(rg.content) - elif username == 'operador_geral' and\ - url.startswith('/sistema/'): - assert btn_login not in str(rg.content) - - if rp: - """ - Se o usuário a ser testado é o usuário da app da url de - post espera-se que não tenha recebido uma tela de login - """ - if username == user_for_url_atual_app and\ - not url.startswith('/sistema/'): - assert btn_login not in str(rp.content) - elif username != 'operador_geral' and\ - url.startswith('/sistema/'): - assert btn_login in str(rp.content) - elif username == 'operador_geral' and\ - url.startswith('/sistema/'): - assert btn_login not in str(rp.content) - - logout = client.get('/logout/', follow=True) diff --git a/sapl/test_urls.py b/sapl/test_urls.py new file mode 100644 index 000000000..e958dd6c0 --- /dev/null +++ b/sapl/test_urls.py @@ -0,0 +1,365 @@ +from django.apps import apps +from django.contrib.auth import get_user_model +from django.contrib.auth.management import _get_all_permissions +from django.contrib.auth.models import Permission, User +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse +from django.db.models import CharField, TextField +from django.http.response import HttpResponseNotFound +from django.utils.translation import string_concat +from django.utils.translation import ugettext_lazy as _ +from model_mommy import mommy +import pytest + +from sapl.crud.base import PermissionRequiredForAppCrudMixin, CrudAux +from scripts.inicializa_grupos_autorizacoes import cria_grupos_permissoes +from scripts.lista_urls import lista_urls + +from .settings import SAPL_APPS + + +pytestmark = pytest.mark.django_db + +sapl_appconfs = [apps.get_app_config(n[5:]) for n in SAPL_APPS] +_lista_urls = lista_urls() + + +def create_perms_post_migrate(app): + + searched_perms = list() + # The codenames and ctypes that should exist. + ctypes = set() + + for klass in list(app.get_models()): + opts = klass._meta + permissions = ( + ("list_" + opts.model_name, + string_concat( + _('Visualizaçao da lista de'), ' ', + opts.verbose_name_plural)), + ("detail_" + opts.model_name, + string_concat( + _('Visualização dos detalhes de'), ' ', + opts.verbose_name_plural)), + ) + opts.permissions = tuple( + set(list(permissions) + list(opts.permissions))) + + if opts.proxy: + # Force looking up the content types in the current database + # before creating foreign keys to them. + app_label, model = opts.app_label, opts.model_name + + try: + ctype = ContentType.objects.get_by_natural_key( + app_label, model) + except: + ctype = ContentType.objects.create( + app_label=app_label, model=model) + else: + ctype = ContentType.objects.get_for_model(klass) + + ctypes.add(ctype) + for perm in _get_all_permissions(klass._meta, ctype): + searched_perms.append((ctype, perm)) + + all_perms = set(Permission.objects.filter( + content_type__in=ctypes, + ).values_list( + "content_type", "codename" + )) + + perms = [ + Permission(codename=codename, name=name, content_type=ct) + for ct, (codename, name) in searched_perms + if (ct.pk, codename) not in all_perms + ] + Permission.objects.bulk_create(perms) + + +def test_charfield_textfield(): + for app in sapl_appconfs: + for model in app.get_models(): + fields = model._meta.local_fields + for field in fields: + if isinstance(field, (CharField, TextField)): + assert not field.null, 'This %s is null: %s.%s' % ( + type(field).__name__, + model.__name__, + field.attname) + + +def test_str_sanity(): + # this simply a sanity check + # __str__ semantics is not considered and should be tested separetely + for app in sapl_appconfs: + for model in app.get_models(): + obj = mommy.prepare(model) + try: + str(obj) + except Exception as exc: + msg = '%s.%s.__str__ is broken.' % ( + model.__module__, model.__name__) + raise AssertionError(msg, exc) + +btn_login = ('') + + +@pytest.mark.parametrize('url_item', _lista_urls) +def test_crudaux_formato_inicio_urls_associadas(url_item): + + # Verifica se um crud é do tipo CrudAux, se sim, sua url deve começar + # com /sistema/ + key, url, var, app_name = url_item + url = '/' + (url % {v: 1 for v in var}) + + view_class = None + if hasattr(key, 'view_class'): + view_class = key.view_class + + # se não tem view_class, possivelmente é não é uma classed base view + if not view_class: + return + + # se não tem atributo crud, não é será nenhum tipo de crud + if not hasattr(view_class, 'crud'): + return + + # se o crud da view_class relativa a url a ser testada, + # implementa a classe CrudAux, seu link deve iniciar com /sistema + for string_class in list(map(str, type.mro(view_class.crud))): + + if 'CrudAux' in string_class: + assert url.startswith('/sistema'), """ + A url (%s) foi gerada a partir de um CrudAux, + o que diz que está é uma implementação de uma + tabela auxiliar, porém a url em questão, está fora + do padrão, que é iniciar com /sistema. + """ % (url) + + +@pytest.mark.parametrize('url_item', _lista_urls) +def test_crudaux_list_do_crud_esta_na_pagina_sistema(url_item, admin_client): + + # Verifica se um crud é do tipo CrudAux, se sim, sua url deve começar + # com /sistema/ + key, url, var, app_name = url_item + url = '/' + (url % {v: 1 for v in var}) + + view_class = None + if hasattr(key, 'view_class'): + view_class = key.view_class + + # se não tem view_class, possivelmente não é uma classed base view + if not view_class: + return + + # se não tem atributo crud, não é será nenhum tipo de crud + if not hasattr(view_class, 'crud'): + return + + herancas_crud = list(map(str, type.mro(view_class.crud))) + for string_class in herancas_crud: + if 'CrudAux' in string_class: + + herancas_view = list(map(str, type.mro(view_class))) + + for string_view_class in herancas_view: + # verifica se o link para manutenção do crud está em /sistema + if 'ListView' in string_view_class: + response = admin_client.get('/sistema', {}, follow=True) + assert url in str(response.content), """ + A url (%s) não consta nas Tabelas Auxiliares, + porem é uma implementação de ListView de CrudAux. + Se encontra em %s.urls + """ % (url, app_name) + + +@pytest.mark.parametrize('url_item', _lista_urls) +def test_urlpatterns(url_item, admin_client): + + key, url, var, app_name = url_item + url = '/' + (url % {v: 1 for v in var}) + app_name = app_name[5:] + + apps_url_patterns_prefixs = { + 'base': [ + '/sistema', + '/login', + '/logout' + ], + 'comissoes': [ + '/comissao', + '/sistema' + ], + 'compilacao': [ + '/ta', + ], + 'lexml': [ + '/lexml', + '/sistema' + ], + 'materia': [ + '/materia', + '/proposicao', + '/sistema' + ], + 'norma': [ + '/norma', + '/sistema' + ], + 'painel': [ + '/painel', + '/sistema' + ], + 'parlamentares': [ + '/parlamentar', + '/mesa-diretora', + '/sistema' + ], + 'protocoloadm': [ + '/protocoloadm', + '/sistema' + ], + 'relatorios': [ + '/relatorios', + ], + 'sessao': [ + '/sessao', + '/sistema', + ], + } + assert app_name in apps_url_patterns_prefixs, """ + A app (%s) da url (%s) não consta na lista de prefixos do teste + """ % (app_name, url) + + if app_name in apps_url_patterns_prefixs: + prefixs = apps_url_patterns_prefixs[app_name] + + isvalid = False + for prefix in prefixs: + if url.startswith(prefix): + isvalid = True + break + + assert isvalid, """ + A url (%s) não está no padrão de sua app (%s). + Os prefixos permitidos são: + %s + """ % (url, app_name, prefixs) + + +@pytest.mark.parametrize('urls_app', _lista_urls) +def em_construcao_crud_permissions_urls(urls_app, client): + if not get_user_model().objects.exists(): + for app in sapl_appconfs: + # readequa permissões dos models adicionando + # list e detail permissions + create_perms_post_migrate(app) + # cria usuários de perfil do sapl + cria_grupos_permissoes() + users = get_user_model().objects.values_list('username', flat=True) + + for url_item in _lista_urls[urls_app]: + + key, url, var, app_name = url_item + url = '/' + (url % {v: 1 for v in var}) + + app_labels = app_name.split('.')[1] + + view_class = None + if hasattr(key, 'view_class'): + view_class = key.view_class + + """ + A classe PermissionRequiredForAppCrudMixin pode ser usada em uma + app mas envolver permissoes para outras + como é o caso de PainelView que está na app 'sessao' + mas é um redirecionamento para 'painel'... aqui é feita + a troca a urls_app a ser testada, por essas outras possíveis + """ + if PermissionRequiredForAppCrudMixin in type.mro(view_class): + # essa classe deve informar app_label + assert hasattr(view_class, 'app_label') + # app_label deve ter conteudo + assert view_class.app_label + app_labels = view_class.app_label + + if isinstance(app_labels, str): + app_labels = app_labels, + + for app in app_labels: + + # monta o username correspondente de a app da url a ser testada + user_for_url_atual_app = 'operador_%s' + if app in ['base', 'parlamentares']: + user_for_url_atual_app = user_for_url_atual_app % 'geral' + elif app in 'protocoloadm': + user_for_url_atual_app = user_for_url_atual_app % 'administrativo' + elif app in ['compilacao']: + return # TODO implementar teste para compilacao + else: + user_for_url_atual_app = user_for_url_atual_app % app + + for username in users: + print(username, user_for_url_atual_app, url) + + client.login(username=username, password='interlegis') + + rg = None + try: + rg = client.get(url, {}, follow=True) + except: + pass + + rp = None + try: + rp = client.post(url, {}, follow=True) + except: + pass + + """ + devido às urls serem incompletas ou com pks e outras valores + inexistentes na base, iniciar a execução da view, seja por get, + post ou qualquer outro método pode causar o erro... + por isso o "try ... except" acima. + No entanto, o objetivo do teste é validar o acesso de toda url. + Independente do erro que vá acontecer, esse erro não ocorrerá + se o user não tiver permissão de acesso pelo fato de que "AS + VIEWS BEM FORMADAS PARA VALIDAÇÃO DE ACESSO DEVEM SEMPRE REDIRECIONAR PARA + LOGIN ANTES DE SUA EXECUÇÃO", desta forma nunca gerando erro + interno dada qualquer incoerência de parâmetros nas urls + """ + if rg: + """ + Se o usuário a ser testado é o usuário da app da url de get + espera-se que não tenha recebido uma tela de login + """ + if username == user_for_url_atual_app and\ + not url.startswith('/sistema/'): + assert btn_login not in str(rg.content) + elif username != 'operador_geral' and\ + url.startswith('/sistema/'): + assert btn_login in str(rg.content) + elif username == 'operador_geral' and\ + url.startswith('/sistema/'): + assert btn_login not in str(rg.content) + + if rp: + """ + Se o usuário a ser testado é o usuário da app da url de + post espera-se que não tenha recebido uma tela de login + """ + if username == user_for_url_atual_app and\ + not url.startswith('/sistema/'): + assert btn_login not in str(rp.content) + elif username != 'operador_geral' and\ + url.startswith('/sistema/'): + assert btn_login in str(rp.content) + elif username == 'operador_geral' and\ + url.startswith('/sistema/'): + assert btn_login not in str(rp.content) + + logout = client.get('/logout/', follow=True)