Skip to content

Issue fixes#295

Merged
HardNorth merged 11 commits into
developfrom
issue-fixes
Sep 8, 2025
Merged

Issue fixes#295
HardNorth merged 11 commits into
developfrom
issue-fixes

Conversation

@HardNorth

@HardNorth HardNorth commented Sep 8, 2025

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Bug Fixes

    • Enforced a minimum I/O pool size to avoid under-provisioned threads.
    • Truncate overly long string fields (including during JSON deserialization) to prevent failures.
  • Improvements

    • Adjusted default batch payload limit to 62 MB for safer requests.
    • Centralized truncation behavior and default replacement for consistent handling.
  • Tests

    • Added unit tests covering truncation scenarios and deserialization limits.
  • Chores

    • Updated changelog and bumped test dependencies.

@coderabbitai

coderabbitai Bot commented Sep 8, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds CommonConstants and BasicUtils.truncateString; updates ListenerParameters defaults; replaces inline truncation in LaunchImpl with BasicUtils; configures ObjectMapper to truncate strings on deserialization; adds unit tests and changelog entries.

Changes

Cohort / File(s) Summary
Changelog updates
CHANGELOG.md
Added two Unreleased -> Fixed entries: minimum IO pool size and truncation of overly long string fields.
Configuration defaults
src/main/java/.../listeners/ListenerParameters.java
Default IO pool size now Math.max((Runtime.getRuntime().availableProcessors() * 2) - 1, 1); DEFAULT_BATCH_PAYLOAD_LIMIT changed to 62L * MEGABYTES; uses CommonConstants.DEFAULT_TRUNCATE_REPLACEMENT.
Utilities: constants
src/main/java/.../utils/CommonConstants.java
New CommonConstants with KILOBYTES, MEGABYTES, TEN_MEGABYTES, and DEFAULT_TRUNCATE_REPLACEMENT = "...".
Utilities: string truncation
src/main/java/.../utils/BasicUtils.java
New BasicUtils.truncateString(String, int, String) implementing truncation rules and default replacement; static-only class.
Launch name truncation integration
src/main/java/.../service/LaunchImpl.java
Replaced inline truncation with BasicUtils.truncateString(...), sourcing limit/replacement from ListenerParameters.
Object mapping and truncation on deserialization
src/main/java/.../utils/ObjectUtils.java
ObjectMapper registers LimitedStringDeserializer for String.class, truncating to TEN_MEGABYTES via BasicUtils; minor exception message formatting tweaks.
Tests: BasicUtils
src/test/java/.../utils/BasicUtilsTest.java
New parameterized tests covering truncation scenarios, zero/negative limits, and replacement handling.
Tests: ObjectUtils
src/test/java/.../utils/ObjectUtilsTest.java
New tests verifying cloning preserves short descriptions and truncates overly long descriptions to TEN_MEGABYTES with DEFAULT_TRUNCATE_REPLACEMENT.
Build/test dependency
build.gradle
Test dependencies updated: org.hamcrest:hamcrest-core:2.2 -> org.hamcrest:hamcrest:2.2; test-utils bumped 0.0.12 -> 0.0.13.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant LaunchImpl
  participant ListenerParameters
  participant BasicUtils

  Client->>LaunchImpl: set name
  LaunchImpl->>ListenerParameters: getTruncateLimit(), getTruncateReplacement()
  LaunchImpl->>BasicUtils: truncateString(name, limit, replacement)
  BasicUtils-->>LaunchImpl: truncatedName
  LaunchImpl->>LaunchImpl: rq.setName(truncatedName)
  LaunchImpl-->>Client: done
  note right of BasicUtils #DDEBF7: centralized truncation logic
