Skip to content

🧪 Add KeyError test for _resolve_field - #3

Merged
tblakex01 merged 2 commits into
mainfrom
fix-engine-test-keyerror-13155194893270359330
Jun 5, 2026
Merged

tblakex01 merged 2 commits into
mainfrom
fix-engine-test-keyerror-13155194893270359330

Conversation

@tblakex01

@tblakex01 tblakex01 commented Jun 5, 2026 •

Copy link
Copy Markdown
Owner

This PR improves the test coverage of the core policy engine by adding unit tests for the internal _resolve_field utility.

🎯 What: The testing gap addressed was the lack of direct unit tests for _resolve_field, specifically its error handling when traversing nested dictionaries.

📊 Coverage:

  • Verified successful resolution of nested dot-paths.
  • Verified KeyError is raised with the correct message when a path segment is missing.
  • Verified KeyError is raised when trying to traverse into a non-dictionary value (e.g., trying to access a.b when a is an integer).

✨ Result: Improved reliability and confidence in the field resolution logic used throughout the policy evaluation engine.


PR created automatically by Jules for task 13155194893270359330 started by @tblakex01

Summary by Sourcery

Add direct tests for the internal field resolution helper and tidy YAML fixture construction in existing tests.

Tests:

  • Add unit tests covering successful resolution and error cases for the internal _resolve_field utility.
  • Standardize test policy YAML creation using more concise textwrap.dedent calls across engine, loader, and decorator tests.

Summary by CodeRabbit

  • Tests
    • Expanded test coverage for field resolution functionality to validate successful path resolution and error handling scenarios.

Add direct unit tests for `_resolve_field` in `policyforge/engine.py`,
covering the success path and both `KeyError` exception branches.

This addresses the testing gap where missing nested fields in a
dictionary context were not explicitly tested at the unit level.

- Adds `test_resolve_field_success`
- Adds `test_resolve_field_missing_key` (covers segment not in current)
- Adds `test_resolve_field_non_dict_traversal` (covers traversal into non-dict)

Co-authored-by: tblakex01 <17657984+tblakex01@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sourcery-ai

sourcery-ai Bot commented Jun 5, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Adds focused unit tests for the internal _resolve_field helper and cleans up existing YAML test fixture construction for brevity and consistency across engine, loader, and decorator tests.

File-Level Changes

Change Details Files
Add direct unit tests for _resolve_field success and KeyError behaviors.
  • Import _resolve_field into the engine test module alongside PolicyEngine and _hash_args.
  • Add TestResolveField test class covering successful nested path resolution.
  • Add tests asserting KeyError messages when a path segment is missing and when traversing into a non-dict value.
tests/test_engine.py
Refactor YAML-writing test setup to use a more concise textwrap.dedent(pattern) style.
  • Collapse multi-line write_text + dedent calls into single-line write_text(textwrap.dedent("""...""")) invocations in engine tests.
  • Apply the same concise write_text pattern to loader tests for malformed and multi-doc YAML inputs.
  • Apply the concise write_text pattern to decorator tests for policy fixtures.
tests/test_engine.py
tests/test_loader.py
tests/test_decorators.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 5, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

tests/test_engine.py is updated to import and test the _resolve_field utility function. A new TestResolveField test class validates successful dotted-path resolution and two error cases: missing path segments and traversing into non-dict values.

Changes

Field Resolution Test Coverage

Layer / File(s) Summary
Test coverage for _resolve_field function
tests/test_engine.py
Import _resolve_field alongside existing imports and add TestResolveField with three test methods validating successful dotted-path lookups, KeyError for missing keys, and KeyError for non-dict traversal, each with specific error message assertions.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐇 A field resolver's truth now tested clear,
Dotted paths and errors without fear,
Keys that hide, non-dicts that block,
All validated with precise test-lock. 🔑

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title mentions 'KeyError test for _resolve_field' which is directly supported by the changeset that adds test cases for this function, though it includes an emoji which adds noise to an otherwise specific title.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-engine-test-keyerror-13155194893270359330

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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The new _resolve_field tests assert on the exact KeyError message, which makes them brittle to minor wording changes; consider matching on a shorter, more stable substring or using a custom exception type if the message format is expected to change.
  • There is extensive repetition of write_text(textwrap.dedent("""...""")) blocks across tests; introducing a small helper to construct policy files from templates could reduce duplication and make the tests easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `_resolve_field` tests assert on the exact `KeyError` message, which makes them brittle to minor wording changes; consider matching on a shorter, more stable substring or using a custom exception type if the message format is expected to change.
- There is extensive repetition of `write_text(textwrap.dedent("""..."""))` blocks across tests; introducing a small helper to construct policy files from templates could reduce duplication and make the tests easier to maintain.

## Individual Comments

### Comment 1
<location path="tests/test_engine.py" line_range="773-776" />
<code_context>
+        context = {"args": {"url": "https://example.com"}}
+        assert _resolve_field(context, "args.url") == "https://example.com"
+
+    def test_resolve_field_missing_key(self):
+        context = {"a": {}}
+        with pytest.raises(KeyError, match="Field 'a.b' not found at segment 'b'"):
+            _resolve_field(context, "a.b")
+
+    def test_resolve_field_non_dict_traversal(self):
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test for a missing top-level key to complement the missing nested key case

Consider adding a case where the root segment is missing (e.g., `context = {}` with path `"a.b"` or `context = {"x": 1}` with path `"a"`) to verify the error and message when resolution fails at the top level.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/test_engine.py
Comment on lines +773 to +776
def test_resolve_field_missing_key(self):
context = {"a": {}}
with pytest.raises(KeyError, match="Field 'a.b' not found at segment 'b'"):
_resolve_field(context, "a.b")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Add a test for a missing top-level key to complement the missing nested key case

Consider adding a case where the root segment is missing (e.g., context = {} with path "a.b" or context = {"x": 1} with path "a") to verify the error and message when resolution fails at the top level.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (3)
tests/test_decorators.py (1)

1-265: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Run Black to fix formatting before merge.

The CI pipeline reports that Black would reformat this file. This is a critical blocker that must be resolved.

Run the following command to apply Black formatting:

#!/bin/bash
# Apply Black formatting to the test file
black tests/test_decorators.py
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_decorators.py` around lines 1 - 265, The file
tests/test_decorators.py is failing CI due to Black formatting; fix by running
Black on that file (or repository) to apply the canonical formatter, re-stage
the modified file, and push a new commit so CI can pass; targets to check
include the decorator/tests referencing policy_gate, PolicyGateWrapper, and
_bind_positional_args to ensure no behavioral edits were made when formatting.
tests/test_loader.py (1)

1-448: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Run Black to fix formatting before merge.

The CI pipeline reports that Black would reformat this file. This is a critical blocker that must be resolved.

Run the following command to apply Black formatting:

#!/bin/bash
# Apply Black formatting to the test file
black tests/test_loader.py
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_loader.py` around lines 1 - 448, This test module (contains
classes like TestLoadFile, TestValidationEdgeCases, TestLoadTrustConfig and
tests such as test_loads_single_policy and test_parses_full_block) needs to be
reformatted with Black to satisfy CI; run Black on the test file (or reformat
the module) so the file meets the project's Black configuration before merging.
tests/test_engine.py (1)

1-784: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix the Black formatting failure before merge.

