def create(self, validated_data):
        # TODO 이슈 생성 시에 태그 같이 생성
        tags_data = validated_data.pop('tags')

        # TODO 로그인된 사용자 세션 입력으로 바꾸기
        validated_data['user_id'] = 1

        issue = Issue(**validated_data)
        for tag_data in tags_data:
            tag = IssueTag.objects.create(**tag_data)
            issue.tags.add(tag)
        issue.save()
        return issue
Beispiel #2
0
    def test_comment(self, slackmock):
        issue = Issue()
        issue.title = self.title_name
        issue.project = self.project
        issue.save()

        self.selenium.get('{}{}'.format(
            self.live_server_url,
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': issue.number
                    })))
        f = self.selenium.find_element_by_id("id_text")
        f.send_keys(self.comment)
        self.selenium.find_element_by_name("action").click()
        slackmock().api_call.assert_called_with(
            "chat.postMessage",
            channel="channel",
            attachments=[{
                'fallback':
                str(self.user) + ' commented on \"' + self.short + '-1 ' +
                self.title_name + '\".',
                'pretext':
                'New comment:',
                'text':
                self.comment,
                'title':
                self.short + "-1 " + self.title_name,
                'title_link':
                "http://localhost:8000/project/" + self.short + "/issue/1/",
                'author_name':
                str(self.user),
                'author_link':
                "http://localhost:8000" + self.user.get_absolute_url(),
                'author_icon':
                "http://localhost:8000" + self.user.avatar.url,
                'color':
                'good',
            }])
Beispiel #3
0
    def test_managerfunctions(self):
        issue1 = Issue(title="issue1",
                       project=self.project,
                       kanbancol=self.column)
        issue1.save()
        sprint_current = Sprint(project=self.project,
                                startdate=(timezone.now() - timedelta(days=1)))
        sprint_current.save()
        sprint_old = Sprint(project=self.project,
                            startdate=(timezone.now() - timedelta(days=14)),
                            enddate=(timezone.now() - timedelta(days=1)))
        sprint_old.save()

        issues_wo_sprint = Issue.objects.without_sprint()
        self.assertTrue(issue1 in issues_wo_sprint)

        issue1.sprint = sprint_current
        issue1.save()
        issues_cur_sprint = Issue.objects.current_sprint()
        self.assertTrue(issue1 in issues_cur_sprint)
        issue1.sprint = sprint_old
        issue1.save()
        issues_cur_sprint = Issue.objects.current_sprint()
        issues_wo_sprint = Issue.objects.without_sprint()
        self.assertFalse(issue1 in issues_cur_sprint)
        self.assertFalse(issue1 in issues_wo_sprint)
Beispiel #4
0
    def test_sprints_and_issues_in_archive_view(self):
        newsprint = Sprint(project=self.project)
        newsprint.save()
        startedsprint = Sprint(project=self.project,
                               startdate=datetime.datetime.now())
        startedsprint.save()
        stoppedsprint = Sprint(project=self.project,
                               startdate=datetime.datetime.now(),
                               enddate=datetime.datetime.now())
        stoppedsprint.save()
        wrongsprint = Sprint(project=self.project,
                             enddate=datetime.datetime.now())
        wrongsprint.save()
        issueinnew = Issue(title="boo", project=self.project,
                           sprint=newsprint, archived=True)
        issueinnew.save()
        issueinstarted = Issue(title="coo", project=self.project,
                               sprint=startedsprint, archived=True)
        issueinstarted.save()
        issueinstopped = Issue(title="doo", project=self.project,
                               sprint=stoppedsprint, archived=True)
        issueinstopped.save()
        issueinwrong = Issue(title="foo", project=self.project,
                             sprint=wrongsprint, archived=True)
        issueinwrong.save()
        issueinno = Issue(title="goo", project=self.project,
                          archived=True)
        issueinno.save()

        response = self.client.get(reverse('archive:archive',
                                   kwargs={'project': self.project.name_short}))
        self.assertNotIn(issueinnew, response.context['archived_issues_without_sprint'])
        self.assertNotIn(issueinwrong, response.context['archived_issues_without_sprint'])
        self.assertNotIn(issueinstopped, response.context['archived_issues_without_sprint'])
        self.assertNotIn(issueinstarted, response.context['archived_issues_without_sprint'])
        self.assertIn(issueinno, response.context['archived_issues_without_sprint'])
        self.assertEqual(response.context['sprints_sorted'].all()[0], wrongsprint)
        self.assertEqual(response.context['sprints_sorted'].all()[1], stoppedsprint)
        self.assertEqual(response.context['sprints_sorted'].all()[2], startedsprint)
        self.assertEqual(response.context['sprints_sorted'].all()[3], newsprint)
Beispiel #5
0
    def test_board_drag_n_drop(self):
        def ajax_complete(driver):
            try:
                return 0 == driver.execute_script("return jQuery.active")
            except WebDriverException:
                pass

        # create project and issues
        driver = self.selenium
        project = Project(name='TestProjekt', name_short='TP', creator=self.user)
        project.save()
        project.developer.add(self.user)
        issue1 = Issue(project=project, title="TestIssue1")
        issue2 = Issue(project=project, title="TestIssue2")
        issue1.save()
        issue2.save()

        driver.get("{}{}".format(self.live_server_url, reverse('sprint:sprintboard',
                                                               kwargs={'project': project.name_short}
                                                               )))
        self.assertEqual(issue1.kanbancol.position, 0)
        self.assertEqual(issue2.kanbancol.position, 0)
        driver.find_element_by_link_text("Board").click()

        # move issue1 and issue2
        source_element = driver.find_element_by_id('board_issue_1')
        dest_element = driver.find_element_by_id('sortable1')
        ActionChains(driver).drag_and_drop(source_element, dest_element).perform()

        source_element = driver.find_element_by_id('board_issue_2')
        dest_element = driver.find_element_by_id('sortable2')
        ActionChains(driver).drag_and_drop(source_element, dest_element).perform()

        WebDriverWait(driver, 10).until(ajax_complete,
                                        "Timeout waiting for page to load"
                                        )
        # assert kanbancol has changed
        issue1.refresh_from_db()
        issue2.refresh_from_db()
Beispiel #6
0
class IndexTest(TestCase):
    """
    smoke test the basic pages
    """ 
    def setUp(self):
        super(IndexTest , self).setUp()
        u = User.objects.create_user("test","*****@*****.**","test")
        self.i = Issue( title="test" , body="test", user=u )
        self.i.save()

    def tearDown(self):
        super( IndexTest , self ).tearDown()


    def test_smoke_test(self):
        """ smoke test loading the basic pages should just work"""
        urls = [ ]
        urls.append('/')
        urls.append(reverse('api_doc'))
        for url in urls:
            response = self.client.get(url)
            self.assertEqual(response.status_code , 200)

    def test_dashboard_test(self):
        """ smoke test views """
        self.assertEquals(
            self.client.login(username='******', password='******'), True) 

        urls = [ ]
        urls.append('/')
        urls.append(reverse('my_issues', args=["new"]))
        for url in urls:
            response = self.client.get(url)
            self.assertEqual(response.status_code , 200)

    def test_anonymous_votes(self):
        url = reverse("vote", args=[self.i.pk] )
        response = self.client.post(url, direction=1)
        self.assertEqual(response.status_code, 302)
Beispiel #7
0
    def test_number_kanbancolumns_for_case_not_default(self):
        driver = self.selenium
        issue = Issue(title="title",
                      kanbancol=KanbanColumn.objects.get(project=self.project,
                                                         name="Todo"),
                      due_date=str(datetime.date.today()),
                      priority=3,
                      storypoints=2,
                      description="blubber",
                      project=self.project)
        issue.save()
        issue.assignee.add(self.user)

        driver.get("{}{}".format(
            self.live_server_url,
            reverse('issue:create',
                    kwargs={'project': self.project2.name_short})))
        driver.find_element_by_id("id_title").send_keys("title")
        # assert that 2nd project has one kanban col more
        self.assertEqual(
            len(Select(driver.find_element_by_id("id_kanbancol")).options), 5)

        # assert that dependsOn now has one entry
        driver.get('{}{}'.format(
            self.live_server_url,
            reverse('backlog:backlog',
                    kwargs={'project': self.project.name_short})))
        driver.find_element_by_link_text("New issue").click()
        driver.find_element_by_xpath("(//input[@type='search'])[2]").send_keys(
            '\n')
        time.sleep(1)
        self.assertEqual(
            len(
                driver.find_elements_by_css_selector(
                    '#select2-id_dependsOn-results li')), 1)
        for i in driver.find_elements_by_css_selector(
                '#select2-id_dependsOn-results li'):
            self.assertIn("title", i.text)
Beispiel #8
0
def propose(user, title, body, direction, source_url,
            source_type=u"website", is_draft=False, issue_no=None):
    """ 
    Propose an issue into the game
    """
    if not issue_no:
        userprofile = user.get_profile()
        if not role_to_actions[userprofile.role].has_key('propose'): return 

        new_issue = Issue(
            title = title,
            url = source_url,
            source_type = source_type,
            body = body,
            user = user,
            is_draft = is_draft,
        )
        new_issue.save()

        user.message_set.create(message="You created issue \"%s\" successfully " % new_issue.title  )
        score.propose(user)
        levels.upgrade[userprofile.role](userprofile)
    else:
        # we are editing one TODO remove this possibility?
        issue = get_object_or_404(Issue, pk=issue_no)

        # Now change the issue and issue body to the appropriate values.
        issue.title = title
        issue.is_draft = is_draft
        issue.body = body
        issue.source_type = source_type
        issue.source_url = source_url
        issue.save()
        new_issue = issue

    Vote.objects.record_vote(user , new_issue, direction,)    

    return new_issue
Beispiel #9
0
    def test_create_and_download_attachment(self):
        issue = Issue(title="Test-Issue",
                      project=self.project,
                      kanbancol=self.column,
                      type="Bug")
        issue.save()

        # create file for uploading
        filecontent = 'Hello World'
        temp = tempfile.NamedTemporaryFile(delete=False)
        temp.write(filecontent.encode())
        temp.close()

        f = File(open(temp.name, 'r'))
        attachment = Attachment(file=f, creator=self.user, issue=issue)
        attachment.save()
        f.close()
        os.unlink(temp.name)

        issue = Issue.objects.get(pk=issue.pk)
        self.assertEqual(issue.attachments.count(), 1)
        attachment = issue.attachments.first()
        self.assertEqual(attachment.creator, self.user)
        self.assertEqual(attachment.seqnum, 1)
        self.assertEqual(attachment.issue.nextAttachmentId, 2)

        response = self.client.get(
            reverse('issue:download_attachment',
                    kwargs={
                        'project': proj_short,
                        'sqn_i': 1,
                        'sqn_a': attachment.seqnum
                    }))
        self.assertEqual(response.status_code, 200)
        self.assertEqual("application/octet-stream",
                         response.get('Content-Type'))
        self.assertEqual(response.resolver_match.func.__name__,
                         AttachmentDownloadView.as_view().__name__)
Beispiel #10
0
    def test_flags(self):
        kanbancol = KanbanColumn(name='Column',
                                 position=1,
                                 project=self.project)
        kanbancol.save()
        issue = Issue(title="Test-Issue",
                      kanbancol=kanbancol,
                      project=self.project,
                      type="Bug",
                      sprint=self.sprint)
        issue.save()
        self.assertEqual(issue.was_in_sprint, False)

        response = self.client.post(
            reverse('sprint:stopsprint',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_s': self.sprint.seqnum
                    }))
        self.assertEqual(Issue.objects.get(pk=issue.pk).was_in_sprint, True)

        issue.save()  # unset the flag
        self.assertEqual(issue.was_in_sprint, False)
Beispiel #11
0
    def test_timelog_loginfo_and_issue_loginfo_log_time_in_multiple_issues_and_projects_for_own_user(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time},
                                    follow=True)
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue2.number}),
                                    {'time': '15m', 'created_at': self.time},
                                    follow=True)
        response = self.client.get(reverse('timelog:loginfo'))
        # first log
        self.assertContains(response, "1 Hour and 30 Minutes")
        # second log - second issue
        self.assertContains(response, "15 Minutes")

        project2 = Project(creator=self.user, name_short='PRJ2')
        project2.save()
        project2.developer.add(self.user)
        # both are shown in timelog:loginfo
        response = self.client.get(reverse('timelog:loginfo'))
        self.assertContains(response, "1 Hour and 30 Minutes")
        self.assertContains(response, "15 Minutes")

        # additional log for a different project
        issue3 = Issue(title='third_issue', project=project2)
        issue3.save()
        issue3.assignee.add(self.user)
        response = self.client.post(reverse('issue:log', kwargs={"project": project2.name_short,
                                                                 "sqn_i": issue3.number}),
                                    {'time': '20m', 'created_at': self.time},
                                    follow=True)

        # all three are shown in timelog:loginfo
        response = self.client.get(reverse('timelog:loginfo'))
        self.assertContains(response, "1 Hour and 30 Minutes")
        self.assertContains(response, "15 Minutes")
        self.assertContains(response, "20 Minutes")
Beispiel #12
0
    def test_attachment_delete(self):
        # create sample issue
        issue = Issue(title="Test-Issue",
                      project=self.project,
                      kanbancol=self.column,
                      type="Bug")
        issue.save()
        # create sample attachment
        filecontent = 'Hello World'
        temp = tempfile.NamedTemporaryFile(delete=False)
        temp.write(filecontent.encode())
        temp.close()
        f = File(open(temp.name, 'r'))
        attachment = Attachment(file=f, creator=self.user, issue=issue)
        attachment.save()
        f.close()
        filePath = attachment.file.path
        self.assertTrue(os.path.isfile(filePath))

        # delete the attachment
        response = self.client.get(reverse('issue:delete_attachment',
                                           kwargs={
                                               'project':
                                               self.project.name_short,
                                               'sqn_i': issue.number,
                                               'sqn_a': attachment.seqnum
                                           }),
                                   follow=True)
        self.assertRedirects(
            response,
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': issue.number
                    }))
        self.assertFalse(issue.attachments.all().exists())
        self.assertFalse(os.path.isfile(filePath))
Beispiel #13
0
    def test_archive_and_unarchive_single_issue_function(self):
        issue = Issue(title="bar", project=self.project)
        issue.save()
        self.assertFalse(issue.archived)
        n = Issue.objects.archived().count()

        values = {'sqn_i': issue.number}
        response = self.client.post(
            reverse('issue:archiveissue',
                    kwargs={'project': self.project.name_short}), values)
        self.assertRedirects(
            response,
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': issue.number
                    }))
        issue.refresh_from_db()

        self.assertEqual(Issue.objects.archived().count(), (n + 1))
        self.assertTrue(issue.archived)

        values = {'sqn_i': issue.number}
        response = self.client.post(
            reverse('issue:unarchiveissue',
                    kwargs={'project': self.project.name_short}), values)
        self.assertRedirects(
            response,
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': issue.number
                    }))
        issue.refresh_from_db()

        self.assertEqual(Issue.objects.archived().count(), n)
        self.assertFalse(issue.archived)
    def test_dependencies(self):
        driver = self.selenium

        # create two issues
        issue = Issue(title="title", kanbancol=KanbanColumn.objects.get(project=self.project, name="Todo"),
                      due_date=str(datetime.date.today()), priority=3, storypoints=2, description="blubber",
                      project=self.project
                      )
        issue.save()
        issue.assignee.add(self.user)
        issue = Issue(title="title", kanbancol=KanbanColumn.objects.get(project=self.project, name="Todo"),
                      due_date=str(datetime.date.today()), priority=3, storypoints=2, description="blubber",
                      project=self.project
                      )
        issue.save()
        issue.assignee.add(self.user)

        driver.get('{}{}'.format(self.live_server_url, reverse('backlog:backlog',
                                                               kwargs={'project': self.project.name_short}
                                                               )))

        driver.find_element_by_link_text("PRJ-1").click()
        driver.find_element_by_id("issue_detail_edit_link").click()
        driver.find_element_by_xpath("(//input[@type='search'])[2]").send_keys('\n')
        time.sleep(1)
        self.assertEqual(len(driver.find_elements_by_css_selector('#select2-id_dependsOn-results li')), 1)
        for i in driver.find_elements_by_css_selector('#select2-id_dependsOn-results li'):
            i.click()
        driver.find_element_by_id("id_submit_edit").click()
        self.assertIn('Depends on', driver.page_source)
        self.assertIn('PRJ-2', driver.page_source)
        url = driver.current_url
        driver.find_element_by_link_text("title").click()
        self.assertIn('Dependent issues', driver.page_source)
        self.assertIn('PRJ-1', driver.page_source)
        driver.find_element_by_link_text("title").click()
        self.assertEqual(url, driver.current_url)
Beispiel #15
0
 def test_next_parameter_in_post_requests(self):
     issue = Issue(title="Test-Issue",
                   project=self.project,
                   kanbancol=self.column,
                   type="Bug")
     issue.save()
     response = self.client.post(reverse(
         'issue:assigntome', kwargs={'project': self.project.name_short}), {
             'sqn_i': issue.number,
             'next': '/timelog'
         },
                                 follow=True)
     self.assertRedirects(response, reverse('timelog:loginfo'))
     response = self.client.post(reverse(
         'issue:rmfromme', kwargs={'project': self.project.name_short}), {
             'sqn_i': issue.number,
             'next': '/timelog'
         },
                                 follow=True)
     self.assertRedirects(response, reverse('timelog:loginfo'))
     response = self.client.post(
         reverse('issue:archiveissue',
                 kwargs={'project': self.project.name_short}), {
                     'sqn_i': issue.number,
                     'next': '/timelog'
                 },
         follow=True)
     self.assertRedirects(response, reverse('timelog:loginfo'))
     response = self.client.post(reverse(
         'issue:unarchiveissue',
         kwargs={'project': self.project.name_short}), {
             'sqn_i': issue.number,
             'next': '/timelog'
         },
                                 follow=True)
     self.assertRedirects(response, reverse('timelog:loginfo'))
Beispiel #16
0
    def test_board_filter_my_issues(self):
        driver = self.selenium
        project = Project(name='TestProjekt', name_short='TP', creator=self.user)
        project.save()
        project.developer.add(self.user)
        issue1 = Issue(project=project, title="TestIssue1", priority=4, type='Story')
        issue2 = Issue(project=project, title="TestIssue2", priority=0, type='Bug')
        issue3 = Issue(project=project, title="TestIssue3", priority=3, type='Task')
        issue4 = Issue(project=project, title="TestIssue3", priority=2, type='Task')
        issue5 = Issue(project=project, title="TestIssue3", priority=0, type='Story')

        issue1.save()
        issue2.save()
        issue3.save()
        issue4.save()
        issue5.save()

        driver.get("{}{}".format(self.live_server_url, reverse('sprint:sprintboard',
                                                               kwargs={'project': project.name_short}
                                                               )))

        # 5 issues in col 0
        col = driver.find_element_by_id("sortable0")
        issues = col.find_elements_by_class_name("issuecard")
        self.assertEqual(len(issues), 5)

        issue1.assignee.add(self.user)

        driver.get("{}{}".format(self.live_server_url, reverse('sprint:sprintboard',
                                                               kwargs={'project': project.name_short}
                                                               ))+"?myissues=true")

        # 1 issue in col 0
        col = driver.find_element_by_id("sortable0")
        issues = col.find_elements_by_class_name("issuecard")
        self.assertEqual(len(issues), 1)
Beispiel #17
0
    def test_view_and_template(self):
        # create
        response = self.client.get(
            reverse('issue:create',
                    kwargs={'project': self.project.name_short}))
        self.assertTemplateUsed(response, create_template)
        self.assertEqual(response.resolver_match.func.__name__,
                         IssueCreateView.as_view().__name__)

        # create issue for edit and detail tests
        issue = Issue(title="foo")
        issue.project = self.project
        issue.save()
        number = issue.number

        # edit
        response = self.client.get(
            reverse('issue:edit',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': number
                    }))
        self.assertTemplateUsed(response, edit_template)
        self.assertEqual(response.resolver_match.func.__name__,
                         IssueEditView.as_view().__name__)

        # detail
        response = self.client.get(
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': number
                    }))
        self.assertTemplateUsed(response, detail_template)
        self.assertEqual(response.resolver_match.func.__name__,
                         IssueDetailView.as_view().__name__)
Beispiel #18
0
    def test_create_and_edit_issues_with_get_requests_disabled(self):
        values = {
            'title': "Test-Issue",
            'kanbancol': self.column.pk,
            'type': "Bug",
            'assignee': (self.user.pk),
            'priority': 2,
        }
        # create
        response = self.client.get(
            reverse('issue:create',
                    kwargs={'project': self.project.name_short}), values)
        # didn't store something
        self.assertTemplateUsed(response, create_template)
        try:
            self.assertIsNone(
                Issue.objects.get(title="Test-Issue", assignee=self.user.pk))
        except Issue.DoesNotExist:
            pass

        # create issue for edit test
        issue = Issue(title="foo")
        issue.project = self.project
        issue.save()
        number = issue.number

        # edit
        response = self.client.get(
            reverse('issue:edit',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': number
                    }), values)
        # didn't store something
        self.assertIsNotNone(
            Issue.objects.get(title="foo", project=self.project))
Beispiel #19
0
    def test_assign_to_me_remove_from_me(self):
        issue = Issue(title="Test-Issue",
                      project=self.project,
                      kanbancol=self.column,
                      type="Bug")
        issue.save()
        response = self.client.post(
            reverse('issue:assigntome',
                    kwargs={'project': self.project.name_short}),
            {'sqn_i': issue.number})
        self.assertIn(self.user, issue.assignee.all())
        response = self.client.post(
            reverse('issue:rmfromme',
                    kwargs={'project': self.project.name_short}),
            {'sqn_i': issue.number})
        self.assertNotIn(self.user, issue.assignee.all())
        response = self.client.post(reverse(
            'issue:rmfromme', kwargs={'project': self.project.name_short}),
                                    {'sqn_i': issue.number},
                                    follow=True)

        self.assertContains(
            response,
            'Issue was not in selected sprint, not performing any action')
Beispiel #20
0
 def test_storypoints_in_backlog(self):
     kanbancol = KanbanColumn(name='Column',
                              position=1,
                              project=self.project)
     kanbancol.save()
     issue = Issue(title="Test-Issue",
                   kanbancol=kanbancol,
                   project=self.project,
                   type="Bug",
                   sprint=self.sprint)
     issue.save()
     response = self.client.get(
         reverse('backlog:backlog',
                 kwargs={'project': self.project.name_short}))
     self.assertNotContains(response, 'Storypoints:')
     issue.storypoints = 5
     issue.save()
     response = self.client.get(
         reverse('backlog:backlog',
                 kwargs={'project': self.project.name_short}))
     self.assertNotContains(response, 'Storypoints:')
     issue.assignee.add(self.user)
     response = self.client.get(
         reverse('backlog:backlog',
                 kwargs={'project': self.project.name_short}))
     self.assertContains(response, 'Storypoints:')
     self.assertContains(response, str(self.user.username) + ': 5')
     user2 = get_user_model().objects.create_user('a2', 'b2', 'c2')
     user2.save()
     self.project.developer.add(user2)
     issue.assignee.add(user2)
     response = self.client.get(
         reverse('backlog:backlog',
                 kwargs={'project': self.project.name_short}))
     self.assertContains(response, str(user2.username) + ': 2.5')
     self.assertContains(response, str(self.user.username) + ': 2.5')
     issue2 = Issue(title="Test-Issue",
                    kanbancol=kanbancol,
                    project=self.project,
                    type="Bug",
                    sprint=self.sprint,
                    storypoints=5)
     issue2.save()
     issue2.assignee.add(self.user)
     response = self.client.get(
         reverse('backlog:backlog',
                 kwargs={'project': self.project.name_short}))
     self.assertContains(response, str(user2.username) + ': 2.5')
     self.assertContains(response, str(self.user.username) + ': 7.5')
Beispiel #21
0
    def test_form(self):
        # create
        vals = {
            'name': "Testcolumn",
            'type': 'ToDo',
            'project': self.project.pk
        }
        response = self.client.post(
            reverse('kanbancol:create', kwargs={'project': self.short}), vals)
        self.client.get(reverse("project:edit", kwargs={"project":
                                                        self.short}))
        self.assertRedirects(
            response, reverse('project:edit', kwargs={'project': self.short}))

        response = self.client.get(response['location'])
        self.assertEqual(response.status_code, 200)
        # We always insert at the end
        self.assertEqual(response.context['columns'][3].name, "Testcolumn")
        self.assertEqual(str(response.context['columns'][3]), "Testcolumn")

        # modify
        vals = {
            'name': "Testmodification",
            'type': 'ToDo',
        }
        response = self.client.post(
            reverse('kanbancol:update',
                    kwargs={
                        'position': 3,
                        'project': self.short
                    }), vals)
        self.assertRedirects(
            response, reverse('project:edit', kwargs={'project': self.short}))

        response = self.client.get(response['location'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context['columns'][3].name,
                         "Testmodification")

        # assign issue to column and try to delete
        issue = Issue(title="Test-Issue",
                      kanbancol=KanbanColumn.objects.get(
                          position=3, project__name_short=self.short),
                      project=self.project,
                      type="Bug")
        issue.save()

        self.assertEqual(KanbanColumn.objects.count(), 4)

        response = self.client.post(
            reverse('kanbancol:delete',
                    kwargs={
                        'position': 3,
                        'project': self.short
                    }), {'delete': 'true'})
        self.assertRedirects(
            response, reverse('project:edit', kwargs={'project': self.short}))
        self.assertEqual(KanbanColumn.objects.count(), 4)

        issue.kanbancol = KanbanColumn.objects.get(
            position=2, project__name_short=self.short)
        issue.save()

        # delete
        response = self.client.post(
            reverse('kanbancol:delete',
                    kwargs={
                        'position': 3,
                        'project': self.short
                    }), {'delete': 'true'})
        self.assertRedirects(
            response, reverse('project:edit', kwargs={'project': self.short}))
        response = self.client.get(response['location'])
        self.assertEqual(KanbanColumn.objects.count(), 3)
        self.assertNotContains(response, vals["name"])
Beispiel #22
0
class TimelogTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify these elements they need to be created in setUp(), instead of here
        cls.user = get_user_model().objects.create_user('test', '*****@*****.**', 'test1234')
        cls.user2 = get_user_model().objects.create_user('test2', '*****@*****.**', 'test1234')
        cls.user.save()
        cls.user2.save()

    def setUp(self):
        self.client.force_login(self.user)
        # NOTE: these elements get modified by some testcases, so they should NOT be created in setUpTestData()
        self.project = Project(creator=self.user, name_short='PRJ', activity_only_for_managers=False)
        self.project.save()
        self.project.developer.add(self.user)

        self.project2 = Project(creator=self.user, activity_only_for_managers=True, name_short='PRJJ')
        self.project2.save()
        self.project2.manager.add(self.user)
        self.project2.developer.add(self.user, self.user2)

        self.kanbancol = KanbanColumn(project=self.project, position=4, name='test')
        self.kanbancol.save()
        self.issue = Issue(title='a very very very very very very very long issue title',
                           project=self.project,
                           due_date='2016-12-16',
                           kanbancol=self.kanbancol,
                           storypoints='3'
                           )
        self.issue.save()
        self.issue.assignee.add(self.user)
        self.issue2 = Issue(title='second_issue', project=self.project)
        self.issue2.save()
        self.issue2.assignee.add(self.user)
        self.time = now().strftime("%Y-%m-%d %H:%M:%S")

        # success logedit_address_kwargs
        self.sqn_l1_address_kwarg = {"project": self.issue.project.name_short,
                                     "sqn_i": self.issue.number,
                                     "sqn_l": 1
                                     }

    def test_view_and_template(self):
        # TODO TESTCASE see invite_users
        #      use view_and_template()
        # TODO which views?
        #      - timelog:loginfo
        #      - timelog:archiv
        #      - issue:log
        #      - issue:logs with (?P<sqn_l>[0-9]+)/edit/ (logedit)
        #      - issue:logs with  (?P<sqn_l>[0-9]+)/delete/? (logdelete)
        #      - issue:logedit
        #      - issue:logdelete
        #      - ProjectUserTimelogView - project:usertimelog
        #      - ProjectDetailTimelogView - project:timelog
        #      - ...
        pass

    def test_redirect_to_login_and_login_required(self):
        self.client.logout()
        # TODO TESTCASE see invite_users
        #      redirect_to_login_and_login_required()
        # TODO which views?
        #      - timelog:loginfo
        #      - timelog:archiv
        #      - issue:log
        #      - issue:logs with (?P<sqn_l>[0-9]+)/edit/ (logedit)
        #      - issue:logs with  (?P<sqn_l>[0-9]+)/delete/? (logdelete)
        #      - issue:logedit
        #      - issue:logdelete
        #      - ProjectUserTimelogView - project:usertimelog
        #      - ProjectDetailTimelogView - project:timelog
        #      - ...
        pass

    def test_user_passes_test_mixin(self):
        # TODO TESTCASE
        #      - timelog:loginfo
        #      - timelog:archiv
        #      - issue:log
        #      - issue:logs with (?P<sqn_l>[0-9]+)/edit/ (logedit)
        #      - issue:logs with  (?P<sqn_l>[0-9]+)/delete/? (logdelete)
        #      - issue:logedit
        #      - issue:logdelete
        #      - ProjectUserTimelogView - project:usertimelog
        #      - ProjectDetailTimelogView - project:timelog
        #      - ...
        pass

    def test_log_time_and_redirect(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time},
                                    follow=True)
        self.assertRedirects(response, reverse('issue:detail', kwargs={'project': self.project.name_short,
                                                                       'sqn_i': 1}))

    # PER USER START
    def test_timelog_loginfo_per_user_only_visible_for_own_user(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time},
                                    follow=True)
        response = self.client.get(reverse('timelog:loginfo'))
        self.assertContains(response, "1 Hour and 30 Minutes")

        # tag filter issue title short
        self.assertContains(response, "...")

        self.client.logout()
        self.client.force_login(self.user2)

        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number,
                                                                 }),
                                    {'time': '15m', 'created_at': self.time})
        self.client.logout()
        self.client.force_login(self.user)
        response = self.client.get(reverse('timelog:loginfo'))
        # first log
        self.assertContains(response, "1 Hour and 30 Minutes")
        # second log - different user
        self.assertNotContains(response, "15 Minutes")

    def test_timelog_loginfo_and_issue_loginfo_log_time_in_multiple_issues_and_projects_for_own_user(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time},
                                    follow=True)
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue2.number}),
                                    {'time': '15m', 'created_at': self.time},
                                    follow=True)
        response = self.client.get(reverse('timelog:loginfo'))
        # first log
        self.assertContains(response, "1 Hour and 30 Minutes")
        # second log - second issue
        self.assertContains(response, "15 Minutes")

        project2 = Project(creator=self.user, name_short='PRJ2')
        project2.save()
        project2.developer.add(self.user)
        # both are shown in timelog:loginfo
        response = self.client.get(reverse('timelog:loginfo'))
        self.assertContains(response, "1 Hour and 30 Minutes")
        self.assertContains(response, "15 Minutes")

        # additional log for a different project
        issue3 = Issue(title='third_issue', project=project2)
        issue3.save()
        issue3.assignee.add(self.user)
        response = self.client.post(reverse('issue:log', kwargs={"project": project2.name_short,
                                                                 "sqn_i": issue3.number}),
                                    {'time': '20m', 'created_at': self.time},
                                    follow=True)

        # all three are shown in timelog:loginfo
        response = self.client.get(reverse('timelog:loginfo'))
        self.assertContains(response, "1 Hour and 30 Minutes")
        self.assertContains(response, "15 Minutes")
        self.assertContains(response, "20 Minutes")

    # PER ISSUE START
    def test_log_time_per_issue_even_from_other_user(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time})
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '15m', 'created_at': self.time})
        self.client.logout()
        self.project.developer.add(self.user2)
        self.issue.assignee.add(self.user2)
        self.client.force_login(self.user2)
        # second user log
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '20m', 'created_at': self.time})
        response = self.client.get(reverse('issue:detail', kwargs={"project": self.issue.project.name_short,
                                                                   "sqn_i": self.issue.number}))
        # first log -  first user
        self.assertContains(response, "1 Hour and 30 Minutes")
        # second log - first user
        self.assertContains(response, "15 Minutes")
        # third log - second user
        self.assertContains(response, "20 Minutes")

    def test_log_time_in_future_fails(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m',
                                     'created_at': (now() + timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S")})
        self.assertEqual(response.status_code, 200)
        # still on the same page (issue:log)
        self.assertTemplateUsed(response, 'timelog/timelog_create.html')
        self.assertContains(response, 'The date entered must be today or lesser.')

    def test_log_time_delete_log_per_issue(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time})
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '2h', 'created_at': self.time})

        response = self.client.post(reverse('issue:logdelete', kwargs={"project": self.issue.project.name_short,
                                                                       "sqn_i": self.issue.number,
                                                                       "sqn_l": 2}),
                                    {'delete': 'true'}, follow=True)
        self.assertTemplateUsed(response, 'timelog/timelog_list_peruser.html')
        self.assertContains(response, "1 Hour and 30 Minutes")
        self.assertNotContains(response, "2 Hours")

    def test_log_time_keep_log_per_issue(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time})
        response = self.client.post(reverse('issue:logdelete', kwargs=self.sqn_l1_address_kwarg),
                                    {'keep': 'true'}, follow=True)
        self.assertTemplateUsed(response, 'timelog/timelog_list_peruser.html')
        self.assertContains(response, "1 Hour and 30 Minutes")

    def test_log_time_edit_log_per_issue(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1d1h30m', 'created_at': self.time})

        response = self.client.get(reverse('issue:detail', kwargs={"project": self.issue.project.name_short,
                                                                   "sqn_i": self.issue.number}))
        self.assertContains(response, "1 Day, 1 Hour and 30 Minutes")

        response = self.client.get(reverse('issue:logedit', kwargs=self.sqn_l1_address_kwarg))
        self.assertContains(response, "1d 1h 30m")

        response = self.client.post(reverse('issue:logedit', kwargs=self.sqn_l1_address_kwarg),
                                    {'time': '3h', 'created_at': self.time, 'save_timelog_change': 'true'})
        response = self.client.get(reverse('issue:detail', kwargs={"project": self.issue.project.name_short,
                                                                   "sqn_i": self.issue.number}))
        self.assertContains(response, "3 Hours")

    def test_edit_and_delete_as_other_user(self):
        response = self.client.post(reverse('issue:log', kwargs={"project": self.issue.project.name_short,
                                                                 "sqn_i": self.issue.number}),
                                    {'time': '1h30m', 'created_at': self.time})
        self.assertEqual(str(Timelog.objects.filter(issue=self.issue)[0].time), "1:30:00")
        # test to edit or delete as other user - should not be possible
        self.client.logout()
        self.client.force_login(self.user2)

        user_doesnt_pass_test_and_gets_404(self, 'issue:logedit', address_kwargs=self.sqn_l1_address_kwarg)
        self.assertEqual(str(Timelog.objects.filter(issue=self.issue)[0].time), "1:30:00")
        user_doesnt_pass_test_and_gets_404(self, 'issue:logdelete', address_kwargs=self.sqn_l1_address_kwarg)
        self.assertEqual(str(Timelog.objects.filter(issue=self.issue)[0].time), "1:30:00")

    # PROJECT START
    def test_project_detail_timelog_from_different_issuesss_of_project_even_from_other_users(self):
        cases = ['2d 2h 5m', '1d 1h 5m', '2d 5m', '1d 3h']
        mappings = ['2 Days, 2 Hours and 5 Minutes', '1 Day, 1 Hour and 5 Minutes',
                    '2 Days and 5 Minutes', '1 Day and 3 Hours']
        sums = ['3 Days, 3 Hours and 10 Minutes', '3 Days, 3 Hours and 5 Minutes']
        # first user, first issue
        response = self.client.post(reverse('timelog:loginfo'),
                                    {'time': cases[0], 'created_at': self.time, 'issue': self.issue.id},
                                    follow=True)
        # first user, second issue
        response = self.client.post(reverse('timelog:loginfo'),
                                    {'time': cases[1], 'created_at': self.time, 'issue': self.issue2.id},
                                    follow=True)
        self.client.logout()
        self.project.developer.add(self.user2)
        self.issue.assignee.add(self.user2)
        self.issue2.assignee.add(self.user2)
        self.client.force_login(self.user2)
        # second user
        # first user, first issue
        response = self.client.post(reverse('timelog:loginfo'),
                                    {'time': cases[2], 'created_at': self.time, 'issue': self.issue.id},
                                    follow=True)
        # first user, second issue
        response = self.client.post(reverse('timelog:loginfo'),
                                    {'time': cases[3], 'created_at': self.time, 'issue': self.issue2.id},
                                    follow=True)

        response = self.client.get(reverse('project:timelog', kwargs={"project": self.issue.project.name_short}))
        for user in self.project.developer.all():
            self.assertContains(response, user.username)

        for i in range(4):
            self.assertContains(response, mappings[i])

        # sums of both users
        self.assertContains(response, 'total: '+sums[0])
        self.assertContains(response, 'total: '+sums[1])

    def test_project_detail_timelog_username_appears(self):
        for dev in self.project.developer.all():
            response = self.client.get(reverse('project:usertimelog', kwargs={"project": self.issue.project.name_short,
                                                                              "username": dev.username}))
        self.assertContains(response, dev.username)

    def test_timelog_edit_render_value_issue_logedit(self):
        cases = ['1d 1h 5m', '1d 5m', '1d 3h',
                 '1h 5m', '1d', '3h', '5m'
                 ]
        for i in range(len(cases)):
            response = self.client.post(reverse('timelog:loginfo'),
                                        {'time': cases[i], 'created_at': self.time, 'issue': self.issue.id},
                                        follow=True)
            response = self.client.get(reverse('issue:logedit',
                                               kwargs={"project": self.issue.project.name_short,
                                                       "sqn_i": self.issue.number, "sqn_l": i+1}))
            self.assertContains(response, cases[i])

    def test_timelog_loginfo(self):
        cases = ['2d 2h 5m', '1d 1h 5m', '1d 5m', '1d 3h',
                 '1h 5m', '1d', '3h', '5m', '1m'
                 ]
        for i in range(len(cases)):
            response = self.client.post(reverse('timelog:loginfo'),
                                        {'time': cases[i], 'created_at': self.time, 'issue': self.issue.id},
                                        follow=True)
            log = Timelog.objects.get(id=i+1)
            response = self.client.get(reverse('timelog:loginfo'))
            self.assertContains(response, duration(log.time))

    def test_timelog_model_str_method(self):
        response = self.client.post(reverse('timelog:loginfo'),
                                    {'time': '2h30m', 'created_at': self.time, 'issue': self.issue.id},
                                    follow=True)
        log = Timelog.objects.get(id=1)
        self.assertEqual(str(log), "logged time: {} for issue: {}".format(log.time, log.issue))

    def test_project_timelogs_only_for_manager_setting(self):
        response = self.client.get(reverse('project:timelog', kwargs={"project": self.project2.name_short}))
        for user in self.project2.developer.all():
            self.assertContains(response, user.username)
        response = self.client.get(reverse('project:usertimelog',
                                   kwargs={"project": self.project2.name_short, "username": self.user2.username}),
                                   follow=True)
        self.assertContains(response, self.user2.username)
        self.client.force_login(self.user2)
        response = self.client.get(reverse('project:timelog', kwargs={"project": self.project2.name_short}))
        self.assertRedirects(response,
                             reverse('project:usertimelog',
                                     kwargs={"project": self.project2.name_short, "username": self.user2.username}
                                     )
                             )
        response = self.client.get(reverse('project:usertimelog',
                                   kwargs={"project": self.project2.name_short, "username": self.user.username}),
                                   follow=True)
        self.assertRedirects(response,
                             reverse('project:usertimelog',
                                     kwargs={"project": self.project2.name_short, "username": self.user2.username}
                                     )
                             )
        self.project2.activity_only_for_managers = False
        self.project2.save()
        response = self.client.get(reverse('project:timelog', kwargs={"project": self.project2.name_short}))
        for user in self.project2.developer.all():
            self.assertContains(response, user.username)
        response = self.client.get(reverse('project:usertimelog',
                                   kwargs={"project": self.project2.name_short, "username": self.user.username}),
                                   follow=True)
        self.assertContains(response, self.user.username)

    def test_timelog_archiv(self):
        response = self.client.get(reverse('timelog:archiv'))
        response = self.client.get(reverse('timelog:archiv')+'?page=2')
Beispiel #23
0
class StripImgMetadataTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify these elements they need to be created in setUp(), instead of here
        cls.user = get_user_model().objects.create_user(
            user_name, user_email, 'c')
        cls.project = Project(creator=cls.user, name_short='PRJ')
        cls.project.save()
        cls.project.manager.add(cls.user)
        cls.project.developer.add(cls.user)
        cls.images = list(cls.create_images())
        # TODO https://blog.brian.jp/python/png/2016/07/07/file-fun-with-pyhon.html
        # TODO malicious payload jpg, png, bmp, gif

    @classmethod
    def tearDownClass(cls):
        for image in cls.images:
            os.unlink(image)
        os.unlink(forbidden_img)
        super().tearDownClass()

    def setUp(self):
        self.client.force_login(self.user)
        # NOTE: these elements get modified by some testcases, so they should NOT be created in setUpTestData()
        self.issue = Issue(title="Test-Issue", project=self.project)
        self.issue.save()

    def tearDown(self):
        # delete uploaded avatars
        rmtree(avatars_path, ignore_errors=True)
        # TODO TESTCASE delete all uploaded files (the files that are stored on server side
        #               so attachments needs to be removed too
        # TODO therefore we need to know the structure of the attachments dir and which one has been added

    # helper function to create all the different image types and returns the file names of all generated images
    def create_images():
        # actual image color: 255,0,0
        img = Image.new("RGB", (100, 20), color='red')
        text = ImageDraw.Draw(img)
        text.text((10, 10), "Hello World", fill=(0, 0, 0))
        image_wmeta = 'image_wmeta'

        # thumbnail color: 0,0,255
        o = io.BytesIO()
        secret_thumbnail = Image.new("RGB", (120, 20), color='blue')
        text = ImageDraw.Draw(secret_thumbnail)
        text.text((10, 10), "secret thumbnail", fill=(0, 0, 0))
        # transform it to bytes
        secret_thumbnail.save(o, "jpeg")
        secret_exif_thumbnail = o.getvalue()
        secret_thumbnail.close()

        # forbidden image_extension
        img.save(forbidden_img, "tiff")
        # bmp doesn't contain critical meta information
        img.save(image_wmeta + "_bmp" + '.bmp')

        # for some reasons some of these values don't match the relative specification:
        # rational numbers are separated at the comma, f.e. 13.37 is represented by [(13), (37)]
        # http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf#page=47 ...
        # http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf#page=87
        jpg_exif = {
            "0th": {
                piexif.ImageIFD.ImageDescription: u"description",
                piexif.ImageIFD.StripOffsets: 3,
                piexif.ImageIFD.Artist: u"artist",
                piexif.ImageIFD.Copyright: u"copyright holder",
                piexif.ImageIFD.DateTime: u"2012:01:08 10:09:01",
            },
            "Exif": {
                piexif.ExifIFD.DateTimeOriginal: u"2016:08:07 13:37:10",
                piexif.ExifIFD.DateTimeDigitized: u"2015:03:07 14:20:30",
                piexif.ExifIFD.OffsetTime: u"2017:05:09 08:04:04",
                piexif.ExifIFD.OffsetTimeOriginal: u"2017:04:12 18:15:00",
                piexif.ExifIFD.OffsetTimeDigitized: u"2016:02:10 11:10:03",
                piexif.ExifIFD.SubSecTime: u"2017:09:04 10:03:10",
                piexif.ExifIFD.SubSecTimeOriginal: u"2019:10:03 10:03:10",
                piexif.ExifIFD.SubSecTimeDigitized: u"2013:10:03 10:03:10",
                piexif.ExifIFD.CameraOwnerName: u"Cameraname",
                piexif.ExifIFD.BodySerialNumber: u"body serialnumber",
                piexif.ExifIFD.LensSerialNumber: u"lens serialnumber",
                piexif.ExifIFD.UserComment: b"secret comment",
            },
            "GPS": {
                piexif.GPSIFD.GPSLatitudeRef: u"N",
                piexif.GPSIFD.GPSLatitude: [(10, 1), (20, 1), (0, 0)],
                piexif.GPSIFD.GPSLongitudeRef: u"W",
                piexif.GPSIFD.GPSLongitude: [(10, 1), (20, 1), (0, 0)],
                piexif.GPSIFD.GPSAltitudeRef: 0,
                piexif.GPSIFD.GPSAltitude: (200, 1),
                piexif.GPSIFD.GPSTimeStamp: [(10), (3)],
                piexif.GPSIFD.GPSSatellites: u"satellites",
                piexif.GPSIFD.GPSStatus: u"A",
                piexif.GPSIFD.GPSMeasureMode: u"3",
                piexif.GPSIFD.GPSDOP: [(1), (4)],
                piexif.GPSIFD.GPSSpeedRef: u"K",
                piexif.GPSIFD.GPSSpeed: [(42), (10)],
                piexif.GPSIFD.GPSTrackRef: u"T",
                piexif.GPSIFD.GPSTrack: [(21), (123)],
                piexif.GPSIFD.GPSImgDirectionRef: u"T",
                piexif.GPSIFD.GPSImgDirection: [(10), (12)],
                piexif.GPSIFD.GPSMapDatum: u"today",
                piexif.GPSIFD.GPSDestLatitudeRef: u"N",
                piexif.GPSIFD.GPSDestLatitude: [(8, 1), (30, 1), (0, 0)],
                piexif.GPSIFD.GPSDestLongitudeRef: u"E",
                piexif.GPSIFD.GPSDestLongitude: [(8), (30)],
                piexif.GPSIFD.GPSDestBearingRef: u"T",
                piexif.GPSIFD.GPSDestBearing: [(1), (10)],
                piexif.GPSIFD.GPSDestDistanceRef: u"K",
                piexif.GPSIFD.GPSDestDistance: [(10), (3)],
                piexif.GPSIFD.GPSProcessingMethod: b"WLAN",
                piexif.GPSIFD.GPSAreaInformation: b"area",
                piexif.GPSIFD.GPSDateStamp: u"2015:10:03 10:03:10",
                piexif.GPSIFD.GPSDifferential: 1,
                piexif.GPSIFD.GPSHPositioningError: [(2), (0)],
            },
            "1st": {
                piexif.ImageIFD.ImageDescription: u"description",
                piexif.ImageIFD.StripOffsets: 3,
                piexif.ImageIFD.Artist: u"artist",
                piexif.ImageIFD.Copyright: u"copyright holder",
                piexif.ImageIFD.DateTime: u"2013:10:03 10:03:10",
            },
            "thumbnail": secret_exif_thumbnail
        }

        png_dict = {
            "ImageDescription": u"description",
            "StripOffsets": "3",
            "Artist": u"artist",
            "Copyright": u"copyright holder",
            "DateTime": u"2012:01:08 10:09:01",
            "DateTimeOriginal": u"2016:08:07 13:37:10",
            "DateTimeDigitized": u"2015:03:07 14:20:30",
            "OffsetTime": u"2017:05:09 08:04:04",
            "OffsetTimeOriginal": u"2017:04:12 18:15:00",
            "OffsetTimeDigitized": u"2016:02:10 11:10:03",
            "SubSecTime": u"2017:09:04 10:03:10",
            "SubSecTimeOriginal": u"2019:10:03 10:03:10",
            "SubSecTimeDigitized": u"2013:10:03 10:03:10",
            "CameraOwnerName": u"Cameraname",
            "BodySerialNumber": u"body serialnumber",
            "LensSerialNumber": u"lens serialnumber",
            "UserComment": b"secret comment",
            "GPSLatitudeRef": u"N",
            "GPSLatitude": "3 deg 20' 0.00",
            "GPSLongitudeRef": u"W",
            "GPSLongitude": "3 deg 20.1' 0.00",
            "GPSAltitudeRef": "0",
            "GPSAltitude": "200 m Above Sea Level",
            "GPSTimeStamp": "03:19:59.999999",
            "GPSSatellites": u"satellites",
            "GPSStatus": u"A",
            "GPSMeasureMode": u"3",
            "GPSSpeedRef": u"K",
            "GPSSpeed": "4.2",
            "GPSTrackRef": u"T",
            "GPSTrack": "0.1707317073",
            "GPSImgDirectionRef": u"T",
            "GPSImgDirection": "0.6333333333",
            "GPSMapDatum": u"today",
            "GPSDestLatitudeRef": u"N",
            "GPSDestLatitude": "3 deg 30' 0.00",
            "GPSDestLongitudeRef": u"E",
            "GPSDestLongitude": "0 deg 16' 0.00",
            "GPSDestBearingRef": u"T",
            "GPSDestBearing": "0.1",
            "GPSDestDistanceRef": u"K",
            "GPSDestDistance": "3.333333333",
            "GPSProcessingMethod": b"WLAN",
            "GPSAreaInformation": b"area",
            "GPSDateStamp": u"2015:10:03 10:03:10",
            "GPSDifferential": "1",
            "ImageDescription": u"description",
            "StripOffsets": "3",
            "Artist": u"artist",
            "Copyright": u"copyright holder",
            "DateTime": u"2013:10:03 10:03:10",
        }

        # jpg with exif
        img.save(image_wmeta + '_jpg' + '.jpg', exif=piexif.dump(jpg_exif))
        # copy jpg to jpe, jpeg
        copyfile(image_wmeta + '_jpg' + '.jpg', image_wmeta + '_jpe' + '.jpe')
        copyfile(image_wmeta + '_jpg' + '.jpg',
                 image_wmeta + '_jpeg' + '.jpeg')

        # png exif-part
        png_info = PngImagePlugin.PngInfo()
        # copy png metadata
        for k, v in png_dict.items():
            png_info.add_text(k, v, 0)
        img.save(image_wmeta + '_png' + '.png', "PNG", pnginfo=png_info)

        img.save(image_wmeta + '_gif' + '.gif')
        img.close()

        # xmp for gif and png
        xmp = XMPMeta()
        xmp.append_array_item(consts.XMP_NS_DC, 'secret', 'secret information',
                              {
                                  'prop_array_is_ordered': True,
                                  'prop_value_is_array': True
                              })

        # gif xmp
        # TODO BUG Exempi library version >= 2.5 does not work with GIF images created by Pillow.
        # TODO BUG The format gets not recognized by Exempi.
        # TODO BUG Maybe a newer Pillow or Exempi version will fix this...
        # gif_image = XMPFiles(file_path=image_wmeta + '_gif' + ".gif", open_forupdate=True)
        # gif_image.put_xmp(xmp)
        # gif_image.close_file()

        # png part 2
        png_image = XMPFiles(file_path=image_wmeta + '_png' + ".png",
                             open_forupdate=True)
        png_image.put_xmp(xmp)
        png_image.close_file()

        return ((image_wmeta + '_' + suffix + "." + suffix)
                for suffix in ALLOWED_IMG_EXTENSIONS)

    def verify_metadatas_are_removed(self, file_path):
        # helper function that verifies that the provided image doesn't contain any sensitive metadata
        # empty exif
        self.assertEqual(piexif.load(file_path)["0th"], {},
                         msg="sensitive exif data left")
        self.assertEqual(piexif.load(file_path)["Exif"], {},
                         msg="sensitive exif data left")
        self.assertEqual(piexif.load(file_path)["GPS"], {},
                         msg="sensitive exif data left")
        self.assertEqual(piexif.load(file_path)["1st"], {},
                         msg="sensitive exif data left")
        # Imagine the following scenario: An image contains sensitive information, it gets modified to hide these.
        # If there is an exif-thumbnail it might represent the previous image and hence could leak those information.
        self.assertEqual(piexif.load(file_path)["thumbnail"],
                         None,
                         msg="The exif thumbnail has not been removed.")

        # verify that xmp is also empty. Normally the xmp content is stored in within the rdf tag
        xmp_file = XMPFiles(file_path=file_path)
        xmp_content = str(xmp_file.get_xmp())
        # this won't match if there are any additional xmp elements left, because they would occur between the opening
        # of the rdf:Description tag and the closing of the rdf:RDF tag.
        sensitive_information = re.findall("  <rdf:Description.*\n </rdf:RDF>",
                                           xmp_content)
        self.assertEqual(
            len(sensitive_information),
            1,
            msg="There are sensitive xmp-tags left:\n\n{}".format(xmp_content))
        xmp_file.close_file()

    def verify_images_are_sanitized(self, file_path):
        # helper function that verifies that the provided image is sanitized
        # TODO TESTCASE image sanitize check
        return True

    def test_change_avatar(self):
        # upload all prepared images
        for img_name in self.images:
            img = open(img_name, "rb")
            img_dict = {
                'avatar': img,
                'email': user_email,
                'timezone': timezone,
                'language': 'en',
            }
            response = self.client.post(reverse('user_profile:edit_profile',
                                                kwargs={"username":
                                                        user_name}),
                                        img_dict,
                                        follow=True)
            img.close()

            # verify the file has been uploaded successfully
            file_name = os.path.basename(img_name).partition(".")[0] + ".jpg"
            self.assertContains(response, file_name)
            file_path = avatars_path + "/" + file_name

            # verify there are no sensitive metadata left
            self.verify_metadatas_are_removed(file_path)
            # verify there is no malicious code left
            self.verify_images_are_sanitized(file_path)

    def test_comment_with_picture(self):
        # TODO TESTCASE comment with picture from issue-detail - picture stripping
        #               upload one image
        # TODO          call verify_metadatas_are_removed() and verify_images_are_sanitized()
        pass

    def test_file_upload_picture(self):
        # TODO TESTCASE attachment from issue-detail - picture stripping
        #               upload one image
        # TODO          call verify_metadatas_are_removed() and verify_images_are_sanitized()
        pass

    def test_change_user_profile_wo_default_avatar(self):
        # TODO TESTCASE create test that changes the user profile while the current avatar is not the default one
        #      this might produce some additional errors
        pass

    def test_reject_forbidden_img_extensions(self):
        # upload a forbidden img extensions
        img = open(forbidden_img, "rb")
        img_dict = {
            'avatar': img,
            'email': user_email,
            'timezone': timezone,
            'language': 'en',
        }
        response = self.client.post(reverse('user_profile:edit_profile',
                                            kwargs={"username": user_name}),
                                    img_dict,
                                    follow=True)
        img.close()
        # verify the file has NOT been uploaded successfully
        file_name = os.path.basename(forbidden_img).partition(".")[0] + ".jpg"
        self.assertNotContains(response, file_name)
        self.assertContains(response,
                            "is not allowed. Allowed extensions are:")

        # upload a forbidden img type with an allowed extensions
        file_name = 'trick.jpg'
        copyfile(forbidden_img, file_name)
        img = open(file_name, "rb")
        img_dict['avatar'] = img
        response = self.client.post(reverse('user_profile:edit_profile',
                                            kwargs={"username": user_name}),
                                    img_dict,
                                    follow=True)
        img.close()
        os.unlink(file_name)
        self.assertNotContains(response, file_name)
        self.assertContains(
            response,
            "Either unable to detect the image type or the image type is not supported. "
            + "Supported image extensions are:")

    def test_file_size_limitation(self):
        # verify that the allowed image size (avatar) is actually limited
        huge_img = TEST_FILE_PATH + '/8mb.png'
        img = open(huge_img, "rb")
        img_dict = {
            'avatar': img,
            'email': user_email,
            'timezone': timezone,
            'language': 'en',
        }
        # TODO BUG ResourceWarning: unclosed file <_io.BufferedReader name='/tmp/tmp......upload.png'>
        #          seems to be a leak in Pillow
        response = self.client.post(reverse('user_profile:edit_profile',
                                            kwargs={"username": user_name}),
                                    img_dict,
                                    follow=True)
        img.close()
        self.assertContains(
            response, "The uploaded image exceeds the allowed file size of: ")

    def test_malicious_pictures(self):
        # TODO TESTCASE upload malicious image and verify it is harmless after upload
        pass
Beispiel #24
0
class GitFrontendTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify these elements they need to be created in setUp(), instead of here
        cls.user = get_user_model().objects.create_user('a', 'b', 'c')
        cls.user2 = get_user_model().objects.create_user('d', 'e', 'f')

    def setUp(self):
        self.client.force_login(self.user)
        # NOTE: these elements get modified by some testcases, so they should NOT be created in setUpTestData()
        self.project = Project(creator=self.user, name_short='PRJ')
        self.project.save()

        self.issue = Issue(title="Test-Issue",
                           project=self.project,
                           kanbancol=self.project.kanbancol.first(),
                           type="Bug")
        self.issue.save()

    def test_view_and_template(self):
        # TODO TESTCASE invite_users
        #      use view_and_template()
        #      - FileDiffView - issue:commit_diff
        # TODO need to create a repository first
        # view_and_template(self, FileDiffView, 'commit/file_diff.html', 'issue:commit_diff',
        #                   address_kwargs={'project': self.project.name_short, 'sqn_i': self.issue.number})
        pass

    def test_redirect_to_login_and_login_required(self):
        self.client.logout()
        # TODO TESTCASE invite_users
        #      use redirect_to_login_and_login_required()
        #      - FileDiffView - issue:commit_diff

    def test_clone_and_import(self):
        repo_path = '/tmp/gitpythonrepo'

        self.project.manager.add(self.user)
        self.project.developer.add(self.user2)
        self.project.save()

        filecontent1 = 'Hello World File 1'
        tmp1 = tempfile.NamedTemporaryFile(delete=False)
        tmp1.write(filecontent1.encode())
        tmp1.close()
        filecontent2 = 'Hello World File 2'
        tmp2 = tempfile.NamedTemporaryFile(delete=False)
        tmp2.write(filecontent2.encode())
        tmp2.close()

        with open(tmp1.name, 'r') as f1:
            with open(tmp2.name, 'r') as f2:
                response = self.client.post(reverse(
                    'project:gitintegration:create',
                    kwargs={'project': self.project.name_short}), {
                        'url': 'http://im-a-dum.my/repo/path',
                        'rsa_priv_path': f1,
                        'rsa_pub_path': f2,
                    },
                                            follow=True)
        self.assertNotContains(
            response, "Your account doesn't have access to this page")
        self.project.refresh_from_db()
        self.assertEqual(self.project.repos.count(), 1)
        repo = self.project.repos.first()
        self.assertEqual(repo.url, 'http://im-a-dum.my/repo/path')

        # set correct repo path. This is not possible via request due to validators
        repo.url = repo_path
        repo.save()

        self.assertEqual(repo.url, repo_path)
        self.assertIn(filecontent1, repo.rsa_priv_path.read().decode())
        self.assertIn(filecontent2, repo.rsa_pub_path.read().decode())
        # read() opened the file
        repo.rsa_priv_path.close()
        repo.rsa_pub_path.close()

        # try importing without valid remote repository
        shutil.rmtree(repo_path, ignore_errors=True)
        shutil.rmtree(repo.get_local_repo_path(), ignore_errors=True)
        import_commits()
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, False)

        # create a local repo
        initial_file = repo_path + '/initial'
        remote_repo = Repo.init(repo_path)
        f_open = open(initial_file, 'wb')
        f_open.write('Content in first file\n'.encode())
        f_open.close()
        remote_repo.index.add([initial_file])
        remote_repo_master = remote_repo.index.commit("initial commit")
        initial_sha = remote_repo_master.hexsha

        # try import again, connection should now work, but no commit to import
        Frontend.import_new_commits(repo)
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(repo.last_commit_processed, remote_repo_master.hexsha)
        self.assertEqual(Commit.objects.count(), 0)

        # create commit with issue identifier in message
        f = repo_path + '/file1'
        open(f, 'wb').close()
        remote_repo.index.add([f])
        remote_repo_master = remote_repo.index.commit(self.project.name_short +
                                                      "-1 commit body")

        # import again, self.issue should now have a commit associated, activity shoud have increased
        activity = len(json.loads(repo.project.activity))
        Frontend.import_new_commits(repo)
        repo.refresh_from_db()
        self.assertGreater(len(json.loads(repo.project.activity)), activity)
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(repo.last_commit_processed, remote_repo_master.hexsha)
        self.assertEqual(Commit.objects.count(), 1)
        self.assertEqual(self.issue.commits.count(), 1)

        # examine commit
        c = self.issue.commits.first()
        self.assertNotEqual(c.author, '')
        self.assertEqual(c.name, remote_repo_master.hexsha)
        self.assertEqual(c.repository, repo)
        self.assertEqual(c.message, "commit body")
        self.assertEqual(len(c.get_tags()), 0)
        firstchange = c.get_changes()['file1']
        self.assertEqual(firstchange['lines'], 0)
        self.assertEqual(firstchange['insertions'], 0)
        self.assertEqual(firstchange['deletions'], 0)

        # empty changes, get_changes() must not crash
        savedchanges = c.get_changes()
        c.changes = ""
        c.save()
        c.get_changes()
        c.set_changes(savedchanges)
        self.assertEqual(c.get_changes(), remote_repo_master.stats.files)

        # permissions checks maps to project's functions
        self.assertEqual(c.user_has_read_permissions(self.user), True)
        self.assertEqual(c.user_has_read_permissions(self.user2), True)
        self.assertEqual(c.user_has_write_permissions(self.user), True)
        self.assertEqual(c.user_has_write_permissions(self.user2), False)

        # reset last_commit_processed and try again: no duplicates should appear in Commits
        repo.last_commit_processed = ''
        repo.save()
        Frontend.import_new_commits(repo)
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(repo.last_commit_processed, remote_repo_master.hexsha)
        self.assertEqual(Commit.objects.count(), 1)
        self.assertEqual(self.issue.commits.count(), 1)

        # commit with invalid issue id
        f = repo_path + '/file2'
        open(f, 'wb').close()
        remote_repo.index.add([f])
        remote_repo_master = remote_repo.index.commit(
            self.project.name_short + "-42 commit body with invalid issue id")
        # activity should not change
        activity = len(json.loads(repo.project.activity))
        Frontend.import_new_commits(repo)
        repo.refresh_from_db()
        self.assertEqual(len(json.loads(repo.project.activity)), activity)
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(repo.last_commit_processed, remote_repo_master.hexsha)
        self.assertEqual(Commit.objects.count(), 1)
        self.assertEqual(self.issue.commits.count(), 1)

        # create commit with issue identifier in message
        f = repo_path + '/file3'
        f_open = open(f, 'wb')
        f_open.write('Fancy file content\nEven with newline\n'.encode())
        f_open.close()
        remote_repo.index.add([f])
        remote_repo_master = remote_repo.index.commit(
            self.project.name_short + "-1 commit body for file 3\n" +
            "This time with longer commit message")
        tag = remote_repo.create_tag('Test-Tag', ref=remote_repo_master)

        # import again, self.issue should now have 2 commits associated
        activity = len(json.loads(repo.project.activity))
        Frontend.import_new_commits(repo)
        repo.refresh_from_db()
        self.assertGreater(len(json.loads(repo.project.activity)), activity)
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(repo.last_commit_processed, remote_repo_master.hexsha)
        self.assertEqual(Commit.objects.count(), 2)
        self.assertEqual(self.issue.commits.count(), 2)

        # examine newest commit
        c = self.issue.commits.get(name=remote_repo_master.hexsha)
        self.assertNotEqual(c.author, '')
        self.assertEqual(c.name, remote_repo_master.hexsha)
        self.assertEqual(c.repository, repo)
        self.assertEqual(
            c.message,
            "commit body for file 3\nThis time with longer commit message")
        self.assertEqual(c.get_title(), "commit body for file 3")
        self.assertEqual(c.get_name_short(), remote_repo_master.hexsha[:7])
        firstchange = c.get_changes()['file3']
        self.assertEqual(firstchange['lines'], 2)
        self.assertEqual(firstchange['insertions'], 2)
        self.assertEqual(firstchange['deletions'], 0)
        self.assertEqual(c.__str__(), "Commit " + remote_repo_master.hexsha)
        self.assertEqual(len(c.get_tags()), 1)
        self.assertEqual(c.get_tags()[0], 'Test-Tag')

        # test file diff view
        diff = '@@ -0,0 +1,2 @@\n+Fancy file content\n+Even with newline\n'.splitlines(
        )
        response = self.client.post(
            reverse('issue:commit_diff',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': c.issue.number,
                    }),
            {
                'filename': list(c.get_changes().keys())[0],
                'repository': repo.pk,
                'commit_sha': c.get_name_short(),
            },
        )
        self.assertNotContains(
            response, "Your account doesn't have access to this page")
        self.assertEqual(response.context_data['diff'], diff)
        self.assertEqual(response.context_data['filename'],
                         list(c.get_changes().keys())[0])
        self.assertEqual(response.context_data['commit_sha'],
                         c.get_name_short())

        # add another tag to an already imported commit
        tag = remote_repo.create_tag('Another_tag', ref=remote_repo_master)
        activity = len(json.loads(repo.project.activity))
        Frontend.import_new_commits(repo)
        repo.refresh_from_db()
        self.assertGreater(len(json.loads(repo.project.activity)), activity)

        c = self.issue.commits.get(name=remote_repo_master.hexsha)
        self.assertEqual(len(c.get_tags()), 2)
        self.assertIn('Test-Tag', c.get_tags())
        self.assertIn('Another_tag', c.get_tags())

        # check with insufficient privileges
        self.project.manager.clear()
        user_doesnt_pass_test_and_gets_404(
            self,
            'issue:commit_diff',
            address_kwargs={
                'project': self.project.name_short,
                'sqn_i': c.issue.number,
            },
            get_kwargs={
                'filename': list(c.get_changes().keys())[0],
                'repository': repo.pk,
                'commit_sha': c.get_name_short(),
            },
        )
        self.project.manager.add(self.user)

        # post broken data to view
        response = self.client.post(reverse('issue:commit_diff',
                                            kwargs={
                                                'project':
                                                self.project.name_short,
                                                'sqn_i': c.issue.number,
                                            }), {
                                                'filename': 'blabla',
                                                'repository': repo.pk,
                                                'commit_sha': 'blubber',
                                            },
                                    follow=True)
        self.assertEqual(response.context_data['diff'], ''.splitlines())
        self.assertEqual(response.context_data['filename'], 'blabla')
        self.assertEqual(response.context_data['commit_sha'], 'blubber')

        # check that commits are searchable
        sr = SearchFrontend.query('commit body', self.user)
        self.assertEqual(len(sr), 2)
        sr = SearchFrontend.query('Commit.message ~ "commit message"',
                                  self.user)
        self.assertEqual(len(sr), 1)
        self.assertEqual(sr[0][0],
                         "(" + c.get_name_short() + ") commit body for file 3")

        # examine file diffs
        f = repo_path + '/file4'
        f_open = open(f, 'wb')
        f_open.write('Fancy file content\n'.encode())
        f_open.close()
        remote_repo.index.add([f])
        remote_repo_master = remote_repo.index.commit('file4')
        Frontend.import_new_commits(repo)

        diff = Frontend.get_diff(repo, remote_repo_master.hexsha, 'file4')
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(diff, "@@ -0,0 +1 @@\n+Fancy file content\n")

        # check that commit changes are searchable
        sr = SearchFrontend.query('file', self.user)
        self.assertEqual(len(sr), 1)

        # check with first commit
        diff = Frontend.get_diff(repo, initial_sha, 'initial')
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, True)
        # TODO TESTCASE we're currently expecting an empty result because gitpython is broken as f**k
        self.assertEqual(diff, "")
        # self.assertEqual(diff, "@@ -0,0 +1 @@\n+Content in first file\n")

        # try with invalid filename
        diff = Frontend.get_diff(repo, c.get_name_short(), 'invalid')
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(diff, "")

        # try with invalid commit sha
        diff = Frontend.get_diff(repo, '1234567', 'invalid')
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, True)
        self.assertEqual(diff, "")

        # try with broken repo path (not yet checked out
        prj_name_short = self.project.name_short
        self.project.name_short = 'AAA'
        self.project.save()
        diff = Frontend.get_diff(repo, initial_sha, 'initial')
        repo.refresh_from_db()
        self.assertEqual(repo.conn_ok, False)
        self.assertEqual(diff, "")
        self.project.name_short = prj_name_short
        self.project.save()

        # delete the key files from the server
        os.unlink(repo.rsa_priv_path.path)
        os.unlink(repo.rsa_pub_path.path)
        # delete the key files locally
        os.unlink(tmp1.name)
        os.unlink(tmp2.name)
        # clean up locally
        shutil.rmtree(repo_path, ignore_errors=True)
        shutil.rmtree(repo.get_local_repo_path(), ignore_errors=True)
