Gerrrr commented on code in PR #106: URL: https://github.com/apache/otava/pull/106#discussion_r2605229495
########## tests/graphite_e2e_test.py: ########## @@ -0,0 +1,231 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import os +import shutil +import socket +import subprocess +import time +from contextlib import contextmanager +from pathlib import Path + +import pytest + + +def test_analyze_graphite(): + """ + End-to-end test for the Graphite example from docs/GRAPHITE.md. + + Starts Graphite docker container, writes sample data, then runs otava analyze, + and verifies the output contains expected change points. + """ + with graphite_container() as graphite_port: + # Run the Otava analysis + proc = subprocess.run( + ["uv", "run", "otava", "analyze", "my-product.test", "--since=-10m"], + capture_output=True, + text=True, + timeout=600, + env=dict( + os.environ, + OTAVA_CONFIG=str(Path("examples/graphite/config/otava.yaml")), + GRAPHITE_ADDRESS=f"http://localhost:{graphite_port}/", + GRAFANA_ADDRESS="http://localhost:3000/", + GRAFANA_USER="admin", + GRAFANA_PASSWORD="admin", + ), + ) + + if proc.returncode != 0: + pytest.fail( + "Command returned non-zero exit code.\n\n" + f"Command: {proc.args!r}\n" + f"Exit code: {proc.returncode}\n\n" + f"Stdout:\n{proc.stdout}\n\n" + f"Stderr:\n{proc.stderr}\n" + ) + + # Verify output contains expected columns and change point indicators + output = _remove_trailing_whitespaces(proc.stdout) + + # Check that the header contains expected column names + assert "throughput" in output + assert "response_time" in output + assert "cpu_usage" in output + + # Data shows throughput dropped from ~61k to ~57k (-5.6%) and cpu increased from 0.2 to 0.8 (+300%) + assert "-5.6%" in output # throughput change + assert "+300.0%" in output # cpu_usage change + + +@contextmanager +def graphite_container(): + """ + Context manager for running a Graphite container with seeded data. + Yields the Graphite HTTP port and ensures cleanup on exit. + """ + if not shutil.which("docker"): + pytest.fail("docker is not available on PATH") + + container_id = None + try: + # Start graphite container + cmd = [ + "docker", + "run", + "-d", + "--publish", + "80", + "--publish", + "2003", + "graphiteapp/graphite-statsd", + ] + proc = subprocess.run(cmd, capture_output=True, text=True, timeout=60) + if proc.returncode != 0: + pytest.fail( + "Docker command returned non-zero exit code.\n\n" + f"Command: {cmd!r}\n" + f"Exit code: {proc.returncode}\n\n" + f"Stdout:\n{proc.stdout}\n\n" + f"Stderr:\n{proc.stderr}\n" + ) + container_id = proc.stdout.strip() + + # Determine the randomly assigned host port for 80/tcp (HTTP) + inspect_cmd = [ + "docker", + "inspect", + "-f", + '{{ (index (index .NetworkSettings.Ports "80/tcp") 0).HostPort }}', + container_id, + ] + inspect_proc = subprocess.run(inspect_cmd, capture_output=True, text=True, timeout=60) + if inspect_proc.returncode != 0: + pytest.fail( + "Docker inspect returned non-zero exit code.\n\n" + f"Command: {inspect_cmd!r}\n" + f"Exit code: {inspect_proc.returncode}\n\n" + f"Stdout:\n{inspect_proc.stdout}\n\n" + f"Stderr:\n{inspect_proc.stderr}\n" + ) + http_port = inspect_proc.stdout.strip() + + # Determine the randomly assigned host port for 2003/tcp (Carbon) + inspect_cmd = [ + "docker", + "inspect", + "-f", + '{{ (index (index .NetworkSettings.Ports "2003/tcp") 0).HostPort }}', + container_id, + ] + inspect_proc = subprocess.run(inspect_cmd, capture_output=True, text=True, timeout=60) + if inspect_proc.returncode != 0: + pytest.fail( + "Docker inspect returned non-zero exit code.\n\n" + f"Command: {inspect_cmd!r}\n" + f"Exit code: {inspect_proc.returncode}\n\n" + f"Stdout:\n{inspect_proc.stdout}\n\n" + f"Stderr:\n{inspect_proc.stderr}\n" + ) + carbon_port = int(inspect_proc.stdout.strip()) + + # Wait until Graphite HTTP responds + deadline = time.time() + 60 + ready = False + while time.time() < deadline: + try: + with socket.create_connection(("localhost", int(http_port)), timeout=1): + ready = True + break + except OSError: + time.sleep(1) + + if not ready: + pytest.fail("Graphite HTTP port did not become ready within timeout.") + + # Wait a bit more for Graphite to fully initialize + time.sleep(5) + + # Seed data into Graphite using the same pattern as datagen.sh + _seed_graphite_data(carbon_port) + + # Wait for data to be written and available + time.sleep(5) + + yield http_port Review Comment: How about now? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
