Skip to content

Rework UseToken test#344

Merged
infeo merged 6 commits intodevelopfrom
feature/fix-use-token-tests
Mar 5, 2026
Merged

Rework UseToken test#344
infeo merged 6 commits intodevelopfrom
feature/fix-use-token-tests

Conversation

@infeo
Copy link
Member

@infeo infeo commented Mar 5, 2026

  • use Awailtility.untilAsserted
  • add assertInUseFile method to check for valid inUse file
  • remove assertNull for useTokens map (tokens are not initially inserted)

* use Awailtility.untilAsserted
* add assertInUseFile method to check for valid inUse file
* remove assertNull for useTokens map (tokens are not initially inserted)
@infeo infeo self-assigned this Mar 5, 2026
@infeo infeo added this to the 2.10.0 milestone Mar 5, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 5, 2026

Walkthrough

The RealUseTokenTest class was updated to replace an Executor with an ExecutorService, add a private helper assertInUseFile(expectedOwner, filePath) that validates OWNER_KEY and LASTUPDATED_KEY in on-disk in-use files, and refactor many tests to rely on that helper and file-state/watch-event checks instead of direct useTokens map assertions. Tests were renamed or added (e.g., testFileContent → testValidFileContent, testMoveToNeverPersistedClosed, testMoveToClosed), some annotated with @RepeatedTest, and lifecycle handling was adjusted to shutdown/await the ExecutorService where needed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Feature: Files-in-Use #312: Changes to RealUseTokenTest and in-use file persistence/validation that touch the same test logic and lifecycle handling.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Rework UseToken test' is concise and directly reflects the main change in the changeset, which involves refactoring the test class with new helper methods, updated assertions, and modified test structure.
Description check ✅ Passed The description is related to the changeset and outlines the key improvements: using Awaitility.untilAsserted, adding assertInUseFile method, and removing assertNull assertions, all of which are evident in the changes.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/fix-use-token-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.

Copy link
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java`:
- Around line 195-231: The tests race on filesystem events; drain and settle
close-related events before calling moveToInternal so move-related assertions
only see new events. For both testMoveToNeverPersistedClosed and
testMoveToClosed: after token.close() use Awaitility (or a short loop) to wait
until watchKey.pollEvents() is non-empty (i.e., close-generated events have
arrived), then call watchKey.pollEvents() once to clear/drain those events; only
after that call token.moveToInternal(targetPath) and assert that
watchKey.pollEvents() is empty and useTokens.get(targetPath) is null. Also avoid
using watchKey.pollEvents() inside Awaitility predicates (consume events there)
— use a separate drain step instead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a690190b-8677-4e10-90f0-e6184bb18231

📥 Commits

Reviewing files that changed from the base of the PR and between 8efb6e2 and 090bf52.

📒 Files selected for processing (1)
  • src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java

infeo added 4 commits March 5, 2026 15:54
Copy link
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: 1

♻️ Duplicate comments (1)
src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java (1)

206-239: ⚠️ Potential issue | 🟠 Major

Closed-move event checks are still flaky due to event consumption/timing.

In testMoveToClosed, Line 233 consumes events inside the Awaitility predicate; and both tests assert emptiness immediately after move (Line 216 / Line 238), which can miss delayed FS events or mix close vs. move events.

Suggested stabilization
 	public void testMoveToNeverPersistedClosed() throws IOException {
@@
 		try (var token = new RealUseToken(filePath, "test3000", cryptor, useTokens, tokenPersistor, CREATION_DELAY_MILLIS, StandardOpenOption.CREATE_NEW, encWrapper)) {
 			token.close();
+			Awaitility.await().pollDelay(FILE_OPERATION_MAX).until(() -> true);
+			watchKey.pollEvents(); // drain potential close-related events
 
 			token.moveToInternal(targetPath);
+			Awaitility.await().pollDelay(FILE_OPERATION_DELAY).until(() -> true);
 
 			MatcherAssert.assertThat(watchKey.pollEvents(), Matchers.empty());
 			Assertions.assertNull(useTokens.get(targetPath));
 		}
 	}
@@
 	public void testMoveToClosed() throws IOException {
@@
 		try (var token = new RealUseToken(filePath, "test3000", cryptor, useTokens, tokenPersistor, CREATION_DELAY_MILLIS, StandardOpenOption.CREATE_NEW, encWrapper)) {
 			Awaitility.await().atLeast(FILE_OPERATION_DELAY).atMost(FILE_OPERATION_MAX) //
 					.untilAsserted(() -> assertInUseFile("test3000", filePath));
 			watchKey.pollEvents(); //clear watchEvents
 			token.close();
-			Awaitility.await().atMost(FILE_OPERATION_MAX) //
-					.until(() -> !watchKey.pollEvents().isEmpty());
+			Awaitility.await().atMost(FILE_OPERATION_MAX)
+					.untilAsserted(() -> Assertions.assertTrue(Files.notExists(filePath)));
+			watchKey.pollEvents(); //drain close-related events
 
 			token.moveToInternal(targetPath);
+			Awaitility.await().pollDelay(FILE_OPERATION_DELAY).until(() -> true);
 
 			MatcherAssert.assertThat(watchKey.pollEvents(), Matchers.empty());
 			Assertions.assertNull(useTokens.get(targetPath));
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java` around
lines 206 - 239, Both tests (testMoveToNeverPersistedClosed and
testMoveToClosed) are flaky due to consuming watchKey events inside Awaitility
and asserting empty immediately after token.moveToInternal; change the event
handling to (1) avoid consuming events inside the Awaitility predicate in
testMoveToClosed—use Awaitility only to wait for the file to appear
(assertInUseFile("test3000", filePath)) and then call watchKey.pollEvents() once
to clear buffered events, and (2) after calling token.moveToInternal(targetPath)
in both tests, don't assert emptiness immediately—use Awaitility to wait for a
short stabilization period (e.g., until watchKey.pollEvents() stays empty for a
small interval) before asserting MatcherAssert.assertThat(watchKey.pollEvents(),
Matchers.empty()) and Assertions.assertNull(useTokens.get(targetPath));
reference watchKey, token.moveToInternal, testMoveToClosed, and
testMoveToNeverPersistedClosed when making the edits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java`:
- Around line 72-79: The teardown code can NPE if beforeEach failed to
initialize tokenPersistor; update the RealUseTokenTest teardown to null-check
tokenPersistor before calling
tokenPersistor.shutdown()/awaitTermination()/shutdownNow() (e.g., if
(tokenPersistor != null) { ... }), preserving the existing InterruptedException
handling so shutdown is only attempted when tokenPersistor is initialized.

---

Duplicate comments:
In `@src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java`:
- Around line 206-239: Both tests (testMoveToNeverPersistedClosed and
testMoveToClosed) are flaky due to consuming watchKey events inside Awaitility
and asserting empty immediately after token.moveToInternal; change the event
handling to (1) avoid consuming events inside the Awaitility predicate in
testMoveToClosed—use Awaitility only to wait for the file to appear
(assertInUseFile("test3000", filePath)) and then call watchKey.pollEvents() once
to clear buffered events, and (2) after calling token.moveToInternal(targetPath)
in both tests, don't assert emptiness immediately—use Awaitility to wait for a
short stabilization period (e.g., until watchKey.pollEvents() stays empty for a
small interval) before asserting MatcherAssert.assertThat(watchKey.pollEvents(),
Matchers.empty()) and Assertions.assertNull(useTokens.get(targetPath));
reference watchKey, token.moveToInternal, testMoveToClosed, and
testMoveToNeverPersistedClosed when making the edits.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4bc6fecd-529a-4ab9-98d9-6e702294cd20

📥 Commits

Reviewing files that changed from the base of the PR and between 090bf52 and c0d82d3.

📒 Files selected for processing (1)
  • src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java

Copy link
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.

♻️ Duplicate comments (1)
src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java (1)

65-79: ⚠️ Potential issue | 🟡 Minor

Make teardown null-safe for partially initialized fixtures.

If setup fails before full initialization, afterEach can throw and hide the real failure (e.g., Line 67/Line 73 dereference nullable fields).

Proposed hardening
 	`@AfterEach`
 	public void afterEach() {
 		try {
-			watchService.close();
+			if (watchService != null) {
+				watchService.close();
+			}
 		} catch (IOException _) {
 			//no-op
 		}
 
 		try {
-			tokenPersistor.shutdown();
-			if (!tokenPersistor.awaitTermination(3000, TimeUnit.MILLISECONDS)) {
-				tokenPersistor.shutdownNow();
+			if (tokenPersistor != null) {
+				tokenPersistor.shutdown();
+				if (!tokenPersistor.awaitTermination(3000, TimeUnit.MILLISECONDS)) {
+					tokenPersistor.shutdownNow();
+				}
 			}
 		} catch (InterruptedException e) {
 			Thread.currentThread().interrupt();
+			if (tokenPersistor != null) {
+				tokenPersistor.shutdownNow();
+			}
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java` around
lines 65 - 79, The teardown in afterEach should be null-safe: guard dereferences
of watchService and tokenPersistor before calling methods on them (e.g., check
watchService != null before calling watchService.close(), and check
tokenPersistor != null before calling tokenPersistor.shutdown(),
awaitTermination(...), and shutdownNow()). Preserve existing exception handling
(IOException for close and InterruptedException for awaitTermination) and re-set
the thread interrupt status when catching InterruptedException. Ensure you
reference the existing afterEach method and the fields watchService and
tokenPersistor when applying these null checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java`:
- Around line 65-79: The teardown in afterEach should be null-safe: guard
dereferences of watchService and tokenPersistor before calling methods on them
(e.g., check watchService != null before calling watchService.close(), and check
tokenPersistor != null before calling tokenPersistor.shutdown(),
awaitTermination(...), and shutdownNow()). Preserve existing exception handling
(IOException for close and InterruptedException for awaitTermination) and re-set
the thread interrupt status when catching InterruptedException. Ensure you
reference the existing afterEach method and the fields watchService and
tokenPersistor when applying these null checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ebad3023-5d5a-40b6-ab22-3db2c88943b3

📥 Commits

Reviewing files that changed from the base of the PR and between c0d82d3 and 853445a.

📒 Files selected for processing (1)
  • src/test/java/org/cryptomator/cryptofs/inuse/RealUseTokenTest.java

@infeo infeo merged commit df84ca1 into develop Mar 5, 2026
8 checks passed
@infeo infeo deleted the feature/fix-use-token-tests branch March 5, 2026 15:34
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.

1 participant