Beispiel #25
0
class ValidatorTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify those element they need to be created in setUp, instead of here
        cls.user = get_user_model().objects.create_user('test', '*****@*****.**', 'test1234')
        cls.project = Project(creator=cls.user, name_short='PRJ')
        cls.project.save()
        cls.project.developer.add(cls.user)
        cls.kanbancol = KanbanColumn(project=cls.project, position=4, name='test')
        cls.kanbancol.save()

    def setUp(self):
        self.client.force_login(self.user)
        # NOTE: this element gets modified by some of those tests, so this shall NOT be created in setUpTestData()
        self.issue = Issue(project=self.project, due_date='2016-12-16', kanbancol=self.kanbancol, storypoints='3')
        self.issue.save()
        self.time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")

    def test_date_is_present_or_past(self):
        valid_testobjs = {
            'datetime-today': timezone.now(),
            'datetime-past': timezone.now() - timedelta(days=1),
        }

        for obj in valid_testobjs:
            date_is_present_or_past(valid_testobjs[obj])

        invalid_testobjs = {
            'datetime-future': timezone.now() + timedelta(days=1),
            'invalid-object': self,
        }

        for obj in invalid_testobjs:
            self.assertRaises(ValidationError, date_is_present_or_past, invalid_testobjs[obj])

    def test_logged_time_is_positive(self):
        valid_testobjs = {
            'timedelta-positive': timedelta(days=1),
            'timedelta-positive2': timedelta(minutes=1)
                    }
        for obj in valid_testobjs:
            logged_time_is_positive(valid_testobjs[obj])

        invalid_testobjs = {
            'timedelta-negative': timedelta(),
            'timedelta-negative2': timedelta(minutes=-1),
            'invalid-object': self
        }

        for obj in invalid_testobjs:
            self.assertRaises(ValidationError, logged_time_is_positive, invalid_testobjs[obj])

    def test_logtime_time_syntax(self):
        valid = ['1d3h5m', '1d3h', '1d5m', '1d',
                 '2h15m', '3h', '10m',
                 '1d 3h 5m', '1d 3h', '1d 5m',
                 '2h 15m', '0d0h1m']

        invalid = ['asd', '12', '3h5', '1d  5h',
                   '3h1d5m', '5m1h', '3h 1d 5m',
                   '5m 1h', '3hm']
        invalid_not_positive = ['0d', '0h', '0m',
                                '0d0h0m', '0h0m', '0d0m']

        for v in valid:
            response = self.client.post(reverse('timelog:loginfo'),
                                        {'time': v, 'created_at': self.time, 'issue': self.issue.id},
                                        follow=True)
            self.assertNotContains(response, "Invalid time syntax" or "The logged time must be at least one minute")

        for v in invalid:
            response = self.client.post(reverse('timelog:loginfo'),
                                        {'time': v, 'created_at': self.time, 'issue': self.issue.id},
                                        follow=True)
            self.assertContains(response, "Invalid time syntax")

        for v in invalid_not_positive:
            response = self.client.post(reverse('timelog:loginfo'),
                                        {'time': v, 'created_at': self.time, 'issue': self.issue.id},
                                        follow=True)
            self.assertContains(response, "The logged time must be at least one minute")
