Skip to content

Conversation

@andrii0lomakin
Copy link
Collaborator

Motivation:

TestNG requires XML configuration files and specialized IDE plugins to run tests effectively, which complicates the development workflow. JUnit 4 offers better out-of-the-box support in all major IDEs (IntelliJ IDEA, Eclipse, VS Code) and CLI tools (Maven Surefire, Gradle), enabling developers to run individual tests or test classes directly without additional configuration. This migration simplifies the developer experience and reduces friction when working with the test suite.

Changes:

  • Migrated 98 test files from TestNG to JUnit 4, placed in a new package com.jetbrains.youtrackdb.auto.junit
  • Created base test classes:
    • BaseTest.java - Foundation class handling database setup/teardown with @Before/@After lifecycle
    • BaseDBTest.java - Extended base class for database-specific tests
    • DatabaseTestSuite.java - JUnit test suite aggregating all migrated tests
  • Updated tests/pom.xml:
    • Added JUnit 4 dependency
    • Disabled TestNG test execution (<skip>true</skip>)
    • Added new test-junit execution phase with surefire-junit47 provider
    • Configured system properties for JUnit test execution (storage settings, security iterations, etc.)
  • Key annotation mappings:
    • @Test (TestNG) → @Test (JUnit 4)
    • @BeforeClass/@AfterClass@BeforeClass/@AfterClass (with static methods)
    • @BeforeMethod/@AfterMethod@Before/@After
    • @Parameters (TestNG) → Constructor-based or method-based parameterization
  • Test categories migrated: CRUD operations, index operations, transactions, SQL queries, schema tests, security tests, concurrency tests, and more

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @andrii0lomakin, 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 undertakes a significant refactoring of the test suite by transitioning from TestNG to JUnit 4. The primary goal is to improve the developer experience by leveraging JUnit 4's widespread IDE integration and simplified configuration, thereby reducing friction in test creation and execution. The changes span across numerous test files and build configurations to achieve this modernization.

Highlights

  • Test Framework Migration: Migrated 98 existing TestNG test files to JUnit 4, enhancing compatibility and simplifying the test development workflow.
  • New Base Test Classes: Introduced BaseTest.java, BaseDBTest.java, and DatabaseTestSuite.java to provide a structured foundation for JUnit 4 tests, handling common setup and teardown procedures.
  • Build Configuration Update: Modified tests/pom.xml to incorporate JUnit 4 dependencies, disable TestNG execution, and configure Maven Surefire plugin for JUnit 4 test execution.
  • Annotation Mapping: Mapped TestNG annotations like @BeforeClass, @AfterClass, @BeforeMethod, @AfterMethod, and @Parameters to their corresponding JUnit 4 equivalents.
  • Comprehensive Test Coverage: Ensured migration of various test categories including CRUD operations, index management, transaction handling, SQL queries, schema tests, security tests, and concurrency tests.
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 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

Copy link
Contributor

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

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 represents a significant and valuable migration of the test suite from TestNG to JUnit 4. The changes are extensive, touching the build configuration, adding new base test classes, and converting a large number of test files. This effort will certainly simplify the development and testing workflow. My review focuses on ensuring the new configuration is correct, the migrated tests are robust, and that best practices are followed in the new test infrastructure. I've identified a few areas for improvement, including a critical issue with Java version compatibility, some potential bugs in test logic, and opportunities to improve performance and maintainability in the new test base classes.

