Mercurial > p > roundup > code
comparison test/test_cgi.py @ 6083:f74d078cfd9a
issue2551019 needs to be handled in the action code itself, not the WSGI handler
for Python 3 we always need to encode the output in the client character set
| author | Christof Meerwald <cmeerw@cmeerw.org> |
|---|---|
| date | Sat, 08 Feb 2020 00:29:13 +0000 |
| parents | 54d0080769f9 |
| children | 15fd91fd3c4c |
comparison
equal
deleted
inserted
replaced
| 6082:a3221c686736 | 6083:f74d078cfd9a |
|---|---|
| 7 # This module is distributed in the hope that it will be useful, | 7 # This module is distributed in the hope that it will be useful, |
| 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 10 | 10 |
| 11 from __future__ import print_function | 11 from __future__ import print_function |
| 12 import unittest, os, shutil, errno, sys, difflib, cgi, re | 12 import unittest, os, shutil, errno, sys, difflib, cgi, re, io |
| 13 | 13 |
| 14 import pytest | 14 import pytest |
| 15 | 15 |
| 16 from roundup.cgi import client, actions, exceptions | 16 from roundup.cgi import client, actions, exceptions |
| 17 from roundup.cgi.exceptions import FormError, NotFound, Redirect | 17 from roundup.cgi.exceptions import FormError, NotFound, Redirect |
| 18 from roundup.exceptions import UsageError, Reject | 18 from roundup.exceptions import UsageError, Reject |
| 19 from roundup.cgi.templating import HTMLItem, HTMLRequest, NoTemplate | 19 from roundup.cgi.templating import HTMLItem, HTMLRequest, NoTemplate |
| 20 from roundup.cgi.templating import HTMLProperty, _HTMLItem, anti_csrf_nonce | 20 from roundup.cgi.templating import HTMLProperty, _HTMLItem, anti_csrf_nonce |
| 21 from roundup.cgi.form_parser import FormParser | 21 from roundup.cgi.form_parser import FormParser |
| 22 from roundup import init, instance, password, hyperdb, date | 22 from roundup import init, instance, password, hyperdb, date |
| 23 from roundup.anypy.strings import StringIO, u2s, b2s | 23 from roundup.anypy.strings import u2s, b2s, s2b |
| 24 | 24 |
| 25 from time import sleep | 25 from time import sleep |
| 26 | 26 |
| 27 # For testing very simple rendering | 27 # For testing very simple rendering |
| 28 from roundup.cgi.engine_zopetal import RoundupPageTemplate | 28 from roundup.cgi.engine_zopetal import RoundupPageTemplate |
| 1785 key_id1=self.db.keyword.create(name='keyword1') | 1785 key_id1=self.db.keyword.create(name='keyword1') |
| 1786 key_id2=self.db.keyword.create(name='keyword2') | 1786 key_id2=self.db.keyword.create(name='keyword2') |
| 1787 self.db.issue.create(title='foo1', status='2', assignedto='4', nosy=['3',demo_id]) | 1787 self.db.issue.create(title='foo1', status='2', assignedto='4', nosy=['3',demo_id]) |
| 1788 self.db.issue.create(title='bar2', status='1', assignedto='3', keyword=[key_id1,key_id2]) | 1788 self.db.issue.create(title='bar2', status='1', assignedto='3', keyword=[key_id1,key_id2]) |
| 1789 self.db.issue.create(title='baz32', status='4') | 1789 self.db.issue.create(title='baz32', status='4') |
| 1790 output = StringIO() | 1790 output = io.BytesIO() |
| 1791 cl.request = MockNull() | 1791 cl.request = MockNull() |
| 1792 cl.request.wfile = output | 1792 cl.request.wfile = output |
| 1793 # call export version that outputs names | 1793 # call export version that outputs names |
| 1794 actions.ExportCSVAction(cl).handle() | 1794 actions.ExportCSVAction(cl).handle() |
| 1795 #print(output.getvalue()) | 1795 #print(output.getvalue()) |
| 1796 should_be=('id,title,status,keyword,assignedto,nosy\r\n' | 1796 should_be=(s2b('id,title,status,keyword,assignedto,nosy\r\n' |
| 1797 '1,foo1,deferred,,"Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n' | 1797 '1,foo1,deferred,,"Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n' |
| 1798 '2,bar2,unread,keyword1;keyword2,"Bork, Chef","Bork, Chef"\r\n' | 1798 '2,bar2,unread,keyword1;keyword2,"Bork, Chef","Bork, Chef"\r\n' |
| 1799 '3,baz32,need-eg,,,\r\n') | 1799 '3,baz32,need-eg,,,\r\n')) |
| 1800 #print(should_be) | 1800 #print(should_be) |
| 1801 #print(output.getvalue()) | 1801 #print(output.getvalue()) |
| 1802 self.assertEqual(output.getvalue(), should_be) | 1802 self.assertEqual(output.getvalue(), should_be) |
| 1803 output = StringIO() | 1803 output = io.BytesIO() |
| 1804 cl.request = MockNull() | 1804 cl.request = MockNull() |
| 1805 cl.request.wfile = output | 1805 cl.request.wfile = output |
| 1806 # call export version that outputs id numbers | 1806 # call export version that outputs id numbers |
| 1807 actions.ExportCSVWithIdAction(cl).handle() | 1807 actions.ExportCSVWithIdAction(cl).handle() |
| 1808 print(output.getvalue()) | 1808 print(output.getvalue()) |
| 1809 self.assertEqual('id,title,status,keyword,assignedto,nosy\r\n' | 1809 self.assertEqual(s2b('id,title,status,keyword,assignedto,nosy\r\n' |
| 1810 "1,foo1,2,[],4,\"['3', '4', '5']\"\r\n" | 1810 "1,foo1,2,[],4,\"['3', '4', '5']\"\r\n" |
| 1811 "2,bar2,1,\"['1', '2']\",3,['3']\r\n" | 1811 "2,bar2,1,\"['1', '2']\",3,['3']\r\n" |
| 1812 '3,baz32,4,[],None,[]\r\n', | 1812 '3,baz32,4,[],None,[]\r\n'), |
| 1813 output.getvalue()) | 1813 output.getvalue()) |
| 1814 | |
| 1815 def testCSVExportCharset(self): | |
| 1816 cl = self._make_client( | |
| 1817 {'@columns': 'id,title,status,keyword,assignedto,nosy'}, | |
| 1818 nodeid=None, userid='1') | |
| 1819 cl.classname = 'issue' | |
| 1820 | |
| 1821 demo_id=self.db.user.create(username='demo', address='demo@test.test', | |
| 1822 roles='User', realname='demo') | |
| 1823 self.db.issue.create(title=b2s(b'foo1\xc3\xa4'), status='2', assignedto='4', nosy=['3',demo_id]) | |
| 1824 | |
| 1825 output = io.BytesIO() | |
| 1826 cl.request = MockNull() | |
| 1827 cl.request.wfile = output | |
| 1828 # call export version that outputs names | |
| 1829 actions.ExportCSVAction(cl).handle() | |
| 1830 should_be=(b'id,title,status,keyword,assignedto,nosy\r\n' | |
| 1831 b'1,foo1\xc3\xa4,deferred,,"Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n') | |
| 1832 self.assertEqual(output.getvalue(), should_be) | |
| 1833 | |
| 1834 output = io.BytesIO() | |
| 1835 cl.request = MockNull() | |
| 1836 cl.request.wfile = output | |
| 1837 # call export version that outputs id numbers | |
| 1838 actions.ExportCSVWithIdAction(cl).handle() | |
| 1839 print(output.getvalue()) | |
| 1840 self.assertEqual(b'id,title,status,keyword,assignedto,nosy\r\n' | |
| 1841 b"1,foo1\xc3\xa4,2,[],4,\"['3', '4', '5']\"\r\n", | |
| 1842 output.getvalue()) | |
| 1843 | |
| 1844 # again with ISO-8859-1 client charset | |
| 1845 cl.charset = 'iso8859-1' | |
| 1846 output = io.BytesIO() | |
| 1847 cl.request = MockNull() | |
| 1848 cl.request.wfile = output | |
| 1849 # call export version that outputs names | |
| 1850 actions.ExportCSVAction(cl).handle() | |
| 1851 should_be=(b'id,title,status,keyword,assignedto,nosy\r\n' | |
| 1852 b'1,foo1\xe4,deferred,,"Contrary, Mary","Bork, Chef;Contrary, Mary;demo"\r\n') | |
| 1853 self.assertEqual(output.getvalue(), should_be) | |
| 1854 | |
| 1855 output = io.BytesIO() | |
| 1856 cl.request = MockNull() | |
| 1857 cl.request.wfile = output | |
| 1858 # call export version that outputs id numbers | |
| 1859 actions.ExportCSVWithIdAction(cl).handle() | |
| 1860 print(output.getvalue()) | |
| 1861 self.assertEqual(b'id,title,status,keyword,assignedto,nosy\r\n' | |
| 1862 b"1,foo1\xe4,2,[],4,\"['3', '4', '5']\"\r\n", | |
| 1863 output.getvalue()) | |
| 1814 | 1864 |
| 1815 def testCSVExportBadColumnName(self): | 1865 def testCSVExportBadColumnName(self): |
| 1816 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, | 1866 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, |
| 1817 userid='1') | 1867 userid='1') |
| 1818 cl.classname = 'status' | 1868 cl.classname = 'status' |
| 1819 output = StringIO() | 1869 output = io.BytesIO() |
| 1820 cl.request = MockNull() | 1870 cl.request = MockNull() |
| 1821 cl.request.wfile = output | 1871 cl.request.wfile = output |
| 1822 self.assertRaises(exceptions.NotFound, | 1872 self.assertRaises(exceptions.NotFound, |
| 1823 actions.ExportCSVAction(cl).handle) | 1873 actions.ExportCSVAction(cl).handle) |
| 1824 | 1874 |
| 1825 def testCSVExportFailPermissionBadColumn(self): | 1875 def testCSVExportFailPermissionBadColumn(self): |
| 1826 cl = self._make_client({'@columns': 'id,email,password'}, nodeid=None, | 1876 cl = self._make_client({'@columns': 'id,email,password'}, nodeid=None, |
| 1827 userid='2') | 1877 userid='2') |
| 1828 cl.classname = 'user' | 1878 cl.classname = 'user' |
| 1829 output = StringIO() | 1879 output = io.BytesIO() |
| 1830 cl.request = MockNull() | 1880 cl.request = MockNull() |
| 1831 cl.request.wfile = output | 1881 cl.request.wfile = output |
| 1832 # used to be self.assertRaises(exceptions.Unauthorised, | 1882 # used to be self.assertRaises(exceptions.Unauthorised, |
| 1833 # but not acting like the column name is not found | 1883 # but not acting like the column name is not found |
| 1834 # see issue2550755 - should this return Unauthorised? | 1884 # see issue2550755 - should this return Unauthorised? |
| 1843 roles='User', realname='demo', | 1893 roles='User', realname='demo', |
| 1844 password=passwd) | 1894 password=passwd) |
| 1845 cl = self._make_client({'@columns': 'id,username,address,password'}, | 1895 cl = self._make_client({'@columns': 'id,username,address,password'}, |
| 1846 nodeid=None, userid=demo_id) | 1896 nodeid=None, userid=demo_id) |
| 1847 cl.classname = 'user' | 1897 cl.classname = 'user' |
| 1848 output = StringIO() | 1898 output = io.BytesIO() |
| 1849 cl.request = MockNull() | 1899 cl.request = MockNull() |
| 1850 cl.request.wfile = output | 1900 cl.request.wfile = output |
| 1851 # used to be self.assertRaises(exceptions.Unauthorised, | 1901 # used to be self.assertRaises(exceptions.Unauthorised, |
| 1852 # but not acting like the column name is not found | 1902 # but not acting like the column name is not found |
| 1853 | 1903 |
| 1854 actions.ExportCSVAction(cl).handle() | 1904 actions.ExportCSVAction(cl).handle() |
| 1855 #print(output.getvalue()) | 1905 #print(output.getvalue()) |
| 1856 self.assertEqual('id,username,address,password\r\n' | 1906 self.assertEqual(s2b('id,username,address,password\r\n' |
| 1857 '1,admin,[hidden],[hidden]\r\n' | 1907 '1,admin,[hidden],[hidden]\r\n' |
| 1858 '2,anonymous,[hidden],[hidden]\r\n' | 1908 '2,anonymous,[hidden],[hidden]\r\n' |
| 1859 '3,Chef,[hidden],[hidden]\r\n' | 1909 '3,Chef,[hidden],[hidden]\r\n' |
| 1860 '4,mary,[hidden],[hidden]\r\n' | 1910 '4,mary,[hidden],[hidden]\r\n' |
| 1861 '5,demo,demo@test.test,%s\r\n'%(passwd), | 1911 '5,demo,demo@test.test,%s\r\n'%(passwd)), |
| 1862 output.getvalue()) | 1912 output.getvalue()) |
| 1863 | 1913 |
| 1864 def testCSVExportWithId(self): | 1914 def testCSVExportWithId(self): |
| 1865 cl = self._make_client({'@columns': 'id,name'}, nodeid=None, | 1915 cl = self._make_client({'@columns': 'id,name'}, nodeid=None, |
| 1866 userid='1') | 1916 userid='1') |
| 1867 cl.classname = 'status' | 1917 cl.classname = 'status' |
| 1868 output = StringIO() | 1918 output = io.BytesIO() |
| 1869 cl.request = MockNull() | 1919 cl.request = MockNull() |
| 1870 cl.request.wfile = output | 1920 cl.request.wfile = output |
| 1871 actions.ExportCSVWithIdAction(cl).handle() | 1921 actions.ExportCSVWithIdAction(cl).handle() |
| 1872 self.assertEqual('id,name\r\n1,unread\r\n2,deferred\r\n3,chatting\r\n' | 1922 self.assertEqual(s2b('id,name\r\n1,unread\r\n2,deferred\r\n3,chatting\r\n' |
| 1873 '4,need-eg\r\n5,in-progress\r\n6,testing\r\n7,done-cbb\r\n' | 1923 '4,need-eg\r\n5,in-progress\r\n6,testing\r\n7,done-cbb\r\n' |
| 1874 '8,resolved\r\n', | 1924 '8,resolved\r\n'), |
| 1875 output.getvalue()) | 1925 output.getvalue()) |
| 1876 | 1926 |
| 1877 def testCSVExportWithIdBadColumnName(self): | 1927 def testCSVExportWithIdBadColumnName(self): |
| 1878 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, | 1928 cl = self._make_client({'@columns': 'falseid,name'}, nodeid=None, |
| 1879 userid='1') | 1929 userid='1') |
| 1880 cl.classname = 'status' | 1930 cl.classname = 'status' |
| 1881 output = StringIO() | 1931 output = io.BytesIO() |
| 1882 cl.request = MockNull() | 1932 cl.request = MockNull() |
| 1883 cl.request.wfile = output | 1933 cl.request.wfile = output |
| 1884 self.assertRaises(exceptions.NotFound, | 1934 self.assertRaises(exceptions.NotFound, |
| 1885 actions.ExportCSVWithIdAction(cl).handle) | 1935 actions.ExportCSVWithIdAction(cl).handle) |
| 1886 | 1936 |
| 1887 def testCSVExportWithIdFailPermissionBadColumn(self): | 1937 def testCSVExportWithIdFailPermissionBadColumn(self): |
| 1888 cl = self._make_client({'@columns': 'id,email,password'}, nodeid=None, | 1938 cl = self._make_client({'@columns': 'id,email,password'}, nodeid=None, |
| 1889 userid='2') | 1939 userid='2') |
| 1890 cl.classname = 'user' | 1940 cl.classname = 'user' |
| 1891 output = StringIO() | 1941 output = io.BytesIO() |
| 1892 cl.request = MockNull() | 1942 cl.request = MockNull() |
| 1893 cl.request.wfile = output | 1943 cl.request.wfile = output |
| 1894 # used to be self.assertRaises(exceptions.Unauthorised, | 1944 # used to be self.assertRaises(exceptions.Unauthorised, |
| 1895 # but not acting like the column name is not found | 1945 # but not acting like the column name is not found |
| 1896 # see issue2550755 - should this return Unauthorised? | 1946 # see issue2550755 - should this return Unauthorised? |
| 1901 | 1951 |
| 1902 def testCSVExportWithIdFailPermissionValidColumn(self): | 1952 def testCSVExportWithIdFailPermissionValidColumn(self): |
| 1903 cl = self._make_client({'@columns': 'id,address,password'}, nodeid=None, | 1953 cl = self._make_client({'@columns': 'id,address,password'}, nodeid=None, |
| 1904 userid='2') | 1954 userid='2') |
| 1905 cl.classname = 'user' | 1955 cl.classname = 'user' |
| 1906 output = StringIO() | 1956 output = io.BytesIO() |
| 1907 cl.request = MockNull() | 1957 cl.request = MockNull() |
| 1908 cl.request.wfile = output | 1958 cl.request.wfile = output |
| 1909 # used to be self.assertRaises(exceptions.Unauthorised, | 1959 # used to be self.assertRaises(exceptions.Unauthorised, |
| 1910 # but not acting like the column name is not found | 1960 # but not acting like the column name is not found |
| 1911 self.assertRaises(exceptions.Unauthorised, | 1961 self.assertRaises(exceptions.Unauthorised, |