CI is currently failing on black --check for this file. Please run Black and commit the formatting-only changes so the lint job passes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_engine.py` around lines 1 - 784, The file tests/test_engine.py is
failing CI due to Black formatting; run the Black formatter (e.g., black
tests/test_engine.py or black .) and commit the resulting formatting-only
changes so the lint job passes. Locate this file and reformat the tests
(including classes like TestBasicEvaluation, TestResolveField and functions
referencing _resolve_field and _hash_args) then add a commit with only
formatting changes.
🧹 Nitpick comments (1)
tests/test_engine.py (1)

1-1: 🏗️ Heavy lift

Align this test file with the test-path mirroring guideline.

This module tests policyforge/engine.py, so placing it under a mirrored path (for example, tests/policyforge/test_engine.py) would satisfy the repository rule and keep test discovery structure consistent.

As per coding guidelines, tests/**/*.py: Test directory structure must mirror the package structure with corresponding test files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_engine.py` at line 1, The test file currently located as
tests/test_engine.py should be moved to mirror the package path: create
tests/policyforge/test_engine.py (matching the tested module
policyforge/engine.py) and remove the top-level tests/test_engine.py; after
moving, ensure any imports in the test refer to policyforge.engine (e.g., from
policyforge import engine or from policyforge.engine import <symbols>) and
update any relative import paths or test discovery configuration accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/test_decorators.py`:
- Around line 1-265: The file tests/test_decorators.py is failing CI due to
Black formatting; fix by running Black on that file (or repository) to apply the
canonical formatter, re-stage the modified file, and push a new commit so CI can
pass; targets to check include the decorator/tests referencing policy_gate,
PolicyGateWrapper, and _bind_positional_args to ensure no behavioral edits were
made when formatting.

In `@tests/test_engine.py`:
- Around line 1-784: The file tests/test_engine.py is failing CI due to Black
formatting; run the Black formatter (e.g., black tests/test_engine.py or black
.) and commit the resulting formatting-only changes so the lint job passes.
Locate this file and reformat the tests (including classes like
TestBasicEvaluation, TestResolveField and functions referencing _resolve_field
and _hash_args) then add a commit with only formatting changes.

In `@tests/test_loader.py`:
- Around line 1-448: This test module (contains classes like TestLoadFile,
TestValidationEdgeCases, TestLoadTrustConfig and tests such as
test_loads_single_policy and test_parses_full_block) needs to be reformatted
with Black to satisfy CI; run Black on the test file (or reformat the module) so
the file meets the project's Black configuration before merging.

---

Nitpick comments:
In `@tests/test_engine.py`:
- Line 1: The test file currently located as tests/test_engine.py should be
moved to mirror the package path: create tests/policyforge/test_engine.py
(matching the tested module policyforge/engine.py) and remove the top-level
tests/test_engine.py; after moving, ensure any imports in the test refer to
policyforge.engine (e.g., from policyforge import engine or from
policyforge.engine import <symbols>) and update any relative import paths or
test discovery configuration accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 11eda8ca-1fde-4c5d-add3-9502e99110ef

📥 Commits

Reviewing files that changed from the base of the PR and between b1b35ca and 811738e.

📒 Files selected for processing (3)
  • tests/test_decorators.py
  • tests/test_engine.py
  • tests/test_loader.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/test_engine.py (1)

915-922: ⚡ Quick win

Escape literal KeyError messages in match= assertions.

pytest.raises(..., match=...) uses regex. The . in a.b is currently wildcard, so these tests can pass on non-exact messages.

Proposed tightening
+import re
...
-        with pytest.raises(KeyError, match="Field 'a.b' not found at segment 'b'"):
+        with pytest.raises(
+            KeyError, match=re.escape("Field 'a.b' not found at segment 'b'")
+        ):
             _resolve_field(context, "a.b")
...
-        with pytest.raises(
-            KeyError, match="Cannot traverse into non-dict at segment 'b' in path 'a.b'"
-        ):
+        with pytest.raises(
+            KeyError,
+            match=re.escape("Cannot traverse into non-dict at segment 'b' in path 'a.b'"),
+        ):
             _resolve_field(context, "a.b")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_engine.py` around lines 915 - 922, The tests calling pytest.raises
in test_resolve_field and test_resolve_field_non_dict_traversal use regex match
strings containing literal dots (e.g. "a.b") which act as wildcards; update the
match patterns for the _resolve_field KeyError assertions to escape regex
metacharacters (e.g. use "a\\.b" or apply re.escape to the expected message) so
the assertions match the exact literal message "a.b" and the segment names like
"b".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/test_engine.py`:
- Around line 915-922: The tests calling pytest.raises in test_resolve_field and
test_resolve_field_non_dict_traversal use regex match strings containing literal
dots (e.g. "a.b") which act as wildcards; update the match patterns for the
_resolve_field KeyError assertions to escape regex metacharacters (e.g. use
"a\\.b" or apply re.escape to the expected message) so the assertions match the
exact literal message "a.b" and the segment names like "b".

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d837d17-d72a-4e44-8604-18b4c18657fe

📥 Commits

Reviewing files that changed from the base of the PR and between 811738e and 4e112f8.

📒 Files selected for processing (1)
  • tests/test_engine.py

@tblakex01
tblakex01 merged commit a290ae8 into main Jun 5, 2026
2 checks passed
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.

2 participants