import io import json import os import tempfile import unittest from contextlib import redirect_stdout from pathlib import Path from unittest.mock import patch from production_analytics.cli.__main__ import main from production_analytics.enlyze.exploration import ExplorationResponse class CliTests(unittest.TestCase): @patch.dict(os.environ, {"ENLYZE_BASE_URL": "https://enlyze.example"}, clear=True) @patch("production_analytics.cli.__main__.ExplorationClient.get") def test_raw_prints_and_saves_sanitized_response(self, get_mock: object) -> None: get_mock.return_value = ExplorationResponse( # type: ignore[attr-defined] status_code=200, path="/verified/path", headers={"Content-Type": "application/json"}, body={"api_token": "secret", "authorization": "Bearer secret", "value": 4.2}, ) with tempfile.TemporaryDirectory() as temporary_directory: output = io.StringIO() with redirect_stdout(output): result = main( [ "enlyze", "raw", "/verified/path", "--save-fixture", "example.json", "--fixture-dir", temporary_directory, "--save-raw", "raw-example.json", "--raw-dir", temporary_directory, ] ) fixture = json.loads((Path(temporary_directory) / "example.json").read_text()) raw_capture = json.loads((Path(temporary_directory) / "raw-example.json").read_text()) self.assertEqual(result, 0) self.assertEqual(fixture["response"]["body"]["api_token"], "") self.assertEqual(fixture["response"]["body"]["authorization"], "") self.assertNotIn("Bearer secret", output.getvalue()) self.assertEqual(raw_capture["response"]["body"]["api_token"], "secret") def test_raw_rejects_unsafe_fixture_name(self) -> None: with patch.dict(os.environ, {"ENLYZE_BASE_URL": "https://enlyze.example"}, clear=True): with patch("production_analytics.cli.__main__.ExplorationClient.get") as get_mock: get_mock.return_value = ExplorationResponse(200, "/path", {}, {}) output = io.StringIO() with redirect_stdout(output): result = main(["enlyze", "raw", "/path", "--save-fixture", "../unsafe.json"]) self.assertEqual(result, 1) self.assertIn("plain filename", output.getvalue()) @patch.dict(os.environ, {"ENLYZE_BASE_URL": "https://enlyze.example"}, clear=True) @patch("production_analytics.cli.__main__.ExplorationClient.post_json") def test_timeseries_builds_schema_supported_read_only_post(self, post_mock: object) -> None: post_mock.return_value = ExplorationResponse( # type: ignore[attr-defined] 200, "/v2/timeseries", {}, {"metadata": {"next_cursor": None}, "data": {"columns": [], "records": []}} ) with tempfile.TemporaryDirectory() as temporary_directory: result = main( [ "enlyze", "timeseries", "--machine", "machine-id", "--start", "2026-01-01T00:00:00+00:00", "--end", "2026-01-01T00:10:00+00:00", "--variable", "variable-id", "--resampling-interval", "10", "--resampling-method", "avg", "--save-fixture", "timeseries.json", "--fixture-dir", temporary_directory, ] ) fixture = json.loads((Path(temporary_directory) / "timeseries.json").read_text()) self.assertEqual(result, 0) self.assertEqual(post_mock.call_args.args[0], "/v2/timeseries") # type: ignore[attr-defined] self.assertEqual(post_mock.call_args.args[1]["resampling_interval"], 10) # type: ignore[attr-defined] self.assertEqual(fixture["request"]["method"], "POST")