Beispiel #26
0
class AttachmentTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify those elements they need to be created in setUp, instead of here
        cls.user = get_user_model().objects.create_user('a', 'b', 'c')
        cls.user2 = get_user_model().objects.create_user('d', 'e', 'f')

    def setUp(self):
        # NOTE: these element gets modified by some of those tests, so they should NOT be created in setUpTestData()
        self.client.force_login(self.user)
        self.project = Project(creator=self.user, name_short=proj_short)
        self.project.save()
        self.project.manager.add(self.user)
        self.project.developer.add(self.user)
        self.column = KanbanColumn(name='Column', position=4, project=self.project)
        self.column.save()
        self.issue = Issue(title="Test-Issue", project=self.project, kanbancol=self.column, type="Bug")
        self.issue.save()

    def test_view_and_template(self):
        # TODO TESTCASE invite_users
        #      use view_and_template()
        # TODO which views?
        #      - issue:download_attachment
        #      - issue:delete_attachment
        #      - ...
        pass

    def test_redirect_to_login_and_login_required(self):
        self.client.logout()
        # TODO TESTCASE invite_users
        #      redirect_to_login_and_login_required()
        # TODO which views?
        #      - issue:download_attachment
        pass

    def test_attachments_from_other_issues_of_same_project_invisible(self):
        # TODO TESTCASE
        pass

    def test_upload_attachment_with_get_request_disabled(self):
        # TODO TESTCASE
        pass

    def test_file_size_restriction(self):
        # verify that the allowed file size (attachment) is actually limited
        huge_file = TEST_FILE_PATH+'/16mb.txt'
        f = open(huge_file, "r")
        file_dict = {
            "file": f,
        }
        response = self.client.post(reverse('issue:detail', kwargs={'project': self.project.name_short,
                                            'sqn_i': self.issue.number}), file_dict)
        f.close()
        # TODO TESTCASE execute in try-except block and delete the file in except
        self.assertContains(response, "The uploaded file exceeds the allowed file size of: ")

    def test_create_and_download_attachment(self):
        # create file for uploading
        filecontent = 'Hello World'
        temp = tempfile.NamedTemporaryFile(delete=False)
        temp.write(filecontent.encode())
        temp.close()

        f = File(open(temp.name, 'r'))
        attachment = Attachment(file=f, creator=self.user, issue=self.issue)
        attachment.save()
        f.close()
        # delete the uploaded file locally
        os.unlink(temp.name)

        issue = Issue.objects.get(pk=self.issue.pk)
        self.assertEqual(self.issue.attachments.count(), 1)
        attachment = self.issue.attachments.first()
        self.assertEqual(attachment.creator, self.user)
        self.assertEqual(attachment.seqnum, 1)
        self.assertEqual(attachment.issue.nextAttachmentId, 2)

        response = self.client.get(reverse('issue:download_attachment',
                                   kwargs={'project': proj_short, 'sqn_i': 1, 'sqn_a': attachment.seqnum}))
        self.assertEqual(response.status_code, 200)
        self.assertEqual("application/octet-stream", response.get('Content-Type'))
        self.assertEqual(response.resolver_match.func.__name__, AttachmentDownloadView.as_view().__name__)
        # delete the uploaded file from the server
        os.unlink(MEDIA_ROOT + '/' + attachment.file.name)

    def test_attachment_delete(self):
        # create sample attachment
        filecontent = 'Hello World'
        temp = tempfile.NamedTemporaryFile(delete=False)
        temp.write(filecontent.encode())
        temp.close()
        f = File(open(temp.name, 'r'))
        attachment = Attachment(file=f, creator=self.user, issue=self.issue)
        attachment.save()
        f.close()
        filePath = attachment.file.path
        self.assertTrue(os.path.isfile(filePath))

        # delete the attachment
        response = self.client.get(reverse('issue:delete_attachment',
                                           kwargs={'project': self.project.name_short,
                                                   'sqn_i': self.issue.number,
                                                   'sqn_a': attachment.seqnum}),
                                   follow=True)
        self.assertRedirects(response, reverse('issue:detail', kwargs={'project': self.project.name_short,
                                                                       'sqn_i': self.issue.number}))
        self.assertFalse(self.issue.attachments.all().exists())
        self.assertFalse(os.path.isfile(filePath))

        # delete temp file locally
        os.unlink(temp.name)
    def test_issue_kanban_moveleftright(self):
        self.assertEqual(self.project.kanbancol.count(), 4)
        issue1 = Issue(title="issue1",
                       project=self.project,
                       kanbancol=self.project.kanbancol.first())
        issue1.save()

        self.assertEqual(issue1.get_left_kCol_for_issue(), -1)
        self.assertEqual(issue1.get_right_kCol_for_issue(), 1)

        # move issue to next row and check results
        issue1.kanbancol = self.project.kanbancol.get(position='1')
        issue1.save()
        self.assertEqual(issue1.get_left_kCol_for_issue(), 0)
        self.assertEqual(issue1.get_right_kCol_for_issue(), 2)

        # move issue to last row and check results
        issue1.kanbancol = self.project.kanbancol.get(position='3')
        issue1.save()
        self.assertEqual(issue1.get_left_kCol_for_issue(), 2)
        self.assertEqual(issue1.get_right_kCol_for_issue(), -1)

        # test view function
        response = self.client.post(
            reverse('issue:setkanbancol',
                    kwargs={'project': self.project.name_short}), {
                        'sqn_k': '0',
                        'sqn_i': issue1.number,
                    },
            follow=True)
        self.assertRedirects(
            response,
            reverse('issue:projList',
                    kwargs={'project': self.project.name_short}))
        issue1.refresh_from_db()
        self.assertEqual(issue1.kanbancol.position, 0)

        # check if error handling works (set col too high)
        response = self.client.post(
            reverse('issue:setkanbancol',
                    kwargs={'project': self.project.name_short}), {
                        'sqn_k': '4',
                        'sqn_i': issue1.number,
                    },
            follow=True)
        self.assertRedirects(
            response,
            reverse('issue:projList',
                    kwargs={'project': self.project.name_short}))
        self.assertEqual(len(list(response.context['messages'])), 1)
        issue1.refresh_from_db()
        self.assertEqual(issue1.kanbancol.position, 0)

        # check with invalid issue
        response = self.client.post(
            reverse('issue:setkanbancol',
                    kwargs={'project': self.project.name_short}), {
                        'sqn_k': '2',
                        'sqn_i': issue1.number + 1,
                    },
            follow=True)
        self.assertRedirects(
            response,
            reverse('issue:projList',
                    kwargs={'project': self.project.name_short}))
        self.assertEqual(len(list(response.context['messages'])), 1)
        issue1.refresh_from_db()
        self.assertEqual(issue1.kanbancol.position, 0)
Beispiel #28
0
class TimelogTest(StaticSeleniumTestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(
            'test', '*****@*****.**', 'test')
        self.project = Project(creator=self.user, name_short='PRJ')
        self.project.save()
        self.project.developer.add(self.user)
        # NOTE: those elements get modified by some of those tests, so this shall NOT be created in setUpTestData()
        self.issue = Issue(title='issue title',
                           project=self.project,
                           due_date='2016-12-16',
                           storypoints='3')
        self.issue.save()
        self.issue2 = Issue(title='title2',
                            project=self.project,
                            due_date='2016-12-16',
                            storypoints='3')
        self.issue2.save()
        self.issue.assignee.add(self.user)

        # Uses the cookie hack from:
        # https://stackoverflow.com/questions/22494583/login-with-code-when-using-liveservertestcase-with-django
        client = Client()
        client.login(username='******', password='******')
        self.cookie = client.cookies['sessionid']
        self.selenium.get("{}{}".format(self.live_server_url,
                                        reverse('timelog:loginfo')))
        self.selenium.add_cookie({
            'name': 'sessionid',
            'value': self.cookie.value,
            'secure': False,
            'path': '/'
        })
        self.selenium.refresh()

    def test_workflow(self):
        driver = self.selenium
        driver.get(self.live_server_url + reverse('landing_page:home'))
        driver.find_element_by_link_text("Projects").click()
        driver.find_element_by_link_text("Create new project").click()
        driver.find_element_by_id("id_name").clear()
        driver.find_element_by_id("id_name").send_keys("Test-Project")
        driver.find_element_by_id("id_name_short").clear()
        driver.find_element_by_id("id_name_short").send_keys("TTT")
        driver.find_element_by_id("id_description").clear()
        driver.find_element_by_id("id_description").send_keys("Description")
        driver.find_element_by_id("id_submit_create").click()
        driver.find_element_by_link_text("Board").click()
        driver.find_element_by_id("expression").clear()
        driver.find_element_by_id("expression").send_keys("Issue" +
                                                          Keys.RETURN)
        driver.find_element_by_id("expression").clear()
        driver.find_element_by_id("expression").send_keys("Another" +
                                                          Keys.RETURN)
        driver.find_element_by_id("expression").clear()
        driver.find_element_by_id("expression").send_keys("So funny" +
                                                          Keys.RETURN)
        driver.find_element_by_id("expression").clear()
        driver.find_element_by_id("expression").send_keys(">2 +10m" +
                                                          Keys.RETURN)
        driver.find_element_by_id("expression").clear()
        driver.find_element_by_id("expression").send_keys(">3 +1h" +
                                                          Keys.RETURN)
        driver.find_element_by_id("expression").clear()
        driver.find_element_by_id("expression").send_keys(">1 +15m" +
                                                          Keys.RETURN)
        driver.find_element_by_link_text("TTT-1").click()
        self.assertIn("15 Minutes",
                      driver.find_element_by_id("issue_detail_log_1").text)
        driver.find_element_by_link_text("TTT").click()
        driver.find_element_by_link_text("TTT-2").click()
        self.assertIn("10 Minutes",
                      driver.find_element_by_id("issue_detail_log_1").text)
        driver.find_element_by_link_text("TTT").click()
        driver.find_element_by_link_text("TTT-3").click()
        self.assertIn("1 Hour",
                      driver.find_element_by_id("issue_detail_log_1").text)
        driver.find_element_by_css_selector(
            "#issue_detail_log_1 > div > a > span.glyphicon.glyphicon-pencil"
        ).click()
        driver.find_element_by_id("id_time").clear()
        driver.find_element_by_id("id_time").send_keys("50m")
        driver.find_element_by_id("id_submit_save_timelog_change").click()
        self.assertIn("50 Minutes",
                      driver.find_element_by_id("issue_detail_log_1").text)
        driver.find_element_by_link_text("Timelogging").click()
        self.assertEqual(
            "total: 1 Hour and 15 Minutes",
            driver.find_element_by_css_selector("li.list-group-item > b").text)
        driver.find_element_by_id("timelog").click()
        self.assertIn(
            "10 Minutes",
            driver.find_element_by_css_selector(
                "#log_1 > div.row > div.col-xs-5").text)
        self.assertIn(
            "15 Minutes",
            driver.find_element_by_css_selector(
                "#log_2 > div.row > div.col-xs-5").text)
        self.assertIn(
            "50 Minutes",
            driver.find_element_by_css_selector(
                "#log_3 > div.row > div.col-xs-5").text)
        driver.find_element_by_link_text("Activity").click()
        driver.find_element_by_link_text("Last Week").click()
        driver.find_element_by_id("dropdownMenu1").click()
        driver.find_element_by_id("content").click()
        driver.find_element_by_id("dropdownMenu1").click()
        driver.find_element_by_link_text("Activity").click()
        driver.find_element_by_id("dropdownMenu1").click()
        driver.find_element_by_xpath(
            "//div[@id='content']/div/div[2]/div/div/ul/li[2]/a/b").click()
        driver.find_element_by_id("cal-heatmap-previous").click()
        driver.find_element_by_id("cal-heatmap-next").click()
        driver.find_element_by_css_selector("i.caret").click()
        driver.find_element_by_link_text("Profile").click()
        self.assertIn(
            "test",
            driver.find_element_by_css_selector("h1.page-header").text)
        driver.find_element_by_id("show_actions").click()

    def test_reachable_and_elements_exist(self):
        # TODO TESTCASE
        # TODO for each site check it is available + check (some) content like the title + check existence of forms
        #      and their form elements by their ids!
        # TODO timelog:loginfo
        # TODO project/<proj_pattern>/issue/<issue_sqn>/log
        pass

    def test_time_field_required(self):
        # TODO TESTCASE
        # TODO timelog:loginfo
        # TODO project/<proj_pattern>/issue/<issue_sqn>/log
        pass

    def test_issue_loglist(self):
        # TODO project/<proj_pattern>/issue/<issue_sqn>/logs
        # TODO this should only test if the elements created with models are listed here
        pass

    # TODO this should only test if the elements created with models are listed here (s.b.)
    def test_overview_log_form(self):
        driver = self.selenium
        driver.get(self.live_server_url + reverse('timelog:loginfo'))
        driver.find_element_by_id("id_time").send_keys("2h")
        self.assertEqual(
            len(Select(driver.find_element_by_id("id_issue")).options), 2)
        Select(driver.find_element_by_id("id_issue")).select_by_visible_text(
            "issue title")
        driver.find_element_by_css_selector(".save").click()
        entry = driver.find_element_by_id("log_1")
        self.assertIn('issue title', entry.text)
        self.assertIn('2 Hours', entry.text)
        driver.find_element_by_id("log_edit_link_1").click()
        driver.find_element_by_id("id_time").clear()
        driver.find_element_by_id("id_time").send_keys("1h")
        driver.find_element_by_css_selector(".save").click()
        driver.get(self.live_server_url + reverse('timelog:loginfo'))
        entry = driver.find_element_by_id("log_1")
        self.assertIn('issue title', entry.text)
        self.assertIn('1 Hour', entry.text)
        self.issue2.assignee.add(self.user)
        self.selenium.refresh()
        self.assertEqual(
            len(Select(driver.find_element_by_id("id_issue")).options), 3)
        driver.find_element_by_id("id_time").send_keys("5h")
        Select(driver.find_element_by_id("id_issue")).select_by_visible_text(
            "title2")
        driver.find_element_by_css_selector(".save").click()
        driver.get('{}{}'.format(
            self.live_server_url,
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': self.issue2.number
                    })))
        entry = driver.find_element_by_id("issue_detail_log_1")
        self.assertIn('5 Hours', entry.text)

        driver.get(self.live_server_url + reverse('timelog:loginfo'))
        driver.find_element_by_id("log_delete_link_2").click()
        driver.find_element_by_id('id_submit_delete').click()

        driver.get('{}{}'.format(
            self.live_server_url,
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': self.issue2.number
                    })))
        self.assertNotIn('5 Hours', driver.page_source)

    def test_issue_detail_log_form(self):
        driver = self.selenium
        t = Timelog(user=self.user, issue=self.issue, time=timedelta(hours=3))
        t.save()
        driver.get('{}{}'.format(
            self.live_server_url,
            reverse('issue:detail',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': self.issue.number
                    })))
        entry = driver.find_element_by_id("issue_detail_log_1")
        self.assertIn('3 Hour', entry.text)

    def test_delete_timelog(self):
        # TODO TESTCASE
        pass

    def test_keep_and_dont_delete_timelog(self):
        # TODO
        # TODO TESTCASE
        pass