Loading
sequenceDiagram
  autonumber
  actor Caller
  participant ObjectUtils
  participant ObjectMapper
  participant LimitedStringDeserializer
  participant BasicUtils

  Caller->>ObjectUtils: clonePojo(obj, Target.class)
  ObjectUtils->>ObjectMapper: writeValueAsString(obj)
  ObjectUtils->>ObjectMapper: readValue(json, Target.class)
  ObjectMapper->>LimitedStringDeserializer: deserialize(String field)
  LimitedStringDeserializer->>BasicUtils: truncateString(value, TEN_MEGABYTES, null)
  BasicUtils-->>LimitedStringDeserializer: possibly truncated value
  LimitedStringDeserializer-->>ObjectMapper: String
  ObjectMapper-->>ObjectUtils: Target instance
  ObjectUtils-->>Caller: cloned Target
  note right of LimitedStringDeserializer #FFF2CC: enforces max string length on deserialization
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I nibble long strings down to dots of three,
Count megabytes softly beneath the tree.
I size the pools so streams don't clog the night,
Clamp runaway text till it fits just right.
Hop—constants safe in my burrow bright. 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 201dc27 and ad115b5.

📒 Files selected for processing (1)
  • src/test/java/com/epam/reportportal/utils/BasicUtilsTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/epam/reportportal/utils/BasicUtilsTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-fixes

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.

@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.

Actionable comments posted: 3

🧹 Nitpick comments (6)
src/main/java/com/epam/reportportal/utils/CommonConstants.java (1)

27-31: Use bit shifts for powers of two (nit) and consider long for future-proofing.

Avoid Math.pow with ints and potential double rounding; bit shifts are clearer. If you expect larger units later, consider long.

Apply:

-	public static final int KILOBYTES = (int) Math.pow(2, 10);
-	public static final int MEGABYTES = (int) Math.pow(KILOBYTES, 2);
+	public static final int KILOBYTES = 1 << 10; // 1024
+	public static final int MEGABYTES = 1 << 20; // 1_048_576
 	public static final int TEN_MEGABYTES = 10 * MEGABYTES;
src/main/java/com/epam/reportportal/service/LaunchImpl.java (1)

267-289: Unify attribute truncation through BasicUtils (consistency + edge cases).

Current manual truncation duplicates logic and shares the same small-limit bug. Delegate to BasicUtils to centralize behavior.

-		int limit = getParameters().getAttributeLengthLimit();
-		String replacement = getParameters().getTruncateReplacement();
-		return attributes.stream().map(attribute -> {
-			ItemAttributesRQ updated = attribute;
-			int keyLength = ofNullable(updated.getKey()).map(String::length).orElse(0);
-			if (keyLength > limit && keyLength > replacement.length()) {
-				updated = new ItemAttributesRQ(
-						updated.getKey().substring(0, limit - replacement.length()) + replacement,
-						updated.getValue(),
-						updated.isSystem()
-				);
-			}
-			int valueLength = ofNullable(updated.getValue()).map(String::length).orElse(0);
-			if (valueLength > limit && valueLength > replacement.length()) {
-				updated = new ItemAttributesRQ(
-						updated.getKey(),
-						updated.getValue().substring(0, limit - replacement.length()) + replacement,
-						updated.isSystem()
-				);
-			}
-			return updated;
-		}).collect(Collectors.toSet());
+		final int limit = getParameters().getAttributeLengthLimit();
+		final String replacement = getParameters().getTruncateReplacement();
+		return attributes.stream().map(a -> {
+			final String key = ofNullable(a.getKey()).map(k -> BasicUtils.truncateString(k, limit, replacement)).orElse(null);
+			final String value = ofNullable(a.getValue()).map(v -> BasicUtils.truncateString(v, limit, replacement)).orElse(null);
+			if (Objects.equals(key, a.getKey()) && Objects.equals(value, a.getValue())) {
+				return a;
+			}
+			return new ItemAttributesRQ(key, value, a.isSystem());
+		}).collect(Collectors.toSet());
src/main/java/com/epam/reportportal/utils/BasicUtils.java (4)

27-36: Clarify edge cases in Javadoc (contract guarantees and errors)

Document the exact contract and edge cases for callers.

-	/**
-	 * Truncates string to the specified limit. If string length exceeds the limit, it will be cut and the truncateReplacement will be
-	 * appended.
-	 *
-	 * @param string              string to truncate
-	 * @param limit               maximum allowed length
-	 * @param truncateReplacement string to append if truncation happens, defaults to {@link CommonConstants#DEFAULT_TRUNCATE_REPLACEMENT}
-	 *                            if null
-	 * @return truncated string if original length exceeds the limit, original string otherwise
-	 */
+	/**
+	 * Truncates a string to the specified limit (UTF-16 code units) and guarantees the result length ≤ {@code limit}.
+	 * If truncation occurs, appends {@code truncateReplacement} (or {@link CommonConstants#DEFAULT_TRUNCATE_REPLACEMENT} when {@code null}).
+	 *
+	 * Edge cases:
+	 * - {@code limit < 0} → {@link IllegalArgumentException}
+	 * - {@code limit == 0} → empty string
+	 * - {@code replacement.length() >= limit} → returns the replacement truncated to {@code limit}
+	 *
+	 * @param string              string to truncate (non-null)
+	 * @param limit               maximum allowed length, must be ≥ 0
+	 * @param truncateReplacement string to append if truncation happens; defaults to {@link CommonConstants#DEFAULT_TRUNCATE_REPLACEMENT} if null
+	 * @return original string when {@code string.length() <= limit}; otherwise a truncated string whose length ≤ {@code limit}
+	 */

37-44: Optional: add convenience overload

Common call sites can omit the replacement parameter.

@Nonnull
public static String truncateString(@Nonnull String string, int limit) {
	return truncateString(string, limit, null);
}

41-42: Optional: Unicode-safe truncation

If truncation near surrogate pairs/emojis matters, use code points to avoid splitting multi-unit chars.

Example approach:

final int cpLimit = limit - replacement.length();
final int end = string.offsetByCodePoints(0, Math.max(0, string.codePointCount(0, Math.min(string.length(), cpLimit))));
return string.substring(0, end) + replacement;

37-44: Add boundary tests for safety

Please add tests for:

  • limit < 0 → IllegalArgumentException
  • limit == 0 → ""
  • replacement length >= limit (e.g., "..." with limit 2 → "..")
  • normal truncation and no-truncation paths

I can draft JUnit cases mirroring these scenarios if helpful.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b26fe9 and 95c8c93.

📒 Files selected for processing (7)
  • CHANGELOG.md (1 hunks)
  • src/main/java/com/epam/reportportal/listeners/ListenerParameters.java (6 hunks)
  • src/main/java/com/epam/reportportal/service/LaunchImpl.java (1 hunks)
  • src/main/java/com/epam/reportportal/utils/BasicUtils.java (1 hunks)
  • src/main/java/com/epam/reportportal/utils/CommonConstants.java (1 hunks)
  • src/main/java/com/epam/reportportal/utils/ObjectUtils.java (3 hunks)
  • src/test/java/com/epam/reportportal/utils/ObjectUtilsTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
src/main/java/com/epam/reportportal/service/LaunchImpl.java (1)
src/main/java/com/epam/reportportal/utils/BasicUtils.java (1)
  • BasicUtils (22-45)
src/test/java/com/epam/reportportal/utils/ObjectUtilsTest.java (2)
src/main/java/com/epam/reportportal/utils/ObjectUtils.java (1)
  • ObjectUtils (34-99)
src/main/java/com/epam/reportportal/utils/CommonConstants.java (1)
  • CommonConstants (22-32)
src/main/java/com/epam/reportportal/utils/ObjectUtils.java (2)
src/main/java/com/epam/reportportal/utils/CommonConstants.java (1)
  • CommonConstants (22-32)
src/main/java/com/epam/reportportal/utils/BasicUtils.java (1)
  • BasicUtils (22-45)
src/main/java/com/epam/reportportal/listeners/ListenerParameters.java (1)
src/main/java/com/epam/reportportal/utils/CommonConstants.java (1)
  • CommonConstants (22-32)
src/main/java/com/epam/reportportal/utils/BasicUtils.java (1)
src/main/java/com/epam/reportportal/utils/CommonConstants.java (1)
  • CommonConstants (22-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (7)
CHANGELOG.md (1)

6-7: Changelog updates—LGTM.

Entries are clear and correctly linked to the issues.

src/main/java/com/epam/reportportal/service/LaunchImpl.java (1)

257-259: Good move: centralized truncation via BasicUtils.

Switching to BasicUtils.truncateString improves consistency and reuse.

src/main/java/com/epam/reportportal/listeners/ListenerParameters.java (3)

45-45: IO pool sizing—LGTM.

Adding a lower bound of 1 avoids zero-thread edge cases on constrained environments.


75-77: Verify units: 62 MiB vs previous 65,000,000 bytes—comment may be misleading.

62L * MEGABYTES equals 65,011,712 bytes, slightly larger than prior 65,000,000. If the intent was to reduce the limit to add headroom, consider using decimal MB or updating the comment to “62 MiB”.

Suggested options:

-	// Due to shortcoming of payload calculation mechanism this value is set to 62 megabytes rather than 65 megabytes
-	public static final long DEFAULT_BATCH_PAYLOAD_LIMIT = 62L * MEGABYTES;
+	// Due to a shortcoming in payload size calculation, use 62 MiB instead of 65 MB
+	public static final long DEFAULT_BATCH_PAYLOAD_LIMIT = 62L * MEGABYTES;

or to actually lower the byte count:

-	public static final long DEFAULT_BATCH_PAYLOAD_LIMIT = 62L * MEGABYTES;
+	public static final long DEFAULT_BATCH_PAYLOAD_LIMIT = 62L * 1_000_000L; // 62 MB (decimal)

218-218: Centralizing default truncate replacement—LGTM.

Referencing CommonConstants.DEFAULT_TRUNCATE_REPLACEMENT removes duplication and keeps defaults consistent.

Also applies to: 299-299

src/main/java/com/epam/reportportal/utils/ObjectUtils.java (1)

36-59: Global String deserializer truncation: confirm no unintended side effects.

This will truncate every String during deserialization (including clonePojo) to TEN_MEGABYTES. Validate that IDs, URLs, or tokens aren’t expected to exceed this and that no sensitive fields rely on exact preservation beyond this length.

Would you like a targeted test to ensure fields like launch UUIDs or issue locators are unaffected?

src/main/java/com/epam/reportportal/utils/BasicUtils.java (1)

22-25: Private constructor pattern: OK

Prevents instantiation as intended.

Comment thread src/main/java/com/epam/reportportal/service/LaunchImpl.java
Comment thread src/main/java/com/epam/reportportal/utils/BasicUtils.java
Comment thread src/test/java/com/epam/reportportal/utils/ObjectUtilsTest.java
@HardNorth HardNorth requested a review from Copilot September 8, 2025 08:14

This comment was marked as outdated.

@HardNorth HardNorth requested a review from Copilot September 8, 2025 10:20

Copilot AI 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.

Pull Request Overview

This PR implements bug fixes to address string field truncation and I/O pool sizing issues in the ReportPortal client. The changes introduce automatic truncation of overly long string fields to prevent failures and enforce a minimum I/O pool size to avoid under-provisioned threads.

Key changes:

  • Added string truncation utilities with configurable limits and replacement text
  • Implemented automatic JSON deserialization truncation for all string fields
  • Enforced minimum I/O pool size of 1 thread
  • Adjusted default batch payload limit from 65MB to 62MB

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/main/java/com/epam/reportportal/utils/CommonConstants.java New constants file defining size limits and default truncation replacement
src/main/java/com/epam/reportportal/utils/BasicUtils.java New utility class with string truncation functionality
src/main/java/com/epam/reportportal/utils/ObjectUtils.java Enhanced with custom JSON deserializer for automatic string truncation
src/main/java/com/epam/reportportal/service/LaunchImpl.java Refactored to use new truncation utility instead of inline logic
src/main/java/com/epam/reportportal/listeners/ListenerParameters.java Updated I/O pool size calculation and batch payload limits
src/test/java/com/epam/reportportal/utils/ObjectUtilsTest.java New tests for cloning behavior with truncation
src/test/java/com/epam/reportportal/utils/BasicUtilsTest.java New parameterized tests for string truncation scenarios
CHANGELOG.md Updated with issue fix descriptions

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread src/main/java/com/epam/reportportal/utils/ObjectUtils.java

@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.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/test/java/com/epam/reportportal/utils/BasicUtilsTest.java (3)

25-41: Add edge-case rows to strengthen coverage.
Consider covering: string length equals limit, replacement length equals limit, and empty input.

Apply this diff inside testData():

         return new Object[][]{
                 // 1. String is longer than limit, truncateReplacement is less than limit
                 {"HelloWorld", 5, "..", "Hel.."},
                 // 2. String is less than limit
                 {"Hello", 10, "...", "Hello"},
                 // 3. String is longer than limit, truncateReplacement is longer than limit
                 {"HelloWorld", 3, "foobar", "Hel"},
                 // 4. Limit is 0
                 {"Hello", 0, "...", ""},
                 // 5. Limit is less than 0
-                {"Hello", -3, "...", ""}
+                {"Hello", -3, "...", ""},
+                // 6. String length equals limit
+                {"Hello", 5, "...", "Hello"},
+                // 7. truncateReplacement length equals limit
+                {"HelloWorld", 3, "xyz", "Hel"},
+                // 8. Empty input
+                {"", 3, "...", ""}
         };

19-24: Add a targeted test for null replacement using the default constant.
Validates integration with CommonConstants.DEFAULT_TRUNCATE_REPLACEMENT without hard-coding its value.

Apply this diff:

 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.api.Test;

 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.endsWith;

@@
     @MethodSource("testData")
     public void test_truncate_string_scenarios(String input, int limit, String replacement, String expected) {
         assertThat(BasicUtils.truncateString(input, limit, replacement), equalTo(expected));
     }
+
+    @Test
+    public void test_truncate_string_null_replacement_uses_default_and_respects_limit() {
+        String input = "HelloWorld";
+        int limit = 5;
+        String result = BasicUtils.truncateString(input, limit, null);
+        assertThat(result.length(), equalTo(limit));
+        assertThat(result, endsWith(CommonConstants.DEFAULT_TRUNCATE_REPLACEMENT));
+    }
 }

Also applies to: 43-48


43-45: Improve parameterized test display names for readability in reports.

Apply this diff:

-    @ParameterizedTest
+    @ParameterizedTest(name = "[{index}] input=\"{0}\" | limit={1} | repl=\"{2}\"")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e9fcb8 and 201dc27.

📒 Files selected for processing (1)
  • src/test/java/com/epam/reportportal/utils/BasicUtilsTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/com/epam/reportportal/utils/BasicUtilsTest.java (1)
src/main/java/com/epam/reportportal/utils/BasicUtils.java (1)
  • BasicUtils (22-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (1)
src/test/java/com/epam/reportportal/utils/BasicUtilsTest.java (1)

1-15: License header looks good.

@HardNorth HardNorth merged commit 9742d7d into develop Sep 8, 2025
3 checks passed
@HardNorth HardNorth deleted the issue-fixes branch September 8, 2025 10:58
@coderabbitai coderabbitai Bot mentioned this pull request Oct 14, 2025
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