@ -1,14 +1,17 @@
import pytest
import pytest
from datetime import date
from datetime import timedelta
from django . contrib . auth import get_user_model
from django . contrib . auth import get_user_model
from django . contrib . auth . models import AnonymousUser
from django . contrib . auth . models import AnonymousUser
from django . db import IntegrityError , transaction
from django . urls import reverse
from django . urls import reverse
from django . utils import timezone
from django . utils . translation import ugettext_lazy as _
from django . utils . translation import ugettext_lazy as _
from model_bakery import baker
from model_bakery import baker
from sapl . parlamentares . models import Legislatura , SessaoLegislativa
from sapl . parlamentares . models import Legislatura , SessaoLegislativa
from sapl . sessao . models import ( SessaoPlenaria , TipoSessaoPlenaria ,
from sapl . sessao . models import ( SessaoPlenaria , TipoSessaoPlenaria ,
IntegranteMesa , SessaoPlenariaPresenca ,
IntegranteMesa , SessaoPlenariaPresenca ,
PresencaOrdemDia ,
JustificativaAusencia , ExpedienteSessao ,
JustificativaAusencia , ExpedienteSessao ,
TipoExpediente , ExpedienteMateria ,
TipoExpediente , ExpedienteMateria ,
Orador , OcorrenciaSessao ,
Orador , OcorrenciaSessao ,
@ -153,13 +156,22 @@ class TestResumoView():
assert resultado_get_ocorrencia [ ' ocorrencias_da_sessao ' ] [ 0 ] == ocorrencia
assert resultado_get_ocorrencia [ ' ocorrencias_da_sessao ' ] [ 0 ] == ocorrencia
# Sessão cadastrada com antecedência: data futura, sem pauta e não iniciada.
def data_futura ( ) :
return timezone . localdate ( ) + timedelta ( days = 30 )
@pytest . mark . django_db ( transaction = False )
@pytest . mark . django_db ( transaction = False )
def test_visiveis_para_oculta_do_anonimo_apenas_a_sessao_previa ( ) :
def test_visiveis_para_oculta_do_anonimo_apenas_a_sessao_previa ( ) :
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False )
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
com_pauta = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = True )
data_inicio = data_futura ( ) )
iniciada = baker . make ( SessaoPlenaria , iniciada = True , publicar_pauta = False )
com_pauta = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = True ,
data_inicio = data_futura ( ) )
iniciada = baker . make ( SessaoPlenaria , iniciada = True , publicar_pauta = False ,
data_inicio = data_futura ( ) )
# Sessões anteriores à migração 0027 ficaram com `iniciada` em NULL.
# Sessões anteriores à migração 0027 ficaram com `iniciada` em NULL.
legada = baker . make ( SessaoPlenaria , iniciada = None , publicar_pauta = False )
legada = baker . make ( SessaoPlenaria , iniciada = None , publicar_pauta = False ,
data_inicio = data_futura ( ) )
visiveis = restringe_sessoes_visiveis (
visiveis = restringe_sessoes_visiveis (
SessaoPlenaria . objects . all ( ) , AnonymousUser ( ) )
SessaoPlenaria . objects . all ( ) , AnonymousUser ( ) )
@ -170,9 +182,25 @@ def test_visiveis_para_oculta_do_anonimo_apenas_a_sessao_previa():
assert legada in visiveis
assert legada in visiveis
@pytest . mark . django_db ( transaction = False )
def test_visiveis_para_mostra_ao_anonimo_sessao_realizada_nao_marcada ( ) :
# Casas que não marcam "Sessão iniciada?" nas sessões já realizadas.
passada = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
data_inicio = timezone . localdate ( ) - timedelta ( days = 1 ) )
de_hoje = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
data_inicio = timezone . localdate ( ) )
visiveis = restringe_sessoes_visiveis (
SessaoPlenaria . objects . all ( ) , AnonymousUser ( ) )
assert passada in visiveis
assert de_hoje in visiveis
@pytest . mark . django_db ( transaction = False )
@pytest . mark . django_db ( transaction = False )
def test_visiveis_para_nao_oculta_nada_de_usuario_autenticado ( ) :
def test_visiveis_para_nao_oculta_nada_de_usuario_autenticado ( ) :
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False )
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
data_inicio = data_futura ( ) )
operador = baker . make ( get_user_model ( ) )
operador = baker . make ( get_user_model ( ) )
@ -183,12 +211,12 @@ def test_visiveis_para_nao_oculta_nada_de_usuario_autenticado():
@pytest . mark . django_db ( transaction = False )
@pytest . mark . django_db ( transaction = False )
def test_pesquisar_sessao_nao_lista_sessao_previa_para_anonimo ( client ) :
def test_pesquisar_sessao_nao_lista_sessao_previa_para_anonimo ( client ) :
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
data_inicio = date ( 2025 , 11 , 5 ) )
data_inicio = data_futura ( ) )
iniciada = baker . make ( SessaoPlenaria , iniciada = True , publicar_pauta = False ,
iniciada = baker . make ( SessaoPlenaria , iniciada = True , publicar_pauta = False ,
data_inicio = date ( 2025 , 11 , 5 ) )
data_inicio = data_futura ( ) )
response = client . get ( reverse ( ' sapl.sessao:pesquisar_sessao ' ) ,
response = client . get ( reverse ( ' sapl.sessao:pesquisar_sessao ' ) ,
{ ' data_inicio__year ' : ' 2025 ' } )
{ ' data_inicio__year ' : data_futura ( ) . year } )
assert response . status_code == 200
assert response . status_code == 200
assert previa not in response . context [ ' object_list ' ]
assert previa not in response . context [ ' object_list ' ]
@ -198,10 +226,10 @@ def test_pesquisar_sessao_nao_lista_sessao_previa_para_anonimo(client):
@pytest . mark . django_db ( transaction = False )
@pytest . mark . django_db ( transaction = False )
def test_pesquisar_sessao_lista_sessao_previa_para_autenticado ( admin_client ) :
def test_pesquisar_sessao_lista_sessao_previa_para_autenticado ( admin_client ) :
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
data_inicio = date ( 2025 , 11 , 5 ) )
data_inicio = data_futura ( ) )
response = admin_client . get ( reverse ( ' sapl.sessao:pesquisar_sessao ' ) ,
response = admin_client . get ( reverse ( ' sapl.sessao:pesquisar_sessao ' ) ,
{ ' data_inicio__year ' : ' 2025 ' } )
{ ' data_inicio__year ' : data_futura ( ) . year } )
assert response . status_code == 200
assert response . status_code == 200
assert previa in response . context [ ' object_list ' ]
assert previa in response . context [ ' object_list ' ]
@ -209,7 +237,8 @@ def test_pesquisar_sessao_lista_sessao_previa_para_autenticado(admin_client):
@pytest . mark . django_db ( transaction = False )
@pytest . mark . django_db ( transaction = False )
def test_detail_sessao_previa_indisponivel_para_anonimo ( client ) :
def test_detail_sessao_previa_indisponivel_para_anonimo ( client ) :
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False )
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
data_inicio = data_futura ( ) )
response = client . get ( reverse ( ' sapl.sessao:sessaoplenaria_detail ' ,
response = client . get ( reverse ( ' sapl.sessao:sessaoplenaria_detail ' ,
kwargs = { ' pk ' : previa . pk } ) )
kwargs = { ' pk ' : previa . pk } ) )
@ -219,7 +248,8 @@ def test_detail_sessao_previa_indisponivel_para_anonimo(client):
@pytest . mark . django_db ( transaction = False )
@pytest . mark . django_db ( transaction = False )
def test_resumo_de_sessao_previa_indisponivel_para_anonimo ( client ) :
def test_resumo_de_sessao_previa_indisponivel_para_anonimo ( client ) :
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False )
previa = baker . make ( SessaoPlenaria , iniciada = False , publicar_pauta = False ,
data_inicio = data_futura ( ) )
response = client . get ( reverse ( ' sapl.sessao:resumo ' ,
response = client . get ( reverse ( ' sapl.sessao:resumo ' ,
kwargs = { ' pk ' : previa . pk } ) )
kwargs = { ' pk ' : previa . pk } ) )
@ -235,3 +265,103 @@ def test_pauta_nao_publicada_indisponivel_para_anonimo(client):
kwargs = { ' pk ' : sem_pauta . pk } ) )
kwargs = { ' pk ' : sem_pauta . pk } ) )
assert response . status_code == 404
assert response . status_code == 404
def cria_sessao_com_parlamentares ( ) :
legislatura = baker . make ( Legislatura )
sessao_legislativa = baker . make ( SessaoLegislativa )
tipo = baker . make ( TipoSessaoPlenaria )
sessao = baker . make ( SessaoPlenaria ,
legislatura = legislatura ,
sessao_legislativa = sessao_legislativa ,
tipo = tipo )
parlamentares = [ baker . make ( Parlamentar , ativo = True ) for _i in range ( 3 ) ]
return sessao , parlamentares
@pytest . mark . django_db ( transaction = False )
def test_presenca_sessao_preserva_registros_ao_salvar_novamente ( admin_client ) :
# Cada salvamento apagava todas as presenças da sessão e as recriava,
# trocando os ids. Além do desperdício, é essa janela entre apagar e
# recriar que permite a duplicação quando o formulário é submetido duas
# vezes em paralelo. Salvar de novo deve ser inócuo.
sessao , parlamentares = cria_sessao_com_parlamentares ( )
url = reverse ( ' sapl.sessao:presenca ' , kwargs = { ' pk ' : sessao . pk } )
dados = { ' presenca_ativos ' : [ str ( p . pk ) for p in parlamentares ] }
admin_client . post ( url , dados , follow = True )
presencas = SessaoPlenariaPresenca . objects . filter ( sessao_plenaria = sessao )
ids_iniciais = sorted ( presencas . values_list ( ' id ' , flat = True ) )
admin_client . post ( url , dados , follow = True )
admin_client . post ( url , dados , follow = True )
assert presencas . count ( ) == len ( parlamentares )
assert sorted ( presencas . values_list ( ' parlamentar_id ' , flat = True ) ) == \
sorted ( p . pk for p in parlamentares )
assert sorted ( presencas . values_list ( ' id ' , flat = True ) ) == ids_iniciais
@pytest . mark . django_db ( transaction = False )
def test_presenca_sessao_ignora_registro_ja_existente ( admin_client ) :
# Reproduz o efeito da submissão concorrente: a linha já existe quando a
# view vai gravar. Deve terminar com uma única presença, sem erro.
sessao , parlamentares = cria_sessao_com_parlamentares ( )
parlamentar = parlamentares [ 0 ]
SessaoPlenariaPresenca . objects . create ( sessao_plenaria = sessao ,
parlamentar = parlamentar )
url = reverse ( ' sapl.sessao:presenca ' , kwargs = { ' pk ' : sessao . pk } )
response = admin_client . post (
url , { ' presenca_ativos ' : [ str ( parlamentar . pk ) ] } , follow = True )
assert response . status_code == 200
assert SessaoPlenariaPresenca . objects . filter (
sessao_plenaria = sessao , parlamentar = parlamentar ) . count ( ) == 1
@pytest . mark . django_db ( transaction = False )
def test_presenca_sessao_remove_desmarcados ( admin_client ) :
sessao , parlamentares = cria_sessao_com_parlamentares ( )
url = reverse ( ' sapl.sessao:presenca ' , kwargs = { ' pk ' : sessao . pk } )
admin_client . post ( url , { ' presenca_ativos ' : [ str ( p . pk )
for p in parlamentares ] } ,
follow = True )
admin_client . post ( url , { ' presenca_ativos ' : [ str ( parlamentares [ 0 ] . pk ) ] } ,
follow = True )
presencas = SessaoPlenariaPresenca . objects . filter ( sessao_plenaria = sessao )
assert list ( presencas . values_list ( ' parlamentar_id ' , flat = True ) ) == \
[ parlamentares [ 0 ] . pk ]
@pytest . mark . django_db ( transaction = False )
def test_presenca_ordem_dia_preserva_registros_ao_resalvar ( admin_client ) :
sessao , parlamentares = cria_sessao_com_parlamentares ( )
url = reverse ( ' sapl.sessao:presencaordemdia ' , kwargs = { ' pk ' : sessao . pk } )
dados = { ' presenca_ativos ' : [ str ( p . pk ) for p in parlamentares ] }
admin_client . post ( url , dados , follow = True )
presencas = PresencaOrdemDia . objects . filter ( sessao_plenaria = sessao )
ids_iniciais = sorted ( presencas . values_list ( ' id ' , flat = True ) )
admin_client . post ( url , dados , follow = True )
assert presencas . count ( ) == len ( parlamentares )
assert sorted ( presencas . values_list ( ' id ' , flat = True ) ) == ids_iniciais
@pytest . mark . django_db ( transaction = False )
def test_presenca_unica_por_sessao_e_parlamentar ( ) :
# A restrição de unicidade é a proteção contra submissões concorrentes,
# que a lógica da view sozinha não cobre.
sessao , parlamentares = cria_sessao_com_parlamentares ( )
parlamentar = parlamentares [ 0 ]
for model in ( SessaoPlenariaPresenca , PresencaOrdemDia ) :
model . objects . create ( sessao_plenaria = sessao , parlamentar = parlamentar )
with pytest . raises ( IntegrityError ) :
with transaction . atomic ( ) :
model . objects . create ( sessao_plenaria = sessao ,
parlamentar = parlamentar )