Skip to content

Add number type conversion validation to ensure proper casting between numeric types#1205

Merged
fjtirado merged 1 commit intoserverlessworkflow:mainfrom
mcruzdev:issue-1200
Mar 6, 2026
Merged

Add number type conversion validation to ensure proper casting between numeric types#1205
fjtirado merged 1 commit intoserverlessworkflow:mainfrom
mcruzdev:issue-1200

Conversation

@mcruzdev
Copy link
Collaborator

@mcruzdev mcruzdev commented Mar 5, 2026

Changes

  • Implement convertNumber method in AbstractWorkflowModel to handle conversions between numeric types
  • Fix issue where asNumber() returned incompatible Number types without proper casting
  • Ensure type safety when converting workflow model outputs to specific numeric types

Closes #1200

@mcruzdev mcruzdev requested a review from fjtirado as a code owner March 5, 2026 15:08
Copilot AI review requested due to automatic review settings March 5, 2026 15:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a convertNumber private method to AbstractWorkflowModel to properly dispatch numeric type conversions when as(Class<T>) is called with a Number-derived class (previously returning whatever asNumber() produced, which could cause a ClassCastException at the call site). It also adds a comprehensive suite of integration tests covering several primitive wrapper conversions (Long↔Integer, Double↔Integer, Float↔Double, Short↔Integer, Byte↔Integer).

Changes:

  • AbstractWorkflowModel.java: Replace the raw asNumber() call with asNumber().map(num -> convertNumber(num, clazz)), and add the convertNumber dispatch method.
  • WorkflowNumberConversionTest.java: New integration test class exercising seven conversion scenarios.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
impl/core/src/main/java/io/serverlessworkflow/impl/AbstractWorkflowModel.java Adds convertNumber method and wires it into as() for Number-typed requests
impl/test/src/test/java/io/serverlessworkflow/impl/test/WorkflowNumberConversionTest.java Integration tests for numeric type conversions across multiple primitive wrapper types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@fjtirado fjtirado marked this pull request as draft March 5, 2026 16:26
@mcruzdev mcruzdev marked this pull request as ready for review March 5, 2026 20:00
Copilot AI review requested due to automatic review settings March 5, 2026 20:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Collaborator

@fjtirado fjtirado left a comment

Choose a reason for hiding this comment

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

@mcruzdev Great work Matheus
I think you can apply copilot suggestions before merge

Copilot AI review requested due to automatic review settings March 6, 2026 14:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings March 6, 2026 14:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…n numeric types

Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Copilot AI review requested due to automatic review settings March 6, 2026 14:52
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Test
void double_to_big_decimal_conversion() {
Workflow workflow =
FuncWorkflowBuilder.workflow("doubleToInt")
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The workflow name "doubleToInt" in the double_to_big_decimal_conversion test is incorrect — it reuses the same name as the double_to_integer_conversion test. The name should reflect the actual conversion being tested (e.g., "doubleToBigDecimal"). While each test creates its own WorkflowApplication instance so there's no runtime collision, using a misleading workflow name makes the test harder to understand and debug.

Suggested change
FuncWorkflowBuilder.workflow("doubleToInt")
FuncWorkflowBuilder.workflow("doubleToBigDecimal")

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +75
if (targetNumberClass == Integer.class || targetNumberClass == BigInteger.class) {
return Optional.of(targetNumberClass.cast(num.intValue()));
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The BigInteger case is incorrectly grouped with Integer in the condition at line 74. When targetNumberClass == BigInteger.class, the code calls num.intValue() which returns a primitive int (autoboxed to Integer), and then attempts targetNumberClass.cast(someInteger) where targetNumberClass == BigInteger.class. This will always throw a ClassCastException because an Integer cannot be cast to BigInteger.

The fix should use BigInteger.valueOf(num.longValue()) instead, and this case should be separated from the Integer branch. For example:

  • The BigInteger branch should call BigInteger.valueOf(num.longValue()) and then cast to targetNumberClass.
Suggested change
if (targetNumberClass == Integer.class || targetNumberClass == BigInteger.class) {
return Optional.of(targetNumberClass.cast(num.intValue()));
if (targetNumberClass == Integer.class) {
return Optional.of(targetNumberClass.cast(num.intValue()));
} else if (targetNumberClass == BigInteger.class) {
return Optional.of(targetNumberClass.cast(BigInteger.valueOf(num.longValue())));

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +79
} else if (targetNumberClass == Double.class || targetNumberClass == BigDecimal.class) {
return Optional.of(targetNumberClass.cast(num.doubleValue()));
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

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

The BigDecimal case is incorrectly grouped with Double in the condition at line 78. When targetNumberClass == BigDecimal.class, the code calls num.doubleValue() which returns a primitive double (autoboxed to Double), and then attempts targetNumberClass.cast(someDouble) where targetNumberClass == BigDecimal.class. This will always throw a ClassCastException because a Double cannot be cast to BigDecimal.

The fix should use BigDecimal.valueOf(num.doubleValue()) instead, and this case should be separated from the Double branch. For example:

  • The BigDecimal branch should call BigDecimal.valueOf(num.doubleValue()) and then cast to targetNumberClass.
Suggested change
} else if (targetNumberClass == Double.class || targetNumberClass == BigDecimal.class) {
return Optional.of(targetNumberClass.cast(num.doubleValue()));
} else if (targetNumberClass == Double.class) {
return Optional.of(targetNumberClass.cast(num.doubleValue()));
} else if (targetNumberClass == BigDecimal.class) {
return Optional.of(targetNumberClass.cast(BigDecimal.valueOf(num.doubleValue())));

Copilot uses AI. Check for mistakes.
@fjtirado fjtirado merged commit a1e3d37 into serverlessworkflow:main Mar 6, 2026
6 of 7 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.

Incompatible type from task's output and outputAs's input

3 participants