Beispiel #29
0
    def test_start_and_stop_sprint(self):
        # self.sprint should not have started
        self.assertNotEqual(self.sprint.seqnum, -1)
        project = Project(creator=self.user, name_short='APF')
        project.save()
        project.manager.add(self.user)

        n = Sprint.objects.get_new_sprints().count()
        sprint = Sprint(project=project)
        sprint.save()
        self.assertEqual(Sprint.objects.get_new_sprints().count(), (n+1))
        self.assertTrue(sprint in Sprint.objects.get_new_sprints())
        self.assertFalse(sprint in Sprint.objects.get_current_sprints())
        self.assertFalse(sprint in Sprint.objects.get_old_sprints())
        self.assertIsNone(project.currentsprint)

        # columns and issues for testing archiving function
        kanbancol3 = KanbanColumn.objects.get(name='Todo', project=project)
        kanbancol3.type = 'Done'
        kanbancol3.save()
        kanbancol2 = KanbanColumn.objects.get(name='In Progress', project=project)
        kanbancol1 = KanbanColumn.objects.get(name='Done', project=project)
        kanbancol1.type = 'ToDo'
        kanbancol1.save()

        issue = Issue(title="Test-Issue", kanbancol=kanbancol1, project=project, type="Bug", sprint=sprint)
        issue.save()
        issue2 = Issue(title="Test-Issue2", kanbancol=kanbancol2, project=project, type="Bug", sprint=sprint)
        issue2.save()
        issue3 = Issue(title="Test-Issue3", kanbancol=kanbancol3, project=project, type="Bug", sprint=sprint)
        issue3.save()
        issue4 = Issue(title="Test-Issue4", kanbancol=kanbancol3, project=project, type="Bug")
        issue4.save()

        # issues (except issue4) should be in not started sprint
        self.assertFalse(issue in Issue.objects.without_sprint())
        self.assertFalse(issue in Issue.objects.current_sprint())
        self.assertFalse(issue in Issue.objects.archived())
        self.assertFalse(project.has_active_sprint())

        # start sprint
        response = self.client.post(reverse('sprint:startsprint',
                                    kwargs={'project': project.name_short, 'sqn_s': sprint.seqnum}))
        self.assertRedirects(response, reverse('backlog:backlog',
                             kwargs={'project': project.name_short, 'sqn_s': sprint.seqnum}))
        sprint.refresh_from_db()
        project.refresh_from_db()

        # sprint should be current sprint
        self.assertIsNotNone(project.currentsprint)
        self.assertEqual(project.currentsprint.seqnum, sprint.seqnum)
        self.assertTrue(project.has_active_sprint())
        self.assertFalse(sprint in Sprint.objects.get_new_sprints())
        self.assertTrue(sprint in Sprint.objects.get_current_sprints())
        self.assertFalse(sprint in Sprint.objects.get_old_sprints())

        # no issues archived until now
        self.assertFalse(issue in Issue.objects.without_sprint())
        self.assertTrue(issue in Issue.objects.current_sprint())
        self.assertFalse(issue in Issue.objects.archived())
        self.assertFalse(issue2 in Issue.objects.archived())
        self.assertFalse(issue3 in Issue.objects.archived())
        self.assertFalse(issue4 in Issue.objects.archived())

        # set sprint inactive(and archive)
        response = self.client.post(reverse('sprint:stopsprint',
                                            kwargs={'project': project.name_short, 'sqn_s': sprint.seqnum}),
                                    {'sprint': 'new', 'move_to_new_sprint': []},
                                    follow=True)
        self.assertRedirects(response, reverse('backlog:backlog',
                             kwargs={'project': project.name_short}))
        sprint.refresh_from_db()
        project.refresh_from_db()

        # sprint should no more be current sprint. not-done-issues back in backlog (not archived, without sprint)

        self.assertFalse(sprint in Sprint.objects.get_new_sprints())
        self.assertFalse(sprint in Sprint.objects.get_current_sprints())
        self.assertTrue(sprint in Sprint.objects.get_old_sprints())
        self.assertIsNone(project.currentsprint)
        self.assertFalse(project.has_active_sprint())
        self.assertTrue(issue in Issue.objects.without_sprint())
        self.assertFalse(issue in Issue.objects.current_sprint())

        # issue3 is archived because of column type and finished sprint,
        # issue (wrong type), issue2 (wrong type) and 4 (wrong sprint) arent
        self.assertFalse(issue in Issue.objects.archived())
        self.assertFalse(issue2 in Issue.objects.archived())
        self.assertTrue(issue3 in Issue.objects.archived())
        self.assertFalse(issue4 in Issue.objects.archived())

        # issue and issue4 are in same column, both without sprint
        issue4.kanbancol = kanbancol1
        issue4.save()

        # archive column of issue and issue4
        values = {'pos_c': kanbancol1.position}
        response = self.client.post(reverse('issue:archivecol',
                                    kwargs={'project': project.name_short}),
                                    values)
        self.assertRedirects(response, reverse('sprint:sprintboard',
                             kwargs={'project': project.name_short}))
        issue4.refresh_from_db()

        self.assertTrue(issue in Issue.objects.archived())  # archived by archivecol
        self.assertFalse(issue2 in Issue.objects.archived())  # not archived wrong type
        self.assertTrue(issue3 in Issue.objects.archived())  # archived by stopsprint
        self.assertTrue(issue4 in Issue.objects.archived())  # archived by archivecol
