diff --git a/tests/test_cli/fakeclient.py b/tests/test_cli/fakeclient.py index f074dce1..0190974c 100644 --- a/tests/test_cli/fakeclient.py +++ b/tests/test_cli/fakeclient.py @@ -44,6 +44,8 @@ class FakeClientSession(BaseFakeClientSession): super(FakeClientSession, self).__init__(*a, **kw) self._calldata = {} self._offsets = {} + self._missing_rsession = None + # caller can set _missing_rsession to a Recording session to handle missing calls def load_calls(self, data): """Load call data @@ -75,6 +77,9 @@ class FakeClientSession(BaseFakeClientSession): # we may have a series of calls for each key calls = self._calldata.get(key) ofs = self._offsets.get(key, 0) + if calls is None: + # we don't have it + return self._handle_missing(name, args, kwargs) call = calls[ofs] ofs += 1 if ofs < len(calls): @@ -90,6 +95,15 @@ class FakeClientSession(BaseFakeClientSession): else: return mock.MagicMock() + def _handle_missing(self, name, args, kwargs): + print('Missing call data for: %s %r %r' % (name, args, kwargs)) + rsession = self._missing_rsession + if rsession is None: + return mock.MagicMock() + + # otherwise use the recording session + return rsession._callMethod(name, args, kwargs) + def _munge(self, data): def callback(value): if isinstance(value, list): diff --git a/tests/test_www/data/pages_calls.json b/tests/test_www/data/pages_calls.json index 5c45cc10..49268e19 100644 --- a/tests/test_www/data/pages_calls.json +++ b/tests/test_www/data/pages_calls.json @@ -51057,6 +51057,12 @@ "method": "mavenEnabled", "result": true }, + { + "args": [], + "kwargs": {}, + "method": "winEnabled", + "result": true + }, { "args": [ 2 @@ -56142,5 +56148,1081 @@ "task_id": null } ] + }, + { + "args": [], + "kwargs": { + "queryOpts": { + "countOnly": true + }, + "repoID": 2580 + }, + "method": "listBuildroots", + "result": 2 + }, + { + "args": [ + [ + [ + "repo_id", + "=", + 2580 + ] + ], + [ + "id" + ] + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [] + }, + { + "args": [ + 798 + ], + "kwargs": {}, + "method": "repo.get", + "result": null + }, + { + "args": [ + [ + [ + "tag_id", + "=", + 798 + ] + ] + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 0 + }, + { + "args": [], + "kwargs": { + "queryOpts": { + "countOnly": true + }, + "repoID": 88 + }, + "method": "listBuildroots", + "result": 0 + }, + { + "args": [ + [ + [ + "repo_id", + "=", + 88 + ] + ], + [ + "id" + ] + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [] + }, + { + "args": [ + 2 + ], + "kwargs": {}, + "method": "repo.get", + "result": { + "begin_event": 497768, + "begin_ts": 1732049290.238136, + "create_event": 497784, + "create_ts": 1732569672.685269, + "creation_time": "2024-11-25 21:21:12.682068+00:00", + "creation_ts": 1732569672.682068, + "custom_opts": {}, + "dist": false, + "end_event": null, + "end_ts": null, + "id": 2599, + "opts": { + "debuginfo": false, + "maven": false, + "separate_src": false, + "src": false + }, + "state": 1, + "state_time": "2024-11-25 21:21:19.711390+00:00", + "state_ts": 1732569679.71139, + "tag_id": 2, + "tag_name": "f24-build", + "task_id": 14431, + "task_state": 2 + } + }, + { + "args": [ + [ + [ + "tag_id", + "=", + 2 + ] + ] + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 0 + }, + { + "args": [ + [ + [ + "task_id", + "=", + 1 + ] + ], + [ + "id" + ] + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [] + }, + { + "args": [ + [ + [ + "active", + true + ] + ], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 7 + }, + { + "args": [ + [ + [ + "active", + true + ] + ], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "-id" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + }, + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + }, + { + "active": true, + "at_event": 497087, + "create_time": "2025-05-08 11:42:18.526583+00:00", + "create_ts": 1746704538.526583, + "id": 131, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:18.526583+00:00", + "update_ts": 1746704538.526583 + }, + { + "active": true, + "at_event": 497088, + "create_time": "2025-05-08 11:42:15.500883+00:00", + "create_ts": 1746704535.500883, + "id": 130, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:15.500883+00:00", + "update_ts": 1746704535.500883 + }, + { + "active": true, + "at_event": 497089, + "create_time": "2025-05-08 11:42:03.370177+00:00", + "create_ts": 1746704523.370177, + "id": 129, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:03.370177+00:00", + "update_ts": 1746704523.370177 + }, + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + } + ] + }, + { + "args": [ + [], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 9 + }, + { + "args": [ + [], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "tag_name" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + }, + { + "active": true, + "at_event": 497089, + "create_time": "2025-05-08 11:42:03.370177+00:00", + "create_ts": 1746704523.370177, + "id": 129, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:03.370177+00:00", + "update_ts": 1746704523.370177 + }, + { + "active": true, + "at_event": 497088, + "create_time": "2025-05-08 11:42:15.500883+00:00", + "create_ts": 1746704535.500883, + "id": 130, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:15.500883+00:00", + "update_ts": 1746704535.500883 + }, + { + "active": true, + "at_event": 497087, + "create_time": "2025-05-08 11:42:18.526583+00:00", + "create_ts": 1746704538.526583, + "id": 131, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:18.526583+00:00", + "update_ts": 1746704538.526583 + }, + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + }, + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:04:43.253576+00:00", + "create_ts": 1737151483.253576, + "id": 126, + "min_event": 497788, + "opts": { + "debuginfo": true + }, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14444, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + }, + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:21:15.725044+00:00", + "create_ts": 1737152475.725044, + "id": 127, + "min_event": 497788, + "opts": {}, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14445, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + } + ] + }, + { + "args": [ + [ + [ + "active", + false + ] + ], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 2 + }, + { + "args": [ + [ + [ + "active", + false + ] + ], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "-id" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:21:15.725044+00:00", + "create_ts": 1737152475.725044, + "id": 127, + "min_event": 497788, + "opts": {}, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14445, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + }, + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:04:43.253576+00:00", + "create_ts": 1737151483.253576, + "id": 126, + "min_event": 497788, + "opts": { + "debuginfo": true + }, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14444, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + } + ] + }, + { + "args": [ + 1 + ], + "kwargs": { + "event": "auto", + "strict": true + }, + "method": "getTag", + "result": { + "arches": "x86_64", + "extra": { + "bar": 101, + "foo": 1, + "fuzz": 3 + }, + "id": 1, + "locked": false, + "maven_include_all": false, + "maven_support": false, + "name": "f24", + "perm": null, + "perm_id": null + } + }, + { + "args": [ + [ + [ + "tag_id", + 1 + ] + ], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 7 + }, + { + "args": [ + [ + [ + "tag_id", + 1 + ] + ], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "-id" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + }, + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + }, + { + "active": true, + "at_event": 497087, + "create_time": "2025-05-08 11:42:18.526583+00:00", + "create_ts": 1746704538.526583, + "id": 131, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:18.526583+00:00", + "update_ts": 1746704538.526583 + }, + { + "active": true, + "at_event": 497088, + "create_time": "2025-05-08 11:42:15.500883+00:00", + "create_ts": 1746704535.500883, + "id": 130, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:15.500883+00:00", + "update_ts": 1746704535.500883 + }, + { + "active": true, + "at_event": 497089, + "create_time": "2025-05-08 11:42:03.370177+00:00", + "create_ts": 1746704523.370177, + "id": 129, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:03.370177+00:00", + "update_ts": 1746704523.370177 + }, + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + } + ] + }, + { + "args": [ + [ + [ + "id", + "=", + 127 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:21:15.725044+00:00", + "create_ts": 1737152475.725044, + "id": 127, + "min_event": 497788, + "opts": {}, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14445, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + } + ] + }, + { + "args": [ + 497788 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497788, + "ts": 1737145925.263978 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 128 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + } + ] + }, + { + "args": [ + 497768 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497768, + "ts": 1732049290.238136 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 132 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + } + ] + }, + { + "args": [ + 497086 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497086, + "ts": 1706727187.174471 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 133 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + } + ] + }, + { + "args": [ + 497085 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497085, + "ts": 1706727156.407864 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 134 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + } + ] + }, + { + "args": [ + 497085 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497085, + "ts": 1706727156.407864 + } } -] \ No newline at end of file +] diff --git a/tests/test_www/test_pages.py b/tests/test_www/test_pages.py index f4be2221..612c8ba6 100644 --- a/tests/test_www/test_pages.py +++ b/tests/test_www/test_pages.py @@ -27,7 +27,9 @@ class TestPages(unittest.TestCase): def setUpClass(cls): # recording session used across tests in recording mode cls.cfile = os.path.dirname(__file__) + f'/data/pages_calls.json' + cls.cfile2 = os.path.dirname(__file__) + f'/data/pages_calls_updates.json' cls.recording = False + cls.updating = False cls.rsession = RecordingClientSession('http://localhost/kojihub', {}) @classmethod @@ -35,6 +37,8 @@ class TestPages(unittest.TestCase): if cls.recording: # save recorded calls cls.rsession.dump(cls.cfile) + elif cls.updating: + cls.rsession.dump(cls.cfile2) def setUp(self): self.environ = { @@ -66,6 +70,8 @@ class TestPages(unittest.TestCase): self.time.return_value = 1735707600.0 def __get_server(env): + # this is replacing the call to _getServer + env['koji.session'] = self.server return self.server self.get_server.side_effect = __get_server @@ -77,6 +83,8 @@ class TestPages(unittest.TestCase): else: self.server = FakeClientSession('SERVER', {}) self.server.load(self.cfile) + if self.updating: + self.server._missing_rsession = self.rsession return self.server def tearDown(self): @@ -166,17 +174,27 @@ class TestPages(unittest.TestCase): ['buildroots', ''], ['buildroots', 'start=50&order=id'], #['builds', 'start=50&order=id'], + ['reporequests', ''], + ['reporequests', 'active=all&order=tag_name'], + ['reporequests', 'active=false&order=-id'], + ['reporequests', 'tag=1&active=all'], + ['reporequest', 'reqID=127'], + ['reporequest', 'reqID=128'], + ['reporequest', 'reqID=132'], + ['reporequest', 'reqID=133'], + ['reporequest', 'reqID=134'], ] def prep_handler(self, method, query): """Takes method name and query string, returns handler and data""" # based loosely on publisher prep_handler - self.environ['QUERY_STRING'] = query - self.environ['koji.method'] = method - self.environ['SCRIPT_NAME'] = method + environ = self.environ.copy() + environ['QUERY_STRING'] = query + environ['koji.method'] = method + environ['SCRIPT_NAME'] = method handler = getattr(webidx, method) - fs = FieldStorageCompat(self.environ) - self.environ['koji.form'] = fs + fs = FieldStorageCompat(environ) + environ['koji.form'] = fs # even though we have curated urls, we need to filter args for some cases, e.g. search args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \ inspect.getfullargspec(handler) @@ -184,14 +202,14 @@ class TestPages(unittest.TestCase): data = dslice(fs.data, args, strict=False) else: data = fs.data.copy() - return handler, data + return handler, data, environ def test_web_handlers(self): """Test a bunch of web handlers""" for method, query in self.CALLS: - handler, data = self.prep_handler(method, query) + handler, data, environ = self.prep_handler(method, query) - result = handler(self.environ, **data) + result = handler(environ, **data) # result should be a string containing the rendered template self.assertIsInstance(result, str) diff --git a/tests/test_www/test_repoinfo.py b/tests/test_www/test_repoinfo.py index 81aafed1..1994b66c 100644 --- a/tests/test_www/test_repoinfo.py +++ b/tests/test_www/test_repoinfo.py @@ -33,7 +33,7 @@ class TestRepoInfo(unittest.TestCase): webidx.repoinfo(self.environ, self.repo_id) self.server.repoInfo.assert_called_once_with(int(self.repo_id), strict=False) - self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id)) + self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id), queryOpts={'countOnly': True}) def test_repoinfo_not_dist(self): """Test repoinfo function - not dist repo""" @@ -46,4 +46,7 @@ class TestRepoInfo(unittest.TestCase): webidx.repoinfo(self.environ, self.repo_id) self.server.repoInfo.assert_called_once_with(int(self.repo_id), strict=False) - self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id)) + self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id), queryOpts={'countOnly': True}) + + +# the end diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index c237d4f1..c9ae5ff3 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -797,6 +797,10 @@ def taskinfo(environ, taskID): else: values['perms'] = [] + values['requests'] = [] + if task['method'] == 'newRepo': + values['requests'] = server.repo.queryQueue([['task_id', '=', task['id']]], ['id']) + values['koji'] = koji values['S'] = SafeValue @@ -1051,8 +1055,10 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N values['srcTargets'] = srcTargets values['destTargets'] = destTargets values['all'] = all - values['repo'] = server.getRepo(tag['id'], state=koji.REPO_READY) + values['repo'] = server.repo.get(tag['id']) values['external_repos'] = server.getExternalRepoList(tag['id']) + values['request_count'] = server.repo.queryQueue([['tag_id', '=', tag['id']]], + opts={'countOnly': True}) child = None if childID is not None: @@ -2696,13 +2702,72 @@ def repoinfo(environ, repoID): else: values['repo_json'] = os.path.join( pathinfo.repo(repo_info['id'], repo_info['tag_name']), 'repo.json') - num_buildroots = len(server.listBuildroots(repoID=repoID)) or 0 - values['numBuildroots'] = num_buildroots + num_buildroots = server.listBuildroots(repoID=repoID, queryOpts={'countOnly': True}) + values['numBuildroots'] = num_buildroots + values['requests'] = server.repo.queryQueue([['repo_id', '=', repoID]], ['id']) + values['state_name'] = kojiweb.util.repoState(repo_info['state']) values['create_time'] = kojiweb.util.formatTimeLong(repo_info['create_ts']) return _genHTML(environ, 'repoinfo.html.j2') +def reporequest(environ, reqID): + values = _initValues(environ, 'Repo Request', 'tags') + server = _getServer(environ) + + req_id = int(reqID) + values['req_id'] = req_id + rows = server.repo.queryQueue([['id', '=', req_id]], '**') + if not rows: + raise koji.GenericError("No such repo request: %s" % req_id) + req = rows[0] + if req['at_event']: + values['at_event'] = server.getEvent(req['at_event']) + elif req['min_event']: + values['min_event'] = server.getEvent(req['min_event']) + else: + # invalid, but technically not blocked in db + values['min_event'] = None + values['req'] = req + return _genHTML(environ, 'reporequest.html.j2') + + +def reporequests(environ, active="true", tag=None, start=None, order=None): + values = _initValues(environ, 'Repo Requests', 'tags') + server = _getServer(environ) + + clauses = [] + desc_parts = [] + if active.lower() == 'all': + desc_parts.append('Recent repo requests') + elif active.lower() in ('false', 'no', '0'): + desc_parts.append('Inactive repo requests') + clauses.append(["active", False]) + active = 'false' + else: + desc_parts.append('Active repo requests') + clauses.append(["active", True]) + active = 'true' + if tag: + taginfo = server.getTag(_convert_if_int(tag), strict=True, event='auto') + clauses.append(["tag_id", taginfo['id']]) + desc_parts.append('for tag %(name)s' % taginfo) + tag = taginfo['name'] + else: + tag = None + if order is None: + order = '-id' + values['desc'] = ' '.join(desc_parts) + values['order'] = order + values['active'] = active + values['tag'] = tag + kojiweb.util.paginateMethod(server, values, 'repo.queryQueue', + args=(clauses, '**'), + start=start, dataName='reqs', prefix='req', order=order, + optsarg='opts') + return _genHTML(environ, 'reporequests.html.j2') + + def activesession(environ, start=None, order=None): values = _initValues(environ, 'Active sessions', 'activesession') server = _getServer(environ) diff --git a/www/kojiweb/templates/repoinfo.html.j2 b/www/kojiweb/templates/repoinfo.html.j2 index cfcd284f..0d856b66 100644 --- a/www/kojiweb/templates/repoinfo.html.j2 +++ b/www/kojiweb/templates/repoinfo.html.j2 @@ -7,16 +7,35 @@ ID{{ repo.id }} Tag{{ repo.tag_name }} {% if repo.task_id %} - Task ID{{ repo.task_id }} + Task ID{{ repo.task_id }} ({{ util.taskState(repo.task_state) }}) {% endif %} State{{ state_name }} - Event{{ repo.create_event }} ({{ create_time }}) + Created{{ util.formatTimeLong(repo.creation_ts) }} + State changed{{ util.formatTimeLong(repo.state_ts) }} + Created from Event{{ repo.create_event }} ({{ util.formatTimeLong(repo.create_ts) }}) + {%- if repo.begin_event %} + {%- if repo.end_event %} + Event range{{ repo.begin_event }} ... {{ repo.end_event }} + {%- else %} + Event range{{ repo.begin_event }} ... + {%- endif %} + {%- endif %} {%- if state_name != 'deleted' %} URLrepodata Repo jsonrepo.json {%- endif %} + #if repo.custom_opts + Custom Opts{{ json.dumps(repo.custom_opts, indent=4) }} + #endif Dist repo?{{ 'yes' if repo.dist else 'no' }} Number of buildroots: {{ numBuildroots }} + #if requests + Fulfills requests: + #for req in requests + {{ req.id }} + #endfor + + #endif {% else %} Repo {{ repo_id }} not found. diff --git a/www/kojiweb/templates/reporequest.html.j2 b/www/kojiweb/templates/reporequest.html.j2 new file mode 100644 index 00000000..19818b03 --- /dev/null +++ b/www/kojiweb/templates/reporequest.html.j2 @@ -0,0 +1,38 @@ + +#include "header.html.j2" + +

Information for repo request {{ req_id }}

+ +#if not req +Repo request {{ req_id }} not found. +#else + + + + + + #if req.at_event + + #elif req.min_event + + #else + + #endif + #if req.opts + + #endif + #if req.repo_id + + #endif + #if req.task_id + + + #endif + + + +
ID{{ req.id }}
Active{{ req.active }}
Priority{{ req.priority }}
Tag{{ req.tag_name }}
At specific event{{ at_event.id }} ({{ util.formatTimeLong(at_event.ts) }})
Minimum event{{ min_event.id }} ({{ util.formatTimeLong(min_event.ts) }})
Invalid eventUnable to determine event for request
Options{{ req.opts | tojson(indent=4) }}
Fulfilled by repo{{ req.repo_id }}
Task ID{{ req.task_id }} ({{ util.taskState(req.task_state) }})
Tries{{ req.tries }}
Owner{{ req.owner_name }}
Created{{ util.formatTimeLong(req.create_ts) }}
Updated{{ util.formatTimeLong(req.update_ts) }}
+#endif + + +#include "footer.html.j2" diff --git a/www/kojiweb/templates/reporequests.html.j2 b/www/kojiweb/templates/reporequests.html.j2 new file mode 100644 index 00000000..c97f9f01 --- /dev/null +++ b/www/kojiweb/templates/reporequests.html.j2 @@ -0,0 +1,127 @@ +#include "header.html.j2" + +# from "macros.html.j2" import rowToggle + +#set Pvars = ('active', 'tag', 'order') +#set P = util.passthrough + +

{{ desc }}

+ + + + + + + + + + + + + + + + #if (reqs |length) > 0 + #for req in reqs + + + + + #if req.task_id + + #else + + #endif + #if req.repo_id + + #else + + #endif + + + #endfor + #else + + + + #endif + + + +
+
+ + + + +
+ Active: + + + + Tag: + + +
+
+
+ #if (reqPages |length) > 1 +
+ Page: + +
+ #endif + #if reqStart > 0 + <<< + #endif + #if totalReqs != 0 + Requests {{ reqStart + 1 }} through {{ reqStart + reqCount }} of {{ totalReqs }} + #endif + #if reqStart + reqCount < totalReqs + >>> + #endif +
ID {{ util.sortImage('id') }}Priority {{ util.sortImage('priority') }}Tag {{ util.sortImage('tag_name') }}TaskRepoStatus
{{ req.id }}{{ req.priority }} + {{ req.tag_name }} + #if not tag + ^ + #endif + {{ req.task_id }}...{{ req.repo_id }}... + ## simulate a more helpful status + #if req.active + {{ util.imageTag('waiting') }} + #elif req.repo_id + {{ util.imageTag('yes') }} + #else + {{ util.imageTag('no') }} + #endif +
No repo requests
+ #if (reqPages |length) > 1 +
+ Page: + +
+ #endif + #if reqStart > 0 + <<< + #endif + #if totalReqs != 0 + Reqs {{ reqStart + 1 }} through {{ reqStart + reqCount }} of {{ totalReqs }} + #endif + #if reqStart + reqCount < totalReqs + >>> + #endif +
+ +#include "footer.html.j2" diff --git a/www/kojiweb/templates/taginfo.html.j2 b/www/kojiweb/templates/taginfo.html.j2 index 3b4bc3e8..52025960 100644 --- a/www/kojiweb/templates/taginfo.html.j2 +++ b/www/kojiweb/templates/taginfo.html.j2 @@ -117,6 +117,10 @@ #endif + + Repo requests + {{ request_count }} + Packages {{ numPackages }} diff --git a/www/kojiweb/templates/taskinfo.html.j2 b/www/kojiweb/templates/taskinfo.html.j2 index 453d37d2..f2ff5a8d 100644 --- a/www/kojiweb/templates/taskinfo.html.j2 +++ b/www/kojiweb/templates/taskinfo.html.j2 @@ -140,6 +140,14 @@ None #endfor #endif + #if requests + For request: + ## we only expect one, but if we get more print them all + #for req in requests + {{ req.id }} + #endfor + + #endif Created{{ util.formatTimeLong(task.create_ts) }} diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index c30a6893..2519f9d1 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -353,17 +353,17 @@ def _sortImage(orderVal, sortKey, orderVar): @safe_return @pass_context -def passthrough(context, *varnames, prefix='&', invert=False): +def passthrough(context, *varnames, prefix='&', invert=False, toggleOrder=None): if invert: _PASSTHROUGH = context.get('_PASSTHROUGH', None) if _PASSTHROUGH is None: raise Exception('template does not define _PASSTHROUGH') varnames = {n for n in _PASSTHROUGH if n not in varnames} data = {n: context.get(n, default=None) for n in varnames} - return _passthrough(data, prefix) + return _passthrough(data, prefix, toggleOrder) -def _passthrough(data, prefix='&'): +def _passthrough(data, prefix='&', toggleOrder=None): """ Construct a url parameter string from template vars @@ -380,6 +380,11 @@ def _passthrough(data, prefix='&'): result = [] for var in sorted(data): value = data[var] + if var == 'order' and toggleOrder is not None: + if value == toggleOrder: + value = '-' + value + else: + value = toggleOrder if value is not None: if isinstance(value, str): if value.isdigit(): @@ -396,7 +401,7 @@ def _passthrough(data, prefix='&'): @pass_context -def passthrough_except(context, *exclude, prefix='&'): +def passthrough_except(context, *exclude, prefix='&', toggleOrder=None): """ Construct a string suitable for use as URL parameters. The template calling this method must have @@ -408,7 +413,7 @@ def passthrough_except(context, *exclude, prefix='&'): """ # note that we have to pass context ourselves here # the decorator only works when called directly from the template - return passthrough(context, *exclude, prefix=prefix, invert=True) + return passthrough(context, *exclude, prefix=prefix, invert=True, toggleOrder=toggleOrder) def sortByKeyFuncNoneGreatest(key): @@ -459,7 +464,7 @@ def paginateList(values, data, start, dataName, prefix=None, order=None, noneGre def paginateMethod(server, values, methodName, args=None, kw=None, start=None, dataName=None, prefix=None, order=None, pageSize=50, - first_page_count=True): + first_page_count=True, optsarg='queryOpts'): """Paginate the results of the method with the given name when called with the given args and kws. The method must support the queryOpts keyword parameter, and pagination is done in the database. @@ -483,12 +488,12 @@ def paginateMethod(server, values, methodName, args=None, kw=None, if start == 0 and not first_page_count: totalRows = None else: - kw['queryOpts'] = {'countOnly': True} + kw[optsarg] = {'countOnly': True} totalRows = getattr(server, methodName)(*args, **kw) - kw['queryOpts'] = {'order': order, - 'offset': start, - 'limit': pageSize} + kw[optsarg] = {'order': order, + 'offset': start, + 'limit': pageSize} data = getattr(server, methodName)(*args, **kw) count = len(data) diff --git a/www/static/images/Makefile b/www/static/images/Makefile index fd7f2cad..3a7891cb 100644 --- a/www/static/images/Makefile +++ b/www/static/images/Makefile @@ -1,5 +1,5 @@ SERVERDIR = /images -FILES = $(wildcard *.gif *.png *.ico) +FILES = $(wildcard *.gif *.png *.ico *.svg) _default: @echo "nothing to make. try make install" diff --git a/www/static/images/funnel.svg b/www/static/images/funnel.svg new file mode 100644 index 00000000..2e1b0548 --- /dev/null +++ b/www/static/images/funnel.svg @@ -0,0 +1,12 @@ + + + + + + + + +