Comment on lines +166 to +225
for (var id : ids) {
session.begin();
var element = session.newEntity("Account");
element.setProperty("id", id);
element.setProperty("name", "Gipsy");
element.setProperty("location", "Italy");
element.setProperty("testLong", 10000000000L);
element.setProperty("salary", id + 300);
element.setProperty("extra", "This is an extra field not included in the schema");
element.setProperty("value", (byte) 10);

binary = new byte[100];
for (var b = 0; b < binary.length; ++b) {
binary[b] = (byte) b;
}
element.setProperty("binary", binary);
Assert.assertTrue(accountCollectionIds.contains(element.getIdentity().getCollectionId()));

session.commit();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The fillInAccountData method performs a session.begin() and session.commit() for each record being inserted inside the loop. This is highly inefficient as it creates a new transaction for every single record. For better performance, the transaction should be started once before the loop and committed once after the loop has completed.

      session.begin();
      for (var id : ids) {
        var element = session.newEntity("Account");
        element.setProperty("id", id);
        element.setProperty("name", "Gipsy");
        element.setProperty("location", "Italy");
        element.setProperty("testLong", 10000000000L);
        element.setProperty("salary", id + 300);
        element.setProperty("extra", "This is an extra field not included in the schema");
        element.setProperty("value", (byte) 10);

        binary = new byte[100];
        for (var b = 0; b < binary.length; ++b) {
          binary[b] = (byte) b;
        }
        element.setProperty("binary", binary);
        Assert.assertTrue(accountCollectionIds.contains(element.getIdentity().getCollectionId()));
      }
      session.commit();

Comment on lines +248 to +260
<youtrackdb.storage.makeFullCheckpointAfterCreate>false
</youtrackdb.storage.makeFullCheckpointAfterCreate>
<storage.makeFullCheckpointAfterCollectionCreate>false
</storage.makeFullCheckpointAfterCollectionCreate>
<storage.wal.syncOnPageFlush>false</storage.wal.syncOnPageFlush>
<storage.configuration.syncOnUpdate>false</storage.configuration.syncOnUpdate>
<index.flushAfterCreate>false</index.flushAfterCreate>
<youtrackdb.security.userPasswordSaltIterations>10
</youtrackdb.security.userPasswordSaltIterations>
<buildDirectory>${project.build.directory}</buildDirectory>
<testPath>${project.basedir}</testPath>
<youtrackdb.memory.directMemory.preallocate>false
</youtrackdb.memory.directMemory.preallocate>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The values for several system properties are split across multiple lines. This causes the property value to include leading whitespace and newline characters from the XML formatting, which can lead to unexpected behavior or parsing errors. For example, the value for youtrackdb.storage.makeFullCheckpointAfterCreate will be false\n instead of just false. All property values should be on a single line within their tags.

Suggested change
<youtrackdb.storage.makeFullCheckpointAfterCreate>false
</youtrackdb.storage.makeFullCheckpointAfterCreate>
<storage.makeFullCheckpointAfterCollectionCreate>false
</storage.makeFullCheckpointAfterCollectionCreate>
<storage.wal.syncOnPageFlush>false</storage.wal.syncOnPageFlush>
<storage.configuration.syncOnUpdate>false</storage.configuration.syncOnUpdate>
<index.flushAfterCreate>false</index.flushAfterCreate>
<youtrackdb.security.userPasswordSaltIterations>10
</youtrackdb.security.userPasswordSaltIterations>
<buildDirectory>${project.build.directory}</buildDirectory>
<testPath>${project.basedir}</testPath>
<youtrackdb.memory.directMemory.preallocate>false
</youtrackdb.memory.directMemory.preallocate>
<youtrackdb.storage.makeFullCheckpointAfterCreate>false</youtrackdb.storage.makeFullCheckpointAfterCreate>
<storage.makeFullCheckpointAfterCollectionCreate>false</storage.makeFullCheckpointAfterCollectionCreate>
<storage.wal.syncOnPageFlush>false</storage.wal.syncOnPageFlush>
<storage.configuration.syncOnUpdate>false</storage.configuration.syncOnUpdate>
<index.flushAfterCreate>false</index.flushAfterCreate>
<youtrackdb.security.userPasswordSaltIterations>10</youtrackdb.security.userPasswordSaltIterations>
<buildDirectory>${project.build.directory}</buildDirectory>
<testPath>${project.basedir}</testPath>
<youtrackdb.memory.directMemory.preallocate>false</youtrackdb.memory.directMemory.preallocate>

try {
session.begin();
session.newEntity("AbstractPerson");
session.begin();
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The test test01_CannotCreateInstances is designed to verify that creating an instance of an abstract class throws an exception. However, if session.newEntity("AbstractPerson") fails to throw the expected exception, the test will proceed to the next line (session.begin()) and then complete successfully without any assertion failures. This can mask a potential bug. You should add Assert.fail() after the call that is expected to throw to ensure the test fails if the exception is not thrown.

      session.newEntity("AbstractPerson");
      Assert.fail("Expected SchemaException to be thrown.");

Comment on lines 316 to 353
protected void assertEmbedded(boolean isEmbedded) {
Assert.assertTrue((!isEmbedded));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The method name assertEmbedded is misleading. It suggests that it asserts that the link bag is embedded, but the implementation Assert.assertTrue((!isEmbedded)) actually asserts the opposite. This can cause confusion for anyone reading or maintaining the test. Consider renaming the method to assertNotEmbedded to accurately reflect its behavior.

Suggested change
protected void assertEmbedded(boolean isEmbedded) {
Assert.assertTrue((!isEmbedded));
}
protected void assertNotEmbedded(boolean isEmbedded) {
Assert.assertFalse(isEmbedded);
}

var activeTx = session.getActiveTransaction();
var account = activeTx.<EntityImpl>load(accountRid);
record.setProperty("account", account);
record.setPropertyInChain("date", new SimpleDateFormat("dd/MM/yyyy").parse("01/33/1976"));
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The SimpleDateFormat class is lenient by default and may parse an invalid date like 01/33/1976 into a valid one by rolling over the date (e.g., into February). This can make the test pass even if the input is not what you intend to validate against. To make this test more robust and explicit, consider either using a date that is unambiguously invalid or setting setLenient(false) on the SimpleDateFormat instance to ensure it throws a ParseException for invalid dates.

andrii0lomakin and others added 10 commits January 31, 2026 09:06
Enable and fix JUnit tests that were incorrectly marked as @ignore during
migration from TestNG. Key fixes include:

- BinaryTest: Make rid field static to preserve state across test methods
- ConcurrentQueriesTest: Call init() in setUpClass to create test class
- ConcurrentUpdatesTest: Fix assertEquals argument order (JUnit uses
  expected, actual vs TestNG actual, expected)
- GraphDatabaseTest: Add test00_Populate to generate graph data, create
  "owns" edge class for test01_SQLAgainstGraph
- IndexManagerTest: Add missing composite index creation tests
  (test01a_CreateCompositeIndexTestWithoutListener and
  test01b_CreateCompositeIndexTestWithListener), fix assertEquals order
- HookOnIndexedMapTest: Change to expect IllegalStateException since the
  BrokenMapHook pattern correctly triggers this error

All 838 tests now pass.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Create LinkBagTest abstract base class with 28 test methods
- Create EmbeddedLinkBagTest extending LinkBagTest with embedded mode assertions
- Add EmbeddedLinkBagTest to DatabaseTestSuite
- Add JUnit execution configuration (test-junit) to pom.xml
- Include comments with references to original TestNG test locations

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add skip=true to test-embedded execution to disable TestNG tests
- Remove surefire-testng plugin dependency to prevent TestNG provider activation

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Configure the test-junit Maven execution with necessary system properties
for storage, security, and path settings. Also fix HookOnIndexedMapTest
to use current directory for database path since in-memory databases don't
create directories, causing "directory not exists" errors with the original path.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The JUnit version was missing the @before and @after methods that configure
the link collection thresholds to -1, which forces BTree mode instead of
embedded mode for link bags during tests.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- LinkBagTest.test14_Remove(): Add missing session.begin() and commit()
- LinkBagTest.test24_RemoveNotExistentElementAndAddIt(): Add session.begin() after first commit and commit() at end
- LinkBagTest.test25_AddNewItemsAndRemoveThem(): Add session.commit() before nested session.begin()
- CRUDTest.test32_TestEmbeddedBinary(): Add missing session.begin()
- TransactionAtomicTest.test02_MVCC(): Add session.rollback() in catch block
- SQLSelectTest.beforeClass(): Add missing super.beforeClass() call

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add @BeforeClass and @after cleanup methods to TransactionConsistencyTest
  for proper database session lifecycle management
- Replace silent test skips (if/return) with Assume.assumeFalse() in
  BTreeBasedLinkBagTest for better test visibility
- Fix assertEquals argument order (expected, actual) in BTreeBasedLinkBagTest
  and LinkBagTest
- Replace Assert.assertTrue((!x)) with Assert.assertFalse(x) for clarity
- Remove dead code (self-assignments) in TransactionConsistencyTest
- Fix resource leak where database2 was reassigned without closing

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…Unit tests

- Add @BeforeClass to 6 test classes that were missing it: TransactionAtomicTest,
  FrontendTransactionImplTest, ComplexTypesTest, DBRecordCreateTest,
  DBRecordMetadataTest, and CRUDDocumentValidationTest
- Fix thread safety issue in SchemaTest by adding session.activateOnCurrentThread()
- Add comprehensive suite dependency documentation to DatabaseTestSuite explaining
  test groups, execution order, and guidelines for adding new tests
- Document suite dependency in BaseTest, BaseDBTest, and individual test classes
  including abstract base classes (LinkBagTest, AbstractIndexReuseTest,
  IndexTxAwareBaseTest)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Reorganize JUnit test classes to clearly indicate their membership in
DatabaseTestSuite by moving them to a dedicated subpackage.

Changes:
- Move 90 test classes to com.jetbrains.youtrackdb.auto.junit.databasesuite
- Keep base/support classes in parent package (BaseTest, BaseDBTest,
  IndexTxAwareBaseTest, AbstractIndexReuseTest, LinkBagTest, DatabaseTestSuite)
- Update package declarations and imports in all moved files
- Update DatabaseTestSuite imports to reference new subpackage
- Update javadoc @see references in base classes

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@github-actions
Copy link

github-actions bot commented Jan 31, 2026

Qodana for JVM

2040 new problems were found

Inspection name Severity Problems
Declaration has problems in Javadoc references 🔴 Failure 11
Maven Model Inspection 🔴 Failure 2
Misordered 'assertEquals()' arguments 🔶 Warning 655
Unused import 🔶 Warning 100
Local variable type can be omitted 🔶 Warning 97
Redundant 'throws' clause 🔶 Warning 78
Raw use of parameterized class 🔶 Warning 63
Unused declaration 🔶 Warning 61
Explicit type can be replaced with '<>' 🔶 Warning 52
Field can be local variable 🔶 Warning 51
Stream API call chain can be simplified 🔶 Warning 20
SequencedCollection method can be used 🔶 Warning 17
AutoCloseable used without 'try'-with-resources 🔶 Warning 13
Redundant type cast 🔶 Warning 13
Catch block may ignore exception 🔶 Warning 12
Statement lambda can be replaced with expression lambda 🔶 Warning 12
Unchecked warning 🔶 Warning 11
Missing '@Override' annotation 🔶 Warning 9
Suspicious collection method call 🔶 Warning 9
Redundant suppression 🔶 Warning 7
Declaration can have 'final' modifier 🔶 Warning 6
Method can be made 'static' 🔶 Warning 6
Lambda can be replaced with method reference 🔶 Warning 5
Empty method 🔶 Warning 5
Deprecated API usage 🔶 Warning 4
Empty 'try' block 🔶 Warning 4
Javadoc declaration problems 🔶 Warning 4
'size() == 0' can be replaced with 'isEmpty()' 🔶 Warning 3
'try finally' can be replaced with 'try' with resources 🔶 Warning 3
Abstract class with a single direct inheritor 🔶 Warning 2
'assertEquals()' between objects of inconvertible types 🔶 Warning 2
Anonymous type can be replaced with lambda 🔶 Warning 2
Loop statement that does not loop 🔶 Warning 2
Method parameter always has the same value 🔶 Warning 2
Variable is assigned to itself 🔶 Warning 2
Text block can be used 🔶 Warning 2
Redundant local variable 🔶 Warning 2
Busy wait 🔶 Warning 1
Result of method call ignored 🔶 Warning 1
Old style JUnit test method in JUnit 4 class 🔶 Warning 1
Blank line should be replaced with <p> to break lines 🔶 Warning 1
Link specified as plain text 🔶 Warning 1
Multiplication or division by power of two 🔶 Warning 1
Null-check method is called with obviously non-null argument 🔶 Warning 1
Inefficient Stream API call chains ending with count() 🔶 Warning 1
String concatenation in loop 🔶 Warning 1
Non-constant 'String' can be replaced with 'StringBuilder' 🔶 Warning 1
Concatenation with empty string 🔶 Warning 1
Unnecessary 'return' statement 🔶 Warning 1
Unused assignment 🔶 Warning 1
Spelling ◽️ Notice 629
Commented out code ◽️ Notice 42
Grammar ◽️ Notice 4
Method can be extracted ◽️ Notice 2
Class can be record class ◽️ Notice 1

💡 Qodana analysis was run in the pull request mode: only the changed files were checked
☁️ View the detailed Qodana report

Contact Qodana team

Contact us at [email protected]

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