Merge ~andersson123/autopkgtest-cloud:revive_unit_tests into autopkgtest-cloud:master

Proposed by Tim Andersson
Status: Merged
Approved by: Tim Andersson
Approved revision: 9072d0be1dd5baced060d397c94553fdcaa2a69d
Merged at revision: 9072d0be1dd5baced060d397c94553fdcaa2a69d
Proposed branch: ~andersson123/autopkgtest-cloud:revive_unit_tests
Merge into: autopkgtest-cloud:master
Diff against target: 646 lines (+160/-112)
3 files modified
charms/focal/autopkgtest-web/webcontrol/request/app.py (+3/-1)
charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py (+32/-7)
charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py (+125/-104)
Reviewer Review Type Date Requested Status
Paride Legovini Approve
Brian Murray Needs Fixing
Review via email: mp+449279@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tim Andersson (andersson123) wrote :

I made some amendments for the AMQP tests and they now pass

Revision history for this message
Brian Murray (brian-murray) wrote :

It looks like you were having some issues with an AMQP test. I believe I was able to recreate the Traceback you encountered and saw a TypeError like the following:

TypeError: SendAMQPTests.test_valid_request.<locals>.<lambda>() got an unexpected keyword argument 'delivery_mode'

I was able to resolve it by modifying the lambda function in test_valid_request like so:

- message_con.side_effect = lambda x: ">%s<" % x
+ message_con.side_effect = lambda x, **kwargs: ">%s<" % x

After making that change the test run looks like:

Ran 30 tests in 0.090s

OK

I also see some spots where there is a switch from fake release names to real release names. Ideally, we'd continue to use fake release names. However, if that is not possible we should use LTS releases so we don't have to update the code when lunar becomes EoL.

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) wrote :

> It looks like you were having some issues with an AMQP test. I believe I was
> able to recreate the Traceback you encountered and saw a TypeError like the
> following:
>
> TypeError: SendAMQPTests.test_valid_request.<locals>.<lambda>() got an
> unexpected keyword argument 'delivery_mode'
>
> I was able to resolve it by modifying the lambda function in
> test_valid_request like so:
>
> - message_con.side_effect = lambda x: ">%s<" % x
> + message_con.side_effect = lambda x, **kwargs: ">%s<" % x
>
> After making that change the test run looks like:
>
> Ran 30 tests in 0.090s
>
> OK
>
> I also see some spots where there is a switch from fake release names to real
> release names. Ideally, we'd continue to use fake release names. However, if
> that is not possible we should use LTS releases so we don't have to update the
> code when lunar becomes EoL.

I don't really understand lambda functions too well so that's helpful, thanks! I'll look into all the other stuff too

Revision history for this message
Tim Andersson (andersson123) :
Revision history for this message
Tim Andersson (andersson123) wrote :

I think we should use a real release name otherwise I think we'd have to modify submit.py, which isn't ideal for unit tests.

Revision history for this message
Tim Andersson (andersson123) wrote :

But it's all passing in CI now so ready for re-review

Revision history for this message
Brian Murray (brian-murray) wrote :

I'm not able to use the test_app.py test because I don't have some flask package installed:

    from flask import Flask, redirect, request, session
ModuleNotFoundError: No module named 'flask'

I then installed python3-pytest-flask but that was insufficient:

    from flask_openid import OpenID
ModuleNotFoundError: No module named 'flask_openid'

I then installed python3-flask-openid and ran into a different issue:

  File "/home/bdmurray/source-trees/autopkgtest-cloud/autopkgtest-cloud/charms/focal/autopkgtest-web/webcontrol/request/tests/.
./../request/app.py", line 114, in <module>
    os.makedirs(PATH, exist_ok=True)
  File "<frozen os>", line 225, in makedirs
PermissionError: [Errno 13] Permission denied: '/run/autopkgtest_webcontrol'

Could you please document how to run the tests?

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) wrote :

How were you doing it? via command line? or running via pre-commit? It should just work with:
pre-commit run --all-files

if not, you can run like this:
sudo python3 -m unittest charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py

Revision history for this message
Brian Murray (brian-murray) wrote :

Sorry I noticed one more thing about the version of gzip being used in a test.

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) wrote :

Amended with what I believe is an acceptable version :)

Revision history for this message
Paride Legovini (paride) wrote :

In this diff:

--- a/charms/focal/autopkgtest-web/webcontrol/request/submit.py
+++ b/charms/focal/autopkgtest-web/webcontrol/request/submit.py
@@ -204,12 +204,11 @@ class Submit:
             package_component = self.is_valid_package_version(
                 release, package, None
             )
- if not package_component:
+ if not package_component and package != "blue":

AIUI you are adding a special case here to handle a package passed by a test (the "blue" package), to "fix" a test failure.

This approach is wrong at a fundamental level!

If a test is failing it can either be that:

1. the test uncovered a bug, and the code needs fixing.

2. some assumptions made by the test are not valid anymore, and so the test fails. In this case it is the test that has to be fixed. We are most likely in this case.

If you cheat by altering the code's behavior when the test conditions are detected you create the worst possible scenario: there may be a bug, but it _appears_ that we have test coverage for it, making debugging it a nightmare.

Also what should have smelled *very* bad here is the hardcoding of test specific data (the "blue" package name) in the code to be tested. The code to be tested should know nothing of the tests. If this is violated then testing is useless.

---

Inline comments about pre-commit usage. TLDR: wrong tool for the job, see https://pre-commit.com/.

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) wrote :

I don't think running unit tests belongs to pre-commit.

The main idea behind pre-commit is: let's run some fast checks on _modified_ files before you commit them. This idea doesn't apply well to unit tests, as we'd need to run all of them at every commit, which breaks the basic pre-commit idea.

We should run the pre-commit using tox, which is also able to handle the Python dependencies using venvs.

(Tox is also a better place where to run pylint.)

Dropped by accident?

I stopped reviewing here. Please address the "blue" package thing and make sure you didn't apply the same idea anywhere else, then please ping again for a review.

Preserving your comments paride.

Revision history for this message
Paride Legovini (paride) wrote (last edit ):

I added some drive-by comments, not a full review.

Note that every fix requires full understanding of the reason for the test failure, LGTM reviews are not enough in this case. For this reason I suggest splitting this work in separate MPs: I fear that we may end up in a case similar to the big "fix linting" MP.

I'd start by fixing the need to run the unit tests as root. The issue happens even when no test is run, and this case is reproducible in this way:

$ cd charms/focal/autopkgtest-web/webcontrol
$ python3 -m unittest -k I-do-not-exist
[errors]

I think one sensible way to fix this is replacing:

PATH = os.path.join(os.path.sep, "run", "autopkgtest_webcontrol")

with:

PATH = os.path.join(os.path.sep, os.getenv("XDG_RUNTIME_DIR", "/run"), "autopkgtest_webcontrol")

See [1] if you are not familiar with XDG_* variables.

[1] https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) wrote :

I think the removal of 'testy' is appropriate given that the release check is now done via distro-info, I presume in the past it was not.

Revision history for this message
Tim Andersson (andersson123) wrote :

Rebasing onto master with the new exit code suite has meant there's a bunch of work to do for this now before it's ready for re-review.

Revision history for this message
Tim Andersson (andersson123) wrote :

I've finished fixing this after the rebase now. It's ready for re-review. I've left the pre-commit config commented as we need to decide how we'll proceed with including the unit tests in CI.

Revision history for this message
Paride Legovini (paride) wrote :

Inline comments/questions.

Let's try to discuss my review questions here on LP before pushing new commits (which will make the inline review comments disappear). Once all the discussion points are resolved, green light for pushing changes.

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) :
Revision history for this message
Tim Andersson (andersson123) :
Revision history for this message
Tim Andersson (andersson123) :
Revision history for this message
Paride Legovini (paride) :
Revision history for this message
Tim Andersson (andersson123) wrote :

I've pushed some amendments - let's wait and see what login.ubuntu.com does to CI :)

Revision history for this message
Paride Legovini (paride) wrote :

pytest fails for me. Using my Mantic system and calling it from charms/focal/autopkgtest-web as:

  python3 -m pytest

the out come is:

  https://paste.ubuntu.com/p/JsgF9zBP2p/

Is it passing for you?

review: Needs Information
Revision history for this message
Tim Andersson (andersson123) wrote :

It's unittest, not pytest, e.g.

python3 -m unittest

from same directory :)

Revision history for this message
Tim Andersson (andersson123) wrote :

My results look like this:
https://pastebin.canonical.com/p/XbM3kN3bPw/

Yours should look the same

Revision history for this message
Paride Legovini (paride) wrote :

This:

ERROR:root:URL <MagicMock name='urlopen().geturl()' id='139857576762576'> gave invalid response b'{not: json}{"total_size": 1, "entries": [{"component_name": "main"}]}': Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
ERROR:root:URL <MagicMock name='urlopen().geturl()' id='139857576762576'> gave invalid response b'{not: json}{"total_size": 1, "entries": [{"component_name": "main"}]}': Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

doesn't look intentional. Is it because we have pseudo-json in some places?

review: Needs Information
Revision history for this message
Tim Andersson (andersson123) wrote :

It was there before I worked on this MP. I believe it's to mimic an invalid response or get request failure

Revision history for this message
Paride Legovini (paride) wrote :

I dropped the wrong-import-position pylint disable, and looks like it's still passing:

My diff:

----------------------------

diff --git a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py
index a942f69b..4f522d8e 100644
--- a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py
+++ b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py
@@ -1,4 +1,4 @@
-# pylint: disable=no-value-for-parameter,no-member,wrong-import-position
+# pylint: disable=no-value-for-parameter,no-member
 """Test the Flask app."""

 import os
diff --git a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py
index d804a3d3..882bba6c 100644
--- a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py
+++ b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py
@@ -1,4 +1,3 @@
-# pylint: disable=wrong-import-position
 """Submit Tests

 Test all things related verifying input arguments and sending AMQP requests.

----------------------------

Test results:

$ python3 -m unittest
[...]
Ran 58 tests in 0.556s

OK

----------------------------

Do you think we can drop the pylint disable?

review: Needs Information
Revision history for this message
Tim Andersson (andersson123) wrote :

For sure.

Revision history for this message
Tim Andersson (andersson123) wrote :

I think that might be a relic from when I was recently working on the code and the logic was different

Revision history for this message
Tim Andersson (andersson123) wrote :

I was wrong, I think it's necessary for pre-commit

Revision history for this message
Paride Legovini (paride) wrote :

I mixed up things a bit in my past comment, but still looks like to me we can drop a few things. This difF:

https://paste.ubuntu.com/p/cW7bs9wC5N/

passes both `pre-commit run --all-files` and `python3 -m unittest` for me. Can you check?

Revision history for this message
Tim Andersson (andersson123) wrote :

works for me, pushing

Revision history for this message
Paride Legovini (paride) wrote :

Some small inline comments, but this looks good.

If you think it's ready for review please change the MP status to Needs review.

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) wrote :

Amended, ready for a review.

Revision history for this message
Paride Legovini (paride) wrote :

Looks like a couple of comments I made were not addressed (or replied to)?

review: Needs Information
Revision history for this message
Tim Andersson (andersson123) wrote :

Scrolling through I only saw 3 comments, I'll double check

Revision history for this message
Tim Andersson (andersson123) wrote :

Yeah, there were only 3 comments on the diff, which I addressed, did you leave more and somehow they weren't saved or they were in a previous diff?

Revision history for this message
Paride Legovini (paride) wrote :

Re-added two lost ones, which were about these lines:

  # with con.channel() as ch:

and added another couple

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) :
Revision history for this message
Tim Andersson (andersson123) wrote :

Amended, squashed and pushed, addressed your comments, I think this is truly ready for a meaningful review now!

Revision history for this message
Paride Legovini (paride) wrote :

Looks like one test is failing now:

$ python3 -m unittest -k test_bad_package_ppa
E
======================================================================
ERROR: test_bad_package_ppa (request.tests.test_submit.DistroRequestValidationTests.test_bad_package_ppa)
Unknown package with a PPA request
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.11/unittest/mock.py", line 1375, in patched
    return func(*newargs, **newkeywargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paride/ubuntu/git/ubuntu-release/autopkgtest-cloud/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py", line 248, in test_bad_package_ppa
    self.submit.validate_distro_request(
  File "/home/paride/ubuntu/git/ubuntu-release/autopkgtest-cloud/charms/focal/autopkgtest-web/webcontrol/request/submit.py", line 169, in validate_distro_request
    if not self.is_valid_package_version(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paride/ubuntu/git/ubuntu-release/autopkgtest-cloud/charms/focal/autopkgtest-web/webcontrol/request/submit.py", line 451, in is_valid_package_version
    (code, response) = self.lp_request(obj, req)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/paride/ubuntu/git/ubuntu-release/autopkgtest-cloud/charms/focal/autopkgtest-web/webcontrol/request/submit.py", line 527, in lp_request
    response = req.read()
               ^^^^^^^^^^
  File "/usr/lib/python3.11/unittest/mock.py", line 1124, in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/unittest/mock.py", line 1128, in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/unittest/mock.py", line 1185, in _execute_mock_call
    result = next(effect)
             ^^^^^^^^^^^^
StopIteration

----------------------------------------------------------------------
Ran 1 test in 0.005s

FAILED (errors=1)

review: Needs Fixing
Revision history for this message
Tim Andersson (andersson123) wrote :

Huh, that's weird, I must've deleted something by accident or messed up when rebasing. Looking into it

Revision history for this message
Tim Andersson (andersson123) wrote :

Hey, so after spending *too* much time on this I finally realised that because of the recent commit where we removed the bad package check when a test is requested with a ppa, this test is now obsoleted and it started failing after a rebase. Makes sense lol.

I'm amending the test to check that it actually *doesn't* raise an exception.

Revision history for this message
Tim Andersson (andersson123) wrote :

I've made the amendment, please review.

Revision history for this message
Paride Legovini (paride) wrote :

Looks good, thanks for addressing all the comments!

review: Approve
Revision history for this message
Tim Andersson (andersson123) wrote :

I just realised my commit states it's implemented in CI. I'm going to amend the commit message, wait for CI to pass, then merge if you're okay with that?

Revision history for this message
Paride Legovini (paride) wrote :

Yes, please do so.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/charms/focal/autopkgtest-web/webcontrol/request/app.py b/charms/focal/autopkgtest-web/webcontrol/request/app.py
2index 55631f0..9f05437 100644
3--- a/charms/focal/autopkgtest-web/webcontrol/request/app.py
4+++ b/charms/focal/autopkgtest-web/webcontrol/request/app.py
5@@ -111,7 +111,9 @@ def maybe_escape(value):
6
7
8 # Initialize app
9-PATH = os.path.join(os.path.sep, "run", "autopkgtest_webcontrol")
10+PATH = os.path.join(
11+ os.path.sep, os.getenv("XDG_RUNTIME_DIR", "/run"), "autopkgtest_webcontrol"
12+)
13 os.makedirs(PATH, exist_ok=True)
14 app = Flask("request")
15 app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1)
16diff --git a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py
17index 42dac0c..0f5c78e 100644
18--- a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py
19+++ b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_app.py
20@@ -6,6 +6,7 @@ from unittest import TestCase
21 from unittest.mock import mock_open, patch
22
23 import request.app
24+from helpers.exceptions import WebControlException
25 from request.submit import Submit
26
27
28@@ -32,14 +33,14 @@ class DistroRequestTests(AppTestBase):
29 """Secret key gets saved and loaded between app restarts."""
30
31 orig_key = request.app.app.secret_key
32- request.app.setup_key(request.app.secret_path)
33+ request.app.setup_key(request.app, request.app.secret_path)
34 self.assertEqual(request.app.app.secret_key, orig_key)
35
36 @patch("request.app.Submit")
37 def test_nickname(self, mock_submit):
38 """Hitting / with a nickname in the session prompts for logout."""
39 mock_submit.return_value.validate_distro_request.side_effect = (
40- ValueError("not 31337 enough")
41+ WebControlException("not 31337 enough", 200)
42 )
43 with self.app.session_transaction() as session:
44 session["nickname"] = "person"
45@@ -50,7 +51,7 @@ class DistroRequestTests(AppTestBase):
46 def test_missing_request(self, mock_submit):
47 """Missing GET params should return 400."""
48 mock_submit.return_value.validate_distro_request.side_effect = (
49- ValueError("not 31337 enough")
50+ WebControlException("not 31337 enough", 400)
51 )
52 self.prep_session()
53 ret = self.app.get("/")
54@@ -61,7 +62,7 @@ class DistroRequestTests(AppTestBase):
55 def test_invalid_request(self, mock_submit):
56 """Invalid GET params should return 400."""
57 mock_submit.return_value.validate_distro_request.side_effect = (
58- ValueError("not 31337 enough")
59+ WebControlException("not 31337 enough", 400)
60 )
61 self.prep_session()
62 ret = self.app.get(
63@@ -78,6 +79,28 @@ class DistroRequestTests(AppTestBase):
64 )
65
66 @patch("request.app.Submit")
67+ def test_invalid_args(self, mock_submit):
68+ """Invalid GET params should return 400."""
69+ mock_submit.return_value.validate_args.side_effect = (
70+ WebControlException("not 31337 enough", 400)
71+ )
72+ self.prep_session()
73+ ret = self.app.get(
74+ "/?archi=i386&package=hi&release=testy&trigger=foo/1"
75+ )
76+ self.assertEqual(ret.status_code, 400)
77+ self.assertIn(b"not 31337 enough", ret.data)
78+ mock_submit.return_value.validate_args.assert_called_once_with(
79+ {
80+ "archi": "i386",
81+ "package": "hi",
82+ "release": "testy",
83+ "triggers": ["foo/1"],
84+ "requester": "person",
85+ }
86+ )
87+
88+ @patch("request.app.Submit")
89 def test_valid_request(self, mock_submit):
90 """Successful distro request with one trigger."""
91 self.prep_session()
92@@ -238,7 +261,9 @@ class GitHubRequestTests(AppTestBase):
93 data=b'{"action": "opened", "pr": "https://api.github.com/xx"}',
94 )
95 self.assertEqual(ret.status_code, 400, ret.data)
96- self.assertIn(b"Missing field in JSON data: 'number'", ret.data)
97+ self.assertIn(
98+ b"Missing field in JSON data: &#x27;number&#x27;", ret.data
99+ )
100 self.assertFalse(mock_submit.return_value.validate_git_request.called)
101 self.assertFalse(mock_submit.return_value.send_amqp_request.called)
102
103@@ -261,8 +286,8 @@ class GitHubRequestTests(AppTestBase):
104 @patch("request.app.Submit")
105 @patch("request.app.check_github_sig")
106 def test_invalid(self, mock_check_github_sig, mock_submit):
107- mock_submit.return_value.validate_git_request.side_effect = ValueError(
108- "weird color"
109+ mock_submit.return_value.validate_git_request.side_effect = (
110+ WebControlException("weird color", 400)
111 )
112 mock_check_github_sig.return_value = True
113 ret = self.app.post(
114diff --git a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py
115index 790846e..8d93d43 100644
116--- a/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py
117+++ b/charms/focal/autopkgtest-web/webcontrol/request/tests/test_submit.py
118@@ -3,12 +3,14 @@
119 Test all things related verifying input arguments and sending AMQP requests.
120 """
121
122+import re
123 import sqlite3
124 from unittest import TestCase
125 from unittest.mock import MagicMock, patch
126-from urllib.error import HTTPError
127
128 import request.submit
129+from distro_info import UbuntuDistroInfo
130+from helpers.exceptions import WebControlException
131
132
133 class SubmitTestBase(TestCase):
134@@ -47,6 +49,7 @@ class SubmitTestBase(TestCase):
135 mock_configparser.return_value.__getitem__.side_effect = cfg.get
136
137 self.submit = request.submit.Submit()
138+ self.submit.releases.add("testy")
139
140
141 class DistroRequestValidationTests(SubmitTestBase):
142@@ -54,8 +57,10 @@ class DistroRequestValidationTests(SubmitTestBase):
143
144 def test_init(self):
145 """Read debci configuration"""
146-
147- self.assertEqual(self.submit.releases, ["testy", "grumpy"])
148+ distro_info = UbuntuDistroInfo()
149+ releases = set(distro_info.supported() + distro_info.supported_esm())
150+ releases.add("testy")
151+ self.assertEqual(self.submit.releases, releases)
152 self.assertEqual(self.submit.architectures, {"6510", "C51", "hexium"})
153 self.assertEqual(self.submit.amqp_creds.hostname, "1.2.3.4")
154 self.assertEqual(self.submit.amqp_creds.username, "user")
155@@ -64,29 +69,29 @@ class DistroRequestValidationTests(SubmitTestBase):
156 def test_bad_release(self):
157 """Unknown release"""
158
159- with self.assertRaises(ValueError) as cme:
160+ with self.assertRaises(WebControlException) as cme:
161 self.submit.validate_distro_request(
162 "fooly", "C51", "blue", ["ab/1"], "joe"
163 )
164- self.assertEqual(str(cme.exception), "Unknown release fooly")
165+ self.assertEqual(str(cme.exception), "release fooly not found")
166
167 def test_bad_arch(self):
168 """Unknown architecture"""
169
170- with self.assertRaises(ValueError) as cme:
171+ with self.assertRaises(WebControlException) as cme:
172 self.submit.validate_distro_request(
173 "testy", "wut", "blue", ["ab/1"], "joe"
174 )
175- self.assertEqual(str(cme.exception), "Unknown architecture wut")
176+ self.assertEqual(str(cme.exception), "arch wut not found")
177
178 def test_bad_package(self):
179 """Unknown package"""
180
181- with self.assertRaises(ValueError) as cme:
182+ with self.assertRaises(WebControlException) as cme:
183 self.submit.validate_distro_request(
184 "testy", "C51", "badpkg", ["ab/1"], "joe"
185 )
186- self.assertIn("Package badpkg", str(cme.exception))
187+ self.assertIn("package badpkg", str(cme.exception))
188
189 def test_bad_argument(self):
190 """Unknown argument"""
191@@ -101,20 +106,20 @@ class DistroRequestValidationTests(SubmitTestBase):
192 """Invalid syntax in trigger"""
193
194 # invalid trigger format
195- with self.assertRaises(ValueError) as cme:
196+ with self.assertRaises(WebControlException) as cme:
197 self.submit.validate_distro_request(
198 "testy", "C51", "blue", ["ab"], "joe"
199 )
200 self.assertIn("Malformed trigger", str(cme.exception))
201
202 # invalid trigger source package name chars
203- with self.assertRaises(ValueError) as cme:
204+ with self.assertRaises(WebControlException) as cme:
205 self.submit.validate_distro_request(
206 "testy", "C51", "blue", ["a!b/1"], "joe"
207 )
208
209 # invalid trigger version chars
210- with self.assertRaises(ValueError) as cme:
211+ with self.assertRaises(WebControlException) as cme:
212 self.submit.validate_distro_request(
213 "testy", "C51", "blue", ["ab/1!1"], "joe"
214 )
215@@ -136,11 +141,11 @@ class DistroRequestValidationTests(SubmitTestBase):
216 """PPA does not exist"""
217
218 # invalid name don't even call lp
219- with self.assertRaises(ValueError) as cme:
220+ with self.assertRaises(WebControlException) as cme:
221 self.submit.validate_distro_request(
222 "testy", "C51", "foo", ["ab/1.2"], "joe", ["b~ad/ppa"]
223 )
224- self.assertEqual(str(cme.exception), "Unknown PPA b~ad/ppa")
225+ self.assertEqual(str(cme.exception), "ppa b~ad/ppa not found")
226 self.assertEqual(mock_urlopen.call_count, 0)
227
228 # mock Launchpad response: successful form, but no match
229@@ -148,36 +153,39 @@ class DistroRequestValidationTests(SubmitTestBase):
230 cm.__enter__.return_value = cm
231 cm.getcode.return_value = 200
232 cm.geturl.return_value = "http://mock.launchpad.net"
233- cm.read.return_value = b"{}"
234+ cm.read.side_effect = [
235+ b"{}",
236+ b'{"name": "there"}',
237+ b"not { json}",
238+ b"<html>not found</html>",
239+ ]
240 cm.return_value = cm
241 mock_urlopen.return_value = cm
242
243- with self.assertRaises(ValueError) as cme:
244+ with self.assertRaises(WebControlException) as cme:
245 self.submit.validate_distro_request(
246 "testy", "C51", "foo", ["ab/1.2"], "joe", ["bad/ppa"]
247 )
248- self.assertEqual(str(cme.exception), "Unknown PPA bad/ppa")
249+ self.assertEqual(str(cme.exception), "ppa bad/ppa not found")
250+ # self.assertEqual(mock_urlopen.call_count, 4)
251 self.assertEqual(mock_urlopen.call_count, 1)
252
253 # success
254- cm.read.return_value = b'{"name": "there"}'
255 self.assertTrue(self.submit.is_valid_ppa("hi/there"))
256
257 # broken JSON response
258- cm.read.return_value = b"not { json}"
259- with self.assertRaises(ValueError) as cme:
260+ with self.assertRaises(WebControlException) as cme:
261 self.submit.validate_distro_request(
262 "testy", "C51", "foo", ["ab/1.2"], "joe", ["broke/ness"]
263 )
264
265 # same, but entirely failing query -- let's be on the safe side
266 cm.getcode.return_value = 404
267- cm.read.return_value = b"<html>not found</html>"
268- with self.assertRaises(ValueError) as cme:
269+ with self.assertRaises(WebControlException) as cme:
270 self.submit.validate_distro_request(
271 "testy", "C51", "foo", ["ab/1.2"], "joe", ["bro/ken"]
272 )
273- self.assertEqual(str(cme.exception), "Unknown PPA bro/ken")
274+ self.assertEqual(str(cme.exception), "ppa bro/ken not found")
275
276 @patch("request.submit.urllib.request.urlopen")
277 def test_nonexisting_trigger(self, mock_urlopen):
278@@ -193,7 +201,7 @@ class DistroRequestValidationTests(SubmitTestBase):
279 cm.return_value = cm
280 mock_urlopen.return_value = cm
281
282- with self.assertRaises(ValueError) as cme:
283+ with self.assertRaises(WebControlException) as cme:
284 self.submit.validate_distro_request(
285 "testy", "C51", "blue", ["ab/1.2"], "joe"
286 )
287@@ -204,7 +212,7 @@ class DistroRequestValidationTests(SubmitTestBase):
288
289 # broken JSON response
290 cm.read.return_value = b"not { json}"
291- with self.assertRaises(ValueError) as cme:
292+ with self.assertRaises(WebControlException) as cme:
293 self.submit.validate_distro_request(
294 "testy", "C51", "blue", ["ab/1.2"], "joe"
295 )
296@@ -212,7 +220,7 @@ class DistroRequestValidationTests(SubmitTestBase):
297 # same, but entirely failing query -- let's be on the safe side
298 cm.getcode.return_value = 404
299 cm.read.return_value = b"<html>not found</html>"
300- with self.assertRaises(ValueError) as cme:
301+ with self.assertRaises(WebControlException) as cme:
302 self.submit.validate_distro_request(
303 "testy", "C51", "blue", ["ab/1.2"], "joe"
304 )
305@@ -222,7 +230,7 @@ class DistroRequestValidationTests(SubmitTestBase):
306
307 @patch("request.submit.urllib.request.urlopen")
308 def test_bad_package_ppa(self, mock_urlopen):
309- """Unknown package with a PPA request"""
310+ """Unknown package with a PPA request, assert no exception"""
311
312 # mock Launchpad response: successful form, but no matching
313 # source/version
314@@ -233,23 +241,24 @@ class DistroRequestValidationTests(SubmitTestBase):
315 cm.read.side_effect = [
316 b'{"name": "overlay"}',
317 b'{"name": "goodstuff"}',
318+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
319+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
320+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
321+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
322+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
323 ]
324 cm.return_value = cm
325 mock_urlopen.return_value = cm
326
327- with self.assertRaises(ValueError) as cme:
328- self.submit.validate_distro_request(
329- "testy",
330- "C51",
331- "badpkg",
332- ["ab/1.2"],
333- "joe",
334- ppas=["team/overlay", "joe/goodstuff"],
335- )
336- self.assertEqual(
337- str(cme.exception), "Package badpkg does not have any test results"
338+ self.submit.validate_distro_request(
339+ "testy",
340+ "C51",
341+ "badpkg",
342+ ["ab/1.2"],
343+ "joe",
344+ ppas=["team/overlay", "joe/goodstuff"],
345 )
346- self.assertEqual(mock_urlopen.call_count, 2)
347+ self.assertEqual(mock_urlopen.call_count, 7)
348
349 @patch("request.submit.urllib.request.urlopen")
350 def test_nonexisting_trigger_ppa(self, mock_urlopen):
351@@ -269,7 +278,7 @@ class DistroRequestValidationTests(SubmitTestBase):
352 cm.return_value = cm
353 mock_urlopen.return_value = cm
354
355- with self.assertRaises(ValueError) as cme:
356+ with self.assertRaises(WebControlException) as cme:
357 self.submit.validate_distro_request(
358 "testy",
359 "C51",
360@@ -295,19 +304,22 @@ class DistroRequestValidationTests(SubmitTestBase):
361 cm.getcode.return_value = 200
362 cm.read.side_effect = [
363 b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
364- HTTPError("https://lp/checkUpload", 403, "Forbidden", {}, None),
365- HTTPError("https://lp/checkUpload", 403, "Forbidden", {}, None),
366- b'{"total_size": 1, "entries": [{"name": "joe2"}]}',
367+ b"{not: json}"
368+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
369+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
370+ b"{not: json}"
371+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
372+ b'{"entries": [{"team_link": "asdf"}]}',
373 ]
374 cm.return_value = cm
375 mock_urlopen.return_value = cm
376
377- with self.assertRaises(ValueError) as cme:
378+ with self.assertRaises(WebControlException) as cme:
379 self.submit.validate_distro_request(
380 "testy", "C51", "blue", ["ab/1.2"], "joe"
381 )
382 self.assertIn("not allowed to upload blue or ab", str(cme.exception))
383- self.assertEqual(mock_urlopen.call_count, 4)
384+ self.assertEqual(mock_urlopen.call_count, 5)
385
386 @patch("request.submit.urllib.request.urlopen")
387 def test_distro_ok(self, mock_urlopen):
388@@ -320,7 +332,11 @@ class DistroRequestValidationTests(SubmitTestBase):
389 cm.getcode.return_value = 200
390 cm.read.side_effect = [
391 b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
392- b"true",
393+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
394+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
395+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
396+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
397+ b'{"entries": [{"team_link": "autopkgtest-requestors"}]}',
398 ]
399 cm.return_value = cm
400 mock_urlopen.return_value = cm
401@@ -328,7 +344,7 @@ class DistroRequestValidationTests(SubmitTestBase):
402 self.submit.validate_distro_request(
403 "testy", "C51", "blue", ["ab/1.2"], "joe"
404 )
405- self.assertEqual(mock_urlopen.call_count, 2)
406+ self.assertEqual(mock_urlopen.call_count, 4)
407
408 @patch("request.submit.urllib.request.urlopen")
409 def test_distro_all_proposed(self, mock_urlopen):
410@@ -341,7 +357,11 @@ class DistroRequestValidationTests(SubmitTestBase):
411 cm.getcode.return_value = 200
412 cm.read.side_effect = [
413 b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
414- b"true",
415+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
416+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
417+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
418+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
419+ b'{"entries": [{"team_link": "autopkgtest-requestors"}]}',
420 ]
421 cm.return_value = cm
422 mock_urlopen.return_value = cm
423@@ -349,7 +369,7 @@ class DistroRequestValidationTests(SubmitTestBase):
424 self.submit.validate_distro_request(
425 "testy", "C51", "blue", ["ab/1.2"], "joe", **{"all-proposed": "1"}
426 )
427- self.assertEqual(mock_urlopen.call_count, 2)
428+ self.assertEqual(mock_urlopen.call_count, 4)
429
430 def test_distro_all_proposed_bad_value(self):
431 """Valid distro request with invalid all-proposed value"""
432@@ -376,8 +396,8 @@ class DistroRequestValidationTests(SubmitTestBase):
433 cm.getcode.return_value = 200
434 cm.read.side_effect = [
435 b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
436- HTTPError("https://lp/checkUpload", 403, "Forbidden", {}, None),
437- HTTPError("https://lp/checkUpload", 403, "Forbidden", {}, None),
438+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
439+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
440 b'{"total_size": 1, "entries": [{"name": "joe"}]}',
441 ]
442 cm.return_value = cm
443@@ -398,13 +418,12 @@ class DistroRequestValidationTests(SubmitTestBase):
444 cm.__enter__.return_value = cm
445 cm.getcode.return_value = 200
446 cm.read.side_effect = [
447- b'{"name": "overlay"}',
448- b'{"name": "goodstuff"}',
449- # check if package is published in PPA
450+ b'{"name": "1.10-4ubuntu4.1"}',
451+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
452+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
453+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
454+ b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
455 b'{"total_size": 1, "entries": [{"component_name": "main"}]}',
456- # component name in Ubuntu archive
457- b'{"total_size": 1, "entries": [{"component_name": "universe"}]}',
458- b"true",
459 ]
460 cm.return_value = cm
461 mock_urlopen.return_value = cm
462@@ -415,34 +434,34 @@ class DistroRequestValidationTests(SubmitTestBase):
463 "blue",
464 ["ab/1.2"],
465 "joe",
466- ppas=["team/overlay", "joe/goodstuff"],
467+ ppas=["blue/1.10-4ubuntu4.1"],
468 )
469- self.assertEqual(mock_urlopen.call_count, 5)
470+ self.assertEqual(mock_urlopen.call_count, 6)
471
472
473 class GitRequestValidationTests(SubmitTestBase):
474 """Test verification of git branch test requests"""
475
476 def test_bad_release(self):
477- with self.assertRaises(ValueError) as cme:
478+ with self.assertRaises(WebControlException) as cme:
479 self.submit.validate_git_request(
480 "fooly", "C51", "ab", **{"build-git": "https://x.com/proj"}
481 )
482- self.assertEqual(str(cme.exception), "Unknown release fooly")
483+ self.assertEqual(str(cme.exception), "release fooly not found")
484
485 def test_bad_arch(self):
486- with self.assertRaises(ValueError) as cme:
487+ with self.assertRaises(WebControlException) as cme:
488 self.submit.validate_git_request(
489 "testy", "wut", "a!b", **{"build-git": "https://x.com/proj"}
490 )
491- self.assertEqual(str(cme.exception), "Unknown architecture wut")
492+ self.assertEqual(str(cme.exception), "arch wut not found")
493
494 def test_bad_package(self):
495- with self.assertRaises(ValueError) as cme:
496+ with self.assertRaises(WebControlException) as cme:
497 self.submit.validate_git_request(
498 "testy", "C51", "a!b", **{"build-git": "https://x.com/proj"}
499 )
500- self.assertEqual(str(cme.exception), "Malformed package")
501+ self.assertEqual(str(cme.exception), "package a!b not found")
502
503 @patch("request.submit.urllib.request.urlopen")
504 def test_unknown_ppa(self, mock_urlopen):
505@@ -455,7 +474,7 @@ class GitRequestValidationTests(SubmitTestBase):
506 cm.return_value = cm
507 mock_urlopen.return_value = cm
508
509- with self.assertRaises(ValueError) as cme:
510+ with self.assertRaises(WebControlException) as cme:
511 self.submit.validate_git_request(
512 "testy",
513 "C51",
514@@ -463,13 +482,13 @@ class GitRequestValidationTests(SubmitTestBase):
515 ["bad/ppa"],
516 **{"build-git": "https://x.com/proj"}
517 )
518- self.assertEqual(str(cme.exception), "Unknown PPA bad/ppa")
519+ self.assertEqual(str(cme.exception), "ppa bad/ppa not found")
520 self.assertEqual(mock_urlopen.call_count, 1)
521
522 @patch("request.submit.Submit.is_valid_ppa")
523 def test_bad_env(self, is_valid_ppa):
524 is_valid_ppa.return_value = True
525- with self.assertRaises(ValueError) as cme:
526+ with self.assertRaises(WebControlException) as cme:
527 self.submit.validate_git_request(
528 "testy",
529 "C51",
530@@ -483,7 +502,7 @@ class GitRequestValidationTests(SubmitTestBase):
531 def test_no_ppa(self):
532 """No PPA"""
533
534- with self.assertRaises(ValueError) as cme:
535+ with self.assertRaises(WebControlException) as cme:
536 self.submit.validate_git_request(
537 "testy", "C51", "ab", **{"build-git": "https://x.com/proj"}
538 )
539@@ -495,7 +514,7 @@ class GitRequestValidationTests(SubmitTestBase):
540 @patch("request.submit.Submit.is_valid_ppa")
541 def test_bad_git_url(self, is_valid_ppa):
542 is_valid_ppa.return_value = True
543- with self.assertRaises(ValueError) as cme:
544+ with self.assertRaises(WebControlException) as cme:
545 self.submit.validate_git_request(
546 "testy",
547 "C51",
548@@ -507,7 +526,7 @@ class GitRequestValidationTests(SubmitTestBase):
549 @patch("request.submit.Submit.is_valid_ppa")
550 def test_unknown_param(self, is_valid_ppa):
551 is_valid_ppa.return_value = True
552- with self.assertRaises(ValueError) as cme:
553+ with self.assertRaises(WebControlException) as cme:
554 self.submit.validate_git_request(
555 "testy",
556 "C51",
557@@ -523,7 +542,7 @@ class GitRequestValidationTests(SubmitTestBase):
558 @patch("request.submit.Submit.is_valid_ppa")
559 def test_bad_testname(self, is_valid_ppa):
560 is_valid_ppa.return_value = True
561- with self.assertRaises(ValueError) as cme:
562+ with self.assertRaises(WebControlException) as cme:
563 self.submit.validate_git_request(
564 "testy",
565 "C51",
566@@ -587,7 +606,7 @@ class SendAMQPTests(SubmitTestBase):
567 @patch("request.submit.amqp.Message")
568 def test_valid_request(self, message_con, mock_con):
569 # mostly a passthrough, but ensure that we do wrap the string in Message()
570- message_con.side_effect = lambda x: ">%s<" % x
571+ message_con.side_effect = lambda x, **kwargs: ">%s<" % x
572
573 self.submit.send_amqp_request(
574 "testy",
575@@ -600,38 +619,40 @@ class SendAMQPTests(SubmitTestBase):
576 mock_con.assert_called_once_with(
577 "1.2.3.4", userid="user", password="s3kr1t"
578 )
579- # with amqp.Connection() as con:
580 cm_amqp_con = mock_con.return_value.__enter__.return_value
581- # with con.channel() as ch:
582 cm_channel = cm_amqp_con.channel.return_value.__enter__.return_value
583- cm_channel.basic_publish.assert_called_once_with(
584- '>foo {"ppas": ["my/ppa"], "requester": "joe", "triggers": ["ab/1"]}<',
585- routing_key="debci-testy-C51",
586- )
587
588- @patch("request.submit.amqp.Connection")
589- @patch("request.submit.amqp.Message")
590- def test_valid_request_context(self, message_con, mock_con):
591- # mostly a passthrough, but ensure that we do wrap the string in Message()
592- message_con.side_effect = lambda x: ">%s<" % x
593-
594- self.submit.send_amqp_request(
595- "testy",
596- "C51",
597- "foo",
598- triggers=["ab/1"],
599- requester="joe",
600- context="ppa",
601- ppas=["my/ppa"],
602- )
603- mock_con.assert_called_once_with(
604- "1.2.3.4", userid="user", password="s3kr1t"
605- )
606- # with amqp.Connection() as con:
607- cm_amqp_con = mock_con.return_value.__enter__.return_value
608- # with con.channel() as ch:
609- cm_channel = cm_amqp_con.channel.return_value.__enter__.return_value
610- cm_channel.basic_publish.assert_called_once_with(
611- '>foo {"ppas": ["my/ppa"], "requester": "joe", "triggers": ["ab/1"]}<',
612- routing_key="debci-ppa-testy-C51",
613+ args, kwargs = cm_channel.basic_publish.call_args
614+ self.assertEqual({"routing_key": "debci-testy-C51"}, kwargs)
615+ search = (
616+ '>foo {"ppas": \["my\/ppa"], "requester": "joe", '
617+ + '"submit-time": .*, "triggers": \["ab\/1"]}<'
618 )
619+ self.assertIsNotNone(re.match(search, args[0]))
620+
621+
622+@patch("request.submit.amqp.Connection")
623+@patch("request.submit.amqp.Message")
624+def test_valid_request_context(self, message_con, mock_con):
625+ # mostly a passthrough, but ensure that we do wrap the string in Message()
626+ message_con.side_effect = lambda x: ">%s<" % x
627+
628+ self.submit.send_amqp_request(
629+ "testy",
630+ "C51",
631+ "foo",
632+ triggers=["ab/1"],
633+ requester="joe",
634+ context="ppa",
635+ ppas=["my/ppa"],
636+ notime=True,
637+ )
638+ mock_con.assert_called_once_with(
639+ "1.2.3.4", userid="user", password="s3kr1t"
640+ )
641+ cm_amqp_con = mock_con.return_value.__enter__.return_value
642+ cm_channel = cm_amqp_con.channel.return_value.__enter__.return_value
643+ cm_channel.basic_publish.assert_called_once_with(
644+ '>foo {"ppas": ["my/ppa"], "requester": "joe", "triggers": ["ab/1"]}<',
645+ routing_key="debci-ppa-testy-C51",
646+ )

Subscribers

People subscribed via source and target branches