Skip to content

[GSoC 2026] Kafka Streams runner: TestPipeline-dispatchable test runner (PAssert works)#39362

Merged
je-ik merged 2 commits into
apache:feat/18479-kafka-streams-runner-skeletonfrom
junaiddshaukat:feat/ks-test-pipeline-runner
Jul 17, 2026
Merged

[GSoC 2026] Kafka Streams runner: TestPipeline-dispatchable test runner (PAssert works)#39362
je-ik merged 2 commits into
apache:feat/18479-kafka-streams-runner-skeletonfrom
junaiddshaukat:feat/ks-test-pipeline-runner

Conversation

@junaiddshaukat

@junaiddshaukat junaiddshaukat commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds TestKafkaStreamsRunner, a PipelineRunner that TestPipeline-based suites -- including Beam's @ValidatesRunner tests -- can dispatch to via --runner. Part of #18479, second step toward the @ValidatesRunner suite (after the metrics PR #39341).

  • Fills in the options the translation needs (a unique applicationId, the
    EMBEDDED harness) on the pipeline's options, translates and drives the
    topology to quiescence through TopologyTestDriver, and returns a terminal
    PipelineResult exposing the harness metrics.
  • TestPipeline.run() verifies through those metrics that every PAssert
    actually ran and succeeded (verifyPAssertsSucceeded counts
    PAssert.SUCCESS_COUNTER) -- this is what the metrics PR was for.
  • A failing PAssert fails the run with the assertion text in the error. Over
    the Fn API a DoFn failure travels as an error string rather than a Java
    AssertionError object, so the metrics count check is what catches silently
    dropped assertions (e.g. a watermark bug suppressing the assertion DoFn).

Tests: containsInAnyOrder on Create (PAssert's GBK + Flatten bootstrap materialization, no side inputs), an assertion on Count.perElement() (Combine.perKey works today via its GBK + ParDo expansion in the harness), and
the failing-assertion case.

Next up: the validatesRunner gradle task enabling Beam's suite (with a sickbay list), following the Flink createValidatesRunnerTask pattern.

Adds TestKafkaStreamsRunner, a PipelineRunner that TestPipeline-based suites --
including Beam's @ValidatesRunner tests -- can dispatch to. It fills in the
options the translation needs (applicationId, EMBEDDED harness), drives the
pipeline to quiescence through TopologyTestDriver via KafkaStreamsTestRunner,
and returns a terminal PipelineResult exposing the harness-reported metrics --
which TestPipeline.verifyPAssertsSucceeded uses to confirm every PAssert in the
pipeline ran and succeeded.

Tests prove the full PAssert mechanism against the runner: containsInAnyOrder
materializes the actual PCollection through the GBK + Flatten bootstrap path
(no side inputs), an assertion on Count.perElement covers the Combine-by-
expansion case, and a failing assertion fails the run with the assertion text
(over the Fn API a DoFn error travels as an error string, so the metrics-based
count check is what catches silently dropped assertions).

Next: the validatesRunner gradle task enabling Beam's suite with a sickbay list.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request adds a specialized test runner for the Kafka Streams implementation in Apache Beam. By providing a dispatchable runner that leverages the TopologyTestDriver and an embedded harness, it enables robust testing of pipelines and PAssert assertions. This is a critical step toward fully supporting the @ValidatesRunner test suite for the Kafka Streams runner.

Highlights

  • New Test Runner: Introduced TestKafkaStreamsRunner to enable TestPipeline-based suites and @ValidatesRunner tests to run against the Kafka Streams runner without a broker.
  • Assertion Handling: Implemented logic to propagate PAssert failures correctly, ensuring that assertion errors within DoFns are unwrapped and reported, while silent failures are caught via success-counter metrics.
  • End-to-End Validation: Added comprehensive test coverage including PAssert success scenarios and verification that failing assertions correctly terminate the pipeline run.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces TestKafkaStreamsRunner and its associated unit tests to allow running TestPipeline-based suites against the Kafka Streams runner without requiring an active broker. The feedback suggests two improvements: first, enhancing the applicationId check in TestKafkaStreamsRunner to handle empty strings in addition to null values; second, updating the exception assertion in TestKafkaStreamsRunnerTest from RuntimeException to Throwable to robustly catch potential AssertionError occurrences.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +68 to +70
if (options.getApplicationId() == null) {
options.setApplicationId("ks-validates-runner-" + UUID.randomUUID());
}

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.

medium

It is safer to check if the applicationId is either null or empty. If an empty string is provided as the application ID, it will bypass the null check but will subsequently fail in Kafka Streams, which requires a non-empty application ID.

Suggested change
if (options.getApplicationId() == null) {
options.setApplicationId("ks-validates-runner-" + UUID.randomUUID());
}
if (options.getApplicationId() == null || options.getApplicationId().isEmpty()) {
options.setApplicationId("ks-validates-runner-" + UUID.randomUUID());
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, also guards the empty string.

Comment on lines +77 to +78
RuntimeException thrown = assertThrows(RuntimeException.class, pipeline::run);
assertThat(Throwables.getStackTraceAsString(thrown), containsString("AssertionError"));

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.

medium

Using assertThrows(RuntimeException.class, ...) can cause the test to fail if the runner's unwrapping logic successfully extracts and rethrows an AssertionError (since AssertionError extends Error and is not a subclass of RuntimeException). To make the test robust and correctly handle both wrapped RuntimeException and unwrapped AssertionError, assert that a Throwable is thrown instead.

Suggested change
RuntimeException thrown = assertThrows(RuntimeException.class, pipeline::run);
assertThat(Throwables.getStackTraceAsString(thrown), containsString("AssertionError"));
PAssert.that(pipeline.apply("create", Create.of(1, 2, 3))).containsInAnyOrder(1, 2, 4);
Throwable thrown = assertThrows(Throwable.class, pipeline::run);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, if the chain ever preserves the DoFn's AssertionError the runner rethrows it as an Error, which would have failed this test. Switched to Throwable with the message check.

@je-ik je-ik left a comment

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.

Great work.

@je-ik
je-ik merged commit 87911f0 into apache:feat/18479-kafka-streams-runner-skeleton Jul 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants