Skip to content

feat(integration-tests): Clean up logging and error reporting in integration tests.#1802

Merged
quinntaylormitchell merged 22 commits into
y-scope:mainfrom
quinntaylormitchell:integration-test-logging-overhaul
Mar 20, 2026
Merged

feat(integration-tests): Clean up logging and error reporting in integration tests.#1802
quinntaylormitchell merged 22 commits into
y-scope:mainfrom
quinntaylormitchell:integration-test-logging-overhaul

Conversation

@quinntaylormitchell
Copy link
Copy Markdown
Collaborator

@quinntaylormitchell quinntaylormitchell commented Dec 18, 2025

Description

This PR modifies integration test run output. It provides a minimal amount of information via the command line, and stores more verbose information in a test log directory (unique; one per test run). The output of each subprocess is stored within its own file in said directory.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

Ran uv run pytest -m 'package'; all tests pass and information is reported accurately.


Summary by CodeRabbit

  • Tests

    • Per-run test log files with millisecond timestamps, richer file output, and header showing log locations.
    • Colourised test name display and labeled test parameter ids for clearer reporting.
    • Tests now write command output to log files and use logging-backed validation for clearer failure context.
  • Chores

    • Refined test runner output options for concise tracebacks and suppressed console capture.
    • Standardised startup/teardown logging and error messages across test utilities.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 18, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The changes implement a comprehensive logging infrastructure for integration tests, including per-run timestamped log directories, structured subprocess output capture, colorized test reporting, and refactored subprocess handling utilities. Configuration updates enable detailed test execution logging while new utilities centralize subprocess execution and output logging.

Changes

Cohort / File(s) Summary
Pytest Configuration
integration-tests/.pytest.ini, integration-tests/tests/conftest.py
Updated pytest configuration to enable console and file logging with structured formats, added per-run log directory initialization (timestamped under test_logs/), and added pytest hooks for colorized test collection reporting and log directory display in session header.
Logging Utilities
integration-tests/tests/utils/logging_utils.py, integration-tests/tests/utils/subprocess_utils.py
New utility modules: logging_utils.py provides structured subprocess output logging to per-run timestamped files; subprocess_utils.py introduces run_and_log_subprocess() function that wraps subprocess execution with logging, timeout enforcement (120s), output capture, and pytest failure on non-zero exit codes.
Test Utilities Refactoring
integration-tests/tests/utils/asserting_utils.py, integration-tests/tests/utils/package_utils.py, integration-tests/tests/utils/config.py
Removed run_and_assert() function from asserting_utils.py and replaced all invocations across utilities with new run_and_log_subprocess(). Added logging statements to validation functions. Removed unused import from config.py.
Fixture and Test Updates
integration-tests/tests/fixtures/package_test_config.py, integration-tests/tests/test_identity_transformation.py, integration-tests/tests/package_tests/clp_json/test_clp_json.py, integration-tests/tests/package_tests/clp_text/test_clp_text.py
Added logging initialization to fixture; replaced subprocess calls with run_and_log_subprocess() in test files; updated test boundary logging to use structured info-level log messages instead of direct string output.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: improving logging and error reporting in integration tests through cleanup and reorganization.
Docstring Coverage ✅ Passed Docstring coverage is 96.30% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can approve the review once all CodeRabbit's comments are resolved.

Enable the reviews.request_changes_workflow setting to automatically approve the review once all CodeRabbit's comments are resolved.

@junhaoliao junhaoliao self-requested a review December 18, 2025 20:25
Copy link
Copy Markdown
Member

@junhaoliao junhaoliao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the understanding that this is a draft, i just took a quick look

Comment thread .gitignore Outdated
.task/
build/
dist/
__* No newline at end of file
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are we trying to exclude here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __pytest_logs directory that stores the log file for each test run.

That being said, right now the __pytest_logs directory gets created/stored under the clp/integration-tests directory. Do you think it might be better to have it under the build/integration-tests directory instead (used for other integration test stuff as well)? Then we wouldn't have to modify this file.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup i agree we can move to build/