class ApiPermissionTest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify those elements they need to be created in setUp(), instead of here
        cls.user1 = get_user_model().objects.create_user('user1', 'mail', 'c')
        cls.user2 = get_user_model().objects.create_user('user2', 'othermail', 'c')

    def setUp(self):
        # NOTE: these elements get modified by some testcases, so they should NOT be created in setUpTestData()
        self.project = Project(creator=self.user1, name_short='asdf')
        self.project.save()
        self.project.developer.add(self.user1)
        self.project.developer.add(self.user2)
        self.project.manager.add(self.user1)
        self.project2 = Project(creator=self.user2, name_short='asdg')
        self.project2.save()
        self.project2.manager.add(self.user2)
        self.issue = Issue(title='test', project=self.project)
        self.issue.save()
        self.issue2 = Issue(title='test', project=self.project2)
        self.issue2.save()
        self.comment = Comment(text='test', creator=self.user1, issue=self.issue)
        self.comment.save()
        self.comment2 = Comment(text='test', creator=self.user2, issue=self.issue2)
        self.comment2.save()
        self.log = Timelog(time=datetime.timedelta(hours=2), user=self.user1, issue=self.issue)
        self.log.save()
        self.log2 = Timelog(time=datetime.timedelta(hours=2), user=self.user2, issue=self.issue2)
        self.log2.save()
        self.user1_auth = 'Basic ' + base64.b64encode('user1:c'.encode()).decode()
        self.user2_auth = 'Basic ' + base64.b64encode('user2:c'.encode()).decode()
        self.client.credentials(HTTP_AUTHORIZATION=self.user1_auth)

    def try_project_put_patch_delete(self, expected):
        project_data = {
            'name': 'yoflow',
        }
        response = self.client.patch(reverse('api:project-detail', kwargs={'name_short': self.project.name_short}),
                                     data=project_data)
        self.assertEqual(response.status_code, expected)
        response = self.client.put(reverse('api:project-detail', kwargs={'name_short': self.project.name_short}),
                                   data=project_data)
        self.assertEqual(response.status_code, expected)
        response = self.client.delete(reverse('api:project-detail', kwargs={'name_short': self.project.name_short}))
        self.assertEqual(response.status_code, expected)

    def test_project_permissions(self):
        # project1 granted
        response = self.client.get(reverse('api:project-detail', kwargs={'name_short': self.project.name_short}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # project2 denied for user1
        response = self.client.get(reverse('api:project-detail', kwargs={'name_short': self.project2.name_short}))
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # project2 granted for user2
        self.client.credentials(HTTP_AUTHORIZATION=self.user2_auth)
        response = self.client.get(reverse('api:project-detail', kwargs={'name_short': self.project2.name_short}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # project1 user2 put, patch, delete denied (no manager)
        self.try_project_put_patch_delete(status.HTTP_403_FORBIDDEN)

        # no credentials
        self.client.credentials()
        self.try_project_put_patch_delete(status.HTTP_401_UNAUTHORIZED)

    def test_project_issues_get_post_permissions(self):
        issue_data = {
                'title': 'laalalla'
                }
        # project1 granted
        response = self.client.get(reverse('api:project_issues-list', kwargs={'project': self.project.name_short}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        response = self.client.post(reverse('api:project_issues-list', kwargs={'project': self.project.name_short}),
                                    issue_data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        # project2 denied for user1
        response = self.client.get(reverse('api:project_issues-list', kwargs={'project': self.project2.name_short}))
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        response = self.client.post(reverse('api:project_issues-list', kwargs={'project': self.project2.name_short}),
                                    issue_data)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # project2 granted for user2
        self.client.credentials(HTTP_AUTHORIZATION=self.user2_auth)
        response = self.client.get(reverse('api:project_issues-list', kwargs={'project': self.project2.name_short}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        response = self.client.post(reverse('api:project_issues-list', kwargs={'project': self.project2.name_short}),
                                    issue_data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        # no credentials
        self.client.credentials()
        response = self.client.get(reverse('api:project_issues-list', kwargs={'project': self.project2.name_short}))
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
        response = self.client.post(reverse('api:project_issues-list', kwargs={'project': self.project2.name_short}),
                                    issue_data)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def try_issue_put_patch_delete(self, expected):
        issue_data = {
            'title': 'yoflow',
        }
        url = 'api:project_issues-detail'
        response = self.client.get(reverse(url, kwargs={'project': self.project2.name_short,
                                                        'number': self.issue2.number}))
        self.assertIn(response.status_code, expected)
        response = self.client.patch(reverse(url, kwargs={'project': self.project2.name_short,
                                                          'number': self.issue2.number}),
                                     data=issue_data)
        self.assertIn(response.status_code, expected)
        response = self.client.put(reverse(url, kwargs={'project': self.project2.name_short,
                                                        'number': self.issue2.number}),
                                   data=issue_data)
        self.assertIn(response.status_code, expected)
        response = self.client.delete(reverse(url, kwargs={'project': self.project2.name_short,
                                                           'number': self.issue2.number}))
        self.assertIn(response.status_code, expected)

    def test_project_issues_detail_put_patch_delete_permissions(self):

        # user1 not in project1
        self.try_issue_put_patch_delete([status.HTTP_403_FORBIDDEN])

        # user2 in project2
        self.client.credentials(HTTP_AUTHORIZATION=self.user2_auth)
        self.try_issue_put_patch_delete([status.HTTP_200_OK, status.HTTP_204_NO_CONTENT])

        # not credentials
        self.client.credentials()
        self.try_issue_put_patch_delete([status.HTTP_401_UNAUTHORIZED])

    def test_project_issue_comments_get_post_permissions(self):
        comment_data = {
                'text': 'laalalla'
                }
        # project1 granted
        response = self.client.get(reverse('api:project_issues_comments-list',
                                           kwargs={'project': self.project.name_short, 'issue': self.issue.number}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        response = self.client.post(reverse('api:project_issues_comments-list',
                                            kwargs={'project': self.project.name_short, 'issue': self.issue.number}),
                                    comment_data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        # project2 denied for user1
        response = self.client.get(reverse('api:project_issues_comments-list',
                                           kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}))
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        response = self.client.post(reverse('api:project_issues_comments-list',
                                            kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}),
                                    comment_data)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # project2 granted for user2
        self.client.credentials(HTTP_AUTHORIZATION=self.user2_auth)
        response = self.client.get(reverse('api:project_issues_comments-list',
                                           kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        response = self.client.post(reverse('api:project_issues_comments-list',
                                            kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}),
                                    comment_data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        # no credentials
        self.client.credentials()
        response = self.client.get(reverse('api:project_issues_comments-list',
                                           kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}))
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
        response = self.client.post(reverse('api:project_issues_comments-list',
                                            kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}),
                                    comment_data)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def try_issue_comments_put_patch_delete(self, expected):
        comment_data = {
            'text': 'lalala',
        }
        url = 'api:project_issues_comments-detail'
        response = self.client.get(reverse(url, kwargs={'project': self.project2.name_short,
                                                        'issue': self.issue2.number,
                                                        'seqnum': self.comment2.seqnum}))
        self.assertIn(response.status_code, expected)
        response = self.client.patch(reverse(url, kwargs={'project': self.project2.name_short,
                                                          'issue': self.issue2.number,
                                                          'seqnum': self.comment2.seqnum}),
                                     data=comment_data)
        self.assertIn(response.status_code, expected)
        response = self.client.put(reverse(url, kwargs={'project': self.project2.name_short,
                                                        'issue': self.issue2.number,
                                                        'seqnum': self.comment2.seqnum}),
                                   data=comment_data)
        self.assertIn(response.status_code, expected)
        response = self.client.delete(reverse(url, kwargs={'project': self.project2.name_short,
                                                           'issue': self.issue2.number,
                                                           'seqnum': self.comment2.seqnum}))
        self.assertIn(response.status_code, expected)

    def test_project_issues_comments_detail_put_patch_delete_permissions(self):
        # TODO TESTCASE difference manager/not manager

        # user1 not in project1
        self.try_issue_comments_put_patch_delete([status.HTTP_403_FORBIDDEN])

        self.client.credentials(HTTP_AUTHORIZATION=self.user2_auth)
        # user2 in project2
        self.try_issue_comments_put_patch_delete([status.HTTP_200_OK, status.HTTP_204_NO_CONTENT])
        self.client.credentials()
        self.try_issue_comments_put_patch_delete([status.HTTP_401_UNAUTHORIZED])

    def test_project_issue_timelogs_get_post_permissions(self):
        log_data = {
                'time': '2h5m'
                }
        # project1 granted
        response = self.client.get(reverse('api:project_issues_timelogs-list',
                                           kwargs={'project': self.project.name_short, 'issue': self.issue.number}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        response = self.client.post(reverse('api:project_issues_timelogs-list',
                                            kwargs={'project': self.project.name_short, 'issue': self.issue.number}),
                                    log_data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        # project2 denied for user1
        response = self.client.get(reverse('api:project_issues_timelogs-list',
                                           kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}))
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        response = self.client.post(reverse('api:project_issues_timelogs-list',
                                            kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}),
                                    log_data)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # project2 granted for user2
        self.client.credentials(HTTP_AUTHORIZATION=self.user2_auth)
        response = self.client.get(reverse('api:project_issues_timelogs-list',
                                           kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        response = self.client.post(reverse('api:project_issues_timelogs-list',
                                            kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}),
                                    log_data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

        # no credentials
        self.client.credentials()
        response = self.client.get(reverse('api:project_issues_timelogs-list',
                                           kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}))
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
        response = self.client.post(reverse('api:project_issues_timelogs-list',
                                            kwargs={'project': self.project2.name_short, 'issue': self.issue2.number}),
                                    log_data)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    # helper function
    def try_issue_timelogs_put_patch_delete(self, expected):
        log_data = {
            'time': '2h5m'
        }
        url = 'api:project_issues_timelogs-detail'
        response = self.client.get(reverse(url, kwargs={'project': self.project2.name_short,
                                                        'issue': self.issue2.number,
                                                        'number': self.log2.number}))
        self.assertIn(response.status_code, expected)
        response = self.client.patch(reverse(url, kwargs={'project': self.project2.name_short,
                                                          'issue': self.issue2.number,
                                                          'number': self.log2.number}),
                                     data=log_data)
        self.assertIn(response.status_code, expected)
        response = self.client.put(reverse(url, kwargs={'project': self.project2.name_short,
                                                        'issue': self.issue2.number,
                                                        'number': self.log2.number}),
                                   data=log_data)
        self.assertIn(response.status_code, expected)
        response = self.client.delete(reverse(url, kwargs={'project': self.project2.name_short,
                                                           'issue': self.issue2.number,
                                                           'number': self.log2.number}))
        self.assertIn(response.status_code, expected)

    def test_project_issues_timelogs_detail_put_patch_delete_permissions(self):
        # TODO TESTCASE difference manager/not manager
        # user1 not in project1
        self.try_issue_timelogs_put_patch_delete([status.HTTP_403_FORBIDDEN])

        self.client.credentials(HTTP_AUTHORIZATION=self.user2_auth)
        # user2 in project2
        self.try_issue_timelogs_put_patch_delete([status.HTTP_200_OK, status.HTTP_204_NO_CONTENT])
        self.client.credentials()
        self.try_issue_timelogs_put_patch_delete([status.HTTP_401_UNAUTHORIZED])
Beispiel #31
0
class IssueSearchTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify those elements they need to be created in setUp, instead of here
        cls.user = get_user_model().objects.create_user('a', 'b', 'c')

    def setUp(self):
        self.project = Project(creator=self.user, name_short='PRJ')
        self.project.save()
        self.project.manager.add(self.user)

        # create issue, comment and attachment
        self.issue = Issue(title="Test-Issue",
                           project=self.project,
                           kanbancol=self.project.kanbancol.first(),
                           type="Bug")
        self.issue.save()

        self.comment = Comment(issue=self.issue, creator=self.user)
        self.comment.save()

        self.attachment = Attachment(issue=self.issue, creator=self.user)
        self.attachment.save()

    def test_check_credentials(self):
        for i in [self.project, self.issue, self.comment, self.attachment]:
            self.assertEqual(
                getattr(i, 'user_has_read_permissions')(self.user), 1)
            self.assertEqual(
                getattr(i, 'user_has_write_permissions')(self.user), 1)

        # remove write permissions
        user2 = get_user_model().objects.create_user('d', 'e', 'f')

        self.project.developer.add(self.user)
        self.project.manager.remove(self.user)
        self.comment.creator = user2
        self.comment.save()
        self.attachment.creator = user2
        self.attachment.save()

        # issue and project do distinguish between read and write permissions
        for i in [self.project, self.issue]:
            self.assertEqual(
                getattr(i, 'user_has_read_permissions')(self.user), 1)
            self.assertEqual(
                getattr(i, 'user_has_write_permissions')(self.user), 0)

        for i in [self.comment, self.attachment]:
            self.assertEqual(
                getattr(i, 'user_has_read_permissions')(self.user), 1)
            self.assertEqual(
                getattr(i, 'user_has_write_permissions')(self.user), 0)

        # remove read permissions
        self.project.developer.remove(self.user)

        for i in [self.project, self.issue, self.comment, self.attachment]:
            self.assertEqual(
                getattr(i, 'user_has_read_permissions')(self.user), 0)
            self.assertEqual(
                getattr(i, 'user_has_write_permissions')(self.user), 0)
Beispiel #32
0
    def test_assign_tag_view(self):
        issue = Issue(title="Test-Issue",
                      project=self.project,
                      kanbancol=self.column,
                      type="Bug")
        issue.save()
        t0 = 'first_tag'
        tag0 = Tag(tag_text=t0, project=self.project)
        tag0.save()
        t1 = 'second_tag'
        tag1 = Tag(tag_text=t1, project=self.project)
        tag1.save()
        t2 = 'third_tag'
        tag2 = Tag(tag_text=t2, project=self.project)
        tag2.save()
        values = {
            # first_tag
            'tags': (1)
        }
        response = self.client.post(
            reverse('issue:edit',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': 1
                    }), values)
        # please don't ask me why the following (which would be a way better assert) doesn't work
        # self.assertContains(response, t0)
        self.assertIn(t0, str(response.content))
        self.assertEqual(Issue.objects.count(), 1)
        # TODO
        """
        self.assertEqual(issue.tags.count(), 1)
        self.assertEqual(issue.tags.first().text, t0)
        """
        response = self.client.get(
            reverse('issue:edit',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': 1
                    }))

        return
        # TODO TODO TODO the following doesn't work because t0 doesn't appear in the response anymore
        self.assertContains(response, t0)
        # self.assertIn(t0, str(response.content))
        values = {
            # 'tags': [t2, t1],
            'tags': [(3), (2)],
        }
        response = self.client.post(
            reverse('issue:edit',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': 1
                    }), values)
        self.assertContains(response, t0)
        self.assertContains(response, t2)
        self.assertContains(response, t1)

        response = self.client.get(
            reverse('issue:edit',
                    kwargs={
                        'project': self.project.name_short,
                        'sqn_i': 1
                    }))
        self.assertContains(response, t0)
        self.assertContains(response, t2)
        self.assertContains(response, t1)
Beispiel #33
0
class MailNotificationTest(TestCase):

    @classmethod
    def setUpTestData(cls):
        # NOTE: if you modify these elements they need to be created in setUp(), instead of here
        cls.user1 = get_user_model().objects.create_user('user1', '', 'c')
        cls.user2 = get_user_model().objects.create_user('user2', 'othermail', 'c')

    def setUp(self):
        # NOTE: these elements get modified by some testcases, so they should NOT be created in setUpTestData()
        # notify user2 on new issues
        self.user2.set_preference("notify_mail", json.dumps({'PRJ': ['NewIssue']}))
        self.project = Project(creator=self.user1, name_short='PRJ', name='Project')
        self.project.save()
        self.project.developer.add(self.user1)
        self.project.developer.add(self.user2)
        self.project.manager.add(self.user1)

        self.issue1 = Issue(creator=self.user1, title='test', project=self.project)
        self.issue1.save()

    def test_workflow(self):
        self.assertEqual(len(Notification.objects.all()), 1)

        # mail to user2 should have been sent
        # TODO TESTCASE temporarily disabled due to failing build on jenkins
        # self.assertEqual(len(outbox), 1)

        # rcvmail = outbox[0]
        # self.assertEqual(rcvmail.subject, "[Iguana] Notification from project \"Project\"")
        # self.assertEqual(rcvmail.to, ['othermail'])
        # self.assertIn("Someone created issue \"test\".", rcvmail.body)
        # self.assertIn("If you want to see it, visit https://" + HOST + self.issue1.get_absolute_url(), rcvmail.body)

    def test_task(self):
        # test invalid pk
        self.assertEqual(send_discussion_mail(4711), 1)

        # test empty mail address
        noti = Notification(user=self.user1, issue=self.issue1)
        noti.save()
        ntype = Notitype(type='NewIssue')
        ntype.save()
        noti.type.add(ntype)
        self.assertEqual(send_discussion_mail(noti.pk), 2)

        # test no notification necessary
        self.user1.email = 'mail'
        self.user1.save()
        self.assertEqual(send_discussion_mail(noti.pk), 3)
        self.user1.set_preference('notify_mail', json.dumps({'PRJ': ['Mention', 'NewComment']}))
        self.assertEqual(send_discussion_mail(noti.pk), 3)

        # test mail send
        self.user1.set_preference('notify_mail', json.dumps({'PRJ': ['Mention', 'NewComment', 'NewIssue']}))
        self.assertEqual(send_discussion_mail(noti.pk), 0)
        # assert that last mail sent goes to user1
        self.assertEqual(mail.outbox[len(mail.outbox)-1].to, ['mail'])

    def test_leave_project(self):
        self.client.force_login(self.user2)
        self.user2.set_preference('notify_mail', json.dumps({'PRJ': ['Mention', 'NewComment', 'NewIssue']}))
        self.user1.set_preference('notify_mail', json.dumps({'PRJ': ['Mention', 'NewComment', 'NewIssue']}))

        props = json.dumps(self.user2.get_preference('notify_mail'))
        self.assertIn('PRJ', props)

        props = json.dumps(self.user1.get_preference('notify_mail'))
        self.assertIn('PRJ', props)

        response = self.client.post(reverse('project:leave', kwargs={'project': 'PRJ'}),
                                    {'delete': 'true'},
                                    follow=True)
        self.assertEqual(response.status_code, 200)

        self.assertNotIn(self.user2, self.project.manager.all())

        props = json.dumps(self.user2.get_preference('notify_mail'))
        self.assertNotIn('PRJ', props)

        props = json.dumps(self.user1.get_preference('notify_mail'))
        self.assertIn('PRJ', props)