kojira on demand work

squashed to keep the history more readable

commit b4383d81f48f9c58cb53119cb453034c5676657f
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Jun 21 09:03:07 2024 -0400

    unit tests

commit 151b6ea053fc2e93b104fb3f01749602401fa0ee
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Jun 18 17:55:35 2024 -0400

    unit tests and fixes

commit 15457499665a0c0e0e45b17d19c6d07b6f681ca8
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Jun 18 17:14:01 2024 -0400

    use tag name in waitrepo task for readability

commit a20a21d39d2cb96b02046788de77aa33a7cbc906
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Jun 18 17:00:45 2024 -0400

    cleanup

commit a0058fce436a39de5cde6f11788ca4aaaa3553c0
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Jun 18 16:44:22 2024 -0400

    better approach to repo lookup from task id

commit 057527d71318d4494d80a2f24510e82ac9bc33f8
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Jun 18 10:42:08 2024 -0400

    support priority for requests

commit 882eaf2c4349e6f75db055fa36c80d66ab40526f
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Jun 18 10:16:44 2024 -0400

    track user for request

commit 273739e2f43170d80dae9e3796185230fae0607e
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Jun 17 15:37:16 2024 -0400

    update additional fields in repo_done_hook

commit d0a886eb161468675720549ad8a31921cd5c3647
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Jun 17 15:14:38 2024 -0400

    simplify updateRepos

commit 2a3ab6839299dd507835804e6326d93f08aa4040
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Jun 17 15:03:39 2024 -0400

    kojira: adjust cleanup of self.repos

commit dfc5934423b7f8f129ac9c737cc21d1798b33c2d
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Jun 17 14:03:57 2024 -0400

    docs updates

commit 4c5d4c2b50b11844d5dd6c8295b33bcc4453928b
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Jun 17 09:18:10 2024 -0400

    Apply repo_lifetime to custom repos even if current

commit 2b2d63a771244358f4a7d77766374448343d2c4c
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Jun 17 09:36:50 2024 -0400

    fix migration script

commit 447a3f47270a324463a335d19b8e2c657a99ee9b
Author: Tomas Kopecek <tkopecek@redhat.com>
Date:   Fri Jun 7 11:32:14 2024 +0200

    migration script

commit f73bbe88eea7caf31c908fdaa5231e39d0f0d0a8
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Jun 14 15:30:24 2024 -0400

    clean up some TODO items

commit 836c89131d2b125c2761cfbd3917473504d459e4
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Jun 14 11:43:13 2024 -0400

    update unit tests

commit 4822ec580b96ae63778b71cee2127364bc31d258
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Jun 14 11:17:24 2024 -0400

    streamline simple case for tag_first/last_change_event

commit 3474384c56a8a2e60288279b459000f3b9c54968
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Jun 11 16:11:55 2024 -0400

    backwards compatible age checks in kojira

commit e796db0bdc6e70b489179bcddaa899855d64b706
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Jun 14 11:49:37 2024 -0400

    repowatch unit test fixes

commit 7f17eb741502ab5417f70413f699c99e140f380d
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Jun 6 21:35:11 2024 -0400

    adjust watch output; die if request fails

commit a0318c44576d6acab459f623c8ff0ab6961bd6b4
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Jun 6 20:45:56 2024 -0400

    handle problem repos

commit d90ca6f9d41a39da86089a0fad7afdb649fd680b
Author: Mike McLean <mikem@redhat.com>
Date:   Thu May 30 22:43:56 2024 -0400

    fix typos

commit 29830d1b8125664ddeae5ccb7e6b6e53260cdc47
Author: Mike McLean <mikem@redhat.com>
Date:   Thu May 30 16:57:48 2024 -0400

    clarify --wait-repo help text

commit 43db92302643b67e7f6f419424d6813e5dca53f3
Author: Mike McLean <mikem@redhat.com>
Date:   Tue May 21 17:32:44 2024 -0400

    unit tests

commit 27f979fbccc5a286fba9caeec16ca7092fa79813
Author: Mike McLean <mikem@redhat.com>
Date:   Tue May 21 17:23:32 2024 -0400

    wait-repo compat

commit f3a8f76d9340b1bdddb5f7bab154962e848d4d10
Author: Mike McLean <mikem@redhat.com>
Date:   Thu May 16 20:14:59 2024 -0400

    fixes

commit 6638b0fd76b31aa49ad0cf79639014ad9ace09f0
Author: Mike McLean <mikem@redhat.com>
Date:   Thu May 16 16:41:50 2024 -0400

    use old regen-repo code for older hubs

commit 7f2d8ec49fe1d2d511759221a821a146a4ef6837
Author: Mike McLean <mikem@redhat.com>
Date:   Thu May 16 16:18:36 2024 -0400

    fixes

commit 791df709c10d3c10c9b79f59f4fda435ac3bd285
Author: Mike McLean <mikem@redhat.com>
Date:   Thu May 16 12:22:09 2024 -0400

    don't trigger regens from scheduler. kojira is enough

commit 75f5e695287b92d53e4f173f57b12b5a7159adaf
Author: Mike McLean <mikem@redhat.com>
Date:   Wed May 15 22:54:08 2024 -0400

    more docs

commit 0e0f53160bbe09e35409dabce63739eb50813310
Author: Mike McLean <mikem@redhat.com>
Date:   Wed May 15 21:49:27 2024 -0400

    support MaxRepoTasksMaven

commit 88da9639860cb7c0d92f7c3bc881cd480b4e1620
Author: Mike McLean <mikem@redhat.com>
Date:   Wed May 15 16:15:12 2024 -0400

    drop unused method

commit 4cdbe6c4d2ba8735312d0cd0095612c159db9cce
Author: Mike McLean <mikem@redhat.com>
Date:   Wed May 15 15:48:55 2024 -0400

    api for querying repo queue

commit 2367eb21e60865c8e5a2e19f2f840938dbbbc58b
Author: Mike McLean <mikem@redhat.com>
Date:   Wed May 15 15:24:44 2024 -0400

    flake8

commit 811378d703a68b63c577468b85f4a49a9be2c441
Author: Mike McLean <mikem@redhat.com>
Date:   Tue May 14 16:20:59 2024 -0400

    record custom opts in repo.json

commit d448b6b3417e95bff2bae3b5a3790877ac834816
Author: Mike McLean <mikem@redhat.com>
Date:   Mon May 13 15:32:33 2024 -0400

    drop unused RawClauses code

    will revisit in a later PR

commit 0422220e05ee3d43e5431a0d741f3632f42a8434
Author: Mike McLean <mikem@redhat.com>
Date:   Sat May 11 13:34:12 2024 -0400

    clean up BulkUpdateProcessor and add tests

commit 6721f847e655a3794d4f2fce383070cb6ad2d2d1
Author: Mike McLean <mikem@redhat.com>
Date:   Fri May 10 17:43:17 2024 -0400

    fix unit test after rebase

commit 833286eead2b278a99fe9ef80c13df88ca3af48c
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Apr 5 00:23:15 2024 -0400

    adjust valid_repo opts checks

commit 7f418d550d8636072292ee05f6e9748b622c2d89
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Apr 5 00:03:33 2024 -0400

    extend valid_repo unit test and fix a bug

commit eb844ba15894cb7fc2a739908e7d83c80fd82524
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Apr 4 15:41:08 2024 -0400

    test_request_existing_req_invalid

commit 2e290453abf9ac31f51a1853aa123a2a34ad9605
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Apr 4 15:22:06 2024 -0400

    test_request_at_event

commit 2c3389c24f5cabfbbaeb70512a4ba917cf5bd09b
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Apr 4 11:14:37 2024 -0400

    test_request_new_req

commit 2cdeab9b5f5b0bff4c4806ae802e5f5e571bb25e
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Apr 4 10:56:36 2024 -0400

    test_request_existing_req

commit 63c9ddab5f3e50b3537a82f390e9da5a66275a25
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Apr 4 10:45:22 2024 -0400

    test_request_existing_repo

commit 03b5ba5c57ce1ade0cf7990d23ec599c8cb19482
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Apr 4 10:04:36 2024 -0400

    more stubs

commit 92d16847f2cc2db0d8ee5afcf2d812b9bb6467ec
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 22:44:00 2024 -0400

    fix import

commit 1f621685532564a1c1ac373e98bec57c59107e6c
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 22:16:25 2024 -0400

    stub test

commit 45eef344e701c910f172d5642676d8f70d44049a
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 22:01:31 2024 -0400

    link repo doc in toc

commit bfffe233051c71785c335a82f64bf2abaae50078
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 21:57:35 2024 -0400

    unused options

commit 19f5a55faecf8229d60d21fd3e334e9a7f813384
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 16:37:50 2024 -0400

    include new setting

commit b7f81bd18016f862d1246ab6c81172fcd9c8b0ed
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 08:21:16 2024 -0400

    test + fixes

commit 16564cfb8e2725b395c624139ce3d878a6dd9d53
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 07:44:15 2024 -0400

    more kojira unit tests

commit 6b55c51302331ea09a126b9f3efbc71da164c0fb
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Apr 3 07:06:20 2024 -0400

    fix unit test

commit 0b000c124b17f965c5606d30da792ba47db542cf
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Apr 2 22:07:08 2024 -0400

    refactor repo delete

commit 0a03623fb018c80c8d38896fc99686cac56307fa
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Apr 2 19:13:15 2024 -0400

    avoid circular import issue

commit 137d699b7653977f63f30041d9f5f1a88ae08d43
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Apr 2 19:03:18 2024 -0400

    some kojira cleanup

commit 252e69d6dd17bb407b88b79efbb243ca5e441765
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Apr 2 17:21:14 2024 -0400

    adjust state transition check

commit 336018081709fd44e7f12933b1ea59e02bff4aed
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Apr 2 16:05:45 2024 -0400

    update RepoQuery

commit 68bb44848d9024c5520d8e7e2cc262adaa083cd1
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Mar 12 11:46:59 2024 -0400

    decode query bytes in log

commit 818431fb9b09db162e73f7cb1adcddc8b151c821
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 29 14:47:16 2024 -0400

    sanity check requests before reusing

commit 63fee0ba1ea9d41d504bb09aeaea064246c16ff9
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 29 11:41:13 2024 -0400

    repo.query api call

commit bcf9a3cf64167612e3cd355aae7c41dd348cb8db
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 29 10:31:58 2024 -0400

    reduce some cli code duplication

commit 3e870cfd088c69c4aaaa9a0f938bcce740b3f42c
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Mar 28 18:27:18 2024 -0400

    tweak warnings in external repo check

commit 0dfda64b806f2377d9c591105c83a4f05851b17a
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Mar 28 14:43:50 2024 -0400

    clean repo queue

commit e5d328faa00c74e087f0b0d20aea7cd79ffb5ee4
Author: Mike McLean <mikem@redhat.com>
Date:   Thu Mar 28 14:05:12 2024 -0400

    implement retry limit for repo queue

commit 2185f3c9e32747c9657f2b9eb9ce6e3ca6d06ff8
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Mar 27 22:40:13 2024 -0400

    cleanup a few TODOs

commit b45be8c44367bca9819561a0e928999b9a9e2428
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Mar 27 22:22:17 2024 -0400

    tweak test

commit 546b161e20d0b310462dda705ae688e25b385cf5
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Mar 27 13:43:06 2024 -0400

    more kojira tests

commit f887fdd12e59e36be561c1a89687a523e112b9d4
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Mar 26 20:16:11 2024 -0400

    unit tests for RepoWatcher

commit e78b41431f3b45ae9e09d9a246982df9bb2c2374
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Mar 26 10:53:14 2024 -0400

    fix unit tests

commit 64328ecb27e5598ec8977617e67d6dd630bc8db7
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Mar 25 14:03:19 2024 -0400

    custom opts sorted out?

commit e3cee8c48bcf585a1a14aa8e56e43aaba2ccd63b
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Mar 25 12:50:34 2024 -0400

    allow containment operator

commit bef7bbc3b2a16a6643bedb47be044c202a2bad2d
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Mar 25 11:59:15 2024 -0400

    partial

commit 01788dfe386a07960c5c7888350e3917b44a0bab
Author: Mike McLean <mikem@redhat.com>
Date:   Sat Mar 23 13:47:22 2024 -0400

    fragment: struggling with repo opt timing

commit 44504bfbde4cf981391ea02127a05c4f0c2fc4a3
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 17:14:57 2024 -0400

    fine to have default values in the class

commit 1bfa520dd599acccd45f221f71c64fbefc3b5554
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 17:14:18 2024 -0400

    option renamed

commit a5db9d015a25f71fdb5e2dadcae55a8c5b7ec956
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 17:04:32 2024 -0400

    flake8

commit c02244f8018b651f309f39eb60f926209454dea2
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 16:59:15 2024 -0400

    more config options in repos.py

commit 9bf3edc0cf2c85a23964b79c4489bc9592656f16
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 15:39:52 2024 -0400

    use requests by default in regen-repo

commit 78c6e8a4459856fa333763b1977633307fd81cc3
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 13:49:00 2024 -0400

    adjust watch_fields

commit eadb2a24b9e0f324ac053c4bdede0865d4ed5bfa
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 12:27:23 2024 -0400

    adjust event validation

commit 3140e73cfccdcc25765c6f330073c991a44cbd9a
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 12:01:24 2024 -0400

    wait-repo tweaks

commit d1a8174cdd917bbf74882c51f1a7eaf4f02e542a
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 10:35:28 2024 -0400

    cli: wait-repo-request command

commit b2d08ac09880a1931b7f40b68d5ca765cd49a3a6
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 10:04:46 2024 -0400

    drop complex request options from wait-repo

commit b4ab55f241a693c0c0d08e386f998394a295fc7c
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 09:36:37 2024 -0400

    fix call

commit c04417439c4684342ac0d4423b341d363bc80e92
Author: Mike McLean <mikem@redhat.com>
Date:   Fri Mar 22 09:32:48 2024 -0400

    typo

commit 29be83b1523d45eb77cfe4959c9d6bc5c940ebbe
Author: Mike McLean <mikem@redhat.com>
Date:   Wed Mar 20 07:28:12 2024 -0400

    partial...

commit cd0ba3b6c2c47fe5bac4cf823b886462e092e2b3
Author: Mike McLean <mikem@redhat.com>
Date:   Tue Mar 19 23:13:47 2024 -0400

    drop event="new" code

commit 7f4f2356eceec03228e4a92b13e5593f956c390d
Author: Mike McLean <mikem@redhat.com>
Date:   Mon Mar 18 21:00:25 2024 -0400

    kojira on demand work

    squashed because the branch was getting unwieldy
    mostly working at this point, but there is a bit out outstanding work

    commit e127878460a932cc77c399f69c40f0993c765dc7
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Mar 18 11:20:33 2024 -0400

        stale comment

    commit d0849d50b865f4f3783ddde5e1e6cf10db56ed39
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 23:58:13 2024 -0400

        don't expire at_event repos

    commit 8866db0e25b072aa12cc2827c62093b000fa7897
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 23:43:24 2024 -0400

        typo

    commit e2a5fd639b88c7b88708e782f0b7398296d2f805
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 23:40:08 2024 -0400

        repos.py: support at_event

    commit 6518f1656976ea2beb2cf732c82db0f159b09d15
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 22:20:35 2024 -0400

        update repo symlink logic

    commit 50d5e179f56393dd52c7225fc6f053d0095e9599
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 22:20:01 2024 -0400

        ...

    commit 429fc85b391e0b5e637e20859f1094a37a5eab39
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 21:18:44 2024 -0400

        block owner opt in makeTask and host.subtask

    commit 40fcfe667ef70987444756f6d5554919d89fb1de
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 20:49:37 2024 -0400

        db lock for repo queue

    commit dfd94fac8fb96328b12bcf2f8f6f7e2d52deea85
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 17:47:39 2024 -0400

        ...

    commit ecd9611e5d84d8a98920c40805616a6376ca652e
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 17:45:38 2024 -0400

        move new exports around

    commit a2e086df07f7b03dc4505a61f9b213e6e2ff20a5
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:46:29 2024 -0400

        drop noisy debug line

    commit 497bd773baa274d205df3bba317ee80617cc56a0
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:20:56 2024 -0400

        ...

    commit 457c986894de754a927bc4880687e0f47c29cbdd
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:19:12 2024 -0400

        ...

    commit 3aa0fa4862b37b7d178b1b7bb9a521ea01e7dded
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:18:30 2024 -0400

        ...

    commit 391c2009671dea1270cce01666d04ad2ade0c323
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:15:32 2024 -0400

        ...

    commit f3794e2acc8eef38e0c65fb27d3b2b3a58f53311
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:12:53 2024 -0400

        ...

    commit aea5e1a91f9246cce5f162bbea3d4846e87b9811
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:11:53 2024 -0400

        ...

    commit dc68ed8f0a43c9418c0c813f05a761bc8303c2b0
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:10:34 2024 -0400

        typo

    commit 73c72c8ed08744a188e4ae977b7ba2d92c75401b
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 16:08:15 2024 -0400

        pruning tweaks

    commit d3a10f8d5ef77a86db0e64a845f360d9f2cc2e17
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 15:50:57 2024 -0400

        kojira: use ordered dict for delete queue

    commit f6d7d44bac22840ee3ae1a93375c3b5ad430869c
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 14:59:05 2024 -0400

        rework repo expiration and lifetimes a bit

    commit 8bb91611c05ccb5d91910718a07494c08665ec22
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 00:27:34 2024 -0400

        more kojira rework

    commit 368d25a31d61eae8712591183bd2db1ff78f59d1
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 17 00:27:17 2024 -0400

        cleanup

    commit 292a1e4fdcc4098137156a42072e5bfda2f711df
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Mar 16 23:51:45 2024 -0400

        track update time for repos

    commit 01a7469ef7bcd952f45d732e4bb3b5f4bab2338a
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Mar 16 17:42:42 2024 -0400

        factor in implicit joins for fields="*"

    commit f9aba4557108b2005cf518e4bf316befa7f29911
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Mar 16 15:25:34 2024 -0400

        partial repo docs

    commit 74eae7104849237a4049a78c94b05187a2219f74
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Mar 16 13:17:36 2024 -0400

        remove some obsolete code from kojira

    commit d883807967a0d6d67a6e262a119ff5e03b8a947e
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Mar 16 11:42:48 2024 -0400

        ...

    commit 3bc3aa98913463aa209bba1cecc71fc30f6ef42f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Mar 16 11:12:50 2024 -0400

        do_auto_repos

    commit da69f05555f05ded973b4ade064ed7e5f7e70acd
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 23 14:56:30 2024 -0500

        fakehub: option to override config

    commit 13a4ffdf9cd915b6af7b85120d87d50b8f6db5ed
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 15 22:35:50 2024 -0400

        tweak logging

    commit 01af487cced25c0edaa9e98e5dc7bb7dc9c4d6bd
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 15 22:16:21 2024 -0400

        adjust archlist for external repo check

    commit eb1c66f57a508f65dcac0e32cfaa3e178ed40bad
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 15 18:45:53 2024 -0400

        tweak logging; wait-repo --new

    commit 3dab52d497926a6be80a3c98cc29f0cb6478926f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 15 15:03:23 2024 -0400

        typo

    commit 503365a79998aa2ee0eb2bd9b412747cdec50ab1
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 14 00:17:24 2024 -0400

        ...

    commit 46ec62e96334690344de18d535f7b9c4fd87d877
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 14 00:16:09 2024 -0400

        separate get/set for erepo data

    commit 25c2861509cfebcfc38be5fff6c0b382dfcca224
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 13 09:08:45 2024 -0400

        only update erepo data in db if it changed

    commit bc5db7494a486ae39b99dba4875547a8e8bc1ee0
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 13 09:03:03 2024 -0400

        ...

    commit 55b947fe2889dcb3b6112e9e80de926ef0ab70fa
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 13 08:48:45 2024 -0400

        partial work

    commit 7e91985a378754ae2ba88e0e2182bdf6302416ef
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 13 08:22:23 2024 -0400

        handle external_repo_data history in cli

    commit 0aeae31215af98ea8580307750389873f1e2521e
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 13 08:15:50 2024 -0400

        set_external_repo_data

    commit d85e93c0c294770d2384a41a3f2c09b4a64ae3c4
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 13 07:58:18 2024 -0400

        support external_repo_data in query_history

    commit 88fcf7ac5b8893bd045af017df1eb22a3cce8cb0
    Merge: 8449ebfeb eba8de247
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Mar 12 00:01:57 2024 -0400

        Merge remote-tracking branch 'origin' into kojira-on-demand

    commit 8449ebfeb7976f5a5bfea78322c536cf0db6aa54
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Mar 11 23:56:25 2024 -0400

        drop stray file

    commit 3d3716454b9f12c1807f8992ecd01cde3d9aade9
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Mar 11 23:49:20 2024 -0400

        flake8

    commit f9014b6b689e5a1baf355842cf13905b8c50c3d8
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Mar 11 23:44:32 2024 -0400

        handle deleted tags sanely in tag_last_change_event

    commit 7d584e99a1a580039d18210c2cc857eb3419394f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Mar 11 14:50:07 2024 -0400

        typo

    commit 6ac5921ce55ed356ba8c66466ebf56bb424591a9
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Mar 11 14:49:35 2024 -0400

        add external_repo_data table. check ext repo tables for first/last tag change events

    commit e107400463679113971daaa400d75ec006f4dca5
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Mar 11 12:14:21 2024 -0400

        fix newer_than logic in WaitrepoTask

    commit 4a1175a35e6ad7c59b3622a6028e2cd68e29bb79
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 23:47:29 2024 -0400

        todos

    commit c13d9e99d19bc40e59fd136b540b6a8c6e12a50f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 23:30:59 2024 -0400

        AllowNewRepo hub config

    commit e3176cda238d3357fed0b905b03dfc0319dab12e
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 23:00:45 2024 -0400

        fixes

    commit d486960a441fbb517492a61ef2529370035a765a
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 22:48:00 2024 -0400

        request min_event never null or in future

    commit 4cc0d38b8e4bf1254bb156d085614f83929e1161
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 22:32:45 2024 -0400

        ...

    commit bb0dc41cd6be4c42d4cd033e07210f1184c2c385
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 22:23:52 2024 -0400

        default min_event. don't allow future events

    commit 1dccf0a56b1e3f83107760111264249527abeb68
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 17:27:11 2024 -0400

        use BulkUpdateProcessor in update_end_events

    commit 03c791edd3bb49359f2a01eaf53cbb717c53833e
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Mar 10 17:26:26 2024 -0400

        BulkUpdateProcessor

    commit 4bd2a0da1c998ce14fd856e68318551747867e06
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 8 14:53:53 2024 -0500

        update_end_events()

    commit b45b13bcba141ea6b30618fb76c1a94593dfe569
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 8 13:03:33 2024 -0500

        record begin/end events in repo_init

    commit 6f1adf51d9e24f80369df8b96010c0d6d123b448
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 8 12:33:40 2024 -0500

        QueryView: accept single field value

    commit 6b292d9a4b1bda56ff8091fbcb126749f952d045
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 8 12:28:02 2024 -0500

        adjust query fields

    commit e9e8e74703de8b6c531944c05d54447f0d7cb13f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 8 12:18:12 2024 -0500

        QueryView: adjust special field name handling

    commit 97d910d70634183a3d5ae804176a5c8691882b7a
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Mar 8 11:45:54 2024 -0500

        adjust event fields

    commit c70d34805227a61ab96176537dae64db3883e58f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 23:37:29 2024 -0500

        honor owner opt to make_task

    commit 40601d220179eb9718023002f8811ce5cbd09860
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 23:29:50 2024 -0500

        ...

    commit 6f84ca3aa8c24d4618294027dce7a23620a3e2d7
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 23:24:22 2024 -0500

        typo

    commit c423b8a4cc5fd4ed5c762e7b5adc06449c72ea70
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 23:22:18 2024 -0500

        use kojira user for repo tasks

    commit 63dacff462ce064bbdf0b5c6e8ef14b2abe08e0c
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 23:05:12 2024 -0500

        hook to fulfill requests when repos are marked ready

    commit aa79055c1e404a4c4fa9ac894fe978c8f9827f72
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 01:08:19 2024 -0500

        no more data field

    commit 7dd029fb94e24004793e2d1232b3225b3cee5c97
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 01:01:41 2024 -0500

        use full opts in request entries too

    commit 73dc2f232b231467d12355af0ace14284f5422a8
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 00:54:41 2024 -0500

        ...

    commit 414d0a55cf66d93b6fb79e9677f68fd141edc655
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 00:54:01 2024 -0500

        propagate opts in repo_init

    commit 99c1dde4771164d215f8c9a9acc0dadb678d047b
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 00:20:57 2024 -0500

        include opts in query

    commit 08289b3444612920856e6a949a379f61cb46b5e7
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 00:15:12 2024 -0500

        missing import

    commit bc3ca72c084b8e8de678ecbdcf6bbcfe972363e1
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Mar 7 00:10:45 2024 -0500

        more opts support

    commit f7c12cfe5f5b6c6c7895cd5eb4cdeb45757022a1
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 6 23:59:08 2024 -0500

        handle repo opts in request call

    commit 02a75f3996d59ae36f046327fca766e8799ef35b
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 6 22:01:06 2024 -0500

        fix import

    commit 7fe52dc83a80c0f68580d274bd2e60c57ab2e26d
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 6 21:58:59 2024 -0500

        fix fields

    commit f016c3a46d901ca762f5e8824fcd5efad2eede57
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 6 21:47:40 2024 -0500

        move code into kojihub/repos

    commit 9953009d3cc6f08cd16cbaa593ae79796ac86fa2
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 6 21:15:17 2024 -0500

        more unit test fixes

    commit f5decfaff3f56601262752e8a06b6f97bc4cfb33
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 6 20:51:07 2024 -0500

        unit test

    commit b51d4979824abe6ddc402011d21394854f46687e
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Mar 6 20:19:06 2024 -0500

        flake8

    commit aeee5b59df4e9da93db83874f022419c24b37162
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Feb 20 18:05:25 2024 -0500

        stub: tracking opts

    commit b5c150b52f575c681bdacb4c87e690653edc465a
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 15:11:40 2024 -0500

        different approach for raw clauses

    commit a9001c97935f3ad90571589688b1f291242bad08
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 14:32:57 2024 -0500

        and any necessary values and joins

    commit 84a46633b7dc1303e48367b614b99de3730a865d
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 14:17:12 2024 -0500

        give hub code a way to raw clauses with QueryView

    commit 5d43c18f56563fc14f12d12c57f044125a5b33f9
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 14:09:27 2024 -0500

        private vars

    commit 91992f2e7b0a6cdd5e7cf8b99f6c37cfb20b08a6
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 14:02:07 2024 -0500

        saner data from get_fields

    commit 1e581cd5a5f3a6e257c3147a8ea763987984403c
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 13:26:34 2024 -0500

        update test and include tag_first_change_event()

    commit 3509300b0b1c6bb516b5552f2b1d37008231efae
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 12:42:53 2024 -0500

        revert global verbose option

    commit 4173e8610b0beed3dcea14849da1f115eb43c293
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 19 07:59:48 2024 -0500

        better ordering support in QueryView

    commit 359543b95cd524d5f4d8d82854680452ee07fd00
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Feb 18 01:19:30 2024 -0500

        also include test from multirepo

    commit 1ceb8c01f92cfe5029c78688b14f643e1fa8be12
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Feb 18 00:18:39 2024 -0500

        constraint

    commit 064bfc18b3a07edd602192bc4f48ac52adeedc3f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sun Feb 18 00:00:15 2024 -0500

        tagFirstChangeEvent, plus fix

    commit 0efbfed21ec3b66841a7e4996e59bc8aaeed352b
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 22:37:08 2024 -0500

        fix

    commit 3ead49b9ed7f643e7ba2db2077993eb515f10e38
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 21:54:05 2024 -0500

        cleanup

    commit be2beb37fd35b46a5b4d60f39c8040640dfc7800
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 21:20:29 2024 -0500

        rename request field, clean up Watcher args

    commit d392a974a1cbba119abc6a9e99e54d45a0cf0d62
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 18:38:21 2024 -0500

        ...

    commit 70ee37dbafc6c4e77a62aac44f11747c0f6bfc25
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 18:37:08 2024 -0500

        use tagLastChangeEvent for min_event=last

    commit 82d0d77679afc163bb5c36e43f834c109d7e6371
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 18:33:04 2024 -0500

        tag_last_change_event: support inheritance

    commit c3c87f8ccf4feea321d9bfa54cc1f223431a8d13
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 17:55:10 2024 -0500

        waitrepo anon mode (no request)

    commit c6994353d8daa4cb615eae4dde0368b97ea33d18
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 09:32:39 2024 -0500

        don't reuse a request for a future event

    commit 22abfadc57adcf11229336eede6459585a293da6
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 09:16:47 2024 -0500

        ...

    commit c7b899c4a62d667d96e8320b6fa96106972f5859
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 09:10:22 2024 -0500

        ...

    commit a185fd86766c283fd9c18a4d95546a8e36fd21c9
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 09:08:31 2024 -0500

        ...

    commit 87401bddac38ebb658f2e9e4fbe36af2e6010e42
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 09:06:48 2024 -0500

        ...

    commit bb72bd0e2d78f2d21168144a976e772473efeb16
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 08:59:44 2024 -0500

        ...

    commit 4dbeb0edfa55cf39f4c897b3c15345e2daf9dad6
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 08:59:10 2024 -0500

        ...

    commit 994e13d538d580ea9f7499310b8a0e4cd841af07
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 08:57:22 2024 -0500

        ...

    commit 1fee9331e72e4d48eccfd640183563a909181af6
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 08:53:06 2024 -0500

        ...

    commit e74eea41048a5ec6f4a9c52025c2e452f640a808
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 00:57:11 2024 -0500

        ...

    commit ec1a581ba23b292ab840b740dabd1f3e4854fe33
    Author: Mike McLean <mikem@redhat.com>
    Date:   Sat Feb 17 00:48:48 2024 -0500

        attempting to wire this up into newRepo and waitrepo task

    commit 7eee457230a2b0e6aa9b974e94e4ca516227a196
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 18:58:18 2024 -0500

        ...

    commit 1c719d642da5f5c2ca0b7ce9af170054767423c6
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 18:56:11 2024 -0500

        adjust checkRepoRequest return

    commit e6e5f15961c7801b1777743b799fbe2c96a08138
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 18:00:27 2024 -0500

        handle repo requests in scheduler loop

    commit a0dde4e3625110671bcea7abbdab0f0c03142cbc
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 11:06:00 2024 -0500

        tweak repo report in taginfo cli

    commit 2d860a17caf770507c67a89ac234d17c200c30ab
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 10:46:13 2024 -0500

        enable/clarify new repo fields

    commit 7204ce3753450981300bf78102fc40f1b41786b4
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 09:38:59 2024 -0500

        syntax

    commit 96236f4ef93e5babeb0800b5b4a16117a3e8c1df
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 10:20:34 2024 -0500

        pull tag_last_change_event and repo fields from multirepo branch

    commit a707c19eda9bc6efc22ce004367cbee960fcccb6
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 16 09:26:07 2024 -0500

        partial: check_repo_queue

    commit a208d128e60bdb4ad531938d55b2c793b65ab24b
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 19:35:03 2024 -0500

        ...

    commit e9a601059fb9ceb89ec9b84680afd6dc276424f9
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 19:22:55 2024 -0500

        ...

    commit 067e385861766d7a355d5671a1e1e73ebd737b97
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 19:14:11 2024 -0500

        use RepoView more

    commit e5b4a58b65c6f195f724fb135acea6dd18abc3c2
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 17:37:47 2024 -0500

        executeOne

    commit 45aecfeb0a32c097fc65574296958573e6405009
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 17:29:06 2024 -0500

        ...

    commit 41314dc10c3a1a13f39628de5caedc7486193c7b
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 17:27:40 2024 -0500

        only return one req

    commit c44ed9e4e3bc349e4107df79847049503a2c75be
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 14:57:11 2024 -0500

        ...

    commit cfd60878ada8196616fd401fb6cbaf7aa2dcc98b
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 11:10:31 2024 -0500

        ...

    commit 11f65335ca9c6167b8f457460a58471c37ae4098
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 15 09:12:34 2024 -0500

        testing

    commit c05f8f3b3f64c3aeef5ff0296dc181123c756952
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Feb 14 22:52:14 2024 -0500

        flesh out stub

    commit fd9c57c2c95bb5a1bd051d9d1e7e73e2f3fcb9b0
    Author: Mike McLean <mikem@redhat.com>
    Date:   Wed Feb 14 22:26:19 2024 -0500

        ...

    commit d59f38a5adc90607556a1671c85b808209389edd
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Feb 6 22:19:36 2024 -0500

        more fragments

    commit 2d1b45c66e1cc3f41f6812b7b6d4bd66c4acf419
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Feb 6 20:38:04 2024 -0500

        XXX DEBUG CODE

    commit d8e3a4bd205acb5ec1940fa30e29701f0a358d51
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Feb 6 20:37:52 2024 -0500

        ...

    commit 0744a29bd303bf9b381aa48e3e5dd98e8b7373ef
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Feb 6 20:37:40 2024 -0500

        ...

    commit 0726f8d22b227e002f7ddd927829a1e3ec66681f
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Feb 6 20:27:22 2024 -0500

        RepoWatcher stub

    commit a74a74ef9688b1d27b528dd8e2de8ff3b63f97ae
    Author: Mike McLean <mikem@redhat.com>
    Date:   Tue Feb 6 00:05:49 2024 -0500

        ...

    commit d68c2902015a4998f59355aa224924e5ace21b0a
    Author: Mike McLean <mikem@redhat.com>
    Date:   Mon Feb 5 08:18:56 2024 -0500

        ...

    commit ff8538344e1bf24d7b94ad45f26fb1548be4782d
    Author: Mike McLean <mikem@redhat.com>
    Date:   Fri Feb 2 00:00:41 2024 -0500

        partial

    commit f618ed321108e0094ab95e054cb5d53fb2e0dfe1
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 1 23:54:57 2024 -0500

        tweak unit test

    commit 208a2f441401cefd65a7a92d91b6b76bf5dd97d3
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 1 22:52:37 2024 -0500

        comments

    commit 8fe5b4f0d773f190c037ab95520623a3d250c069
    Author: Mike McLean <mikem@redhat.com>
    Date:   Thu Feb 1 01:43:28 2024 -0500

        repo_queue stub
This commit is contained in:
Mike McLean 2024-06-21 14:35:59 -04:00
parent d68a9273e1
commit df4a54e204
34 changed files with 5366 additions and 1301 deletions

View file

@ -63,7 +63,8 @@ class TestParseTaskParams(unittest.TestCase):
# match
self.assertIn(list(spec), koji.tasks.LEGACY_SIGNATURES[method])
if len(missing) > 0.1 * len(koji.tasks.LEGACY_SIGNATURES):
# we should hit most of the legacy entries this way
raise Exception('Unable to test enough legacy signatures. Missing: '
external = ['runroot', 'saveFailedTree', 'vmExec', 'winbuild']
missing = [m for m in missing if m not in external]
if missing:
raise Exception('Unable to test legacy signatures. Missing: '
'%r' % missing)

View file

@ -46,7 +46,7 @@ admin commands:
lock-tag Lock a tag
make-task Create an arbitrary task
prune-signed-copies Prune signed copies
regen-repo Force a repo to be regenerated
regen-repo Generate a current repo if there is not one
remove-external-repo Remove an external repo from a tag or tags, or remove entirely
remove-group Remove group from tag
remove-host-from-channel Remove a host from a channel

View file

@ -46,7 +46,7 @@ admin commands:
lock-tag Lock a tag
make-task Create an arbitrary task
prune-signed-copies Prune signed copies
regen-repo Force a repo to be regenerated
regen-repo Generate a current repo if there is not one
remove-external-repo Remove an external repo from a tag or tags, or remove entirely
remove-group Remove group from tag
remove-host-from-channel Remove a host from a channel
@ -130,6 +130,7 @@ miscellaneous commands:
import-comps Import group/package information from a comps file
moshimoshi Introduce yourself
promote-build Promote a draft build
request-repo Request a repo for a tag
version Report client and hub versions
monitor commands:
@ -142,6 +143,7 @@ monitor commands:
scheduler-logs Query scheduler logs
unblock-notification Unblock user's notification
wait-repo Wait for a repo to be regenerated
wait-repo-request Wait for an existing repo request
watch-logs Watch logs in realtime
watch-task Track progress of particular tasks

View file

@ -165,7 +165,7 @@ Options:
--no-rebuild-srpm Force not to rebuild srpm for scratch build only
--wait Wait on the build, even if running in the background
--nowait Don't wait on build
--wait-repo Wait for the actual buildroot repo of given target
--wait-repo Wait for a current repo for the build tag
--wait-build=NVR Wait for the given nvr to appear in buildroot repo
--quiet Do not print the task information
--arch-override=ARCH_OVERRIDE

View file

@ -39,6 +39,7 @@ class TestRegenRepo(utils.CliTestCase):
self.options.weburl = 'https://localhost.local'
self.session = mock.MagicMock()
self.session.hub_version = (1, 35, 0)
self.session.getTag.return_value = copy.deepcopy(self.TAG)
self.session.newRepo.return_value = self.task_id
self.session.getBuildTarget.return_value = {'build_tag_name': self.tag_name}
@ -104,7 +105,7 @@ class TestRegenRepo(utils.CliTestCase):
self.session.getTag.return_value = copy.copy(self.TAG)
self.session.getBuildTargets.return_value = []
expected_warn = "%s is not a build tag" % self.tag_name + "\n"
self.__run_test_handle_regen_repo([self.tag_name], return_value=True,
self.__run_test_handle_regen_repo([self.tag_name, '--make-task'], return_value=True,
expected_warn=expected_warn)
self.resetMocks()
@ -113,12 +114,12 @@ class TestRegenRepo(utils.CliTestCase):
noarch_tag.update({'arches': ''})
self.session.getTag.return_value = noarch_tag
expected_warn += "Tag %s has an empty arch list" % noarch_tag['name'] + "\n"
self.__run_test_handle_regen_repo([self.tag_name], return_value=True,
self.__run_test_handle_regen_repo([self.tag_name, '--make-task'], return_value=True,
expected_warn=expected_warn)
def test_handle_regen_repo_with_target_opt(self):
"""Test handle_regen_repo function with --target option"""
arguments = [self.tag_name, '--target']
arguments = [self.tag_name, '--target', '--make-task']
# show error if target is not matched
self.session.getBuildTarget.return_value = {}
@ -138,11 +139,11 @@ class TestRegenRepo(utils.CliTestCase):
def test_handle_regen_repo_with_other_opts(self):
"""Test handle_regen_repo function with options"""
# --nowait
self.__run_test_handle_regen_repo([self.tag_name, '--nowait'], return_value=None)
self.__run_test_handle_regen_repo([self.tag_name, '--nowait', '--make-task'], return_value=None)
self.resetMocks()
# --source && --debuginfo
self.__run_test_handle_regen_repo([self.tag_name, '--source', '--debuginfo'],
self.__run_test_handle_regen_repo([self.tag_name, '--source', '--debuginfo', '--make-task'],
return_value=True)
self.session.newRepo.assert_called_with(self.tag_name, **{'debuginfo': True, 'src': True})
@ -150,10 +151,10 @@ class TestRegenRepo(utils.CliTestCase):
"""Test handle_regen_repo function errors and exceptions"""
tests = [
# [ arguments, error_string ]
[[], self.format_error_message("A tag name must be specified")],
[['tag1', 'tag2'],
[['--make-task'], self.format_error_message("A tag name must be specified")],
[['tag1', 'tag2', '--make-task'],
self.format_error_message("Only a single tag name may be specified")],
[['tag1', 'tag2', '--target'],
[['tag1', 'tag2', '--target', '--make-task'],
self.format_error_message("Only a single target may be specified")],
]
@ -180,10 +181,14 @@ Options:
--wait Wait on for regen to finish, even if running in the
background
--nowait Don't wait on for regen to finish
--make-task Directly create a newRepo task
--debuginfo Include debuginfo rpms in repo
--source, --src Include source rpms in each of repos
--separate-source, --separate-src
Include source rpms in separate src repo
--timeout=TIMEOUT Wait timeout (default: 120)
-v, --verbose More verbose output
--quiet Reduced output
""" % self.progname)

View file

@ -6,14 +6,19 @@ import unittest
import copy
import mock
import pytest
import six
import koji
from koji_cli.commands import anon_handle_wait_repo
from . import utils
class TestWaitRepo(utils.CliTestCase):
"""
These older tests cover the non-request code path for the cli handler
"""
# Show long diffs in error output...
maxDiff = None
longMessage = True
@ -22,7 +27,7 @@ class TestWaitRepo(utils.CliTestCase):
'maven_support': False,
'locked': False,
'name': 'fedora26-build',
'extra': {},
'extra': {'repo.auto': True},
'perm': None,
'id': 2,
'arches': 'x86_64',
@ -40,6 +45,7 @@ class TestWaitRepo(utils.CliTestCase):
self.options.weburl = 'https://localhost.local'
self.session = mock.MagicMock()
self.session.hub_version = (1, 35, 0)
self.session.getTag.return_value = copy.deepcopy(self.TAG)
self.session.newRepo.return_value = self.task_id
self.session.getBuildTarget.return_value = {'build_tag_name': self.tag_name}
@ -55,18 +61,19 @@ class TestWaitRepo(utils.CliTestCase):
def setUpMocks(self):
self.activate_session = mock.patch('koji_cli.commands.activate_session').start()
self.ensure_connection = mock.patch('koji_cli.commands.ensure_connection').start()
self.checkForBuilds = mock.patch('koji.util.checkForBuilds').start()
self.watcher = mock.MagicMock()
self.RepoWatcher = mock.patch('koji.util.RepoWatcher', return_value=self.watcher).start()
self.wait_logger = mock.MagicMock()
self.getLogger = mock.patch('logging.getLogger', return_value=self.wait_logger).start()
def tearDown(self):
mock.patch.stopall()
@mock.patch('time.time')
@mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('sys.stderr', new_callable=six.StringIO)
def __test_wait_repo(self, args, expected, stderr, stdout, time_mock, ret_code=0,
def __test_wait_repo(self, args, expected, stderr, stdout, ret_code=0,
expected_warn=''):
self.options.quiet = False
time_mock.side_effect = [0, 1, 2, 3]
if ret_code:
with self.assertRaises(SystemExit) as ex:
anon_handle_wait_repo(self.options, self.session, args)
@ -79,59 +86,46 @@ class TestWaitRepo(utils.CliTestCase):
self.assert_console_message(stderr, expected_warn)
self.assertIn(rv, [0, None])
@mock.patch('time.time')
@mock.patch('sys.stdout', new_callable=six.StringIO)
@mock.patch('sys.stderr', new_callable=six.StringIO)
def __test_wait_repo_timeout(self, args, expected, stderr, stdout, time_mock, ret_code=0):
self.options.quiet = False
time_mock.side_effect = [0, 61, 62]
if ret_code:
with self.assertRaises(SystemExit) as ex:
anon_handle_wait_repo(self.options, self.session, args + ['--timeout', '1'])
self.assertExitCode(ex, ret_code)
self.assert_console_message(stderr, expected)
self.assert_console_message(stdout, '')
else:
rv = anon_handle_wait_repo(self.options, self.session, args + ['--timeout', '1'])
self.assert_console_message(stdout, expected)
self.assert_console_message(stderr, '')
self.assertIn(rv, [0, None])
def test_anon_handle_wait_repo(self):
"""Test anon_handle_wait_repo function"""
arguments = [self.tag_name]
arguments = [self.tag_name, '--no-request']
self.options.quiet = False
self.session.getRepo.side_effect = [{}, {}, {'id': 1, 'name': 'DEFAULT'}]
expected = 'Successfully waited 0:03 for a new %s repo' % self.tag_name + '\n'
self.watcher.waitrepo.return_value = {'id': 1, 'name': 'DEFAULT'}
expected = (
'Got repo 1\n'
'Repo info: https://localhost.local/repoinfo?repoID=1\n'
)
self.__test_wait_repo(arguments, expected)
def test_anon_handle_wait_repo_with_target_opt(self):
"""Test anon_handle_wait_repo function with --target option"""
arguments = [self.tag_name, '--target']
arguments = [self.tag_name, '--target', '--no-request']
self.options.quiet = False
self.session.getBuildTarget.return_value = {'build_tag_name': self.tag_name,
'build_tag': 1}
self.session.getRepo.side_effect = [{}, {}, {'id': 1, 'name': 'DEFAULT'}]
expected = 'Successfully waited 0:03 for a new %s repo' % self.tag_name + '\n'
self.watcher.waitrepo.return_value = {'id': 1, 'name': 'DEFAULT'}
expected = (
'Got repo 1\n'
'Repo info: https://localhost.local/repoinfo?repoID=1\n'
)
self.__test_wait_repo(arguments, expected)
def test_anon_handle_wait_repo_timeout(self):
"""Test anon_handle_wait_repo function on timeout case"""
arguments = [self.tag_name]
arguments = [self.tag_name, '--no-request']
self.options.quiet = False
self.session.getRepo.return_value = {}
self.checkForBuilds.return_value = True
expected = 'Unsuccessfully waited 1:02 for a new %s repo' % self.tag_name + '\n'
self.__test_wait_repo_timeout(arguments, expected, ret_code=1)
self.watcher.waitrepo.side_effect = koji.GenericError('timeout')
expected = 'Failed to get repo -- timeout\n'
self.__test_wait_repo(arguments, expected, ret_code=1)
def test_anon_handle_wait_repo_with_build(self):
"""Test anon_handle_wait_repo function with --build options"""
builds = ['bash-4.4.12-5.fc26', 'sed-4.4-1.fc26']
new_ver = 'bash-4.4.12-7.fc26'
arguments = [self.tag_name]
arguments = [self.tag_name, '--no-request']
pkgs = ''
for b in builds:
arguments += ['--build', b]
@ -142,21 +136,22 @@ class TestWaitRepo(utils.CliTestCase):
self.session.getLatestBuilds.side_effect = [
[{'nvr': new_ver}], []
]
self.checkForBuilds.return_value = True
self.session.getRepo.side_effect = [
{}, {}, {'id': 1, 'name': 'DEFAULT', 'create_event': 1}
]
expected_warn = 'nvr %s is not current in tag %s\n latest build in %s is %s' % \
(builds[0], self.tag_name, self.tag_name, new_ver) + "\n"
self.watcher.waitrepo.return_value = {'id': 1, 'name': 'DEFAULT', 'create_event': 1}
expected_warn = 'nvr %s is not current in tag %s\n latest build is %s' % \
(builds[0], self.tag_name, new_ver) + "\n"
expected_warn += 'No sed builds in tag %s' % self.tag_name + '\n'
expected = 'Successfully waited 0:03 for %s to appear in the ' \
'%s repo\n' % (pkgs, self.tag_name)
expected = (
'Got repo 1\n'
'Repo info: https://localhost.local/repoinfo?repoID=1\n'
)
self.__test_wait_repo(arguments, expected, expected_warn=expected_warn)
self.RepoWatcher.assert_called_with(self.session, self.TAG['id'], nvrs=builds, min_event=None, logger=self.wait_logger)
def test_anon_handle_wait_repo_with_build_timeout(self):
"""Test anon_handle_wait_repo function with --build options on timeout cases"""
builds = ['bash-4.4.12-5.fc26', 'sed-4.4-1.fc26']
arguments = [self.tag_name]
arguments = [self.tag_name, '--no-request']
pkgs = ''
for b in builds:
arguments += ['--build', b]
@ -168,20 +163,18 @@ class TestWaitRepo(utils.CliTestCase):
[{'nvr': builds[0]}],
[{'nvr': builds[1]}],
]
self.checkForBuilds.return_value = True
self.session.getRepo.return_value = {}
expected = 'Unsuccessfully waited 1:02 for %s to appear in the %s ' \
'repo\n' % (pkgs, self.tag_name)
self.__test_wait_repo_timeout(arguments, expected, ret_code=1)
self.watcher.waitrepo.side_effect = koji.GenericError('timeout')
expected = 'Failed to get repo -- timeout\n'
self.__test_wait_repo(arguments, expected, ret_code=1)
def test_anon_handle_wait_repo_errors(self):
"""Test anon_handle_wait_repo function errors and exceptions"""
tests = [
# [ arguments, error_string ]
[[], "Please specify a tag name"],
[['tag1', 'tag2'], "Only one tag may be specified"],
[[self.tag_name], "No such tag: %s" % self.tag_name],
[[self.tag_name, '--target'], "No such build target: %s" % self.tag_name],
[['--no-request'], "Please specify a tag name"],
[['tag1', 'tag2', '--no-request'], "Only one tag may be specified"],
[[self.tag_name, '--no-request'], "No such tag: %s" % self.tag_name],
[[self.tag_name, '--target', '--no-request'], "No such build target: %s" % self.tag_name],
]
self.session.getBuildTarget.return_value = {}
@ -200,17 +193,20 @@ class TestWaitRepo(utils.CliTestCase):
@mock.patch('sys.stderr', new_callable=six.StringIO)
def test_anon_handle_wait_repo_target_not_found(self, stderr):
"""Test anon_handle_wait_repo function on target not found cases"""
# Should warn, but continue to watch
# Case 1. both build and dest targets are not found
self.session.getTag.return_value = self.TAG.copy()
self.session.getBuildTargets.return_value = []
with self.assertRaises(SystemExit) as ex:
anon_handle_wait_repo(self.options, self.session, [self.tag_name])
self.assertExitCode(ex, 1)
anon_handle_wait_repo(self.options, self.session, [self.tag_name, '--no-request'])
expected = "%(name)s is not a build tag for any target" % self.TAG + "\n"
self.assert_console_message(stderr, expected)
self.RepoWatcher.assert_called_with(self.session, self.TAG['id'], nvrs=[], min_event=None, logger=self.wait_logger)
# Cas 2. dest is matched, show suggestion
self.RepoWatcher.reset_mock()
self.session.getBuildTargets.side_effect = [
[],
[
@ -219,12 +215,11 @@ class TestWaitRepo(utils.CliTestCase):
{'build_tag_name': 'build-tag-3'},
],
]
with self.assertRaises(SystemExit) as ex:
anon_handle_wait_repo(self.options, self.session, [self.tag_name])
self.assertExitCode(ex, 1)
anon_handle_wait_repo(self.options, self.session, [self.tag_name, '--no-request'])
expected = "%(name)s is not a build tag for any target" % self.TAG + "\n"
expected += "Suggested tags: build-tag-1, build-tag-2, build-tag-3\n"
self.assert_console_message(stderr, expected)
self.RepoWatcher.assert_called_with(self.session, self.TAG['id'], nvrs=[], min_event=None, logger=self.wait_logger)
def test_anon_handle_wait_repo_help(self):
"""Test anon_handle_wait_repo help message"""
@ -238,8 +233,11 @@ Options:
--build=NVR Check that the given build is in the newly-generated repo
(may be used multiple times)
--target Interpret the argument as a build target name
--request Create a repo request (requires auth)
--no-request Do not create a repo request (the default)
--timeout=TIMEOUT Amount of time to wait (in minutes) before giving up
(default: 120)
-v, --verbose Be verbose
--quiet Suppress output, success or failure will be indicated by
the return value only
""" % self.progname)

View file

@ -0,0 +1,91 @@
import mock
import unittest
from kojihub import db
class TestUpdateProcessor(unittest.TestCase):
maxDiff = None
def setUp(self):
self.context = mock.patch('kojihub.db.context').start()
pass
def tearDown(self):
mock.patch.stopall()
def test_basic_instantiation(self):
proc = db.BulkUpdateProcessor('sometable')
repr(proc)
# No exception!
def test_basic_bulk_update(self):
data = [{'id': n, 'field': f'value {n}'} for n in range(2)]
proc = db.BulkUpdateProcessor('sometable', data=data, match_keys=('id',))
# check sql
actual = str(proc)
expected_sql = ('UPDATE sometable SET field = __kojibulk_sometable.field, id = __kojibulk_sometable.id\n'
'FROM (VALUES (%(val_field_0)s, %(val_id_0)s), (%(val_field_1)s, %(val_id_1)s))\n'
'AS __kojibulk_sometable (field, id)\n'
'WHERE (sometable.id = __kojibulk_sometable.id)')
self.assertEqual(actual, expected_sql)
# check values
expected_values = {'val_field_0': 'value 0',
'val_field_1': 'value 1',
'val_id_0': 0,
'val_id_1': 1}
self.assertEqual(proc._values, expected_values)
# verify execution
cursor = mock.MagicMock()
self.context.cnx.cursor.return_value = cursor
proc.execute()
cursor.execute.assert_called_once_with(
expected_sql,
expected_values,
log_errors=True,
)
def test_incomplete(self):
proc = db.BulkUpdateProcessor('sometable')
expected = '-- incomplete bulk update'
self.assertEqual(str(proc), expected)
with self.assertRaises(ValueError) as ex:
proc.get_keys()
expected = 'no update data'
self.assertEqual(str(ex.exception), expected)
def test_bad_key(self):
data = [{'id': n, 100: f'value {n}'} for n in range(2)]
proc = db.BulkUpdateProcessor('sometable', data=data, match_keys=('id',))
with self.assertRaises(TypeError) as ex:
str(proc)
expected = 'update data must use string keys'
self.assertEqual(str(ex.exception), expected)
def test_key_mismatch(self):
# extra key in later row
data = [
{'id': 1, 'A': 1},
{'id': 2, 'A': 1, 'B': 2},
]
proc = db.BulkUpdateProcessor('sometable', data=data, match_keys=('id',))
with self.assertRaises(ValueError) as ex:
str(proc)
expected = 'mismatched update keys'
self.assertEqual(str(ex.exception), expected)
# missing key in later row
data = [
{'id': 1, 'A': 1},
{'id': 2},
]
proc = db.BulkUpdateProcessor('sometable', data=data, match_keys=('id',))
with self.assertRaises(ValueError) as ex:
str(proc)
expected = 'mismatched update keys'
self.assertEqual(str(ex.exception), expected)

View file

@ -1,40 +1,33 @@
import mock
import unittest
import koji
import kojihub
import kojihub.repos
from koji.context import context
QP = kojihub.QueryProcessor
RQ = kojihub.repos.RepoQuery
class TestGetActiveRepos(unittest.TestCase):
def setUp(self):
self.QueryProcessor = mock.patch('kojihub.kojihub.QueryProcessor',
side_effect=self.getQuery).start()
self.context = mock.patch('kojihub.db.context').start()
self.RepoQuery = mock.patch('kojihub.kojihub.repos.RepoQuery',
side_effect=self.getQuery).start()
self.queries = []
def getQuery(self, *args, **kwargs):
query = QP(*args, **kwargs)
query.execute = mock.MagicMock()
self.queries.append(query)
return query
def tearDown(self):
mock.patch.stopall()
def getQuery(self, *args, **kwargs):
query = RQ(*args, **kwargs)
#query.execute = mock.MagicMock()
self.queries.append(query)
return query
def test_get_active_repos(self):
# currently not really a lot of parameters to test
kojihub.get_active_repos()
self.assertEqual(len(self.queries), 1)
self.RepoQuery.assert_called_once()
query = self.queries[0]
# make sure the following does not error
str(query)
self.assertEqual(query.tables, ['repo'])
columns = ['repo.id', 'repo.state', 'repo.task_id', 'repo.create_event',
"date_part('epoch', events.time)", 'repo.tag_id', 'repo.dist', 'tag.name']
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(query.clauses, ['repo.state != %(st_deleted)s'])
self.assertEqual(query.joins, ['tag ON repo.tag_id=tag.id',
'events ON repo.create_event = events.id'])
self.assertEqual(query.values['st_deleted'], koji.REPO_DELETED)
self.assertEqual(len(query.clauses), 1)

View file

@ -9,6 +9,7 @@ class TestGetTagExternalRepos(DBQueryTestCase):
def setUp(self):
super(TestGetTagExternalRepos, self).setUp()
self.maxDiff = None
self.get_tag_id = mock.patch('kojihub.kojihub.get_tag_id').start()
self.get_tag = mock.patch('kojihub.kojihub.get_tag').start()
self.get_external_repo = mock.patch('kojihub.kojihub.get_external_repo').start()
self.exports = kojihub.RootExports()
@ -23,6 +24,7 @@ class TestGetTagExternalRepos(DBQueryTestCase):
mock.patch.stopall()
def test_valid(self):
self.get_tag_id.return_value = self.build_tag_info['id']
self.get_tag.return_value = self.build_tag_info
self.get_external_repo.return_value = self.repo_info
kojihub.get_tag_external_repos(tag_info=self.build_tag, repo_info=self.repo)

View file

@ -407,7 +407,7 @@ class TestQueryHistory(DBQueryTestCase):
def test_external_repo_key(self):
self.get_external_repo_id.return_value = 49
kojihub.query_history(external_repo='test-ext-repo')
self.assertEqual(len(self.queries), 2)
self.assertEqual(len(self.queries), 3)
query = self.queries[0]
self.assertEqual(query.tables, ['external_repo_config'])
self.assertEqual(query.clauses, ['external_repo.id = %(external_repo_id)i'])
@ -430,6 +430,28 @@ class TestQueryHistory(DBQueryTestCase):
self.assertEqual(query.values, {'external_repo_id': 49})
query = self.queries[1]
self.assertEqual(query.tables, ['external_repo_data'])
self.assertEqual(query.clauses, ['external_repo.id = %(external_repo_id)i'])
self.assertEqual(query.columns, ['external_repo_data.active',
'external_repo_data.create_event',
"date_part('epoch', ev1.time) AS create_ts",
'external_repo_data.creator_id', 'creator.name',
'external_repo_data.data',
'external_repo.name',
'external_repo_data.external_repo_id',
'external_repo_data.revoke_event',
"date_part('epoch', ev2.time) AS revoke_ts",
'external_repo_data.revoker_id', 'revoker.name',
])
self.assertEqual(query.joins,
["events AS ev1 ON ev1.id = create_event",
"LEFT OUTER JOIN events AS ev2 ON ev2.id = revoke_event",
"users AS creator ON creator.id = creator_id",
"LEFT OUTER JOIN users AS revoker ON revoker.id = revoker_id",
'LEFT OUTER JOIN external_repo ON external_repo_id = external_repo.id'])
self.assertEqual(query.values, {'external_repo_id': 49})
query = self.queries[2]
self.assertEqual(query.tables, ['tag_external_repos'])
self.assertEqual(query.clauses, ['external_repo.id = %(external_repo_id)i'])
self.assertEqual(query.columns, ['tag_external_repos.active',

View file

@ -62,5 +62,5 @@ class TestQueryView(unittest.TestCase):
self.assertEqual(set(view.query.aliases), set(self.viewclass.default_fields))
def test_all_fields(self):
view = self.viewclass(fields='*')
view = self.viewclass(fields='**')
self.assertEqual(set(view.query.aliases), set(self.viewclass.fieldmap.keys()))

File diff suppressed because it is too large Load diff

View file

@ -3,22 +3,25 @@ import mock
import unittest
import datetime
from koji.context import context
import koji
import kojihub
import kojihub.repos
QP = kojihub.QueryProcessor
IP = kojihub.InsertProcessor
UP = kojihub.UpdateProcessor
RQ = kojihub.repos.RepoQuery
class TestRepoFunctions(unittest.TestCase):
def setUp(self):
self.QueryProcessor = mock.patch('kojihub.kojihub.QueryProcessor',
side_effect=self.getQuery).start()
self.RepoQuery = mock.patch('kojihub.repos.RepoQuery',
side_effect=self.getQuery).start()
self.queries = []
self.InsertProcessor = mock.patch('kojihub.kojihub.InsertProcessor',
self.InsertProcessor = mock.patch('kojihub.InsertProcessor',
side_effect=self.getInsert).start()
self.inserts = []
self.UpdateProcessor = mock.patch('kojihub.kojihub.UpdateProcessor',
@ -27,15 +30,16 @@ class TestRepoFunctions(unittest.TestCase):
self._dml = mock.patch('kojihub.kojihub._dml').start()
self.exports = kojihub.RootExports()
self.get_tag = mock.patch('kojihub.kojihub.get_tag').start()
self.get_tag_id = mock.patch('kojihub.kojihub.get_tag_id').start()
self.query_executeOne = mock.MagicMock()
self.context = mock.patch('kojihub.db.context').start()
def tearDown(self):
mock.patch.stopall()
def getQuery(self, *args, **kwargs):
query = QP(*args, **kwargs)
query.execute = mock.MagicMock()
query.executeOne = self.query_executeOne
query = RQ(*args, **kwargs)
#query.execute = mock.MagicMock()
self.queries.append(query)
return query
@ -81,44 +85,46 @@ class TestRepoFunctions(unittest.TestCase):
raise Exception('Missing dist condition')
def test_repo_info(self):
repo_row = {'id': 10,
'state': 0,
'task_id': 15,
'create_event': 32,
'creation_time': datetime.datetime(2021, 3, 30, 12, 34, 5, 204023,
tzinfo=datetime.timezone.utc),
'create_ts': 1617107645.204023,
'tag_id': 3,
'tag_name': 'test-tag',
'dist': False}
self.query_executeOne.return_value = repo_row
rv = kojihub.repo_info(3)
self.assertEqual(rv, repo_row)
self.RepoQuery.assert_called_once()
def test_get_repo_default(self):
self.exports.getRepo(2)
self.get_tag_id.return_value = 100
self.exports.getRepo('TAG')
self.RepoQuery.assert_called_once()
qv = self.queries[0]
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
# make sure the following does not error
str(query)
self.assertEqual(query.tables, ['repo'])
columns = ['repo.id', 'repo.state', 'repo.task_id', 'repo.create_event',
"date_part('epoch', events.time)", 'repo.dist', 'events.time']
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(query.joins, ['events ON repo.create_event = events.id'])
self.assertEqual(query.clauses, ['repo.dist is false', 'repo.state = %(state)s',
'repo.tag_id = %(id)i'])
self.assertEqual(qv.clauses,
[['tag_id', '=', 100], ['dist', 'IS', False], ['state', '=', 1]])
def test_get_repo_with_dist_and_event(self):
self.exports.getRepo(2, event=111, dist=True)
self.get_tag_id.return_value = 100
self.exports.getRepo('TAG', event=111, dist=True)
self.RepoQuery.assert_called_once()
qv = self.queries[0]
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
# make sure the following does not error
str(query)
self.assertEqual(query.tables, ['repo'])
columns = ['repo.id', 'repo.state', 'repo.task_id', 'repo.create_event',
"date_part('epoch', events.time)", 'repo.dist', 'events.time']
self.assertEqual(set(query.columns), set(columns))
self.assertEqual(query.joins, ['events ON repo.create_event = events.id'])
self.assertEqual(query.clauses, ['create_event <= %(event)i', 'repo.dist is true',
'repo.tag_id = %(id)i'])
self.assertEqual(qv.clauses,
[['tag_id', '=', 100],
['dist', 'IS', True],
['create_event', '<=', 111]])
def test_get_repo_with_min_event(self):
self.get_tag_id.return_value = 100
self.exports.getRepo('TAG', min_event=101010)
self.RepoQuery.assert_called_once()
qv = self.queries[0]
self.assertEqual(len(self.queries), 1)
self.assertEqual(qv.clauses,
[['tag_id', '=', 100],
['dist', 'IS', False],
['state', '=', 1],
['create_event', '>=', 101010]])
# the end

View file

@ -52,6 +52,7 @@ class BaseTest(unittest.TestCase):
self.get_task_refusals = mock.patch('kojihub.scheduler.get_task_refusals').start()
self.get_task_runs = mock.patch('kojihub.scheduler.get_task_runs').start()
self.check_repo_queue = mock.patch('kojihub.repos.check_repo_queue').start()
def tearDown(self):
mock.patch.stopall()

View file

@ -0,0 +1,222 @@
from __future__ import absolute_import
import mock
import os
import re
try:
import unittest2 as unittest
except ImportError:
import unittest
import koji
import kojihub
QP = kojihub.QueryProcessor
class TestTagChangeEvent(unittest.TestCase):
def setUp(self):
self.QueryProcessor = mock.patch('kojihub.kojihub.QueryProcessor',
side_effect=self.get_query).start()
self.queries = []
self.singleValue = mock.MagicMock()
self.get_tag_id = mock.patch('kojihub.kojihub.get_tag_id').start()
self.get_tag = mock.patch('kojihub.kojihub.get_tag').start()
self.readFullInheritance = mock.patch('kojihub.kojihub.readFullInheritance').start()
self.get_tag_external_repos = mock.patch('kojihub.kojihub.get_tag_external_repos').start()
self.get_tag_external_repos.return_value = []
def tearDown(self):
mock.patch.stopall()
def get_query(self, *args, **kwargs):
query = QP(*args, **kwargs)
query.execute = mock.MagicMock()
query.singleValue = self.singleValue
self.queries.append(query)
return query
def test_tag_last_change_simple(self):
tags = [5, 6, 7, 8, 17, 23, 42]
self.get_tag.return_value = {'id': tags[0], 'revoke_event': None}
self.readFullInheritance.return_value = [{'parent_id':n} for n in tags[1:]]
erepos = [101, 201]
self.get_tag_external_repos.return_value = [{'external_repo_id': n} for n in erepos]
events = [8, 8, 8, 8, 8, 8, None, 8, 8, 42, 23, 23, 23, 23, 23, None, 23, 23, 23,
8, 8, 8, 8] # len=23
# called once for tag_updates, twice for versioned tag tables, and twice for erepo tables
# 1 + 2*9 + 2*2 = 23
self.singleValue.side_effect = events
event = kojihub.tag_last_change_event('TAG')
self.assertEqual(event, 42) # max(events)
self.assertEqual(len(self.queries), 23)
self.readFullInheritance.assert_called_once_with(tags[0], event=None)
for query in self.queries[:19]: # tag queries
self.assertEqual(query.clauses[0], 'tag_id IN %(tags)s')
self.assertEqual(query.values['tags'], tags)
# we didn't pass an event, so there should be no second clause
self.assertEqual(len(query.clauses), 1)
for query in self.queries[19:]: # erepo queries
self.assertEqual(query.clauses[0], 'external_repo_id IN %(repos)s')
self.assertEqual(query.values['repos'], erepos)
# we didn't pass an event, so there should be no second clause
self.assertEqual(len(query.clauses), 1)
def test_tag_last_change_noinherit(self):
tags = [5, 6, 7, 8, 17, 23, 42]
self.get_tag.return_value = {'id': tags[0], 'revoke_event': None}
self.readFullInheritance.return_value = [{'parent_id':n} for n in tags[1:]]
events = [8, 8, 8, 8, 8, 8, None, 8, 8, 42, 23, 23, 23, 23, 23, None, 23, 23, 23] # len=19
self.singleValue.side_effect = events
event = kojihub.tag_last_change_event('TAG', inherit=False)
self.assertEqual(event, 42) # max(events)
self.assertEqual(len(self.queries), 19)
self.readFullInheritance.assert_not_called()
for query in self.queries:
self.assertEqual(query.clauses[0], 'tag_id IN %(tags)s')
# only the tag itself should be in the query condition
self.assertEqual(query.values['tags'], [tags[0]])
# we didn't pass an event, so there should be no second clause
self.assertEqual(len(query.clauses), 1)
def test_tag_last_change_deleted(self):
self.get_tag.return_value = {'id': 5, 'revoke_event': 9999}
event = kojihub.tag_last_change_event('TAG')
self.assertEqual(event, 9999)
self.readFullInheritance.assert_not_called()
self.get_tag_external_repos.assert_not_called()
self.singleValue.assert_not_called()
self.assertEqual(len(self.queries), 0)
def test_tag_last_change_before(self):
tags = [5, 6, 7, 8, 17, 23, 42]
before = 123
self.get_tag.return_value = {'id': tags[0], 'revoke_event': None}
self.readFullInheritance.return_value = [{'parent_id':n} for n in tags[1:]]
erepos = [101, 201]
self.get_tag_external_repos.return_value = [{'external_repo_id': n} for n in erepos]
events = [8, 8, 8, 8, 8, 8, None, 8, 8, 42, 23, 23, 23, 23, 23, None, 23, 23, 23,
8, 8, 8, 8] # len=23
# called once for tag_updates, twice for versioned tag tables, and twice for erepo tables
# 1 + 2*9 + 2*2 = 23
self.singleValue.side_effect = events
event = kojihub.tag_last_change_event('TAG', before=before)
self.assertEqual(event, 42) # max(events)
self.assertEqual(len(self.queries), 23)
self.readFullInheritance.assert_called_once_with(tags[0], event=before)
for query in self.queries[:19]:
self.assertEqual(query.values['tags'], tags)
self.assertEqual(query.values['before'], before)
# QP sorts the clauses, so they are not in the order the code adds them
self.assertIn('tag_id IN %(tags)s', query.clauses)
self.assertEqual(len(query.clauses), 2)
for query in self.queries[19:]: # erepo queries
self.assertIn('external_repo_id IN %(repos)s', query.clauses)
self.assertEqual(query.values['repos'], erepos)
self.assertEqual(query.values['before'], before)
# we didn't pass an event, so there should be no second clause
self.assertEqual(len(query.clauses), 2)
def test_tag_first_change_simple(self):
self.get_tag_id.return_value = 99
events = [88]
self.singleValue.side_effect = events
event = kojihub.tag_first_change_event('TAG')
self.assertEqual(event, 88)
self.assertEqual(len(self.queries), 1)
self.readFullInheritance.assert_not_called()
# first query is for tag_config
query = self.queries[0]
self.assertEqual(query.tables, ['tag_config'])
self.assertEqual(query.clauses[0], 'tag_id = %(tag_id)s')
self.assertEqual(query.values['tag_id'], 99)
self.assertEqual(len(query.clauses), 1)
def test_tag_first_change_noinherit(self):
self.get_tag_id.return_value = 99
events = [88]
self.singleValue.side_effect = events
event = kojihub.tag_first_change_event('TAG', inherit=False)
# with no after arg, we should only query tag_config
self.assertEqual(event, 88)
self.readFullInheritance.assert_not_called()
self.assertEqual(len(self.queries), 1)
query = self.queries[0]
self.assertEqual(query.tables, ['tag_config'])
self.assertEqual(query.clauses[0], 'tag_id = %(tag_id)s')
# only the tag itself should be in the query condition
self.assertEqual(query.values['tag_id'], 99)
# we didn't pass an event, so there should be no second clause
self.assertEqual(len(query.clauses), 1)
def test_tag_first_change_after(self):
tags = [5, 6, 7, 8, 17, 23, 42]
after = 5
self.get_tag_id.return_value = tags[0]
self.readFullInheritance.return_value = [{'parent_id':n} for n in tags[1:]]
erepos = [101, 201]
self.get_tag_external_repos.return_value = [{'external_repo_id': n} for n in erepos]
events = [8, 8, 8, 8, 8, 8, 8, None, 8, 8, 42, 23, 23, 23, 23, 23, None, 23, 23, 23,
8, 8, 8, 8] # len=24
self.assertEqual(len(events), 24)
# called once for tag_config, once for tag_updates, twice for versioned tag tables,
# and twice for erepo tables
# 1 + 1 + 2*9 + 2*2 = 23
self.singleValue.side_effect = events
event = kojihub.tag_first_change_event('TAG', after=after)
self.assertEqual(event, 8) # min(events)
self.assertEqual(len(self.queries), 24)
self.readFullInheritance.assert_called_once_with(tags[0], event=after)
for query in self.queries[1:20]:
self.assertEqual(query.values['tags'], tags)
self.assertEqual(query.values['after'], after)
# QP sorts the clauses, so they are not in the order the code adds them
self.assertIn('tag_id IN %(tags)s', query.clauses)
self.assertEqual(len(query.clauses), 2)
for query in self.queries[20:]:
self.assertEqual(query.values['repos'], erepos)
self.assertEqual(query.values['after'], after)
# QP sorts the clauses, so they are not in the order the code adds them
self.assertIn('external_repo_id IN %(repos)s', query.clauses)
self.assertEqual(len(query.clauses), 2)
def test_tag_first_change_after_noinherit(self):
# without inheritance, we'll only query the tag itself
tag_id = 999
after = 5
self.get_tag_id.return_value = tag_id
events = [8, 8, 8, 8, 8, 8, 8, None, 8, 8, 42, 23, 23, 23, 23, 23, None, 23, 23, 23]
self.assertEqual(len(events), 20)
# called once for tag_config, once for tag_updates, and twice for versioned tag tables
# 2 + 2*9 = 20
self.singleValue.side_effect = events
event = kojihub.tag_first_change_event('TAG', after=after, inherit=False)
self.assertEqual(event, 8) # min(events)
self.assertEqual(len(self.queries), 20)
self.readFullInheritance.assert_not_called()
for query in self.queries[1:]:
self.assertEqual(query.values['tags'], [tag_id])
self.assertEqual(query.values['after'], after)
# QP sorts the clauses, so they are not in the order the code adds them
self.assertIn('tag_id IN %(tags)s', query.clauses)
self.assertEqual(len(query.clauses), 2)
# the end

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
<revision>1711390493</revision>
<data type="primary">
<checksum type="sha256">2d64a128e2b0f527c4ede4203e4c6bfd111a89d4c6cb72d5f503c33759a76075</checksum>
<open-checksum type="sha256">53cd17e486fd5ddc4b94c41d17492496f39b0f71a1fbd80974e4c72dbe96499c</open-checksum>
<location href="repodata/2d64a128e2b0f527c4ede4203e4c6bfd111a89d4c6cb72d5f503c33759a76075-primary.xml.gz"/>
<timestamp>1711390493</timestamp>
<size>224483</size>
<open-size>1699242</open-size>
</data>
<data type="filelists">
<checksum type="sha256">f5fd4083abd9c497faddc53093ef596ca50bf9419b523d712762dc6157fc8d5b</checksum>
<open-checksum type="sha256">dc0e5bfcb4fe2fdf4573f4b3d306dbf3e0c72ef8711961af12f3212585aba0c3</open-checksum>
<location href="repodata/f5fd4083abd9c497faddc53093ef596ca50bf9419b523d712762dc6157fc8d5b-filelists.xml.gz"/>
<timestamp>1711390493</timestamp>
<size>436625</size>
<open-size>5189688</open-size>
</data>
<data type="other">
<checksum type="sha256">badfeedb0c48fa9f638c5bb64909103b3eafacfd19a9bfc6edb2ed6438c9e029</checksum>
<open-checksum type="sha256">8f2b3e4179d1c7dde4d06fcdf0e3d287345c65bf410530ee0f55b9a03f1038d8</open-checksum>
<location href="repodata/badfeedb0c48fa9f638c5bb64909103b3eafacfd19a9bfc6edb2ed6438c9e029-other.xml.gz"/>
<timestamp>1711390493</timestamp>
<size>266251</size>
<open-size>2205259</open-size>
</data>
<data type="primary_db">
<checksum type="sha256">20f4cb13be2c8aa1b1c4c612e6480559c42f835b9683a5fb0f405167533a460e</checksum>
<open-checksum type="sha256">1700092e52a4c972d684ab8e9a5d7551ed37176c41ac2a20dc77f5576e1d5198</open-checksum>
<location href="repodata/20f4cb13be2c8aa1b1c4c612e6480559c42f835b9683a5fb0f405167533a460e-primary.sqlite.bz2"/>
<timestamp>1711390493</timestamp>
<size>438912</size>
<open-size>1773568</open-size>
<database_version>10</database_version>
</data>
<data type="filelists_db">
<checksum type="sha256">2d1ce3b2dd348c484e59cd27a378c8374d5df0e82e332a5899e38e257f219ef5</checksum>
<open-checksum type="sha256">a94ffb9f906e54b1092d69f2797cdabd52085605f29fa8740e96f1e7cc5952c8</open-checksum>
<location href="repodata/2d1ce3b2dd348c484e59cd27a378c8374d5df0e82e332a5899e38e257f219ef5-filelists.sqlite.bz2"/>
<timestamp>1711390493</timestamp>
<size>501200</size>
<open-size>2539520</open-size>
<database_version>10</database_version>
</data>
<data type="other_db">
<checksum type="sha256">0d3b0537fc40cde08cf37cf8cf83a59ab1fab43c27cf5f9b5f8b22ea3ba93020</checksum>
<open-checksum type="sha256">09e30f1bf9213e629b73a0893ce6172d6e5b8d791d1e7b5e53b684fa44ddc6aa</open-checksum>
<location href="repodata/0d3b0537fc40cde08cf37cf8cf83a59ab1fab43c27cf5f9b5f8b22ea3ba93020-other.sqlite.bz2"/>
<timestamp>1711390493</timestamp>
<size>349440</size>
<open-size>2142208</open-size>
<database_version>10</database_version>
</data>
<data type="group">
<checksum type="sha256">2310cdad2843ead6ea646ce091f0f73de5898fcb303acbe20fc26364dbf550fe</checksum>
<location href="repodata/2310cdad2843ead6ea646ce091f0f73de5898fcb303acbe20fc26364dbf550fe-comps.xml"/>
<timestamp>1711390492</timestamp>
<size>5048</size>
</data>
<data type="group_gz">
<checksum type="sha256">f913cb539babfcbe9611f306b2b9b8fe270fe103780ff3f2f64b59cd445ab425</checksum>
<open-checksum type="sha256">2310cdad2843ead6ea646ce091f0f73de5898fcb303acbe20fc26364dbf550fe</open-checksum>
<location href="repodata/f913cb539babfcbe9611f306b2b9b8fe270fe103780ff3f2f64b59cd445ab425-comps.xml.gz"/>
<timestamp>1711390493</timestamp>
<size>651</size>
<open-size>5048</open-size>
</data>
</repomd>

View file

@ -0,0 +1,129 @@
from __future__ import absolute_import
import json
import mock
import os.path
import shutil
import tempfile
import time
import unittest
import koji
from . import loadkojira
kojira = loadkojira.kojira
class OurException(Exception):
pass
class ManagedRepoTest(unittest.TestCase):
def setUp(self):
self.workdir = tempfile.mkdtemp()
self.kojidir = self.workdir + '/koji'
os.mkdir(self.kojidir)
self.pathinfo = koji.PathInfo(self.kojidir)
mock.patch.object(kojira, 'pathinfo', new=self.pathinfo, create=True).start()
self.session = mock.MagicMock()
self.options = mock.MagicMock()
self.mgr = mock.MagicMock()
self.mgr.options = self.options
self.mgr.session = self.session
self.unlink = mock.patch('os.unlink').start()
self.data = {
'create_event': 497359,
'create_ts': 1709791593.368943,
'creation_ts': 1709791593.367575,
'dist': False,
'end_event': None,
'id': 2385,
'opts': {'debuginfo': False, 'separate_src': False, 'src': False},
'state': 1,
'state_ts': 1710705227.166751,
'tag_id': 50,
'tag_name': 'some-tag',
'task_id': 13290,
}
self.repo = self.mkrepo(self.data)
def mkrepo(self, data):
repodir = self.kojidir + ('/repos/%(tag_name)s/%(id)s' % self.data)
os.makedirs(repodir)
with open('%s/repo.json' % repodir, 'wt', encoding='utf-8') as fp:
# technically not quite the right data, but close enough
json.dump(data, fp, indent=2)
for arch in ('x86_64', 'aarch64'):
os.mkdir(repodir + '/' + arch)
repo = kojira.ManagedRepo(self.mgr, data.copy())
return repo
def tearDown(self):
mock.patch.stopall()
shutil.rmtree(self.workdir)
def test_get_info(self):
info = self.repo.get_info()
self.assertEqual(info, self.data)
def test_get_path(self):
path = self.repo.get_path()
repodir = self.kojidir + ('/repos/%(tag_name)s/%(id)s' % self.repo.data)
self.assertEqual(path, repodir)
def test_delete_check(self):
self.options.expired_repo_lifetime = 3600 * 24
self.options.reference_recheck_period = 3600
base_ts = 444888888
now = base_ts + 100
self.repo.data['state'] = koji.REPO_EXPIRED
self.repo.data['state_ts'] = base_ts
with mock.patch('time.time') as _time:
_time.return_value = now
self.repo.delete_check()
# we should have stopped at the age check
self.session.repo.references.assert_not_called()
self.mgr.rmtree.assert_not_called()
path = self.repo.get_path()
if not os.path.exists(path):
raise Exception('Missing directory: %s' % path)
# try again with later time but also references
now += self.options.expired_repo_lifetime
self.session.repo.references.return_value = ['REF1', 'REF2']
with mock.patch('time.time') as _time:
_time.return_value = now
self.repo.delete_check()
self.mgr.rmtree.assert_not_called()
path = self.repo.get_path()
if not os.path.exists(path):
raise Exception('Missing directory: %s' % path)
self.session.reset_mock()
# no refs, but same time as last check
# (now unchanged)
self.session.repo.references.return_value = []
with mock.patch('time.time') as _time:
_time.return_value = now
self.repo.delete_check()
# we should have stopped at the recheck_period check
self.session.repo.references.assert_not_called()
self.mgr.rmtree.assert_not_called()
# finally, let's check again with no refs
now += self.options.reference_recheck_period
with mock.patch('time.time') as _time:
_time.return_value = now
self.repo.delete_check()
self.session.repo.setState.assert_called_once_with(self.repo.id, koji.REPO_DELETED)
self.mgr.rmtree.assert_called_once_with(path)
# the end

View file

@ -1,5 +1,8 @@
from __future__ import absolute_import
import mock
import os.path
import shutil
import tempfile
import time
import unittest
@ -19,71 +22,24 @@ class RepoManagerTest(unittest.TestCase):
self.session = mock.MagicMock()
self.options = mock.MagicMock()
self.mgr = kojira.RepoManager(self.options, self.session)
self.rmtree = mock.patch('koji.util.rmtree').start()
# also mock in kojira namespace
mock.patch.object(kojira, 'rmtree', new=self.rmtree).start()
self.workdir = tempfile.mkdtemp()
self.kill = mock.patch('os.kill').start()
self.fork = mock.patch('os.fork').start()
self.unlink = mock.patch('os.unlink').start()
self.waitpid = mock.patch('os.waitpid', new=self.my_waitpid).start()
# kojira defines global pathinfo in start block
self.pathinfo = mock.patch.object(kojira, 'pathinfo', create=True).start()
def tearDown(self):
mock.patch.stopall()
shutil.rmtree(self.workdir)
def test_check_tasks_none(self):
self.mgr.tasks = {}
self.mgr.other_tasks = {}
self.session.listTasks.return_value = []
self.mgr.checkTasks()
self.session.getTaskInfo.assert_not_called()
self.assertEqual(self.mgr.tasks, {})
self.assertEqual(self.mgr.other_tasks, {})
def test_check_tasks_other(self):
self.mgr.tasks = {}
self.mgr.other_tasks = {}
self.session.listTasks.return_value = [
{
'id': 1294,
'method': 'newRepo',
'state': 0,
'waiting': None,
},
]
self.mgr.logger = mock.MagicMock()
self.mgr.checkTasks()
self.session.getTaskInfo.assert_not_called()
self.assertEqual(self.mgr.tasks, {})
self.assertEqual(len(self.mgr.other_tasks), 1)
self.mgr.logger.info.assert_called_once()
# the extra task should not be logged if we run again
self.mgr.logger.reset_mock()
self.mgr.checkTasks()
self.mgr.logger.info.assert_not_called()
def test_check_tasks_ours(self):
tasks = [
{'id': 101, 'state': koji.TASK_STATES['FREE']},
{'id': 102, 'state': koji.TASK_STATES['OPEN']},
{'id': 103, 'state': koji.TASK_STATES['CLOSED']},
{'id': 104, 'state': koji.TASK_STATES['CANCELED']},
{'id': 105, 'state': koji.TASK_STATES['FAILED']},
]
task_idx = dict([(t['id'], t) for t in tasks])
order = []
def getTaskInfo(task_id):
# record the order of calls in multicall
order.append(task_id)
def multiCall(strict):
return [[task_idx[tid]] for tid in order]
self.session.getTaskInfo.side_effect = getTaskInfo
self.session.multiCall.side_effect = multiCall
self.mgr.tasks = dict([
(t['id'], {'taskinfo': t, 'tag_id': 'TAG'})
for t in tasks])
self.mgr.other_tasks = {}
self.session.listTasks.return_value = []
self.mgr.checkTasks()
# should have removed the close tasks
self.assertEqual(list(self.mgr.tasks.keys()), [101, 102])
def my_waitpid(self, pid, *a):
# by default, report all processes exit normally
return pid, 0
@mock.patch('time.sleep')
def test_regen_loop(self, sleep):
@ -98,72 +54,202 @@ class RepoManagerTest(unittest.TestCase):
self.assertEqual(self.mgr.regenRepos.call_count, 11)
subsession.logout.assert_called_once()
def test_set_tag_score(self):
self.mgr.tagUseStats = mock.MagicMock()
self.mgr.tagUseStats.return_value = {
'n_recent': 5
}
self.mgr.needed_tags = {}
entry = {
'taginfo': {
'id': 'TAGID',
'name': 'TAGNAME',
},
'expire_ts': time.time() - 300
}
self.mgr.setTagScore(entry)
score = entry['score']
if score < 0.0:
raise Exception('score too low')
@mock.patch('time.sleep')
def test_rmtree_loop(self, sleep):
subsession = mock.MagicMock()
self.mgr.checkQueue = mock.MagicMock()
self.mgr.checkQueue.side_effect = [None] * 10 + [OurException()]
# we need the exception to terminate the infinite loop
_entry = entry.copy()
_entry['expire_ts'] -= 300
self.mgr.setTagScore(_entry)
if score > entry['score']:
raise Exception('score should have increased')
with self.assertRaises(OurException):
self.mgr.rmtreeLoop(subsession)
self.mgr.tagUseStats.return_value = {
'n_recent': 10
# higher than before
}
self.mgr.setTagScore(entry)
if score > entry['score']:
raise Exception('score should have increased')
self.assertEqual(self.mgr.checkQueue.call_count, 11)
subsession.logout.assert_called_once()
def test_check_needed(self):
self.options.no_repo_effective_age = 999
self.session.getBuildTargets.return_value = [
{'build_tag': 1, 'build_tag_name': 'tag 1'},
{'build_tag': 2, 'build_tag_name': 'tag 2'},
{'build_tag': 3, 'build_tag_name': 'tag 3'},
]
# make two repo entries
repo1 = mock.MagicMock()
repo1.tag_id = 1
repo1.current = True
repo2 = mock.MagicMock()
repo2.tag_id = 2
repo2.current = False
repo2.pending.return_value = True
self.mgr.repos = {1: repo1, 2: repo2}
@mock.patch('time.sleep')
def test_currency_loop(self, sleep):
subsession = mock.MagicMock()
subsession.repo.updateEndEvents.side_effect = [None] * 10 + [OurException()]
# we need the exception to terminate the infinite loop
# more mocks
def my_get_tag(tag_id):
return {'id': tag_id, 'name': 'TAG %i' % tag_id}
self.session.getTag.side_effect = my_get_tag
self.mgr.logger = mock.MagicMock()
self.mgr.setTagScore = mock.MagicMock()
with self.assertRaises(OurException):
self.mgr.currencyChecker(subsession)
with mock.patch('time.time') as mytime:
mytime.side_effect = [1000, 1100]
self.mgr.checkNeeded()
self.assertEqual(subsession.repo.updateEndEvents.call_count, 11)
subsession.logout.assert_called_once()
# only the third tag should show up as needed
expected = {3:
{'expire_ts': 1, # time minus effective age
'needed_since': 1100,
'taginfo': {
'id': 3,
'name': 'TAG 3'
}}}
self.assertEqual(self.mgr.needed_tags, expected)
@mock.patch('time.sleep')
def test_external_loop(self, sleep):
subsession = mock.MagicMock()
self.mgr.checkExternalRepos = mock.MagicMock()
self.mgr.checkExternalRepos.side_effect = [None] * 10 + [OurException()]
# we need the exception to terminate the infinite loop
with self.assertRaises(OurException):
self.mgr.currencyExternalChecker(subsession)
self.assertEqual(self.mgr.checkExternalRepos.call_count, 11)
subsession.logout.assert_called_once()
def test_rmtree(self):
subsession = mock.MagicMock()
dir1 = self.workdir + '/one'
dir2 = self.workdir + '/two'
self.assertEqual(list(self.mgr.delete_queue), [])
# add a dir to the queue
self.mgr.rmtree(dir1)
self.assertEqual(list(self.mgr.delete_queue), [dir1])
# duplicate should be ignored
self.mgr.rmtree(dir1)
self.assertEqual(list(self.mgr.delete_queue), [dir1])
# new entry should appear in correct order
self.mgr.rmtree(dir2)
self.assertEqual(list(self.mgr.delete_queue), [dir1, dir2])
def test_check_queue(self):
self.options.max_delete_processes = 3
nums = range(1, 11) # 1 to 10
# avoiding n=0 because we use it as a fake pid
# queue up some deletes
dirs = [self.workdir + '/dir_%02i' % n for n in nums]
for d in dirs:
self.mgr.rmtree(d)
check = mock.MagicMock()
self.rmtree.side_effect = [(n, check) for n in nums]
# fake pids match dir number
self.assertEqual(list(self.mgr.delete_queue), dirs)
# first pass
self.mgr.checkQueue()
self.assertEqual(list(self.mgr.delete_queue), dirs[3:])
self.assertEqual(set(self.mgr.delete_pids), set([1, 2, 3]))
# second pass
self.mgr.checkQueue()
self.assertEqual(list(self.mgr.delete_queue), dirs[6:])
self.assertEqual(set(self.mgr.delete_pids), set([4, 5, 6]))
# third pass
self.mgr.checkQueue()
self.assertEqual(list(self.mgr.delete_queue), dirs[9:])
self.assertEqual(set(self.mgr.delete_pids), set([7, 8, 9]))
# fourth pass
self.mgr.checkQueue()
self.assertEqual(list(self.mgr.delete_queue), [])
self.assertEqual(set(self.mgr.delete_pids), set([10]))
# last pass
self.mgr.checkQueue()
self.assertEqual(list(self.mgr.delete_queue), [])
self.assertEqual(set(self.mgr.delete_pids), set([]))
def test_read_current(self):
self.assertEqual(set(self.mgr.repos), set())
# fake repo data
data = {'create_event': 100, 'create_ts': 101010, 'tag_id': 999, 'state': 1,
'dist': False, 'tag_name': 'TAG'}
repo_ids = range(1000, 1015)
repos = [dict(id=n, **data) for n in repo_ids]
# pass 1
self.session.repo.query.return_value = repos
self.mgr.readCurrentRepos()
self.assertEqual(set(self.mgr.repos), set([r['id'] for r in repos]))
# pass 2 - no new repos
self.mgr.readCurrentRepos()
self.assertEqual(set(self.mgr.repos), set([r['id'] for r in repos]))
# pass 3 - repo changes state
repos[0] = repos[0].copy() # don't change the data in mgr.repos
repos[0]['state'] = 2 # expired
repo_id = repos[0]['id']
self.mgr.readCurrentRepos()
self.assertEqual(set(self.mgr.repos), set([r['id'] for r in repos]))
self.assertEqual(self.mgr.repos[repo_id].state, 2)
self.assertEqual(self.mgr.repos[repo_id].data['state'], 2)
# pass 4 - repo disappears from hub
repos.pop(0)
self.mgr.readCurrentRepos()
self.assertEqual(set(self.mgr.repos), set([r['id'] for r in repos]))
# using autospec so we can grab self from mock_calls
@mock.patch.object(kojira.ManagedRepo, 'delete_check', autospec=True)
def test_update_repos(self, delete_check):
self.options.init_timeout = 3600
self.options.repo_lifetime = 3600 * 24
self.options.dist_repo_lifetime = 3600 * 24
base_ts = 444888888
# fake repo data
data = {'tag_id': 999, 'state': koji.REPO_READY, 'tag_name': 'TAG', 'dist': False,
'create_event': 100, 'end_event': 200, 'opts': {}, 'custom_opts': {},
'state_ts': base_ts, 'creation_ts': base_ts}
repo_ids = range(1000, 1015)
repos = [dict(id=n, **data) for n in repo_ids]
# make one old enough to expire
repos[0]['state_ts'] = base_ts - self.options.repo_lifetime
# make one stale
repos[1]['state'] = koji.REPO_INIT
repos[1]['creation_ts'] = base_ts - self.options.init_timeout
# make one expired
repos[2]['state'] = koji.REPO_EXPIRED
# do the run
self.session.repo.query.return_value = repos
with mock.patch('time.time') as _time:
_time.return_value = base_ts + 100 # shorter than all timeouts
self.mgr.updateRepos()
# confirm the expiration
repo_id = repos[0]['id']
self.session.repoExpire.assert_called_once_with(repo_id)
self.assertEqual(self.mgr.repos[repo_id].state, koji.REPO_EXPIRED)
self.assertEqual(self.mgr.repos[repo_id].data['state'], koji.REPO_EXPIRED)
# confirm action on the stale repo
repo_id = repos[1]['id']
self.session.repoProblem.assert_called_once_with(repo_id)
self.assertEqual(self.mgr.repos[repo_id].state, koji.REPO_PROBLEM)
self.assertEqual(self.mgr.repos[repo_id].data['state'], koji.REPO_PROBLEM)
# only repo 2 should have been checked for deletion
repo_id = repos[2]['id']
delete_check.assert_called_once()
mrepo = delete_check.mock_calls[0][1][0] # self arg
self.assertEqual(mrepo.repo_id, repo_id)
@mock.patch('requests.get')
def test_check_external(self, get):
# fake ext repo data
repo1 = {'external_repo_id': 1, 'external_repo_name': 'myrepo',
'url': 'https://localhost/NOSUCHPATH'}
repo2 = {'external_repo_id': 2, 'external_repo_name': 'myotherrepo',
'url': 'https://localhost/FAKEPATH/$arch'}
self.session.getTagExternalRepos.return_value = [repo1, repo2]
data1 = {}
data2 = {}
self.session.repo.getExternalRepoData.side_effect = [data1, data2]
self.session.getAllArches.return_value = ['i386', 'x86_64', 'riscv']
repomd_fn = os.path.dirname(__file__) + '/data/external-repomd.xml'
with open(repomd_fn, 'rt') as fo:
repomd = fo.read()
get.return_value.text = repomd
self.mgr.checkExternalRepos()
self.session.repo.setExternalRepoData.assert_has_calls([
mock.call(1, {'max_ts': 1711390493}),
mock.call(2, {'max_ts': 1711390493}),
])
# the end

View file

@ -0,0 +1,233 @@
import mock
import unittest
import koji.tasks
from koji.util import RepoWatcher
class TestRepoWatcher(unittest.TestCase):
TAG = {'id': 137, 'name': 'MY-TAG'}
def setUp(self):
self.session = mock.MagicMock()
self.checkForBuilds = mock.patch('koji.util.checkForBuilds').start()
self.session.getTag.return_value = self.TAG
self.sleep = mock.patch('time.sleep').start()
def tearDown(self):
mock.patch.stopall()
def test_getRepo_ready(self):
repoinfo = {'id': 123, 'tag_id': self.TAG['id']}
self.session.repo.request.return_value = {'repo': repoinfo}
watcher = RepoWatcher(self.session, 'TAG')
result = watcher.getRepo()
self.assertEqual(result, repoinfo)
def test_getRepo_request(self):
self.session.repo.request.return_value = {'repo': None, 'request': {'id': 999}}
watcher = RepoWatcher(self.session, 'TAG')
result = watcher.getRepo()
self.assertEqual(result, None)
def test_getRepo_builds_missing(self):
self.session.repo.request.return_value = {'repo': None, 'request': {'id': 999}}
self.checkForBuilds.return_value = False
watcher = RepoWatcher(self.session, 'TAG', nvrs=['package-1.2-34'])
result = watcher.getRepo()
self.assertEqual(result, None)
self.checkForBuilds.assert_called_once()
def test_waitrepo_request_gives_repo(self):
repoinfo = {'id': 123, 'tag_id': self.TAG['id']}
self.session.repo.get.return_value = None
self.session.repo.request.return_value = {'repo': repoinfo}
watcher = RepoWatcher(self.session, 'TAG')
result = watcher.waitrepo()
self.assertEqual(result, repoinfo)
def test_waitrepo_request_wait(self):
repoinfo = {'id': 123, 'tag_id': self.TAG['id']}
req = {'id': 999, 'min_event': 10001, 'task_id': 'TASK', 'task_state': 0, 'repo_id': None,
'score': None, 'active': True, 'tries': 1}
self.session.repo.get.return_value = None
check = {'repo': None, 'request': req}
req2 = req.copy()
req2['task_state'] = 1
check2 = {'repo': None, 'request': req2}
self.session.repo.request.return_value = check
done = {'repo': repoinfo, 'request': req}
self.session.repo.checkRequest.side_effect = [check, check, check2, done]
watcher = RepoWatcher(self.session, 'TAG')
result = watcher.waitrepo()
self.assertEqual(result, repoinfo)
def test_waitrepo_anon_wait(self):
repoinfo = {'id': 123, 'tag_id': self.TAG['id']}
self.session.repo.get.side_effect = [None] * 5 + [repoinfo]
watcher = RepoWatcher(self.session, 'TAG')
result = watcher.waitrepo(anon=True)
self.assertEqual(result, repoinfo)
self.session.repo.request.assert_not_called()
def test_waitrepo_request_timeout(self):
req = {'id': 999, 'min_event': 10001, 'task_id': 'TASK', 'task_state': 0, 'repo_id': None,
'score': None, 'active': True, 'tries': 1}
self.session.repo.get.return_value = None
check = {'repo': None, 'request': req}
self.session.repo.request.return_value = check
self.session.repo.checkRequest.side_effect = [check] * 20
watcher = RepoWatcher(self.session, 'TAG')
watcher.check_timeout = mock.MagicMock()
watcher.check_timeout.side_effect = [False] * 10 + [True]
with self.assertRaises(koji.GenericError) as err:
watcher.waitrepo()
def test_taskargs(self):
watcher = RepoWatcher(self.session, 'TAG')
args = watcher.task_args()
params = koji.tasks.parse_task_params('waitrepo', args)
def test_waitrepo_build_wait(self):
self.session.repo.get.return_value = None
# we'll pass with nvrs, so we should wait for builds before making request
nvrs = ['package-1.2-34']
builds = [{'name': 'package', 'version': '1.2', 'release': '34', 'epoch': ''}]
self.session.tagLastChangeEvent.return_value = 10000
def got_builds():
# called when we start reporting the builds in the tag
self.session.repo.request.assert_not_called()
self.session.tagLastChangeEvent.return_value = 10002
return True
self.checkForBuilds.side_effect = [False, False, False, got_builds, True]
# once we report the build, checkForBuilds should be called just once more to verify the repo
req = {'id': 999, 'min_event': 10000, 'task_id': 'TASK', 'task_state': 0, 'repo_id': None,
'score': None, 'active': True, 'tries': 1}
check = {'repo': None, 'request': req}
self.session.repo.request.return_value = check
repoinfo = {'id': 123, 'tag_id': self.TAG['id'], 'create_event': 10002}
done = {'repo': repoinfo, 'request': req}
self.session.repo.checkRequest.side_effect = [check, check, check, done]
watcher = RepoWatcher(self.session, 'TAG', nvrs=nvrs)
result = watcher.waitrepo()
self.assertEqual(result, repoinfo)
# checkForBuilds is called several times, the event arg can vary, but the others should not
for call in self.checkForBuilds.mock_calls:
# name, args, kwargs
# session, tag, builds, event, latest
self.assertEqual(call[1][0], self.session)
self.assertEqual(call[1][1], self.TAG['id'])
self.assertEqual(call[1][2], builds)
def test_waitrepo_build_timeout(self):
self.session.repo.get.return_value = None
nvrs = ['package-1.2-34']
# just keep reporting that the build is not there
self.checkForBuilds.side_effect = [False] * 20
watcher = RepoWatcher(self.session, 'TAG', nvrs=nvrs)
watcher.check_timeout = mock.MagicMock()
watcher.check_timeout.side_effect = [False] * 10 + [True]
with self.assertRaises(koji.GenericError) as err:
watcher.waitrepo()
# we should not have reached the request stage
self.session.repo.request.assert_not_called()
def test_waitrepo_build_not_in_repo(self):
self.session.repo.get.return_value = None
nvrs = ['package-1.2-34']
self.session.tagLastChangeEvent.return_value = 10000
# replace checkForBuilds
def my_check(session, tag, builds, event, latest=False):
if event and event < 10002:
# called from check_repo with repo event id
return False
return True
self.checkForBuilds.side_effect = my_check
req1 = {'id': 999, 'min_event': 10000, 'task_id': 'TASK', 'task_state': 0, 'repo_id': None,
'score': None}
req2 = req1.copy()
req2['min_event'] = 10002
repo1 = {'id': 123, 'tag_id': self.TAG['id'], 'create_event': 10000}
repo2 = {'id': 123, 'tag_id': self.TAG['id'], 'create_event': 10002}
check1 = {'repo': None, 'request': req1}
check1b = {'repo': repo1, 'request': req1}
check2 = {'repo': None, 'request': req2}
check2b = {'repo': repo2, 'request': req2}
# request should be made twice
self.session.repo.request.side_effect = [check1, check2]
# and each checked once
self.session.repo.checkRequest.side_effect = [check1b, check2b]
watcher = RepoWatcher(self.session, 'TAG', nvrs=nvrs)
result = watcher.waitrepo()
self.assertEqual(result, repo2)
def test_check_repo(self):
watcher = RepoWatcher(self.session, 'TAG')
repo = {'tag_id': self.TAG['id'], 'create_event': 10000, 'opts': {'src': True}}
self.checkForBuilds.return_value = True
# wrong tag
_repo = repo.copy()
_repo['tag_id'] += 1
result = watcher.check_repo(_repo)
self.assertEqual(result, False)
# wrong at_event
watcher = RepoWatcher(self.session, 'TAG', at_event=5000)
result = watcher.check_repo(repo)
self.assertEqual(result, False)
# wrong min_event
watcher = RepoWatcher(self.session, 'TAG', min_event=20000)
result = watcher.check_repo(repo)
self.assertEqual(result, False)
# wrong opts
watcher = RepoWatcher(self.session, 'TAG', opts={'src': False})
result = watcher.check_repo(repo)
self.assertEqual(result, False)
# wrong builds
nvrs = ['package-1.2-34']
self.checkForBuilds.return_value = False
watcher = RepoWatcher(self.session, 'TAG', nvrs=nvrs)
result = watcher.check_repo(repo)
self.assertEqual(result, False)
# good
self.checkForBuilds.return_value = True
watcher = RepoWatcher(self.session, 'TAG', nvrs=nvrs, at_event=10000, opts={'src': True})
result = watcher.check_repo(repo)
self.assertEqual(result, True)
def test_event_args(self):
# both min and at
with self.assertRaises(koji.ParameterError):
watcher = RepoWatcher(self.session, 'TAG', min_event=100, at_event=99)
self.session.tagLastChangeEvent.return_value = 101010
watcher = RepoWatcher(self.session, 'TAG', min_event='last')
self.assertEqual(watcher.min_event, 101010)
self.session.tagLastChangeEvent.assert_called_once()
# the end

View file

@ -1,4 +1,5 @@
from __future__ import absolute_import
import mock
import random
import shutil
import six
@ -575,10 +576,9 @@ class TasksTestCase(unittest.TestCase):
('host test.domain.local (i386) does not support any arches '
'of tag some_package-1.2-build (aarch64, x86_64)'))
def test_getRepo_tied_to_session(self):
""" Tests that the getRepo function calls session.getRepo(), and returns the result
when successful.
"""
@patch('koji.util.RepoWatcher')
def test_getRepo_no_wait_task(self, RepoWatcher):
""" Tests that the getRepo method does not wait if repo is available"""
temp_path = get_tmp_dir_path('TaskTest')
makedirs(temp_path)
@ -590,89 +590,75 @@ class TasksTestCase(unittest.TestCase):
'state': 1
}
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.session = Mock()
obj.session.getRepo.return_value = repo_dict
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
handler.session = mock.MagicMock()
handler.wait = mock.MagicMock()
watcher = mock.MagicMock()
watcher.getRepo.return_value = repo_dict
RepoWatcher.return_value = watcher
self.assertEqual(obj.getRepo(8472), repo_dict)
result = handler.getRepo(8472)
@patch('{0}.TaskTest.wait'.format(__name__))
def test_getRepo_not_tied_to_session(self, mock_wait):
""" Tests that the getRepo function waits until the results are available
for session.getRepo, when it is not available at the start of the function call.
"""
handler.session.host.subtask.assert_not_called()
handler.wait.assert_not_called()
self.assertEqual(result, repo_dict)
@patch('koji.util.RepoWatcher')
def test_getRepo_last_event(self, RepoWatcher):
""" Tests that the getRepo method uses min_event='last' when requested"""
temp_path = get_tmp_dir_path('TaskTest')
makedirs(temp_path)
repo_dict = {
'create_event': 13413120,
'create_ts': 1466140834.9119599,
'creation_time': '2016-06-17 05:20:34.911962',
'id': 1592850,
'create_event': 13635166,
'create_ts': 1469039671.5743899,
'creation_time': '2016-07-20 18:34:31.574386',
'id': 1630631,
'state': 1
}
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.session = Mock()
obj.session.getRepo.return_value = None
obj.session.getTag.return_value = {
'arches': 'i386 ia64 x86_64 ppc s390 s390x ppc64',
'extra': {},
'id': 851,
'locked': True,
'maven_include_all': False,
'maven_support': False,
'name': 'dist-3.0E-build',
'perm': None,
'perm_id': None
}
obj.session.getBuildTargets.return_value = [{
'build_tag': 3093,
'build_tag_name': 'dist-6E-dsrv-9-build',
'dest_tag': 3092,
'dest_tag_name': 'dist-6E-dsrv-9-qu-candidate',
'id': 851,
'name': 'dist-6E-dsrv-9-qu-candidate'
}
]
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
handler.session = mock.MagicMock()
handler.wait = mock.MagicMock()
watcher = mock.MagicMock()
watcher.getRepo.return_value = repo_dict
RepoWatcher.return_value = watcher
obj.session.host.subtask.return_value = 123
mock_wait.return_value = {123: repo_dict}
result = handler.getRepo(8472, wait=True)
self.assertEqual(obj.getRepo(851), repo_dict)
obj.session.getRepo.assert_called_once_with(851)
obj.session.getTag.assert_called_once_with(851, strict=True)
RepoWatcher.assert_called_once_with(handler.session, 8472, nvrs=None, min_event='last', logger=handler.logger)
handler.session.host.subtask.assert_not_called()
handler.wait.assert_not_called()
self.assertEqual(result, repo_dict)
@patch('{0}.TaskTest.wait'.format(__name__))
def test_getRepo_not_tied_to_session_no_build_targets(self, mock_wait):
""" Tests that the getRepo function raises an exception
when session.getBuildTargets returns an empty list
"""
@patch('koji.util.RepoWatcher')
def test_getRepo_wait_task(self, RepoWatcher):
""" Tests that the getRepo function waits for subtask if repo not immediately available"""
temp_path = get_tmp_dir_path('TaskTest')
makedirs(temp_path)
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.session = Mock()
obj.session.getRepo.return_value = None
obj.session.getTag.return_value = {
'arches': 'i686 x86_64 ppc ppc64 ppc64le s390 s390x aarch64',
'extra': {},
'id': 8472,
'locked': False,
'maven_include_all': False,
'maven_support': False,
'name': 'rhel-7.3-build',
'perm': 'admin',
'perm_id': 1
repo_dict = {
'create_event': 13635166,
'create_ts': 1469039671.5743899,
'creation_time': '2016-07-20 18:34:31.574386',
'id': 1630631,
'state': 1
}
obj.session.getBuildTargets.return_value = []
try:
obj.getRepo(8472)
raise Exception('The BuildError Exception was not raised')
except koji.BuildError as e:
obj.session.getRepo.assert_called_once_with(8472)
self.assertEqual(e.args[0], 'no repo (and no target) for tag rhel-7.3-build')
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
handler.session = mock.MagicMock()
handler.session.host.subtask.return_value = 'TASKID'
handler.wait = mock.MagicMock()
handler.wait.return_value = {'TASKID': repo_dict}
watcher = mock.MagicMock()
watcher.getRepo.return_value = None
RepoWatcher.return_value = watcher
result = handler.getRepo(8472)
handler.session.host.subtask.assert_called_once()
handler.wait.assert_called_once_with('TASKID')
self.assertEqual(result, repo_dict)
def test_FakeTask_handler(self):
""" Tests that the FakeTest handler can be instantiated and returns 42 when run.