in general we want to be more explicit about what to ignore, so if we keep the folder in the current location, it would be better to just write __pytest_logs. That said, it really depends on which part of the paths we want to be fuzzy about. in general wildcards like path/*/subpath and *.log are fine in my opinion

Comment thread integration-tests/.pytest.ini
Comment thread integration-tests/.pytest.ini Outdated
log_cli_date_format = %Y-%m-%d %H:%M:%S,%f
log_cli_format = %(name)s %(asctime)s [%(levelname)s] %(message)s
log_cli_level = INFO
log_cli_format = %(levelname)-8s %(asctime)-25s %(message)s
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to change this? i thought we were trying to match references like

logging_formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only reason was aesthetics, cause this is the format of the logs that will be reported to the user via the command line. If there is some reason we want to match references like you describe above, I can change it; I just wasn't aware we were doing that

Comment thread integration-tests/.pytest.ini Outdated

log_file_mode = a
log_file_level = DEBUG
log_file_format = %(levelname)-8s %(asctime)-30s [%(name)s:%(filename)s:%(lineno)d]: %(message)s
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to use a different format string between the cli and the log file?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to provide more information to the dev; I don't want to crowd the cli output, especially for a successful (no failure) test run. I think it's safe to assume that the main reason a dev would be looking at the test log would be if something fails, and in the event of failure, they'll need more information.

log_cli_format = %(levelname)-8s %(asctime)-25s %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S

log_file_mode = a
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of appending to the same file, can isolate the logs from different test runs instead? e.g., use set_log_path with the default log_file_mode = w. see the test code at pytest-dev/pytest#4752 for example

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this isn't here, the test output file will contain all of the pytest logging messages followed by all of the output from the various subprocesses called during the test run, like this:

INFO 2025-01-06 event1
INFO 2025-01-06 event2
INFO 2025-01-06 event3

<output following event 1>
<output following event 2>
<output following event 3>

It seems better to me to have everything written to the log file in order, so that it looks like this:

INFO 2025-01-06 event1
<output following event 1>
INFO 2025-01-06 event2
<output following event 2>
INFO 2025-01-06 event3
<output following event 3>

The logs from different test runs are already isolated by the code in conftest.py.


def run_and_assert(cmd: list[str], **kwargs: Any) -> subprocess.CompletedProcess[Any]:
def run_and_assert(
request: pytest.FixtureRequest, cmd: list[str], **kwargs: Any
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update below docstring for the param

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean the request? if so, right on, thank you



def stop_clp_package(package_config: PackageConfig) -> None:
def stop_clp_package(request: pytest.FixtureRequest, package_config: PackageConfig) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

param docstring for request

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on, thanks


try:
run_and_assert(request, stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
except SubprocessError:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in what case would this be thrown here? i thought we would have caught this in run_and_assert? like also if there are other SubprocessError than CalledProcessError that might be thrown but uncaught in run_and_assert, why don't we modify run_and_assert to handle such in the first place?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a good point. See my message that I sent you offline.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


try:
run_and_assert(request, start_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
except SubprocessError:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto -

in what case would this be thrown here? i thought we would have caught this in run_and_assert? like also if there are other SubprocessError than CalledProcessError that might be thrown but uncaught in run_and_assert, why don't we modify run_and_assert to handle such in the first place?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto my other comment -



def start_clp_package(package_config: PackageConfig) -> None:
def start_clp_package(request: pytest.FixtureRequest, package_config: PackageConfig) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

param docstring for request

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on, thanks

@quinntaylormitchell quinntaylormitchell requested review from junhaoliao and removed request for junhaoliao January 31, 2026 18:18
@quinntaylormitchell quinntaylormitchell changed the title feat(integration-tests): system-wide overhaul of logging and error reporting. feat(integration-tests): Clean up logging and error reporting in integration tests. Jan 31, 2026
@quinntaylormitchell quinntaylormitchell marked this pull request as ready for review January 31, 2026 18:21
@quinntaylormitchell quinntaylormitchell requested a review from a team as a code owner January 31, 2026 18:21
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@integration-tests/tests/conftest.py`:
- Around line 33-41: Change the parser.addini for "log_file_path" to use
type="string" (not "paths") and keep the default as the generated Path string;
update code that calls Path(config.getini("log_file_path")) to handle a string
return (no list) — look for uses of config.getini("log_file_path") in this file
and where the path is constructed. Also ensure the parent directory
"__pytest_logs" is created before any file write by adding a directory-creation
step (mkdir(parents=True, exist_ok=True)) at the point where the log file path
is first resolved/used (e.g., just after building log_file_path in this module
and before any write operations in asserting_utils.py).
- Around line 44-51: The current pytest_itemcollected hook overwrites
item._nodeid using item.name which loses module/class context; update the
assignment in pytest_itemcollected to use item.nodeid instead of item.name so
the prettified _nodeid preserves the full, globally unique identifier (i.e., set
item._nodeid = f"{BOLD}{BLUE}{item.nodeid}{RESET}" inside the
pytest_itemcollected function).

In `@integration-tests/tests/utils/logging_utils.py`:
- Line 5: Remove the unused GREEN constant from
integration-tests/tests/utils/logging_utils.py to eliminate dead code: locate
the symbol GREEN and delete its definition (the "\033[32m" assignment) or, if
it's intended for future use, add a clear explanatory comment above the GREEN
declaration indicating why it's kept; ensure no imports or references to GREEN
exist elsewhere before removal.

In `@integration-tests/tests/utils/package_utils.py`:
- Around line 38-44: The except block around run_and_assert should catch
OS-level launch failures and include the exception text: change the handler from
except SubprocessError: to except (SubprocessError, OSError) as e and include
str(e) in the error log and pytest.fail message so missing/non-executable
scripts are surfaced; update the logger.error call that uses
construct_log_err_msg(err_msg) to include the exception string (e.g.,
f"{err_msg} Error: {e}") and similarly append the exception text to the
pytest.fail call; relevant symbols: run_and_assert, SubprocessError,
package_test_config.mode_config.mode_name, construct_log_err_msg,
DEFAULT_CMD_TIMEOUT_SECONDS.

Comment thread integration-tests/tests/conftest.py Outdated
Comment thread integration-tests/tests/conftest.py
Comment thread integration-tests/tests/utils/logging_utils.py Outdated
Comment thread integration-tests/tests/utils/package_utils.py Outdated
Copy link
Copy Markdown
Member

@junhaoliao junhaoliao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no major issue found except the log file writing concurrency issue. i originally proposed https://github.com/y-scope/clp/pull/1802/files#r2660585892 to solve the issue, but now i agree with the rationale for keeping all logs in the same file. However, the concurrency problem is still valid, please see if this proposal works - "would it be better to use just Python loggers to log the output of the sub processes"

Comment thread .gitignore Outdated
.task/
build/
dist/
__* No newline at end of file
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup i agree we can move to build/

in general we want to be more explicit about what to ignore, so if we keep the folder in the current location, it would be better to just write __pytest_logs. That said, it really depends on which part of the paths we want to be fuzzy about. in general wildcards like path/*/subpath and *.log are fine in my opinion

