)]}'
{"doc/source/contributor/testing.rst":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"e9f073a6382d83ecc90436b83a46cf645668678e","unresolved":false,"context_lines":[{"line_number":1084,"context_line":"                \u0027parameters\u0027: {"},{"line_number":1085,"context_line":"                    \u0027metrics\u0027: [\u0027instance_cpu_usage\u0027],"},{"line_number":1086,"context_line":"                    \u0027thresholds\u0027: {\u0027instance_cpu_usage\u0027: 0.10},"},{"line_number":1087,"context_line":"                    \u0027weights\u0027: {\u0027instance_cpu_usage\u0027: 1.0},"},{"line_number":1088,"context_line":"                    \u0027host_choice\u0027: \u0027fullsearch\u0027,"},{"line_number":1089,"context_line":"                    \u0027period\u0027: 300,"},{"line_number":1090,"context_line":"                },"}],"source_content_type":"text/x-rst","patch_set":3,"id":"5d1756f1_58ddc5b5","line":1087,"updated":"2026-06-25 09:42:13.000000000","message":"Documentation \u0027Complete example\u0027 uses incorrect strategy parameter names: \u0027instance_cpu_usage\u0027 instead of \u0027instance_cpu_usage_weight\u0027, and \u0027period\u0027 instead of \u0027periods\u0027 dict.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: A contributor copying this example verbatim will get a failing audit or incorrect strategy behavior. The strategy expects weight keys with a \u0027_weight\u0027 suffix and a \u0027periods\u0027 dict with \u0027instance\u0027/\u0027compute_node\u0027 keys (verified against the strategy schema and the real tests in this patch).\n\n**Suggestion**:\nChange \u0027weights\u0027: {\u0027instance_cpu_usage\u0027: 1.0} to \u0027weights\u0027: {\u0027instance_cpu_usage_weight\u0027: 1.0}, and change \u0027period\u0027: 300 to \u0027periods\u0027: {\u0027instance\u0027: 720, \u0027compute_node\u0027: 600}, \u0027aggregation_method\u0027: {\u0027instance\u0027: \u0027mean\u0027, \u0027compute_node\u0027: \u0027mean\u0027}. Match the parameter structure used in the actual test methods in test_workload_stabilization.py.","commit_id":"979ce88e27d631a88790a239a1133a5b2aef1389"}],"watcher/tests/functional/base.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":348,"context_line":"        Requires USE_PROMETHEUS_EMULATOR \u003d True (or METRICS_HOSTS set)"},{"line_number":349,"context_line":"        on the test class so the Prometheus emulator fixture exists."},{"line_number":350,"context_line":"        \"\"\""},{"line_number":351,"context_line":"        self.env.prometheus_fixture.reload_metrics("},{"line_number":352,"context_line":"            hosts\u003dhosts, instances\u003dinstances"},{"line_number":353,"context_line":"        )"},{"line_number":354,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"e4101ccb_04831610","line":351,"updated":"2026-07-02 19:50:17.000000000","message":"load_metrics() accesses self.env.prometheus_fixture without a guard; when USE_PROMETHEUS_EMULATOR is False the attribute is None and reload_metrics raises an opaque AttributeError instead of a clear error.\n\n**Severity**: HIGH | **Confidence**: 0.8\n\n**Risk**: A test that calls self.load_metrics() without setting USE_PROMETHEUS_EMULATOR\u003dTrue fails with \u0027NoneType object has no attribute reload_metrics\u0027, which gives no hint about the missing flag, wasting debugging time.\n\n**Priority**: Before merge\n**Why This Matters**: The docstring says the method \u0027Requires USE_PROMETHEUS_EMULATOR \u003d True\u0027 but the code does not enforce or check this precondition, so the contract is unguarded and the failure mode is hostile to test authors.\n\n**Recommendation**:\nAdd an explicit guard at the top of load_metrics, e.g.: if self.env.prometheus_fixture is None: raise RuntimeError(\u0027load_metrics() requires USE_PROMETHEUS_EMULATOR \u003d True on the test class\u0027). This turns a confusing AttributeError into an actionable message.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"}],"watcher/tests/functional/test_workload_stabilization.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"e9f073a6382d83ecc90436b83a46cf645668678e","unresolved":false,"context_lines":[{"line_number":1035,"context_line":"                params \u003d dict(base_params)"},{"line_number":1036,"context_line":"                params[\u0027thresholds\u0027] \u003d {\u0027instance_cpu_usage\u0027: threshold}"},{"line_number":1037,"context_line":""},{"line_number":1038,"context_line":"                resp \u003d self.admin_api.post("},{"line_number":1039,"context_line":"                    \u0027/audits\u0027,"},{"line_number":1040,"context_line":"                    {"},{"line_number":1041,"context_line":"                        \u0027audit_type\u0027: \u0027ONESHOT\u0027,"}],"source_content_type":"text/x-python","patch_set":3,"id":"b3ae98f0_d5fcb92b","line":1038,"updated":"2026-06-25 09:42:13.000000000","message":"The audit-creation, action-fetching, and assertion logic is duplicated across test_threshold_impact_on_migrations (lines 1038-1132), test_workload_stabilization_with_both_metrics (lines 1242-1339), and test_workload_stabilization_weight_impact (lines 1385-1479).\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Benefit**: Extracting a shared helper (e.g. _run_parametrized_audit) for the common create-audit, wait, fetch-actions, assert-migrations, assert-indicators flow would reduce ~300 lines of near-identical code and make future parametrized tests easier to add and maintain.\n\n**Recommendation**:\nAdd a private helper that accepts (params, topology, metrics, expected_ap_state, expected_migrations, sd_before, sd_after, instances_count) and encapsulates the repeated post-wait-assert cycle. The existing _create_audit_and_get_actions helper covers part of this but not the efficacy-indicator assertions.","commit_id":"979ce88e27d631a88790a239a1133a5b2aef1389"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"e9f073a6382d83ecc90436b83a46cf645668678e","unresolved":false,"context_lines":[{"line_number":1135,"context_line":"        \"\"\"Balanced cluster produces no migrations."},{"line_number":1136,"context_line":""},{"line_number":1137,"context_line":"        Both hosts have similar CPU (~30%), so standard deviation"},{"line_number":1138,"context_line":"        is below the threshold. No migrations should be proposed."},{"line_number":1139,"context_line":"        \"\"\""},{"line_number":1140,"context_line":"        actions \u003d self._create_audit_no_actions("},{"line_number":1141,"context_line":"            parameters\u003d{"}],"source_content_type":"text/x-python","patch_set":3,"id":"5b8a14e4_552e9cb6","line":1138,"updated":"2026-06-25 09:42:13.000000000","message":"Test docstring states hosts have ~30% CPU but the actual BALANCED_HOSTS metrics define compute-1 at 22% and compute-2 at 20%.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Minor: the docstring misleads a reader trying to understand or debug the test. The comment block above BALANCED_HOSTS (line 170-174) correctly documents 22%/20%, so the in-method docstring is inconsistent with the data it references.\n\n**Suggestion**:\nUpdate the docstring to say \u0027Both hosts have similar CPU (~20-22%)\u0027 to match the actual BALANCED_HOSTS metric values.","commit_id":"979ce88e27d631a88790a239a1133a5b2aef1389"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":1053,"context_line":""},{"line_number":1054,"context_line":"                self._wait_for_audit_state(audit_uuid, \u0027SUCCEEDED\u0027)"},{"line_number":1055,"context_line":""},{"line_number":1056,"context_line":"                resp \u003d self.admin_api.get("},{"line_number":1057,"context_line":"                    \u0027/action_plans?audit_uuid\u003d%s\u0027 % audit_uuid"},{"line_number":1058,"context_line":"                )"},{"line_number":1059,"context_line":"                action_plans \u003d resp.json()[\u0027action_plans\u0027]"}],"source_content_type":"text/x-python","patch_set":4,"id":"c5c52f1a_1b98c7b4","line":1056,"updated":"2026-07-02 19:50:17.000000000","message":"test_workload_stabilization.py duplicates the audit/action-fetch/efficacy-assertion sequence nearly verbatim across test_threshold_impact_on_migrations, _with_both_metrics, _weight_impact, and the three aggregate_scope subTests (13 admin_api.post sites, 5 copies of the migrate_actions loop).\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Duplicated assertion logic means a change to how actions are fetched or validated must be applied in many places; copy-paste drift will cause some copies to diverge and mask bugs. The file is 1787 lines where a handful of shared helpers could cut it substantially.\n\n**Suggestion**:\nExtract a shared helper such as _run_audit_and_collect_migrations(parameters, topology, metrics, scope) returning (migrate_actions, action_plan, indicators), and a _assert_migrations(migrate_actions, expected) helper. The existing _create_audit_and_get_actions already does part of this but the parametrized tests bypass it and re-implement the flow inline.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"}],"watcher/tests/local_fixtures/prometheus.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":77,"context_line":"        self._instances \u003d instances"},{"line_number":78,"context_line":"        self._json_path \u003d json_path"},{"line_number":79,"context_line":"        self.prometheus_emulator \u003d None"},{"line_number":80,"context_line":"        self.prometheus_url \u003d None"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    def setUp(self):"},{"line_number":83,"context_line":"        super().setUp()"}],"source_content_type":"text/x-python","patch_set":4,"id":"f117f493_41ccee07","line":80,"updated":"2026-07-02 19:50:17.000000000","message":"PrometheusFixture stores self.prometheus_emulator and self.prometheus_url but the fixture never references self.prometheus_url after setUp; it is only set for external inspection, yet no test reads it.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: Removing the unused attribute (or documenting it as an inspection hook) keeps the fixture surface minimal and avoids implying tests should construct URLs manually when wsgi-intercept already handles routing.\n\n**Recommendation**:\nEither add a comment that prometheus_url is exposed for debugging/inspection, or drop the attribute since the CONF overrides are what actually route requests through wsgi-intercept.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"}],"watcher/tests/local_fixtures/prometheus_api_emulator.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":148,"context_line":"        \"\"\""},{"line_number":149,"context_line":"        with open(path) as f:"},{"line_number":150,"context_line":"            data \u003d jsonutils.load(f)"},{"line_number":151,"context_line":"        self.load_metrics("},{"line_number":152,"context_line":"            hosts\u003ddata.get(\u0027hosts\u0027), instances\u003ddata.get(\u0027instances\u0027)"},{"line_number":153,"context_line":"        )"},{"line_number":154,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"04390793_aa84efdf","line":151,"updated":"2026-07-02 19:50:17.000000000","message":"load_from_json uses oslo_serialization.jsonutils.load instead of the stdlib json module; jsonutils is an oslo convenience for i18n-aware payloads but adds no value for a plain local metrics file.\n\n**Severity**: SUGGESTION | **Confidence**: 0.6\n\n**Benefit**: Using the stdlib json module would be marginally simpler and avoids implying oslo semantics (like unicode handling) that are irrelevant here. This is a minor consistency point; jsonutils is acceptable if the project prefers oslo conventions throughout.\n\n**Recommendation**:\nConsider \u0027import json\u0027 and json.load(f) for plain JSON metric files, unless the project convention is to use jsonutils everywhere in tests.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":158,"context_line":"        \"\"\"Identify the metric kind and lookup key from a PromQL query."},{"line_number":159,"context_line":""},{"line_number":160,"context_line":"        Returns (kind, key) where kind is one of: instance_ram,"},{"line_number":161,"context_line":"        instance_cpu, host_cpu, host_ram, host_ram_total; and key"},{"line_number":162,"context_line":"        is the UUID or FQDN used for lookup."},{"line_number":163,"context_line":"        \"\"\""},{"line_number":164,"context_line":"        if \u0027ceilometer_memory_usage\u0027 in query:"}],"source_content_type":"text/x-python","patch_set":4,"id":"a9139f95_9e2f8d7a","line":161,"updated":"2026-07-02 19:50:17.000000000","message":"_identify_query docstring claims it can return kind \u0027host_ram_total\u0027, but no code path ever returns that value; the only RAM kind returned is \u0027host_ram\u0027. The docstring is misleading.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: A maintainer reading the docstring will expect a host_ram_total kind and may write code against it, then find it never occurs, leading to dead branches or silent no-ops (the final \u0027return None, None\u0027 fallback).\n\n**Suggestion**:\nCorrect the docstring to list only the kinds actually returned: instance_ram, instance_cpu, host_cpu, host_ram. Remove the spurious \u0027host_ram_total\u0027 entry.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":178,"context_line":""},{"line_number":179,"context_line":"        if ("},{"line_number":180,"context_line":"            \u0027node_memory_MemTotal_bytes\u0027 in query"},{"line_number":181,"context_line":"            or \u0027node_memory_MemAvailable_bytes\u0027 in query"},{"line_number":182,"context_line":"        ):"},{"line_number":183,"context_line":"            m \u003d FQDN_RE.search(query)"},{"line_number":184,"context_line":"            if m:"}],"source_content_type":"text/x-python","patch_set":4,"id":"cb97dfe7_ade35206","line":181,"updated":"2026-07-02 19:50:17.000000000","message":"_identify_query maps both node_memory_MemTotal_bytes (capacity) and node_memory_MemAvailable_bytes (available) to the same \u0027host_ram\u0027 kind returning host[\u0027ram\u0027] (usage), conflating total capacity with used memory.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: The real Prometheus host-RAM query (prometheus_base.py:303-310) is a compound \u0027(MemTotal - MemAvailable) / 1024\u0027. The emulator short-circuits it to one lookup, which works today, but any future code querying MemTotal alone (capacity) would receive the usage value, silently producing wrong results.\n\n**Suggestion**:\nEither document explicitly in _identify_query that the compound query is intentionally collapsed to a single usage lookup, or distinguish the two meters: store a separate \u0027ram_total\u0027 field per host and return it for MemTotal queries while returning usage for MemAvailable. At minimum update the _identify_query docstring which lists a non-existent \u0027host_ram_total\u0027 kind.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":218,"context_line":"            query \u003d request.args.get(\u0027query\u0027, \u0027\u0027)"},{"line_number":219,"context_line":"            kind, key \u003d self._identify_query(query)"},{"line_number":220,"context_line":""},{"line_number":221,"context_line":"            if kind \u003d\u003d \u0027instance_ram\u0027:"},{"line_number":222,"context_line":"                inst \u003d self.instances.get(key)"},{"line_number":223,"context_line":"                if inst is None:"},{"line_number":224,"context_line":"                    return jsonify(self._prom_empty())"}],"source_content_type":"text/x-python","patch_set":4,"id":"9caa17ea_4919965c","line":221,"updated":"2026-07-02 19:50:17.000000000","message":"The query handler uses four independent \u0027if kind \u003d\u003d\u0027 blocks (lines 221-247) each returning, rather than elif chains. The instance_cpu block is reachable after instance_ram already matched but returned None key, which is harmless but makes the control flow harder to reason about.\n\n**Severity**: WARNING | **Confidence**: 0.7\n\n**Impact**: Independent ifs with returns are correct here, but a future edit that removes an early return could accidentally fall through to a later branch, producing the wrong metric. An elif chain or a dispatch dict makes the mutual exclusivity explicit and self-documenting.\n\n**Suggestion**:\nConvert the sequential \u0027if kind \u003d\u003d\u0027 blocks into an elif chain, or refactor to a dispatch dictionary mapping kind to a (lookup_dict, value_key, labels) tuple. This documents that exactly one branch fires per query.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"504b596dda4368e151cc192a27d0afbbb7a4a2c2","unresolved":false,"context_lines":[{"line_number":355,"context_line":"            \u0027--metrics)\u0027"},{"line_number":356,"context_line":"        )"},{"line_number":357,"context_line":""},{"line_number":358,"context_line":"    emulator.app.run("},{"line_number":359,"context_line":"        host\u003dargs.host, port\u003dargs.port, debug\u003dargs.debug, ssl_context\u003dssl_ctx"},{"line_number":360,"context_line":"    )"},{"line_number":361,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"c28b8447_df957405","line":358,"updated":"2026-07-02 19:50:17.000000000","message":"main() passes debug\u003dargs.debug directly to Flask app.run(); with --debug the Werkzeug reloader and interactive debugger are enabled, which is fine for a local debugging tool but the --debug flag should be documented as enabling the interactive PIN debugger.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: Clearer documentation prevents a contributor from running --debug on a shared host and exposing the Werkzeug debugger. This is a standalone debug entry point (never used by the test suite via wsgi-intercept) so the risk is low, but a one-line help-text note improves safety.\n\n**Recommendation**:\nAdd a note to the --debug argparse help string, e.g. help\u003d\u0027Enable Flask debug mode (Werkzeug reloader + interactive debugger; do not use on shared hosts)\u0027.","commit_id":"8b7938f71a7b660b6cc8f1b3d15da62591faed9d"}]}
