##// END OF EJS Templates
tests: move test.ini to kallithea/tests/
Mads Kiilerich -
r5416:19267f23 default
parent child Browse files
Show More
@@ -1,29 +1,30 b''
1 syntax: glob
1 syntax: glob
2 *.pyc
2 *.pyc
3 *.swp
3 *.swp
4 *.sqlite
4 *.sqlite
5 *.tox
5 *.tox
6 *.egg-info
6 *.egg-info
7 *.egg
7 *.egg
8 *.mo
8 *.mo
9 .eggs/
9 .eggs/
10 tarballcache/
10 tarballcache/
11
11
12 syntax: regexp
12 syntax: regexp
13 ^rcextensions
13 ^rcextensions
14 ^build
14 ^build
15 ^dist/
15 ^dist/
16 ^docs/build/
16 ^docs/build/
17 ^docs/_build/
17 ^docs/_build/
18 ^data$
18 ^data$
19 ^kallithea/tests/data$
19 ^sql_dumps/
20 ^sql_dumps/
20 ^\.settings$
21 ^\.settings$
21 ^\.project$
22 ^\.project$
22 ^\.pydevproject$
23 ^\.pydevproject$
23 ^\.coverage$
24 ^\.coverage$
24 ^kallithea\.db$
25 ^kallithea\.db$
25 ^test\.db$
26 ^test\.db$
26 ^Kallithea\.egg-info$
27 ^Kallithea\.egg-info$
27 ^my\.ini$
28 ^my\.ini$
28 ^fabfile.py
29 ^fabfile.py
29 ^\.idea$
30 ^\.idea$
@@ -1,21 +1,21 b''
1 include Apache-License-2.0.txt
1 include Apache-License-2.0.txt
2 include CONTRIBUTORS
2 include CONTRIBUTORS
3 include COPYING
3 include COPYING
4 include LICENSE-MERGELY.html
4 include LICENSE-MERGELY.html
5 include LICENSE.md
5 include LICENSE.md
6 include MIT-Permissive-License.txt
6 include MIT-Permissive-License.txt
7 include README.rst
7 include README.rst
8 include development.ini
8 include development.ini
9 recursive-include docs *
9 recursive-include docs *
10 recursive-include init.d *
10 recursive-include init.d *
11 include kallithea/bin/ldap_sync.conf
11 include kallithea/bin/ldap_sync.conf
12 include kallithea/bin/template.ini.mako
12 include kallithea/bin/template.ini.mako
13 include kallithea/config/deployment.ini_tmpl
13 include kallithea/config/deployment.ini_tmpl
14 recursive-include kallithea/i18n *
14 recursive-include kallithea/i18n *
15 recursive-include kallithea/lib/dbmigrate *.py_tmpl README migrate.cfg
15 recursive-include kallithea/lib/dbmigrate *.py_tmpl README migrate.cfg
16 recursive-include kallithea/public *
16 recursive-include kallithea/public *
17 recursive-include kallithea/templates *
17 recursive-include kallithea/templates *
18 recursive-include kallithea/tests/fixtures *
18 recursive-include kallithea/tests/fixtures *
19 recursive-include kallithea/tests/scripts *
19 recursive-include kallithea/tests/scripts *
20 include kallithea/tests/test.ini
20 include kallithea/tests/vcs/aconfig
21 include kallithea/tests/vcs/aconfig
21 include test.ini
@@ -1,149 +1,149 b''
1 .. _contributing:
1 .. _contributing:
2
2
3 =========================
3 =========================
4 Contributing to Kallithea
4 Contributing to Kallithea
5 =========================
5 =========================
6
6
7 Kallithea is developed and maintained by its users. Please join us and scratch
7 Kallithea is developed and maintained by its users. Please join us and scratch
8 your own itch.
8 your own itch.
9
9
10
10
11 Infrastructure
11 Infrastructure
12 --------------
12 --------------
13
13
14 The main repository is hosted at Our Own Kallithea (aka OOK) on
14 The main repository is hosted at Our Own Kallithea (aka OOK) on
15 https://kallithea-scm.org/repos/kallithea/ (which is our self-hosted instance
15 https://kallithea-scm.org/repos/kallithea/ (which is our self-hosted instance
16 of Kallithea).
16 of Kallithea).
17
17
18 For now, we use Bitbucket_ for `Pull Requests`_ and `Issue Tracker`_ services. The
18 For now, we use Bitbucket_ for `Pull Requests`_ and `Issue Tracker`_ services. The
19 issue tracker is for tracking bugs, not for support, discussion, or ideas -
19 issue tracker is for tracking bugs, not for support, discussion, or ideas -
20 please use the `mailing list`_ to reach the community.
20 please use the `mailing list`_ to reach the community.
21
21
22 We use Weblate_ to translate the user interface messages into languages other
22 We use Weblate_ to translate the user interface messages into languages other
23 than English. Join our project on `Hosted Weblate`_ to help us.
23 than English. Join our project on `Hosted Weblate`_ to help us.
24 To register, you can use your Bitbucket or GitHub account. See :ref:`translations`
24 To register, you can use your Bitbucket or GitHub account. See :ref:`translations`
25 for more details.
25 for more details.
26
26
27 Getting started
27 Getting started
28 ---------------
28 ---------------
29
29
30 To get started with development::
30 To get started with development::
31
31
32 hg clone https://kallithea-scm.org/repos/kallithea
32 hg clone https://kallithea-scm.org/repos/kallithea
33 cd kallithea
33 cd kallithea
34 virtualenv ../kallithea-venv
34 virtualenv ../kallithea-venv
35 source ../kallithea-venv/bin/activate
35 source ../kallithea-venv/bin/activate
36 python setup.py develop
36 python setup.py develop
37 paster make-config Kallithea my.ini
37 paster make-config Kallithea my.ini
38 paster setup-db my.ini --user=user --email=user@example.com --password=password --repos=/tmp
38 paster setup-db my.ini --user=user --email=user@example.com --password=password --repos=/tmp
39 paster serve my.ini --reload &
39 paster serve my.ini --reload &
40 firefox http://127.0.0.1:5000/
40 firefox http://127.0.0.1:5000/
41
41
42 You can also start out by forking https://bitbucket.org/conservancy/kallithea
42 You can also start out by forking https://bitbucket.org/conservancy/kallithea
43 on Bitbucket_ and create a local clone of your own fork.
43 on Bitbucket_ and create a local clone of your own fork.
44
44
45
45
46 Running tests
46 Running tests
47 -------------
47 -------------
48
48
49 After finishing your changes make sure all tests pass cleanly. You can run
49 After finishing your changes make sure all tests pass cleanly. You can run
50 the testsuite running ``nosetests`` from the project root, or if you use tox
50 the testsuite running ``nosetests`` from the project root, or if you use tox
51 run ``tox`` for python2.6-2.7 with multiple database test.
51 run ``tox`` for python2.6-2.7 with multiple database test.
52
52
53 When using `nosetests`, the `test.ini` file is used with an SQLite database. Edit
53 When running tests, Kallithea uses `kallithea/tests/test.ini` and populates the
54 this file to change your testing enviroment.
54 SQLite database specified there.
55
55
56 It is possible to avoid recreating the full test database on each invocation of
56 It is possible to avoid recreating the full test database on each invocation of
57 the tests, thus eliminating the initial delay. To achieve this, run the tests as::
57 the tests, thus eliminating the initial delay. To achieve this, run the tests as::
58
58
59 paster serve test.ini --pid-file=test.pid --daemon
59 paster serve kallithea/tests/test.ini --pid-file=test.pid --daemon
60 KALLITHEA_WHOOSH_TEST_DISABLE=1 KALLITHEA_NO_TMP_PATH=1 nosetests
60 KALLITHEA_WHOOSH_TEST_DISABLE=1 KALLITHEA_NO_TMP_PATH=1 nosetests
61 kill -9 $(cat test.pid)
61 kill -9 $(cat test.pid)
62
62
63 You can run individual tests by specifying their path as argument to nosetests.
63 You can run individual tests by specifying their path as argument to nosetests.
64 nosetests also has many more options, see `nosetests -h`. Some useful options
64 nosetests also has many more options, see `nosetests -h`. Some useful options
65 are::
65 are::
66
66
67 -x, --stop Stop running tests after the first error or failure
67 -x, --stop Stop running tests after the first error or failure
68 -s, --nocapture Don't capture stdout (any stdout output will be
68 -s, --nocapture Don't capture stdout (any stdout output will be
69 printed immediately) [NOSE_NOCAPTURE]
69 printed immediately) [NOSE_NOCAPTURE]
70 --failed Run the tests that failed in the last test run.
70 --failed Run the tests that failed in the last test run.
71
71
72 Coding/contribution guidelines
72 Coding/contribution guidelines
73 ------------------------------
73 ------------------------------
74
74
75 Kallithea is GPLv3 and we assume all contributions are made by the
75 Kallithea is GPLv3 and we assume all contributions are made by the
76 committer/contributor and under GPLv3 unless explicitly stated. We do care a
76 committer/contributor and under GPLv3 unless explicitly stated. We do care a
77 lot about preservation of copyright and license information for existing code
77 lot about preservation of copyright and license information for existing code
78 that is brought into the project.
78 that is brought into the project.
79
79
80 We don't have a formal coding/formatting standard. We are currently using a mix
80 We don't have a formal coding/formatting standard. We are currently using a mix
81 of Mercurial (http://mercurial.selenic.com/wiki/CodingStyle), pep8, and
81 of Mercurial (http://mercurial.selenic.com/wiki/CodingStyle), pep8, and
82 consistency with existing code. Run whitespacecleanup.sh to avoid stupid
82 consistency with existing code. Run whitespacecleanup.sh to avoid stupid
83 whitespace noise in your patches.
83 whitespace noise in your patches.
84
84
85 We support both Python 2.6.x and 2.7.x and nothing else. For now we don't care
85 We support both Python 2.6.x and 2.7.x and nothing else. For now we don't care
86 about Python 3 compatibility.
86 about Python 3 compatibility.
87
87
88 We try to support the most common modern web browsers. IE9 is still supported
88 We try to support the most common modern web browsers. IE9 is still supported
89 to the extent it is feasible, IE8 is not.
89 to the extent it is feasible, IE8 is not.
90
90
91 We primarily support Linux and OS X on the server side but Windows should also work.
91 We primarily support Linux and OS X on the server side but Windows should also work.
92
92
93 Html templates should use 2 spaces for indentation ... but be pragmatic. We
93 Html templates should use 2 spaces for indentation ... but be pragmatic. We
94 should use templates cleverly and avoid duplication. We should use reasonable
94 should use templates cleverly and avoid duplication. We should use reasonable
95 semantic markup with classes and ids that can be used for styling and testing.
95 semantic markup with classes and ids that can be used for styling and testing.
96 We should only use inline styles in places where it really is semantic (such as
96 We should only use inline styles in places where it really is semantic (such as
97 display:none).
97 display:none).
98
98
99 JavaScript must use ';' between/after statements. Indentation 4 spaces. Inline
99 JavaScript must use ';' between/after statements. Indentation 4 spaces. Inline
100 multiline functions should be indented two levels - one for the () and one for
100 multiline functions should be indented two levels - one for the () and one for
101 {}. jQuery value arrays should have a leading $.
101 {}. jQuery value arrays should have a leading $.
102
102
103 Commit messages should have a leading short line summarizing the changes. For
103 Commit messages should have a leading short line summarizing the changes. For
104 bug fixes, put "(Issue #123)" at the end of this line.
104 bug fixes, put "(Issue #123)" at the end of this line.
105
105
106 Contributions will be accepted in most formats - such as pull requests on
106 Contributions will be accepted in most formats - such as pull requests on
107 bitbucket, something hosted on your own Kallithea instance, or patches sent by
107 bitbucket, something hosted on your own Kallithea instance, or patches sent by
108 email to the kallithea-general mailing list.
108 email to the kallithea-general mailing list.
109
109
110 Make sure to test your changes both manually and with the automatic tests
110 Make sure to test your changes both manually and with the automatic tests
111 before posting.
111 before posting.
112
112
113 We care about quality and review and keeping a clean repository history. We
113 We care about quality and review and keeping a clean repository history. We
114 might give feedback that requests polishing contributions until they are
114 might give feedback that requests polishing contributions until they are
115 "perfect". We might also rebase and collapse and make minor adjustments to your
115 "perfect". We might also rebase and collapse and make minor adjustments to your
116 changes when we apply them.
116 changes when we apply them.
117
117
118 We try to make sure we have consensus on the direction the project is taking.
118 We try to make sure we have consensus on the direction the project is taking.
119 Everything non-sensitive should be discussed in public - preferably on the
119 Everything non-sensitive should be discussed in public - preferably on the
120 mailing list. We aim at having all non-trivial changes reviewed by at least
120 mailing list. We aim at having all non-trivial changes reviewed by at least
121 one other core developer before pushing. Obvious non-controversial changes will
121 one other core developer before pushing. Obvious non-controversial changes will
122 be handled more casually.
122 be handled more casually.
123
123
124 For now we just have one official branch ("default") and will keep it so stable
124 For now we just have one official branch ("default") and will keep it so stable
125 that it can be (and is) used in production. Experimental changes should live
125 that it can be (and is) used in production. Experimental changes should live
126 elsewhere (for example in a pull request) until they are ready.
126 elsewhere (for example in a pull request) until they are ready.
127
127
128 .. _translations:
128 .. _translations:
129 .. include:: ./../kallithea/i18n/how_to
129 .. include:: ./../kallithea/i18n/how_to
130
130
131 "Roadmap"
131 "Roadmap"
132 ---------
132 ---------
133
133
134 We do not have a road map but are waiting for your contributions. Refer to the
134 We do not have a road map but are waiting for your contributions. Refer to the
135 wiki_ for some ideas of places we might want to go - contributions in these
135 wiki_ for some ideas of places we might want to go - contributions in these
136 areas are very welcome.
136 areas are very welcome.
137
137
138
138
139 Thank you for your contribution!
139 Thank you for your contribution!
140 --------------------------------
140 --------------------------------
141
141
142
142
143 .. _Weblate: http://weblate.org/
143 .. _Weblate: http://weblate.org/
144 .. _Issue Tracker: https://bitbucket.org/conservancy/kallithea/issues?status=new&status=open
144 .. _Issue Tracker: https://bitbucket.org/conservancy/kallithea/issues?status=new&status=open
145 .. _Pull Requests: https://bitbucket.org/conservancy/kallithea/pull-requests
145 .. _Pull Requests: https://bitbucket.org/conservancy/kallithea/pull-requests
146 .. _bitbucket: http://bitbucket.org/
146 .. _bitbucket: http://bitbucket.org/
147 .. _mailing list: http://lists.sfconservancy.org/mailman/listinfo/kallithea-general
147 .. _mailing list: http://lists.sfconservancy.org/mailman/listinfo/kallithea-general
148 .. _Hosted Weblate: https://hosted.weblate.org/projects/kallithea/kallithea/
148 .. _Hosted Weblate: https://hosted.weblate.org/projects/kallithea/kallithea/
149 .. _wiki: https://bitbucket.org/conservancy/kallithea/wiki/Home
149 .. _wiki: https://bitbucket.org/conservancy/kallithea/wiki/Home
@@ -1,92 +1,92 b''
1 ============
1 ============
2 Translations
2 Translations
3 ============
3 ============
4
4
5 Translations are available on Hosted Weblate at the following URL:
5 Translations are available on Hosted Weblate at the following URL:
6
6
7 https://hosted.weblate.org/projects/kallithea/kallithea/
7 https://hosted.weblate.org/projects/kallithea/kallithea/
8
8
9 Registered users may contribute to the existing languages, or request a new
9 Registered users may contribute to the existing languages, or request a new
10 language translations.
10 language translations.
11
11
12 Translating using Weblate
12 Translating using Weblate
13 -------------------------
13 -------------------------
14
14
15 Weblate_ offers a simple and easy to use interface featuring glossary, machine
15 Weblate_ offers a simple and easy to use interface featuring glossary, machine
16 translation, suggestions based on similar translations in other projects,
16 translation, suggestions based on similar translations in other projects,
17 automatic checks etc. Weblate imports the source code tree directly from
17 automatic checks etc. Weblate imports the source code tree directly from
18 the version control system, and commits edits back from time to time.
18 the version control system, and commits edits back from time to time.
19
19
20 When registering at Weblate, make sure you name and email address you prefer to
20 When registering at Weblate, make sure you name and email address you prefer to
21 be used when your changes are committed. We can and probably will amend changesets
21 be used when your changes are committed. We can and probably will amend changesets
22 coming from Weblate, but having things right from the beginning makes things easier.
22 coming from Weblate, but having things right from the beginning makes things easier.
23
23
24 Weblate performs sanity checks all the time and tries to prevent you from ignoring
24 Weblate performs sanity checks all the time and tries to prevent you from ignoring
25 them. Most common mistakes are inconsistent punctuation, whitespaces, missing or extra
25 them. Most common mistakes are inconsistent punctuation, whitespaces, missing or extra
26 format parameters, untranslated strings copied into the translation. Please perform
26 format parameters, untranslated strings copied into the translation. Please perform
27 necessary corrections when they're needed, or override the false positives.
27 necessary corrections when they're needed, or override the false positives.
28
28
29 Merging translations from Weblate
29 Merging translations from Weblate
30 ---------------------------------
30 ---------------------------------
31
31
32 Weblate rebases its changes every time it pulls from our repository. Pulls are triggered
32 Weblate rebases its changes every time it pulls from our repository. Pulls are triggered
33 by a web hook from Our Own Kallithea every time it receives new commits. Usually merging
33 by a web hook from Our Own Kallithea every time it receives new commits. Usually merging
34 the new translations is a straightforward process consisting of a pull from Weblate-hosted
34 the new translations is a straightforward process consisting of a pull from Weblate-hosted
35 repository which is available under Data Exports tab in Weblate interface.
35 repository which is available under Data Exports tab in Weblate interface.
36
36
37 Weblate tries to minimise the number of commits, but that's not always work, especially
37 Weblate tries to minimise the number of commits, but that's not always work, especially
38 when two translators work with different languages at more or less the same time.
38 when two translators work with different languages at more or less the same time.
39 It makes sense sometimes to re-order or fold commits by the same author when they touch
39 It makes sense sometimes to re-order or fold commits by the same author when they touch
40 just the same language translation. That, however, may confuse Weblate sometimes, in
40 just the same language translation. That, however, may confuse Weblate sometimes, in
41 which case it should be manually convinced it has to discard the commits it created by
41 which case it should be manually convinced it has to discard the commits it created by
42 using its administrative interface.
42 using its administrative interface.
43
43
44 Manual creation of a new language translation
44 Manual creation of a new language translation
45 ---------------------------------------------
45 ---------------------------------------------
46
46
47 In the prepared development environment, run the following to ensure
47 In the prepared development environment, run the following to ensure
48 all translation strings are extracted and up-to-date::
48 all translation strings are extracted and up-to-date::
49
49
50 python setup.py extract_messages
50 python setup.py extract_messages
51
51
52 Create new language by executing following command::
52 Create new language by executing following command::
53
53
54 python setup.py init_catalog -l <new_language_code>
54 python setup.py init_catalog -l <new_language_code>
55
55
56 This creates a new translation under directory `kallithea/i18n/<new_language_code>`
56 This creates a new translation under directory `kallithea/i18n/<new_language_code>`
57 based on the translation template file, `kallithea/i18n/kallithea.pot`.
57 based on the translation template file, `kallithea/i18n/kallithea.pot`.
58
58
59 Edit the new PO file located in `LC_MESSAGES` directory with poedit or your
59 Edit the new PO file located in `LC_MESSAGES` directory with poedit or your
60 favorite PO files editor. After you finished with the translations, check the
60 favorite PO files editor. After you finished with the translations, check the
61 translation file for errors by executing::
61 translation file for errors by executing::
62
62
63 msgfmt -f -c kallithea/i18n/<new_language_code>/LC_MESSAGES/<updated_file.po>
63 msgfmt -f -c kallithea/i18n/<new_language_code>/LC_MESSAGES/<updated_file.po>
64
64
65 Finally, compile the translations::
65 Finally, compile the translations::
66
66
67 python setup.py compile_catalog -l <new_language_code>
67 python setup.py compile_catalog -l <new_language_code>
68
68
69 Updating translations
69 Updating translations
70 ---------------------
70 ---------------------
71
71
72 Extract the latest versions of strings for translation by running::
72 Extract the latest versions of strings for translation by running::
73
73
74 python setup.py extract_messages
74 python setup.py extract_messages
75
75
76 Update the PO file by doing::
76 Update the PO file by doing::
77
77
78 python setup.py update_catalog -l <new_language_code>
78 python setup.py update_catalog -l <new_language_code>
79
79
80 Edit the new updated translation file. Repeat all steps after `init_catalog` step from
80 Edit the new updated translation file. Repeat all steps after `init_catalog` step from
81 new translation instructions
81 new translation instructions
82
82
83 Testing translations
83 Testing translations
84 --------------------
84 --------------------
85
85
86 Edit test.ini file and set lang attribute to::
86 Edit kallithea/tests/test.ini file and set lang attribute to::
87
87
88 lang=<new_language_code>
88 lang=<new_language_code>
89
89
90 Run Kallithea tests by executing::
90 Run Kallithea tests by executing::
91
91
92 nosetests
92 nosetests
@@ -1,34 +1,34 b''
1 import os
1 import os
2 import sys
2 import sys
3 import logging
3 import logging
4
4
5 import pkg_resources
5 import pkg_resources
6 from paste.deploy import loadapp
6 from paste.deploy import loadapp
7 import pylons.test
7 import pylons.test
8 from pylons.i18n.translation import _get_translator
8 from pylons.i18n.translation import _get_translator
9
9
10
10
11 def pytest_configure():
11 def pytest_configure():
12 path = os.getcwd()
12 path = os.getcwd()
13 sys.path.insert(0, path)
13 sys.path.insert(0, path)
14 pkg_resources.working_set.add_entry(path)
14 pkg_resources.working_set.add_entry(path)
15
15
16 # Disable INFO logging of test database creation, restore with NOTSET
16 # Disable INFO logging of test database creation, restore with NOTSET
17 logging.disable(logging.INFO)
17 logging.disable(logging.INFO)
18 pylons.test.pylonsapp = loadapp('config:test.ini', relative_to=path)
18 pylons.test.pylonsapp = loadapp('config:kallithea/tests/test.ini', relative_to=path)
19 logging.disable(logging.NOTSET)
19 logging.disable(logging.NOTSET)
20
20
21 # Setup the config and app_globals, only works if we can get
21 # Setup the config and app_globals, only works if we can get
22 # to the config object
22 # to the config object
23 conf = getattr(pylons.test.pylonsapp, 'config')
23 conf = getattr(pylons.test.pylonsapp, 'config')
24 if conf:
24 if conf:
25 pylons.config._push_object(conf)
25 pylons.config._push_object(conf)
26
26
27 if 'pylons.app_globals' in conf:
27 if 'pylons.app_globals' in conf:
28 pylons.app_globals._push_object(conf['pylons.app_globals'])
28 pylons.app_globals._push_object(conf['pylons.app_globals'])
29
29
30 # Initialize a translator for tests that utilize i18n
30 # Initialize a translator for tests that utilize i18n
31 translator = _get_translator(pylons.config.get('lang'))
31 translator = _get_translator(pylons.config.get('lang'))
32 pylons.translator._push_object(translator)
32 pylons.translator._push_object(translator)
33
33
34 return pylons.test.pylonsapp
34 return pylons.test.pylonsapp
@@ -1,537 +1,537 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program 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. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.tests.other.manual_test_vcs_operations
15 kallithea.tests.other.manual_test_vcs_operations
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 Test suite for making push/pull operations.
18 Test suite for making push/pull operations.
19
19
20 Run it in two terminals::
20 Run it in two terminals::
21 paster serve test.ini
21 paster serve kallithea/tests/test.ini
22 KALLITHEA_WHOOSH_TEST_DISABLE=1 KALLITHEA_NO_TMP_PATH=1 nosetests kallithea/tests/other/manual_test_vcs_operations.py
22 KALLITHEA_WHOOSH_TEST_DISABLE=1 KALLITHEA_NO_TMP_PATH=1 nosetests kallithea/tests/other/manual_test_vcs_operations.py
23
23
24 You must have git > 1.8.1 for tests to work fine
24 You must have git > 1.8.1 for tests to work fine
25
25
26 This file was forked by the Kallithea project in July 2014.
26 This file was forked by the Kallithea project in July 2014.
27 Original author and date, and relevant copyright and licensing information is below:
27 Original author and date, and relevant copyright and licensing information is below:
28 :created_on: Dec 30, 2010
28 :created_on: Dec 30, 2010
29 :author: marcink
29 :author: marcink
30 :copyright: (c) 2013 RhodeCode GmbH, and others.
30 :copyright: (c) 2013 RhodeCode GmbH, and others.
31 :license: GPLv3, see LICENSE.md for more details.
31 :license: GPLv3, see LICENSE.md for more details.
32
32
33 """
33 """
34
34
35 import re
35 import re
36 import tempfile
36 import tempfile
37 import time
37 import time
38 from os.path import join as jn
38 from os.path import join as jn
39
39
40 from tempfile import _RandomNameSequence
40 from tempfile import _RandomNameSequence
41 from subprocess import Popen, PIPE
41 from subprocess import Popen, PIPE
42
42
43 from kallithea.tests import *
43 from kallithea.tests import *
44 from kallithea.model.db import User, Repository, UserIpMap, CacheInvalidation
44 from kallithea.model.db import User, Repository, UserIpMap, CacheInvalidation
45 from kallithea.model.meta import Session
45 from kallithea.model.meta import Session
46 from kallithea.model.repo import RepoModel
46 from kallithea.model.repo import RepoModel
47 from kallithea.model.user import UserModel
47 from kallithea.model.user import UserModel
48
48
49 DEBUG = True
49 DEBUG = True
50 HOST = '127.0.0.1:4999' # test host
50 HOST = '127.0.0.1:4999' # test host
51
51
52
52
53 class Command(object):
53 class Command(object):
54
54
55 def __init__(self, cwd):
55 def __init__(self, cwd):
56 self.cwd = cwd
56 self.cwd = cwd
57
57
58 def execute(self, cmd, *args):
58 def execute(self, cmd, *args):
59 """
59 """
60 Runs command on the system with given ``args``.
60 Runs command on the system with given ``args``.
61 """
61 """
62
62
63 command = cmd + ' ' + ' '.join(args)
63 command = cmd + ' ' + ' '.join(args)
64 if DEBUG:
64 if DEBUG:
65 print '*** CMD %s ***' % command
65 print '*** CMD %s ***' % command
66 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
66 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
67 stdout, stderr = p.communicate()
67 stdout, stderr = p.communicate()
68 if DEBUG:
68 if DEBUG:
69 print 'stdout:', repr(stdout)
69 print 'stdout:', repr(stdout)
70 print 'stderr:', repr(stderr)
70 print 'stderr:', repr(stderr)
71 return stdout, stderr
71 return stdout, stderr
72
72
73
73
74 def _get_tmp_dir():
74 def _get_tmp_dir():
75 return tempfile.mkdtemp(prefix='rc_integration_test')
75 return tempfile.mkdtemp(prefix='rc_integration_test')
76
76
77
77
78 def _construct_url(repo, dest=None, **kwargs):
78 def _construct_url(repo, dest=None, **kwargs):
79 if dest is None:
79 if dest is None:
80 #make temp clone
80 #make temp clone
81 dest = _get_tmp_dir()
81 dest = _get_tmp_dir()
82 params = {
82 params = {
83 'user': TEST_USER_ADMIN_LOGIN,
83 'user': TEST_USER_ADMIN_LOGIN,
84 'passwd': TEST_USER_ADMIN_PASS,
84 'passwd': TEST_USER_ADMIN_PASS,
85 'host': HOST,
85 'host': HOST,
86 'cloned_repo': repo,
86 'cloned_repo': repo,
87 'dest': dest
87 'dest': dest
88 }
88 }
89 params.update(**kwargs)
89 params.update(**kwargs)
90 if params['user'] and params['passwd']:
90 if params['user'] and params['passwd']:
91 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s %(dest)s' % params
91 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s %(dest)s' % params
92 else:
92 else:
93 _url = 'http://(host)s/%(cloned_repo)s %(dest)s' % params
93 _url = 'http://(host)s/%(cloned_repo)s %(dest)s' % params
94 return _url
94 return _url
95
95
96
96
97 def _add_files_and_push(vcs, DEST, **kwargs):
97 def _add_files_and_push(vcs, DEST, **kwargs):
98 """
98 """
99 Generate some files, add it to DEST repo and push back
99 Generate some files, add it to DEST repo and push back
100 vcs is git or hg and defines what VCS we want to make those files for
100 vcs is git or hg and defines what VCS we want to make those files for
101
101
102 :param vcs:
102 :param vcs:
103 :param DEST:
103 :param DEST:
104 """
104 """
105 # commit some stuff into this repo
105 # commit some stuff into this repo
106 cwd = path = jn(DEST)
106 cwd = path = jn(DEST)
107 #added_file = jn(path, '%ssetupΔ…ΕΌΕΊΔ‡.py' % _RandomNameSequence().next())
107 #added_file = jn(path, '%ssetupΔ…ΕΌΕΊΔ‡.py' % _RandomNameSequence().next())
108 added_file = jn(path, '%ssetup.py' % _RandomNameSequence().next())
108 added_file = jn(path, '%ssetup.py' % _RandomNameSequence().next())
109 Command(cwd).execute('touch %s' % added_file)
109 Command(cwd).execute('touch %s' % added_file)
110 Command(cwd).execute('%s add %s' % (vcs, added_file))
110 Command(cwd).execute('%s add %s' % (vcs, added_file))
111
111
112 for i in xrange(kwargs.get('files_no', 3)):
112 for i in xrange(kwargs.get('files_no', 3)):
113 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
113 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
114 Command(cwd).execute(cmd)
114 Command(cwd).execute(cmd)
115 author_str = 'User ǝɯɐᴎ <me@email.com>'
115 author_str = 'User ǝɯɐᴎ <me@email.com>'
116 if vcs == 'hg':
116 if vcs == 'hg':
117 cmd = """hg commit -m 'commited new %s' -u '%s' %s """ % (
117 cmd = """hg commit -m 'commited new %s' -u '%s' %s """ % (
118 i, author_str, added_file
118 i, author_str, added_file
119 )
119 )
120 elif vcs == 'git':
120 elif vcs == 'git':
121 cmd = """EMAIL="me@email.com" git commit -m 'commited new %s' --author '%s' %s """ % (
121 cmd = """EMAIL="me@email.com" git commit -m 'commited new %s' --author '%s' %s """ % (
122 i, author_str, added_file
122 i, author_str, added_file
123 )
123 )
124 Command(cwd).execute(cmd)
124 Command(cwd).execute(cmd)
125
125
126 # PUSH it back
126 # PUSH it back
127 _REPO = None
127 _REPO = None
128 if vcs == 'hg':
128 if vcs == 'hg':
129 _REPO = HG_REPO
129 _REPO = HG_REPO
130 elif vcs == 'git':
130 elif vcs == 'git':
131 _REPO = GIT_REPO
131 _REPO = GIT_REPO
132
132
133 kwargs['dest'] = ''
133 kwargs['dest'] = ''
134 clone_url = _construct_url(_REPO, **kwargs)
134 clone_url = _construct_url(_REPO, **kwargs)
135 if 'clone_url' in kwargs:
135 if 'clone_url' in kwargs:
136 clone_url = kwargs['clone_url']
136 clone_url = kwargs['clone_url']
137 stdout = stderr = None
137 stdout = stderr = None
138 if vcs == 'hg':
138 if vcs == 'hg':
139 stdout, stderr = Command(cwd).execute('hg push --verbose', clone_url)
139 stdout, stderr = Command(cwd).execute('hg push --verbose', clone_url)
140 elif vcs == 'git':
140 elif vcs == 'git':
141 stdout, stderr = Command(cwd).execute('git push --verbose', clone_url + " master")
141 stdout, stderr = Command(cwd).execute('git push --verbose', clone_url + " master")
142
142
143 return stdout, stderr
143 return stdout, stderr
144
144
145
145
146 def set_anonymous_access(enable=True):
146 def set_anonymous_access(enable=True):
147 user = User.get_by_username(User.DEFAULT_USER)
147 user = User.get_by_username(User.DEFAULT_USER)
148 user.active = enable
148 user.active = enable
149 Session().add(user)
149 Session().add(user)
150 Session().commit()
150 Session().commit()
151 print '\tanonymous access is now:', enable
151 print '\tanonymous access is now:', enable
152 if enable != User.get_by_username(User.DEFAULT_USER).active:
152 if enable != User.get_by_username(User.DEFAULT_USER).active:
153 raise Exception('Cannot set anonymous access')
153 raise Exception('Cannot set anonymous access')
154
154
155
155
156 #==============================================================================
156 #==============================================================================
157 # TESTS
157 # TESTS
158 #==============================================================================
158 #==============================================================================
159
159
160
160
161 def _check_proper_git_push(stdout, stderr):
161 def _check_proper_git_push(stdout, stderr):
162 #WTF Git stderr is output ?!
162 #WTF Git stderr is output ?!
163 assert 'fatal' not in stderr
163 assert 'fatal' not in stderr
164 assert 'rejected' not in stderr
164 assert 'rejected' not in stderr
165 assert 'Pushing to' in stderr
165 assert 'Pushing to' in stderr
166 assert 'master -> master' in stderr
166 assert 'master -> master' in stderr
167
167
168
168
169 class TestVCSOperations(BaseTestCase):
169 class TestVCSOperations(BaseTestCase):
170
170
171 @classmethod
171 @classmethod
172 def setup_class(cls):
172 def setup_class(cls):
173 #DISABLE ANONYMOUS ACCESS
173 #DISABLE ANONYMOUS ACCESS
174 set_anonymous_access(False)
174 set_anonymous_access(False)
175
175
176 def setUp(self):
176 def setUp(self):
177 r = Repository.get_by_repo_name(GIT_REPO)
177 r = Repository.get_by_repo_name(GIT_REPO)
178 Repository.unlock(r)
178 Repository.unlock(r)
179 r.enable_locking = False
179 r.enable_locking = False
180 Session().add(r)
180 Session().add(r)
181 Session().commit()
181 Session().commit()
182
182
183 r = Repository.get_by_repo_name(HG_REPO)
183 r = Repository.get_by_repo_name(HG_REPO)
184 Repository.unlock(r)
184 Repository.unlock(r)
185 r.enable_locking = False
185 r.enable_locking = False
186 Session().add(r)
186 Session().add(r)
187 Session().commit()
187 Session().commit()
188
188
189 def test_clone_hg_repo_by_admin(self):
189 def test_clone_hg_repo_by_admin(self):
190 clone_url = _construct_url(HG_REPO)
190 clone_url = _construct_url(HG_REPO)
191 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
191 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
192
192
193 assert 'requesting all changes' in stdout
193 assert 'requesting all changes' in stdout
194 assert 'adding changesets' in stdout
194 assert 'adding changesets' in stdout
195 assert 'adding manifests' in stdout
195 assert 'adding manifests' in stdout
196 assert 'adding file changes' in stdout
196 assert 'adding file changes' in stdout
197
197
198 assert stderr == ''
198 assert stderr == ''
199
199
200 def test_clone_git_repo_by_admin(self):
200 def test_clone_git_repo_by_admin(self):
201 clone_url = _construct_url(GIT_REPO)
201 clone_url = _construct_url(GIT_REPO)
202 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
202 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
203
203
204 assert 'Cloning into' in stdout + stderr
204 assert 'Cloning into' in stdout + stderr
205 assert stderr == '' or stdout == ''
205 assert stderr == '' or stdout == ''
206
206
207 def test_clone_wrong_credentials_hg(self):
207 def test_clone_wrong_credentials_hg(self):
208 clone_url = _construct_url(HG_REPO, passwd='bad!')
208 clone_url = _construct_url(HG_REPO, passwd='bad!')
209 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
209 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
210 assert 'abort: authorization failed' in stderr
210 assert 'abort: authorization failed' in stderr
211
211
212 def test_clone_wrong_credentials_git(self):
212 def test_clone_wrong_credentials_git(self):
213 clone_url = _construct_url(GIT_REPO, passwd='bad!')
213 clone_url = _construct_url(GIT_REPO, passwd='bad!')
214 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
214 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
215 assert 'fatal: Authentication failed' in stderr
215 assert 'fatal: Authentication failed' in stderr
216
216
217 def test_clone_git_dir_as_hg(self):
217 def test_clone_git_dir_as_hg(self):
218 clone_url = _construct_url(GIT_REPO)
218 clone_url = _construct_url(GIT_REPO)
219 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
219 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
220 assert 'HTTP Error 404: Not Found' in stderr
220 assert 'HTTP Error 404: Not Found' in stderr
221
221
222 def test_clone_hg_repo_as_git(self):
222 def test_clone_hg_repo_as_git(self):
223 clone_url = _construct_url(HG_REPO)
223 clone_url = _construct_url(HG_REPO)
224 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
224 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
225 assert 'not found' in stderr
225 assert 'not found' in stderr
226
226
227 def test_clone_non_existing_path_hg(self):
227 def test_clone_non_existing_path_hg(self):
228 clone_url = _construct_url('trololo')
228 clone_url = _construct_url('trololo')
229 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
229 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
230 assert 'HTTP Error 404: Not Found' in stderr
230 assert 'HTTP Error 404: Not Found' in stderr
231
231
232 def test_clone_non_existing_path_git(self):
232 def test_clone_non_existing_path_git(self):
233 clone_url = _construct_url('trololo')
233 clone_url = _construct_url('trololo')
234 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
234 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
235 assert 'not found' in stderr
235 assert 'not found' in stderr
236
236
237 def test_push_new_file_hg(self):
237 def test_push_new_file_hg(self):
238 DEST = _get_tmp_dir()
238 DEST = _get_tmp_dir()
239 clone_url = _construct_url(HG_REPO, dest=DEST)
239 clone_url = _construct_url(HG_REPO, dest=DEST)
240 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
240 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
241
241
242 stdout, stderr = _add_files_and_push('hg', DEST)
242 stdout, stderr = _add_files_and_push('hg', DEST)
243
243
244 assert 'pushing to' in stdout
244 assert 'pushing to' in stdout
245 assert 'Repository size' in stdout
245 assert 'Repository size' in stdout
246 assert 'Last revision is now' in stdout
246 assert 'Last revision is now' in stdout
247
247
248 def test_push_new_file_git(self):
248 def test_push_new_file_git(self):
249 DEST = _get_tmp_dir()
249 DEST = _get_tmp_dir()
250 clone_url = _construct_url(GIT_REPO, dest=DEST)
250 clone_url = _construct_url(GIT_REPO, dest=DEST)
251 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
251 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
252
252
253 # commit some stuff into this repo
253 # commit some stuff into this repo
254 stdout, stderr = _add_files_and_push('git', DEST)
254 stdout, stderr = _add_files_and_push('git', DEST)
255
255
256 print [(x.repo_full_path,x.repo_path) for x in Repository.get_all()]
256 print [(x.repo_full_path,x.repo_path) for x in Repository.get_all()]
257 _check_proper_git_push(stdout, stderr)
257 _check_proper_git_push(stdout, stderr)
258
258
259 def test_push_invalidates_cache_hg(self):
259 def test_push_invalidates_cache_hg(self):
260 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
260 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
261 ==HG_REPO).scalar()
261 ==HG_REPO).scalar()
262 if not key:
262 if not key:
263 key = CacheInvalidation(HG_REPO, HG_REPO)
263 key = CacheInvalidation(HG_REPO, HG_REPO)
264
264
265 key.cache_active = True
265 key.cache_active = True
266 Session().add(key)
266 Session().add(key)
267 Session().commit()
267 Session().commit()
268
268
269 DEST = _get_tmp_dir()
269 DEST = _get_tmp_dir()
270 clone_url = _construct_url(HG_REPO, dest=DEST)
270 clone_url = _construct_url(HG_REPO, dest=DEST)
271 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
271 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
272
272
273 stdout, stderr = _add_files_and_push('hg', DEST, files_no=1)
273 stdout, stderr = _add_files_and_push('hg', DEST, files_no=1)
274
274
275 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
275 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
276 ==HG_REPO).one()
276 ==HG_REPO).one()
277 self.assertEqual(key.cache_active, False)
277 self.assertEqual(key.cache_active, False)
278
278
279 def test_push_invalidates_cache_git(self):
279 def test_push_invalidates_cache_git(self):
280 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
280 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
281 ==GIT_REPO).scalar()
281 ==GIT_REPO).scalar()
282 if not key:
282 if not key:
283 key = CacheInvalidation(GIT_REPO, GIT_REPO)
283 key = CacheInvalidation(GIT_REPO, GIT_REPO)
284
284
285 key.cache_active = True
285 key.cache_active = True
286 Session().add(key)
286 Session().add(key)
287 Session().commit()
287 Session().commit()
288
288
289 DEST = _get_tmp_dir()
289 DEST = _get_tmp_dir()
290 clone_url = _construct_url(GIT_REPO, dest=DEST)
290 clone_url = _construct_url(GIT_REPO, dest=DEST)
291 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
291 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
292
292
293 # commit some stuff into this repo
293 # commit some stuff into this repo
294 stdout, stderr = _add_files_and_push('git', DEST, files_no=1)
294 stdout, stderr = _add_files_and_push('git', DEST, files_no=1)
295 _check_proper_git_push(stdout, stderr)
295 _check_proper_git_push(stdout, stderr)
296
296
297 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
297 key = CacheInvalidation.query().filter(CacheInvalidation.cache_key
298 ==GIT_REPO).one()
298 ==GIT_REPO).one()
299 print CacheInvalidation.get_all()
299 print CacheInvalidation.get_all()
300 self.assertEqual(key.cache_active, False)
300 self.assertEqual(key.cache_active, False)
301
301
302 def test_push_wrong_credentials_hg(self):
302 def test_push_wrong_credentials_hg(self):
303 DEST = _get_tmp_dir()
303 DEST = _get_tmp_dir()
304 clone_url = _construct_url(HG_REPO, dest=DEST)
304 clone_url = _construct_url(HG_REPO, dest=DEST)
305 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
305 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
306
306
307 stdout, stderr = _add_files_and_push('hg', DEST, user='bad',
307 stdout, stderr = _add_files_and_push('hg', DEST, user='bad',
308 passwd='name')
308 passwd='name')
309
309
310 assert 'abort: authorization failed' in stderr
310 assert 'abort: authorization failed' in stderr
311
311
312 def test_push_wrong_credentials_git(self):
312 def test_push_wrong_credentials_git(self):
313 DEST = _get_tmp_dir()
313 DEST = _get_tmp_dir()
314 clone_url = _construct_url(GIT_REPO, dest=DEST)
314 clone_url = _construct_url(GIT_REPO, dest=DEST)
315 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
315 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
316
316
317 stdout, stderr = _add_files_and_push('git', DEST, user='bad',
317 stdout, stderr = _add_files_and_push('git', DEST, user='bad',
318 passwd='name')
318 passwd='name')
319
319
320 assert 'fatal: Authentication failed' in stderr
320 assert 'fatal: Authentication failed' in stderr
321
321
322 def test_push_back_to_wrong_url_hg(self):
322 def test_push_back_to_wrong_url_hg(self):
323 DEST = _get_tmp_dir()
323 DEST = _get_tmp_dir()
324 clone_url = _construct_url(HG_REPO, dest=DEST)
324 clone_url = _construct_url(HG_REPO, dest=DEST)
325 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
325 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
326
326
327 stdout, stderr = _add_files_and_push('hg', DEST,
327 stdout, stderr = _add_files_and_push('hg', DEST,
328 clone_url='http://%s/tmp' % HOST)
328 clone_url='http://%s/tmp' % HOST)
329
329
330 assert 'HTTP Error 404: Not Found' in stderr
330 assert 'HTTP Error 404: Not Found' in stderr
331
331
332 def test_push_back_to_wrong_url_git(self):
332 def test_push_back_to_wrong_url_git(self):
333 DEST = _get_tmp_dir()
333 DEST = _get_tmp_dir()
334 clone_url = _construct_url(GIT_REPO, dest=DEST)
334 clone_url = _construct_url(GIT_REPO, dest=DEST)
335 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
335 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
336
336
337 stdout, stderr = _add_files_and_push('git', DEST,
337 stdout, stderr = _add_files_and_push('git', DEST,
338 clone_url='http://%s/tmp' % HOST)
338 clone_url='http://%s/tmp' % HOST)
339
339
340 assert 'not found' in stderr
340 assert 'not found' in stderr
341
341
342 def test_clone_and_create_lock_hg(self):
342 def test_clone_and_create_lock_hg(self):
343 # enable locking
343 # enable locking
344 r = Repository.get_by_repo_name(HG_REPO)
344 r = Repository.get_by_repo_name(HG_REPO)
345 r.enable_locking = True
345 r.enable_locking = True
346 Session().add(r)
346 Session().add(r)
347 Session().commit()
347 Session().commit()
348 # clone
348 # clone
349 clone_url = _construct_url(HG_REPO)
349 clone_url = _construct_url(HG_REPO)
350 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
350 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
351
351
352 #check if lock was made
352 #check if lock was made
353 r = Repository.get_by_repo_name(HG_REPO)
353 r = Repository.get_by_repo_name(HG_REPO)
354 assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
354 assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
355
355
356 def test_clone_and_create_lock_git(self):
356 def test_clone_and_create_lock_git(self):
357 # enable locking
357 # enable locking
358 r = Repository.get_by_repo_name(GIT_REPO)
358 r = Repository.get_by_repo_name(GIT_REPO)
359 r.enable_locking = True
359 r.enable_locking = True
360 Session().add(r)
360 Session().add(r)
361 Session().commit()
361 Session().commit()
362 # clone
362 # clone
363 clone_url = _construct_url(GIT_REPO)
363 clone_url = _construct_url(GIT_REPO)
364 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
364 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
365
365
366 #check if lock was made
366 #check if lock was made
367 r = Repository.get_by_repo_name(GIT_REPO)
367 r = Repository.get_by_repo_name(GIT_REPO)
368 assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
368 assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
369
369
370 def test_clone_after_repo_was_locked_hg(self):
370 def test_clone_after_repo_was_locked_hg(self):
371 #lock repo
371 #lock repo
372 r = Repository.get_by_repo_name(HG_REPO)
372 r = Repository.get_by_repo_name(HG_REPO)
373 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
373 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
374 #pull fails since repo is locked
374 #pull fails since repo is locked
375 clone_url = _construct_url(HG_REPO)
375 clone_url = _construct_url(HG_REPO)
376 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
376 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
377 msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
377 msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
378 % (HG_REPO, TEST_USER_ADMIN_LOGIN))
378 % (HG_REPO, TEST_USER_ADMIN_LOGIN))
379 assert msg in stderr
379 assert msg in stderr
380
380
381 def test_clone_after_repo_was_locked_git(self):
381 def test_clone_after_repo_was_locked_git(self):
382 #lock repo
382 #lock repo
383 r = Repository.get_by_repo_name(GIT_REPO)
383 r = Repository.get_by_repo_name(GIT_REPO)
384 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
384 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
385 #pull fails since repo is locked
385 #pull fails since repo is locked
386 clone_url = _construct_url(GIT_REPO)
386 clone_url = _construct_url(GIT_REPO)
387 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
387 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
388 msg = ("""The requested URL returned error: 423""")
388 msg = ("""The requested URL returned error: 423""")
389 assert msg in stderr
389 assert msg in stderr
390
390
391 def test_push_on_locked_repo_by_other_user_hg(self):
391 def test_push_on_locked_repo_by_other_user_hg(self):
392 #clone some temp
392 #clone some temp
393 DEST = _get_tmp_dir()
393 DEST = _get_tmp_dir()
394 clone_url = _construct_url(HG_REPO, dest=DEST)
394 clone_url = _construct_url(HG_REPO, dest=DEST)
395 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
395 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
396
396
397 #lock repo
397 #lock repo
398 r = Repository.get_by_repo_name(HG_REPO)
398 r = Repository.get_by_repo_name(HG_REPO)
399 # let this user actually push !
399 # let this user actually push !
400 RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN,
400 RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN,
401 perm='repository.write')
401 perm='repository.write')
402 Session().commit()
402 Session().commit()
403 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
403 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
404
404
405 #push fails repo is locked by other user !
405 #push fails repo is locked by other user !
406 stdout, stderr = _add_files_and_push('hg', DEST,
406 stdout, stderr = _add_files_and_push('hg', DEST,
407 user=TEST_USER_REGULAR_LOGIN,
407 user=TEST_USER_REGULAR_LOGIN,
408 passwd=TEST_USER_REGULAR_PASS)
408 passwd=TEST_USER_REGULAR_PASS)
409 msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
409 msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
410 % (HG_REPO, TEST_USER_ADMIN_LOGIN))
410 % (HG_REPO, TEST_USER_ADMIN_LOGIN))
411 assert msg in stderr
411 assert msg in stderr
412
412
413 def test_push_on_locked_repo_by_other_user_git(self):
413 def test_push_on_locked_repo_by_other_user_git(self):
414 #clone some temp
414 #clone some temp
415 DEST = _get_tmp_dir()
415 DEST = _get_tmp_dir()
416 clone_url = _construct_url(GIT_REPO, dest=DEST)
416 clone_url = _construct_url(GIT_REPO, dest=DEST)
417 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
417 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
418
418
419 #lock repo
419 #lock repo
420 r = Repository.get_by_repo_name(GIT_REPO)
420 r = Repository.get_by_repo_name(GIT_REPO)
421 # let this user actually push !
421 # let this user actually push !
422 RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN,
422 RepoModel().grant_user_permission(repo=r, user=TEST_USER_REGULAR_LOGIN,
423 perm='repository.write')
423 perm='repository.write')
424 Session().commit()
424 Session().commit()
425 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
425 Repository.lock(r, User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id)
426
426
427 #push fails repo is locked by other user !
427 #push fails repo is locked by other user !
428 stdout, stderr = _add_files_and_push('git', DEST,
428 stdout, stderr = _add_files_and_push('git', DEST,
429 user=TEST_USER_REGULAR_LOGIN,
429 user=TEST_USER_REGULAR_LOGIN,
430 passwd=TEST_USER_REGULAR_PASS)
430 passwd=TEST_USER_REGULAR_PASS)
431 err = 'Repository `%s` locked by user `%s`' % (GIT_REPO, TEST_USER_ADMIN_LOGIN)
431 err = 'Repository `%s` locked by user `%s`' % (GIT_REPO, TEST_USER_ADMIN_LOGIN)
432 assert err in stderr
432 assert err in stderr
433
433
434 #TODO: fix this somehow later on Git, Git is stupid and even if we throw
434 #TODO: fix this somehow later on Git, Git is stupid and even if we throw
435 #back 423 to it, it makes ANOTHER request and we fail there with 405 :/
435 #back 423 to it, it makes ANOTHER request and we fail there with 405 :/
436
436
437 msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
437 msg = ("""abort: HTTP Error 423: Repository `%s` locked by user `%s`"""
438 % (GIT_REPO, TEST_USER_ADMIN_LOGIN))
438 % (GIT_REPO, TEST_USER_ADMIN_LOGIN))
439 #msg = "405 Method Not Allowed"
439 #msg = "405 Method Not Allowed"
440 #assert msg in stderr
440 #assert msg in stderr
441
441
442 def test_push_unlocks_repository_hg(self):
442 def test_push_unlocks_repository_hg(self):
443 # enable locking
443 # enable locking
444 r = Repository.get_by_repo_name(HG_REPO)
444 r = Repository.get_by_repo_name(HG_REPO)
445 r.enable_locking = True
445 r.enable_locking = True
446 Session().add(r)
446 Session().add(r)
447 Session().commit()
447 Session().commit()
448 #clone some temp
448 #clone some temp
449 DEST = _get_tmp_dir()
449 DEST = _get_tmp_dir()
450 clone_url = _construct_url(HG_REPO, dest=DEST)
450 clone_url = _construct_url(HG_REPO, dest=DEST)
451 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
451 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
452
452
453 #check for lock repo after clone
453 #check for lock repo after clone
454 r = Repository.get_by_repo_name(HG_REPO)
454 r = Repository.get_by_repo_name(HG_REPO)
455 uid = User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
455 uid = User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
456 assert r.locked[0] == uid
456 assert r.locked[0] == uid
457
457
458 #push is ok and repo is now unlocked
458 #push is ok and repo is now unlocked
459 stdout, stderr = _add_files_and_push('hg', DEST)
459 stdout, stderr = _add_files_and_push('hg', DEST)
460 assert ('remote: Released lock on repo `%s`' % HG_REPO) in stdout
460 assert ('remote: Released lock on repo `%s`' % HG_REPO) in stdout
461 #we need to cleanup the Session Here !
461 #we need to cleanup the Session Here !
462 Session.remove()
462 Session.remove()
463 r = Repository.get_by_repo_name(HG_REPO)
463 r = Repository.get_by_repo_name(HG_REPO)
464 assert r.locked == [None, None]
464 assert r.locked == [None, None]
465
465
466 #TODO: fix me ! somehow during tests hooks don't get called on Git
466 #TODO: fix me ! somehow during tests hooks don't get called on Git
467 def test_push_unlocks_repository_git(self):
467 def test_push_unlocks_repository_git(self):
468 # enable locking
468 # enable locking
469 r = Repository.get_by_repo_name(GIT_REPO)
469 r = Repository.get_by_repo_name(GIT_REPO)
470 r.enable_locking = True
470 r.enable_locking = True
471 Session().add(r)
471 Session().add(r)
472 Session().commit()
472 Session().commit()
473 #clone some temp
473 #clone some temp
474 DEST = _get_tmp_dir()
474 DEST = _get_tmp_dir()
475 clone_url = _construct_url(GIT_REPO, dest=DEST)
475 clone_url = _construct_url(GIT_REPO, dest=DEST)
476 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
476 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
477
477
478 #check for lock repo after clone
478 #check for lock repo after clone
479 r = Repository.get_by_repo_name(GIT_REPO)
479 r = Repository.get_by_repo_name(GIT_REPO)
480 assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
480 assert r.locked[0] == User.get_by_username(TEST_USER_ADMIN_LOGIN).user_id
481
481
482 #push is ok and repo is now unlocked
482 #push is ok and repo is now unlocked
483 stdout, stderr = _add_files_and_push('git', DEST)
483 stdout, stderr = _add_files_and_push('git', DEST)
484 _check_proper_git_push(stdout, stderr)
484 _check_proper_git_push(stdout, stderr)
485
485
486 #assert ('remote: Released lock on repo `%s`' % GIT_REPO) in stdout
486 #assert ('remote: Released lock on repo `%s`' % GIT_REPO) in stdout
487 #we need to cleanup the Session Here !
487 #we need to cleanup the Session Here !
488 Session.remove()
488 Session.remove()
489 r = Repository.get_by_repo_name(GIT_REPO)
489 r = Repository.get_by_repo_name(GIT_REPO)
490 assert r.locked == [None, None]
490 assert r.locked == [None, None]
491
491
492 def test_ip_restriction_hg(self):
492 def test_ip_restriction_hg(self):
493 user_model = UserModel()
493 user_model = UserModel()
494 try:
494 try:
495 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
495 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
496 Session().commit()
496 Session().commit()
497 clone_url = _construct_url(HG_REPO)
497 clone_url = _construct_url(HG_REPO)
498 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
498 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
499 assert 'abort: HTTP Error 403: Forbidden' in stderr
499 assert 'abort: HTTP Error 403: Forbidden' in stderr
500 finally:
500 finally:
501 #release IP restrictions
501 #release IP restrictions
502 for ip in UserIpMap.getAll():
502 for ip in UserIpMap.getAll():
503 UserIpMap.delete(ip.ip_id)
503 UserIpMap.delete(ip.ip_id)
504 Session().commit()
504 Session().commit()
505
505
506 time.sleep(2)
506 time.sleep(2)
507 clone_url = _construct_url(HG_REPO)
507 clone_url = _construct_url(HG_REPO)
508 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
508 stdout, stderr = Command('/tmp').execute('hg clone', clone_url)
509
509
510 assert 'requesting all changes' in stdout
510 assert 'requesting all changes' in stdout
511 assert 'adding changesets' in stdout
511 assert 'adding changesets' in stdout
512 assert 'adding manifests' in stdout
512 assert 'adding manifests' in stdout
513 assert 'adding file changes' in stdout
513 assert 'adding file changes' in stdout
514
514
515 assert stderr == ''
515 assert stderr == ''
516
516
517 def test_ip_restriction_git(self):
517 def test_ip_restriction_git(self):
518 user_model = UserModel()
518 user_model = UserModel()
519 try:
519 try:
520 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
520 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
521 Session().commit()
521 Session().commit()
522 clone_url = _construct_url(GIT_REPO)
522 clone_url = _construct_url(GIT_REPO)
523 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
523 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
524 # The message apparently changed in Git 1.8.3, so match it loosely.
524 # The message apparently changed in Git 1.8.3, so match it loosely.
525 assert re.search(r'\b403\b', stderr)
525 assert re.search(r'\b403\b', stderr)
526 finally:
526 finally:
527 #release IP restrictions
527 #release IP restrictions
528 for ip in UserIpMap.getAll():
528 for ip in UserIpMap.getAll():
529 UserIpMap.delete(ip.ip_id)
529 UserIpMap.delete(ip.ip_id)
530 Session().commit()
530 Session().commit()
531
531
532 time.sleep(2)
532 time.sleep(2)
533 clone_url = _construct_url(GIT_REPO)
533 clone_url = _construct_url(GIT_REPO)
534 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
534 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
535
535
536 assert 'Cloning into' in stdout + stderr
536 assert 'Cloning into' in stdout + stderr
537 assert stderr == '' or stdout == ''
537 assert stderr == '' or stdout == ''
1 NO CONTENT: file renamed from test.ini to kallithea/tests/test.ini
NO CONTENT: file renamed from test.ini to kallithea/tests/test.ini
@@ -1,52 +1,52 b''
1 [egg_info]
1 [egg_info]
2 tag_build =
2 tag_build =
3 tag_svn_revision = 0
3 tag_svn_revision = 0
4 tag_date = 0
4 tag_date = 0
5
5
6 [nosetests]
6 [nosetests]
7 verbose = True
7 verbose = True
8 verbosity = 2
8 verbosity = 2
9 with-pylons = test.ini
9 with-pylons = kallithea/tests/test.ini
10 detailed-errors = 1
10 detailed-errors = 1
11 nologcapture = 1
11 nologcapture = 1
12
12
13 [pytest]
13 [pytest]
14 # only look for tests in kallithea/tests
14 # only look for tests in kallithea/tests
15 python_files = kallithea/tests/**/test_*.py
15 python_files = kallithea/tests/**/test_*.py
16 addopts =
16 addopts =
17 # --verbose
17 # --verbose
18 # show extra test summary info as specified by chars (f)ailed, (E)error, (s)skipped, (x)failed, (X)passed, (w)warnings.
18 # show extra test summary info as specified by chars (f)ailed, (E)error, (s)skipped, (x)failed, (X)passed, (w)warnings.
19 -rfEsxXw
19 -rfEsxXw
20 # Shorter scrollbacks; less stuff to scroll through
20 # Shorter scrollbacks; less stuff to scroll through
21 --tb=short
21 --tb=short
22
22
23 [compile_catalog]
23 [compile_catalog]
24 domain = kallithea
24 domain = kallithea
25 directory = kallithea/i18n
25 directory = kallithea/i18n
26 statistics = true
26 statistics = true
27
27
28 [extract_messages]
28 [extract_messages]
29 add_comments = TRANSLATORS:
29 add_comments = TRANSLATORS:
30 output_file = kallithea/i18n/kallithea.pot
30 output_file = kallithea/i18n/kallithea.pot
31 msgid-bugs-address = translations@kallithea-scm.org
31 msgid-bugs-address = translations@kallithea-scm.org
32 copyright-holder = Various authors, licensing as GPLv3
32 copyright-holder = Various authors, licensing as GPLv3
33 no-wrap = true
33 no-wrap = true
34
34
35 [init_catalog]
35 [init_catalog]
36 domain = kallithea
36 domain = kallithea
37 input_file = kallithea/i18n/kallithea.pot
37 input_file = kallithea/i18n/kallithea.pot
38 output_dir = kallithea/i18n
38 output_dir = kallithea/i18n
39
39
40 [update_catalog]
40 [update_catalog]
41 domain = kallithea
41 domain = kallithea
42 input_file = kallithea/i18n/kallithea.pot
42 input_file = kallithea/i18n/kallithea.pot
43 output_dir = kallithea/i18n
43 output_dir = kallithea/i18n
44 previous = true
44 previous = true
45
45
46 [build_sphinx]
46 [build_sphinx]
47 source-dir = docs/
47 source-dir = docs/
48 build-dir = docs/_build
48 build-dir = docs/_build
49 all_files = 1
49 all_files = 1
50
50
51 [upload_sphinx]
51 [upload_sphinx]
52 upload-dir = docs/_build/html
52 upload-dir = docs/_build/html
General Comments 0
You need to be logged in to leave comments. Login now