import pytest
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from openslides.core.config import config
from openslides.motions.models import (
Category,
Motion,
MotionBlock,
MotionChangeRecommendation,
MotionComment,
MotionCommentSection,
State,
StatuteParagraph,
Workflow,
)
from openslides.utils.auth import get_group_model
from openslides.utils.autoupdate import inform_changed_data
from tests.common_groups import GROUP_ADMIN_PK, GROUP_DELEGATE_PK, GROUP_STAFF_PK
from tests.count_queries import count_queries
from tests.test_case import TestCase
@pytest.mark.django_db(transaction=False)
def test_category_db_queries():
"""
Tests that only the following db queries are done:
* 1 requests to get the list of all categories.
"""
for index in range(10):
Category.objects.create(name=f"category{index}")
assert count_queries(Category.get_elements)() == 1
@pytest.mark.django_db(transaction=False)
def test_statute_paragraph_db_queries():
"""
Tests that only the following db queries are done:
* 1 requests to get the list of all statute paragraphs.
"""
for index in range(10):
StatuteParagraph.objects.create(
title=f"statute_paragraph{index}", text=f"text{index}"
)
assert count_queries(StatuteParagraph.get_elements)() == 1
@pytest.mark.django_db(transaction=False)
def test_workflow_db_queries():
"""
Tests that only the following db queries are done:
* 1 request to get the list of all workflows and
* 1 request to get all states.
"""
assert count_queries(Workflow.get_elements)() == 2
@pytest.mark.django_db(transaction=False)
def test_motion_block_db_queries():
"""
Tests that only the following db queries are done:
* 1 request to get all motion blocks
* 1 request to get all agenda items
* 1 request to get all lists of speakers
* 1 request to get all motions
"""
for i in range(5):
motion_block = MotionBlock.objects.create(title=f"block{i}")
for j in range(3):
Motion.objects.create(
title=f"motion{i}_{j}", text="text", motion_block=motion_block
)
assert count_queries(MotionBlock.get_elements)() == 4
class TestStatuteParagraphs(TestCase):
"""
Tests all CRUD operations of statute paragraphs.
"""
def setUp(self):
self.client = APIClient()
self.client.login(username="admin", password="admin")
def create_statute_paragraph(self):
self.title = "test_title_fiWs82D0D)2kje3KDm2s"
self.text = "test_text_3jfjoDqm,S;cmor3DJwk"
self.cp = StatuteParagraph.objects.create(title=self.title, text=self.text)
def test_create_simple(self):
response = self.client.post(
reverse("statuteparagraph-list"),
{
"title": "test_title_f3FM328cq)tzdU238df2",
"text": "test_text_2fb)BEjwdI38=kfemiRkcOW",
},
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
cp = StatuteParagraph.objects.get()
self.assertEqual(cp.title, "test_title_f3FM328cq)tzdU238df2")
self.assertEqual(cp.text, "test_text_2fb)BEjwdI38=kfemiRkcOW")
def test_create_without_data(self):
response = self.client.post(reverse("statuteparagraph-list"), {})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data,
{"title": ["This field is required."], "text": ["This field is required."]},
)
def test_create_non_admin(self):
self.admin = get_user_model().objects.get(username="admin")
self.admin.groups.add(GROUP_DELEGATE_PK)
self.admin.groups.remove(GROUP_ADMIN_PK)
inform_changed_data(self.admin)
response = self.client.post(
reverse("statuteparagraph-list"),
{
"title": "test_title_f3(Dj2jdP39fjW2kdcwe",
"text": "test_text_vlC)=fwWmcwcpWMvnuw(",
},
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_retrieve_simple(self):
self.create_statute_paragraph()
response = self.client.get(
reverse("statuteparagraph-detail", args=[self.cp.pk])
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(
sorted(response.data.keys()), sorted(("id", "title", "text", "weight"))
)
def test_update_simple(self):
self.create_statute_paragraph()
response = self.client.patch(
reverse("statuteparagraph-detail", args=[self.cp.pk]),
{"text": "test_text_ke(czr/cwk1Sl2seeFwE"},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
cp = StatuteParagraph.objects.get()
self.assertEqual(cp.title, self.title)
self.assertEqual(cp.text, "test_text_ke(czr/cwk1Sl2seeFwE")
def test_update_non_admin(self):
self.admin = get_user_model().objects.get(username="admin")
self.admin.groups.add(GROUP_DELEGATE_PK)
self.admin.groups.remove(GROUP_ADMIN_PK)
inform_changed_data(self.admin)
self.create_statute_paragraph()
response = self.client.patch(
reverse("statuteparagraph-detail", args=[self.cp.pk]),
{"text": "test_text_ke(czr/cwk1Sl2seeFwE"},
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
cp = StatuteParagraph.objects.get()
self.assertEqual(cp.text, self.text)
def test_delete_simple(self):
self.create_statute_paragraph()
response = self.client.delete(
reverse("statuteparagraph-detail", args=[self.cp.pk])
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(StatuteParagraph.objects.count(), 0)
def test_delete_non_admin(self):
self.admin = get_user_model().objects.get(username="admin")
self.admin.groups.add(GROUP_DELEGATE_PK)
self.admin.groups.remove(GROUP_ADMIN_PK)
inform_changed_data(self.admin)
self.create_statute_paragraph()
response = self.client.delete(
reverse("statuteparagraph-detail", args=[self.cp.pk])
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(StatuteParagraph.objects.count(), 1)
class ManageComments(TestCase):
"""
Tests the manage_comment view.
Tests creation/updating and deletion of motion comments.
"""
def setUp(self):
self.client = APIClient()
self.client.login(username="admin", password="admin")
self.admin = get_user_model().objects.get()
self.group_out = get_group_model().objects.get(
pk=GROUP_DELEGATE_PK
) # The admin should not be in this group
# Put the admin into the staff group, becaust in the admin group, he has all permissions for
# every single comment section.
self.admin.groups.add(GROUP_STAFF_PK)
self.admin.groups.remove(GROUP_ADMIN_PK)
inform_changed_data(self.admin)
self.group_in = get_group_model().objects.get(pk=GROUP_STAFF_PK)
self.motion = Motion(
title="test_title_SlqfMw(waso0saWMPqcZ",
text="test_text_f30skclqS9wWF=xdfaSL",
)
self.motion.save()
self.section_no_groups = MotionCommentSection(
name='test_name_gj4F§(fj"(edm"§F3f3fs'
)
self.section_no_groups.save()
self.section_read = MotionCommentSection(name="test_name_2wv30(d2S&kvelkakl39")
self.section_read.save()
self.section_read.read_groups.add(
self.group_in, self.group_out
) # Group out for testing multiple groups
self.section_read.write_groups.add(self.group_out)
self.section_read_write = MotionCommentSection(
name="test_name_a3m9sd0(Mw2%slkrv30,"
)
self.section_read_write.save()
self.section_read_write.read_groups.add(self.group_in)
self.section_read_write.write_groups.add(self.group_in)
def test_retrieve_comment(self):
comment = MotionComment(
motion=self.motion,
section=self.section_read_write,
comment="test_comment_gwic37Csc&3lf3eo2",
)
comment.save()
response = self.client.get(reverse("motion-detail", args=[self.motion.pk]))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue("comments" in response.data)
comments = response.data["comments"]
self.assertTrue(isinstance(comments, list))
self.assertEqual(len(comments), 1)
self.assertEqual(comments[0]["comment"], "test_comment_gwic37Csc&3lf3eo2")
def test_retrieve_comment_no_read_permission(self):
comment = MotionComment(
motion=self.motion,
section=self.section_no_groups,
comment="test_comment_fgkj3C7veo3ijWE(j2DJ",
)
comment.save()
response = self.client.get(reverse("motion-detail", args=[self.motion.pk]))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue("comments" in response.data)
comments = response.data["comments"]
self.assertTrue(isinstance(comments, list))
self.assertEqual(len(comments), 0)
def test_wrong_data_type(self):
response = self.client.post(
reverse("motion-manage-comments", args=[self.motion.pk]), None
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data["detail"], "You have to provide a section_id of type int."
)
def test_wrong_comment_data_type(self):
response = self.client.post(
reverse("motion-manage-comments", args=[self.motion.pk]),
{
"section_id": self.section_read_write.id,
"comment": [32, "no_correct_data"],
},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data["detail"], "The comment should be a string.")
def test_non_existing_section(self):
response = self.client.post(
reverse("motion-manage-comments", args=[self.motion.pk]), {"section_id": 42}
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data["detail"], "A comment section with id {0} does not exist."
)
self.assertEqual(response.data["args"][0], "42")
def test_create_comment(self):
response = self.client.post(
reverse("motion-manage-comments", args=[self.motion.pk]),
{
"section_id": self.section_read_write.pk,
"comment": "test_comment_fk3jrnfwsdg%fj=feijf",
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(MotionComment.objects.count(), 1)
comment = MotionComment.objects.get()
self.assertEqual(comment.comment, "test_comment_fk3jrnfwsdg%fj=feijf")
def test_update_comment(self):
comment = MotionComment(
motion=self.motion,
section=self.section_read_write,
comment="test_comment_fji387fqwdf&ff=)Fe3j",
)
comment.save()
response = self.client.post(
reverse("motion-manage-comments", args=[self.motion.pk]),
{
"section_id": self.section_read_write.pk,
"comment": "test_comment_fk3jrnfwsdg%fj=feijf",
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
comment = MotionComment.objects.get()
self.assertEqual(comment.comment, "test_comment_fk3jrnfwsdg%fj=feijf")
def test_delete_comment(self):
comment = MotionComment(
motion=self.motion,
section=self.section_read_write,
comment='test_comment_5CJ"8f23jd3j2,r93keZ',
)
comment.save()
response = self.client.delete(
reverse("motion-manage-comments", args=[self.motion.pk]),
{"section_id": self.section_read_write.pk},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(MotionComment.objects.count(), 0)
def test_delete_not_existing_comment(self):
"""
This should fail silently; no error, if the user wants to delete
a not existing comment.
"""
response = self.client.delete(
reverse("motion-manage-comments", args=[self.motion.pk]),
{"section_id": self.section_read_write.pk},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(MotionComment.objects.count(), 0)
def test_create_comment_no_write_permission(self):
response = self.client.post(
reverse("motion-manage-comments", args=[self.motion.pk]),
{
"section_id": self.section_read.pk,
"comment": "test_comment_f38jfwqfj830fj4j(FU3",
},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(MotionComment.objects.count(), 0)
self.assertEqual(
response.data["detail"],
"You are not allowed to see or write to the comment section.",
)
def test_update_comment_no_write_permission(self):
comment = MotionComment(
motion=self.motion,
section=self.section_read,
comment="test_comment_jg38dwiej2D832(D§dk)",
)
comment.save()
response = self.client.post(
reverse("motion-manage-comments", args=[self.motion.pk]),
{
"section_id": self.section_read.pk,
"comment": "test_comment_fk3jrnfwsdg%fj=feijf",
},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
comment = MotionComment.objects.get()
self.assertEqual(comment.comment, "test_comment_jg38dwiej2D832(D§dk)")
def test_delete_comment_no_write_permission(self):
comment = MotionComment(
motion=self.motion,
section=self.section_read,
comment="test_comment_fej(NF§kfePOF383o8DN",
)
comment.save()
response = self.client.delete(
reverse("motion-manage-comments", args=[self.motion.pk]),
{"section_id": self.section_read.pk},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(MotionComment.objects.count(), 1)
comment = MotionComment.objects.get()
self.assertEqual(comment.comment, "test_comment_fej(NF§kfePOF383o8DN")
class TestMotionCommentSection(TestCase):
"""
Tests creating, updating and deletion of comment sections.
"""
def setUp(self):
self.client = APIClient()
self.client.login(username="admin", password="admin")
self.admin = get_user_model().objects.get()
self.admin.groups.add(
GROUP_STAFF_PK
) # Put the admin in a groiup with limited permissions for testing.
self.admin.groups.remove(GROUP_ADMIN_PK)
inform_changed_data(self.admin)
self.group_in = get_group_model().objects.get(pk=GROUP_STAFF_PK)
self.group_out = get_group_model().objects.get(
pk=GROUP_DELEGATE_PK
) # The admin should not be in this group
def test_retrieve(self):
"""
Checks, if the sections can be seen by a manager.
"""
section = MotionCommentSection(name="test_name_f3jOF3m8fp.
New test
", "type": "0", }, ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response = self.client.post( reverse("motionchangerecommendation-list"), { "line_from": "3", "line_to": "6", "motion_id": "1", "text": "New test
", "type": "0", }, ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.data["args"][0], "3") self.assertEqual(response.data["args"][1], "6") def test_no_collission_different_motions(self): """ Two change recommendations with overlapping lines, but affecting different motions, should not interfere """ self.client.post( reverse("motion-list"), { "title": "test_title_OoCoo3MeiT9li5Iengu9", "text": "test_text_thuoz0iecheiheereiCi", }, ) response = self.client.post( reverse("motionchangerecommendation-list"), { "line_from": "5", "line_to": "7", "motion_id": "1", "text": "New test
", "type": "0", }, ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response = self.client.post( reverse("motionchangerecommendation-list"), { "line_from": "3", "line_to": "6", "motion_id": "2", "text": "New test
", "type": "0", }, ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) class NumberMotionsInCategories(TestCase): """ Tests numbering motions in categories. Default test environment: - *without* blanks - 1 min digit Testdata. All names (and prefixes) are prefixed with "test_". The ordering is ensured with "category_weight". Category tree (with motions M and amendments A): A-A