Comment thread .gitignore Outdated
.task/
build/
dist/
__*
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(some response to a previous discussion somehow refuses to be shown in the "Files changed" tab)

duplicating the comment here for visibility -

yup i agree we can move to build/

in general we want to be more explicit about what to ignore, so if we keep the folder in the current location, it would be better to just write __pytest_logs. That said, it really depends on which part of the paths we want to be fuzzy about. in general wildcards like path/*/subpath and *.log are fine in my opinion

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to build/

Comment thread integration-tests/tests/conftest.py Outdated
"log_file_path",
help="Path to the log file for this test.",
type="paths",
default=log_file_path,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.pytest.org/en/stable/reference/reference.html#pytest.Parser.addini:~:text=paths%3A%20a%20list%20of%20pathlib%2EPath%2C%20separated%20as%20in%20a%20shell

seems like with type="paths", the parser returns a list of Path objects when we actually specify any value in the config file?

shall we use [log_file_path] or change to type="string" which might make more sense?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just store it as a string

Comment thread integration-tests/tests/conftest.py Outdated
pytest.fail(f"Command timed out: {' '.join(cmd)}: {e}")
return proc
log_file_path = Path(request.config.getini("log_file_path"))
with log_file_path.open("ab") as log_file:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the pytest logging plugin writes to the same file handle, right? any concurrency concerns?

I understand the outputs are usually flushed only on newlines, but this feels a bit fragile.

would it be better to use just Python loggers to log the output of the sub processes once any content is available?

something like opening a (combined) stream then iterating line-by-line should work?

proc = subprocess.Popen(
    cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs
)
for line in proc.stdout:
    logger.debug("%s", line.decode(errors="replace").rstrip())
proc.wait()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, it might be worth using a prefix in the logs otherwise we easily get mixed contents from different commands if we ever call this multiple times concurrently.

e.g.
logger.debug("[%s] %s", cmd, line.decode...)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really have any concurrency concerns here, because pytest only runs sequentially (the only way to run it in parallel is to use plugins that we don't use). With that in mind, it seems to me like there is no way that the logging file would be accessed by multiple processes at the same time, and by the same token, there's no way that this would get called multiple times concurrently

Comment thread integration-tests/tests/conftest.py

def start_clp_package(package_test_config: PackageTestConfig) -> None:
def start_clp_package(
request: pytest.FixtureRequest, package_test_config: PackageTestConfig
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing docstring

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on


def stop_clp_package(package_test_config: PackageTestConfig) -> None:
def stop_clp_package(
request: pytest.FixtureRequest, package_test_config: PackageTestConfig
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing docstring

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on

RESET = "\033[0m"


def construct_log_err_msg(err_msg: str) -> str:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing param / return dosctring

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank u

Comment thread integration-tests/tests/conftest.py Outdated

:param item:
"""
item._nodeid = f"{BOLD}{BLUE}{item.name}{RESET}" # noqa: SLF001
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a heads up -

The ANSI constants are terminal (print("...")) formatting concerns, not logging (logger.info("...")) concerns.

We usually don't want to mix them together, though here the complexity is fine to be tolerated as i'm fine keeping it if you think it helps visualization. Normally, if we want facier colorful outputs, we should look into creating a custom LoggerAdapter like explained by https://docs.python.org/3/howto/logging-cookbook.html

(anyways, don't bother until it becomes more complex like we don't only add colorful prefixes but also colorful postfixes, lol

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I'll keep that in mind

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
integration-tests/tests/utils/package_utils.py (2)

18-45: 🧹 Nitpick | 🔵 Trivial

Include exception details in error message for easier debugging.

The exception handler catches SubprocessError and OSError but discards the exception details. Including the exception text would help identify the root cause (e.g., missing script, permission denied, non-zero exit code) without requiring users to dig through log files.

Proposed fix
     try:
         run_and_log_to_file(request, start_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
-    except (SubprocessError, OSError):
+    except (SubprocessError, OSError) as err:
         mode_name = package_test_config.mode_config.mode_name
-        err_msg = f"The '{mode_name}' package failed to start."
+        err_msg = f"The '{mode_name}' package failed to start: {err}"
         logger.error(construct_log_err_msg(err_msg))
         pytest.fail(err_msg)

48-75: 🧹 Nitpick | 🔵 Trivial

Same suggestion: include exception details in stop error message.

Apply the same pattern to the stop function for consistency.

Proposed fix
     try:
         run_and_log_to_file(request, stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
-    except (SubprocessError, OSError):
+    except (SubprocessError, OSError) as err:
         mode_name = package_test_config.mode_config.mode_name
-        err_msg = f"The '{mode_name}' package failed to stop."
+        err_msg = f"The '{mode_name}' package failed to stop: {err}"
         logger.error(construct_log_err_msg(err_msg))
         pytest.fail(err_msg)

@junhaoliao junhaoliao modified the milestones: February 2026, March 2026 Mar 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
integration-tests/tests/package_tests/clp_text/test_clp_text.py (1)

19-19: ⚠️ Potential issue | 🔴 Critical

Restore the logging import or drop the module logger.

logger = logging.getLogger(__name__) runs at import time, so this module currently raises NameError before pytest can collect the tests.

Possible fix
+import logging
 import pytest
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/package_tests/clp_text/test_clp_text.py` at line 19,
The module-level statement logger = logging.getLogger(__name__) runs at import
time but logging is not imported, causing a NameError during pytest collection;
either add an import logging at the top of the module or remove/replace the
module-level logger usage (the line containing logger =
logging.getLogger(__name__)) with local logging calls or pass-through to
pytest's caplog. Update the file so that logging is defined before logger is
created and ensure any references to logger use the newly imported logging or
are removed.
integration-tests/tests/utils/package_utils.py (1)

12-39: ⚠️ Potential issue | 🔴 Critical

This startup helper introduces several undefined names.

pytest, run_and_log_to_file, SubprocessError, logger, and construct_log_err_msg are all referenced here without matching imports in this module. That leaves the new startup path broken before its error-reporting logic can run.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/utils/package_utils.py` around lines 12 - 39, The
start_clp_package function references several names that aren't imported; add
imports for pytest, SubprocessError, and the test logging/helpers used by
run_and_log_to_file, construct_log_err_msg, and logger so the function can run
and report errors: e.g. add "import pytest", "from subprocess import
SubprocessError", and import run_and_log_to_file, construct_log_err_msg, and
logger from whatever test utils/logging module provides them so
start_clp_package can call run_and_log_to_file and log failures correctly.
♻️ Duplicate comments (1)
integration-tests/tests/utils/package_utils.py (1)

42-62: ⚠️ Potential issue | 🔴 Critical

stop_clp_package is still on the old execution path.

request is unused here, stop-clp.sh output still bypasses the per-test log file, and this function still depends on run_and_assert. Migrate it to the same helper as startup so the new logging/reporting behaviour is applied consistently.

Possible fix
-    run_and_assert(stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
+    run_and_log_to_file(request, stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
Based on learnings, `run_and_assert` should only be used for CLP package functionality commands; helper commands should use the non-asserting subprocess helper instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@integration-tests/tests/utils/asserting_utils.py`:
- Around line 76-88: The failure paths call construct_log_err_msg but it is not
imported, causing a NameError; add an import for construct_log_err_msg at the
top of the module (alongside other imports) so the calls in the component
validation block (where err_msg is built and
logger.error(construct_log_err_msg(err_msg)) / pytest.fail(err_msg) are executed
correctly; ensure the import matches the module that defines
construct_log_err_msg and remove any duplicate or unused imports.
- Around line 21-34: The new helper run_and_log_to_file replaced run_and_assert
prematurely—restore a run_and_assert compatibility helper that preserves the
original behavior (exit-on-failure/assert semantics and return/exception
contract) so existing callers like verify_package_compression and functions in
tests.utils.package_utils continue to work; implement run_and_assert as either
the original implementation or a thin wrapper that calls run_and_log_to_file but
mimics the prior return/exception semantics and signature so no caller changes
are required until those tests are updated.

---

Outside diff comments:
In `@integration-tests/tests/package_tests/clp_text/test_clp_text.py`:
- Line 19: The module-level statement logger = logging.getLogger(__name__) runs
at import time but logging is not imported, causing a NameError during pytest
collection; either add an import logging at the top of the module or
remove/replace the module-level logger usage (the line containing logger =
logging.getLogger(__name__)) with local logging calls or pass-through to
pytest's caplog. Update the file so that logging is defined before logger is
created and ensure any references to logger use the newly imported logging or
are removed.

In `@integration-tests/tests/utils/package_utils.py`:
- Around line 12-39: The start_clp_package function references several names
that aren't imported; add imports for pytest, SubprocessError, and the test
logging/helpers used by run_and_log_to_file, construct_log_err_msg, and logger
so the function can run and report errors: e.g. add "import pytest", "from
subprocess import SubprocessError", and import run_and_log_to_file,
construct_log_err_msg, and logger from whatever test utils/logging module
provides them so start_clp_package can call run_and_log_to_file and log failures
correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 789bab7a-4922-41e9-a0b8-51e9ef0895da

📥 Commits

Reviewing files that changed from the base of the PR and between 8490035 and 0aec888.

📒 Files selected for processing (6)
  • integration-tests/.pytest.ini
  • integration-tests/tests/package_tests/clp_json/test_clp_json.py
  • integration-tests/tests/package_tests/clp_text/test_clp_text.py
  • integration-tests/tests/utils/asserting_utils.py
  • integration-tests/tests/utils/config.py
  • integration-tests/tests/utils/package_utils.py

Comment on lines +21 to +34
def run_and_log_to_file(request: pytest.FixtureRequest, cmd: list[str], **kwargs: Any) -> None:
"""
Runs a command with subprocess and asserts that it succeeds with pytest.
Runs a command with subprocess.

:param request: Pytest fixture request.
:param cmd: Command and arguments to execute.
:param kwargs: Additional keyword arguments passed through to the subprocess.
:return: The completed process object, for inspection or further handling.
:raise: pytest.fail if the command exits with a non-zero return code.
:raise: Propagates `subprocess.run`'s errors.
"""
logger.info("Running command: %s", shlex.join(cmd))

try:
proc = subprocess.run(cmd, check=True, **kwargs)
except subprocess.CalledProcessError as e:
pytest.fail(f"Command failed: {' '.join(cmd)}: {e}")
except subprocess.TimeoutExpired as e:
pytest.fail(f"Command timed out: {' '.join(cmd)}: {e}")
return proc
log_file_path = Path(request.config.getini("log_file_path"))
with log_file_path.open("ab") as log_file:
log_debug_msg = f"Now running command: {cmd}"
logger.debug(log_debug_msg)
subprocess.run(cmd, stdout=log_file, stderr=log_file, check=True, **kwargs)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Don't drop run_and_assert until the callers move.

This replacement is incomplete: verify_package_compression in this module and tests.utils.package_utils still reference run_and_assert. In the current state, those compression paths break before the new log-file flow can help.

Based on learnings, run_and_assert is still the expected helper for CLP package functionality commands such as compression and decompression.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/utils/asserting_utils.py` around lines 21 - 34, The
new helper run_and_log_to_file replaced run_and_assert prematurely—restore a
run_and_assert compatibility helper that preserves the original behavior
(exit-on-failure/assert semantics and return/exception contract) so existing
callers like verify_package_compression and functions in
tests.utils.package_utils continue to work; implement run_and_assert as either
the original implementation or a thin wrapper that calls run_and_log_to_file but
mimics the prior return/exception semantics and signature so no caller changes
are required until those tests are updated.

Comment thread integration-tests/tests/utils/asserting_utils.py Outdated
@quinntaylormitchell quinntaylormitchell removed the request for review from junhaoliao March 18, 2026 16:53
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
integration-tests/tests/fixtures/package_test_config.py (1)

31-45: 🧹 Nitpick | 🔵 Trivial

Consider lazy-evaluation formatting for log statements.

Similar to other files, the log statements use f-strings. Per project learnings, consider using printf-style formatting.

Suggested fix
-    log_msg = f"Assigning ports to the '{mode_config.mode_name}' package."
-    logger.info(log_msg)
+    logger.info("Assigning ports to the '%s' package.", mode_config.mode_name)
     ...
-    log_msg = (
-        f"Constructing the PackageTestConfig object for the '{mode_config.mode_name}' package."
-    )
-    logger.info(log_msg)
+    logger.info(
+        "Constructing the PackageTestConfig object for the '%s' package.",
+        mode_config.mode_name,
+    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/fixtures/package_test_config.py` around lines 31 -
45, The logger.info calls in this block use f-strings (e.g., building log_msg
with f"Assigning ports to the '{mode_config.mode_name}' package." and the
subsequent construction message); change these to deferred/printf-style logging
by passing the format string and arguments to logger.info (e.g., "Assigning
ports to the '%s' package.", mode_config.mode_name) and similarly for the
PackageTestConfig message, removing the intermediate f-string construction so
the logger performs formatting lazily.
integration-tests/tests/test_identity_transformation.py (1)

142-152: 🧹 Nitpick | 🔵 Trivial

Missing docstring for helper function.

Per a past review comment, _clp_s_compress_and_decompress should have a docstring. The reviewer mentioned this would be addressed once the overarching approach was approved.

Suggested docstring
 def _clp_s_compress_and_decompress(
     clp_core_path_config: ClpCorePathConfig,
     test_paths: CompressionTestPathConfig,
 ) -> None:
+    """
+    Compresses and decompresses using clp-s binary.
+
+    :param clp_core_path_config: Configuration containing paths to CLP core binaries.
+    :param test_paths: Configuration containing test input/output paths.
+    """
     test_paths.clear_test_outputs()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/test_identity_transformation.py` around lines 142 -
152, The helper function _clp_s_compress_and_decompress is missing a docstring;
add a concise triple-quoted docstring immediately after the def describing the
function's purpose (compress source logs and then decompress them for tests),
its parameters (clp_core_path_config: ClpCorePathConfig, test_paths:
CompressionTestPathConfig), the side effects (clears test outputs, invokes
run_and_log_subprocess to run the clp_s binary), and that it returns None; keep
it brief and follow the module's docstring style for internal helper functions.
integration-tests/tests/utils/asserting_utils.py (1)

102-112: 🧹 Nitpick | 🔵 Trivial

Add :raise: documentation for run_and_log_subprocess errors.

The docstring doesn't document that this function can propagate errors from run_and_log_subprocess. For consistency with other functions in this codebase (e.g., _validate_running_mode_correct documents its raises), consider adding this.

📝 Proposed fix
 def verify_package_compression(
     path_to_original_dataset: Path,
     package_test_config: PackageTestConfig,
 ) -> None:
     """
     Verify that compression has been executed correctly by decompressing the contents of
     `clp-package/var/data/archives` and comparing the decompressed logs to the originals stored at
     `path_to_original_dataset`.
 
     :param path_to_original_dataset:
     :param package_test_config:
+    :raise: Propagates `run_and_log_subprocess`'s errors.
     """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/utils/asserting_utils.py` around lines 102 - 112, The
docstring for this verification function is missing a :raise: entry documenting
that it can propagate exceptions from run_and_log_subprocess; update the
function's docstring (the one containing the current verification text and
logger.info call) to include a :raise: clause stating that errors raised by
run_and_log_subprocess (e.g., subprocess.CalledProcessError or other
subprocess-related exceptions) may be propagated, mirroring the style used in
_validate_running_mode_correct.
integration-tests/tests/utils/package_utils.py (1)

56-61: 🧹 Nitpick | 🔵 Trivial

Add :raise: documentation for consistency.

Unlike start_clp_package and stop_clp_package, this function's docstring doesn't document that it propagates errors from run_and_log_subprocess.

📝 Proposed fix
 def run_package_compression_script(
     compression_job: PackageCompressionJob,
     package_test_config: PackageTestConfig,
 ) -> None:
     """
     Constructs and runs a compression command on the CLP package.
 
     :param compression_job:
     :param package_test_config:
+    :raise: Propagates `run_and_log_subprocess`'s errors.
     """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/utils/package_utils.py` around lines 56 - 61, The
docstring for the function that "Constructs and runs a compression command on
the CLP package" should include a :raises: section, matching the style used in
start_clp_package and stop_clp_package; document that this function propagates
exceptions raised by run_and_log_subprocess (e.g., subprocess.CalledProcessError
or generic Exception as appropriate), add a brief :raises: line to the
function's docstring stating which exception(s) are raised and under what
condition, and ensure the wording matches existing docstring conventions used
for start_clp_package/stop_clp_package.
♻️ Duplicate comments (1)
integration-tests/tests/utils/logging_utils.py (1)

17-17: 🧹 Nitpick | 🔵 Trivial

Incomplete docstring.

The docstring is a placeholder. Please add proper documentation including parameter descriptions and return value (or lack thereof).

Suggested docstring
-    """Docstring."""
+    """
+    Logs subprocess output to a timestamped file in the test log directory.
+
+    :param subprocess: The completed subprocess result containing stdout and stderr.
+    :param cmd: The command list that was executed.
+    """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/tests/utils/logging_utils.py` at line 17, Replace the
placeholder triple-quoted docstring in the logging_utils module with a proper
module-level docstring that describes the module purpose and behavior, and add,
for each public function in this file (e.g., configure_logger, get_test_logger,
or any other exposed helpers), a short docstring explaining parameters (name and
type) and return value (or None) and any raised exceptions; ensure the docstring
follows the project's docstring style and documents side effects and usage
examples if applicable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@integration-tests/tests/conftest.py`:
- Around line 76-83: The current pytest_itemcollected hook overwrites pytest's
internal item._nodeid with only item.name, losing path/context; change the
assignment to use item.nodeid (e.g., set item._nodeid to the formatted
item.nodeid) so the full test identity is preserved while keeping the same
BOLD/BLUE/RESET formatting; keep using item.name elsewhere for display-only
purposes but do not mutate _nodeid with item.name.

In `@integration-tests/tests/utils/logging_utils.py`:
- Around line 13-16: The parameter name subprocess in the function
log_subprocess_output_to_file shadows the imported subprocess module; rename
that parameter (e.g., to completed_process or proc) and update all references
inside the function accordingly so the module name remains available for future
use; ensure the function signature def
log_subprocess_output_to_file(completed_process:
subprocess.CompletedProcess[str], cmd: list[str]) -> None and any internal
uses/annotations are updated to the new name (and update any callers in this
file that pass/expect the old name).

In `@integration-tests/tests/utils/package_utils.py`:
- Line 9: DEFAULT_CMD_TIMEOUT_SECONDS in package_utils.py is dead code; either
remove the constant or make package_utils.py use it when invoking
run_and_log_subprocess (so the value isn't duplicated). Locate
DEFAULT_CMD_TIMEOUT_SECONDS and either delete its declaration or import/forward
it into calls to run_and_log_subprocess (or wire it into any helper that calls
subprocess_utils.run_and_log_subprocess) so the constant is referenced rather
than unused.

In `@integration-tests/tests/utils/subprocess_utils.py`:
- Around line 15-16: The docstring for run_and_log_subprocess is a placeholder;
replace it with a full docstring describing the function's purpose, parameters,
return value, and behavior (including that cmd is a list[str], it executes a
subprocess, logs stdout/stderr, and returns subprocess.CompletedProcess[str])
and mention any exceptions or side effects (e.g., logging, timeout behavior) so
callers understand usage; update the docstring directly above the
run_and_log_subprocess definition.
- Line 1: Replace the placeholder module docstring at the top of
subprocess_utils.py with a clear descriptive docstring that summarizes the
module's purpose (what utilities it provides for spawning/managing subprocesses
in tests), describes key behaviors or responsibilities, and notes any important
usage or side-effects; update the module-level string (the first triple-quoted
string) to include this information so readers and test authors immediately
understand the intent of subprocess_utils.py.

---

Outside diff comments:
In `@integration-tests/tests/fixtures/package_test_config.py`:
- Around line 31-45: The logger.info calls in this block use f-strings (e.g.,
building log_msg with f"Assigning ports to the '{mode_config.mode_name}'
package." and the subsequent construction message); change these to
deferred/printf-style logging by passing the format string and arguments to
logger.info (e.g., "Assigning ports to the '%s' package.",
mode_config.mode_name) and similarly for the PackageTestConfig message, removing
the intermediate f-string construction so the logger performs formatting lazily.

In `@integration-tests/tests/test_identity_transformation.py`:
- Around line 142-152: The helper function _clp_s_compress_and_decompress is
missing a docstring; add a concise triple-quoted docstring immediately after the
def describing the function's purpose (compress source logs and then decompress
them for tests), its parameters (clp_core_path_config: ClpCorePathConfig,
test_paths: CompressionTestPathConfig), the side effects (clears test outputs,
invokes run_and_log_subprocess to run the clp_s binary), and that it returns
None; keep it brief and follow the module's docstring style for internal helper
functions.

In `@integration-tests/tests/utils/asserting_utils.py`:
- Around line 102-112: The docstring for this verification function is missing a
:raise: entry documenting that it can propagate exceptions from
run_and_log_subprocess; update the function's docstring (the one containing the
current verification text and logger.info call) to include a :raise: clause
stating that errors raised by run_and_log_subprocess (e.g.,
subprocess.CalledProcessError or other subprocess-related exceptions) may be
propagated, mirroring the style used in _validate_running_mode_correct.

In `@integration-tests/tests/utils/package_utils.py`:
- Around line 56-61: The docstring for the function that "Constructs and runs a
compression command on the CLP package" should include a :raises: section,
matching the style used in start_clp_package and stop_clp_package; document that
this function propagates exceptions raised by run_and_log_subprocess (e.g.,
subprocess.CalledProcessError or generic Exception as appropriate), add a brief
:raises: line to the function's docstring stating which exception(s) are raised
and under what condition, and ensure the wording matches existing docstring
conventions used for start_clp_package/stop_clp_package.

---

Duplicate comments:
In `@integration-tests/tests/utils/logging_utils.py`:
- Line 17: Replace the placeholder triple-quoted docstring in the logging_utils
module with a proper module-level docstring that describes the module purpose
and behavior, and add, for each public function in this file (e.g.,
configure_logger, get_test_logger, or any other exposed helpers), a short
docstring explaining parameters (name and type) and return value (or None) and
any raised exceptions; ensure the docstring follows the project's docstring
style and documents side effects and usage examples if applicable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9835f2f1-b48d-419f-9f57-456f276a3dc8

📥 Commits

Reviewing files that changed from the base of the PR and between 0aec888 and 12dc297.

📒 Files selected for processing (11)
  • integration-tests/.pytest.ini
  • integration-tests/tests/conftest.py
  • integration-tests/tests/fixtures/package_test_config.py
  • integration-tests/tests/package_tests/clp_json/test_clp_json.py
  • integration-tests/tests/package_tests/clp_text/test_clp_text.py
  • integration-tests/tests/test_identity_transformation.py
  • integration-tests/tests/utils/asserting_utils.py
  • integration-tests/tests/utils/config.py
  • integration-tests/tests/utils/logging_utils.py
  • integration-tests/tests/utils/package_utils.py
  • integration-tests/tests/utils/subprocess_utils.py
💤 Files with no reviewable changes (1)
  • integration-tests/tests/utils/config.py

Comment thread integration-tests/tests/conftest.py Outdated
Comment thread integration-tests/tests/utils/logging_utils.py
Comment thread integration-tests/tests/utils/package_utils.py Outdated
Comment thread integration-tests/tests/utils/subprocess_utils.py Outdated
Comment thread integration-tests/tests/utils/subprocess_utils.py Outdated
Copy link
Copy Markdown
Member

@junhaoliao junhaoliao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the rest lgtm

Comment thread integration-tests/tests/conftest.py Outdated
Comment thread integration-tests/tests/conftest.py Outdated
from tests.utils.subprocess_utils import run_and_log_subprocess


def start_clp_package(package_test_config: PackageTestConfig) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note in package_instnace.py we still catch RuntimeError which is outdated?

if we don't catch OSError and TimeoutExpired in run_and_log_subprocess() and rethrow with RuntimeError, i believe we want to catch those two in package_instnace.py

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, we don't need to catch RuntimeError in package_instance.py anymore. I've removed that check.

For our purposes here, I feel that we don't need to catch OSError or TimeoutExpired, because either of those errors would indicate conditions or circumstances that are beyond the scope of the testing system. We can just let them be handled automatically; for example, I just forced a timeout in a testing subprocess by setting timeout=0.1, and the timeout was reported to the user in the ERROR summary at the end of the test.

What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure sgtm

@quinntaylormitchell quinntaylormitchell merged commit 801ec26 into y-scope:main Mar 20, 2026
21 checks passed
junhaoliao added a commit to junhaoliao/clp that referenced this pull request May 17, 2026
…gration tests. (y-scope#1802)

Co-authored-by: Jack Luo <jack.luo@yscope.com>
Co-authored-by: Junhao Liao <junhao.liao@yscope.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants