diff --git a/api/src/org/labkey/api/ApiModule.java b/api/src/org/labkey/api/ApiModule.java index 4c06c05a3d6..b687e91f753 100644 --- a/api/src/org/labkey/api/ApiModule.java +++ b/api/src/org/labkey/api/ApiModule.java @@ -576,7 +576,7 @@ public void registerServlets(ServletContext servletCtx) TabLoader.TabLoaderTestCase.class, Table.DataIteratorTestCase.class, Table.TestCase.class, - TableSelectorTestCase.class, + TableSelectorTestCase.CoreTableSelectorTest.class, TempTableInClauseGenerator.TestCase.class, URLHelper.TestCase.class, UserManager.TestCase.class, diff --git a/api/src/org/labkey/api/data/DbScope.java b/api/src/org/labkey/api/data/DbScope.java index 834b2b6192c..1dcd1542550 100644 --- a/api/src/org/labkey/api/data/DbScope.java +++ b/api/src/org/labkey/api/data/DbScope.java @@ -2145,6 +2145,7 @@ public static DbScope getDbScope(String dsName) /** * Some DbScopes shouldn't be exercised by junit tests (e.g., an external data source connected to LabKey Server via * the PostgreSQL wire protocol) + * Tests that use this should be annotated with '@TestWhen(TestWhen.When.DB_SCOPE)' * * @return A collection of DbScopes that are suitable for testing */ @@ -2960,7 +2961,7 @@ public void afterLoadTable(SchemaTableInfo ti) } // Test dialects that are in-use; only for tests that require connecting to the database. - @TestWhen(TestWhen.When.BVT) + @TestWhen(TestWhen.When.DBSCOPE) public static class DialectTestCase extends Assert { @Test @@ -3011,6 +3012,7 @@ private void testDateDiff(DbScope scope, SqlDialect dialect, String date1, Strin } } + @TestWhen(TestWhen.When.DBSCOPE) public static class GroupConcatTestCase extends Assert { @Test @@ -3056,7 +3058,7 @@ private void testGroupConcat(DbScope scope, SqlDialect dialect, boolean distinct } } - + @TestWhen(TestWhen.When.DBSCOPE) public static class TransactionTestCase extends Assert { @Test diff --git a/api/src/org/labkey/api/data/TableSelectorTestCase.java b/api/src/org/labkey/api/data/TableSelectorTestCase.java index 68d5c2ce623..af5a1af8987 100644 --- a/api/src/org/labkey/api/data/TableSelectorTestCase.java +++ b/api/src/org/labkey/api/data/TableSelectorTestCase.java @@ -28,6 +28,7 @@ import org.labkey.api.module.ModuleContext; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; +import org.labkey.api.test.TestWhen; import org.labkey.api.util.ExceptionUtil; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.TestContext; @@ -40,229 +41,116 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Stream; -public class TableSelectorTestCase extends AbstractSelectorTestCase +public abstract class TableSelectorTestCase extends AbstractSelectorTestCase { private static final Logger LOG = LogHelper.getLogger(TableSelectorTestCase.class, "Test progress"); - @Test - public void testTableSelector() throws SQLException + @TestWhen(TestWhen.When.DBSCOPE) + public static class CoreTableSelectorTest extends TableSelectorTestCase { -// Call below can be used to test that Oracle dialect behaves as expected, following our maxRows, offset, and other -// rules. Uncomment this line and the corresponding bean class below. -// testTableSelector(DbSchema.get("oracle.granite", DbSchemaType.Bare).getTable("account"), Account.class); - - // Test MySQL or MariaDB database, if present - List mySqlScopes = DbScope.getDbScopesToTest().stream() - .filter(scope -> Set.of("MySQL", "MariaDB").contains(scope.getSqlDialect().getProductName())) - .toList(); - - for (DbScope mySqlScope: mySqlScopes) - { - DbSchema sakila = mySqlScope.getSchema("sakila", DbSchemaType.Bare); - if (sakila.existsInDatabase()) - { - testTableSelector(sakila.getTable("Country"), Country.class); - } - else - { - DbSchema sys = mySqlScope.getSchema("sys", DbSchemaType.Bare); - if (sys.existsInDatabase()) - { - testTableSelector(sys.getTable("sys_config"), Config.class); - } - } - } - testTableSelector(CoreSchema.getInstance().getTableInfoActiveUsers(), User.class); - testTableSelector(CoreSchema.getInstance().getTableInfoModules(), ModuleContext.class); - } - - @SuppressWarnings("unused") - public static class Country - { - private int _country_id; - private String _country; - private Date _last_update; - - public int getCountry_id() - { - return _country_id; - } - - public void setCountry_id(int country_id) - { - _country_id = country_id; - } - - public String getCountry() + @Test + public void testTableSelector() throws SQLException { - return _country; + testTableSelector(CoreSchema.getInstance().getTableInfoActiveUsers(), User.class); + testTableSelector(CoreSchema.getInstance().getTableInfoModules(), ModuleContext.class); } - public void setCountry(String country) + @Test + public void testGetObject() { - _country = country; - } + TableSelector userSelector = new TableSelector(CoreSchema.getInstance().getTableInfoActiveUsers()); - public Date getLast_update() - { - return _last_update; - } + User user = TestContext.get().getUser(); + User selectedUser = userSelector.getObject(user.getUserId(), User.class); + assertEquals(user, selectedUser); - public void setLast_update(Date last_update) - { - _last_update = last_update; - } + // TableSelector to test a couple exception scenarios + TableSelector moduleSelector = new TableSelector(CoreSchema.getInstance().getTableInfoModules()); + moduleSelector.setLogLevel(Level.OFF); // Suppress auto-logging since we're intentionally causing SQLExceptions - @Override - public boolean equals(Object o) - { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Country country = (Country) o; - return _country_id == country._country_id && Objects.equals(_country, country._country) && Objects.equals(_last_update, country._last_update); - } + // Make sure that getObject() throws if more than one row is selected + try + { + moduleSelector.getObject(ModuleContext.class); + fail("getObject() should have thrown when returning multiple objects"); + } + catch (UncategorizedSQLException e) + { + String message = e.getMessage(); + // Verify that the exception message does not contain SQL (we don't want to display SQL to users... + assertFalse("Exception message " + message + " seems to contain SQL", message.contains("SELECT")); + // ...and that the exception is decorated, so the SQL does end up in mothership + String decoration = ExceptionUtil.getExceptionDecoration(e, ExceptionUtil.ExceptionInfo.DialectSQL); + assertNotNull("Exception was not decorated", decoration); + } - @Override - public int hashCode() - { - return Objects.hash(_country_id, _country, _last_update); - } - } + // Make sure that getObject() throws if pk == null, #20057 - record Config(String Variable, String Value, Date Set_Time, String Set_By){} + // For now, null returns null to get DataReportsTest running again + ModuleContext ctx = moduleSelector.getObject(null, ModuleContext.class); + assertNull("getObject(null) should return null", ctx); -// public static class Account -// { -// private int _account_id; -// private String _account_number; -// private String _account_desc; -// -// public int getAccount_id() -// { -// return _account_id; -// } -// -// public void setAccount_id(int account_id) -// { -// _account_id = account_id; -// } -// -// public String getAccount_number() -// { -// return _account_number; -// } -// -// public void setAccount_number(String account_number) -// { -// _account_number = account_number; -// } -// -// public String getAccount_desc() -// { -// return _account_desc; -// } -// -// public void setAccount_desc(String account_desc) -// { -// _account_desc = account_desc; -// } -// -// @Override -// public boolean equals(Object o) +// try // { -// if (this == o) return true; -// if (o == null || getClass() != o.getClass()) return false; -// Account account = (Account) o; -// return _account_id == account._account_id && Objects.equals(_account_number, account._account_number) && Objects.equals(_account_desc, account._account_desc); +// moduleSelector.getObject(null, ModuleContext.class); +// fail("getObject() should have thrown with null pk"); // } -// -// @Override -// public int hashCode() +// catch (IllegalStateException e) // { -// return Objects.hash(_account_id, _account_number, _account_desc); +// assertEquals("PK on getObject() must not be null", e.getMessage()); // } -// } + } - @Test - public void testGetObject() - { - TableSelector userSelector = new TableSelector(CoreSchema.getInstance().getTableInfoActiveUsers()); + @Test + public void testColumnLists() throws SQLException + { + TableInfo ti = CoreSchema.getInstance().getTableInfoActiveUsers(); - User user = TestContext.get().getUser(); - User selectedUser = userSelector.getObject(user.getUserId(), User.class); - assertEquals(user, selectedUser); + testColumnList(new TableSelector(ti, new HashSet<>(Arrays.asList("Email", "UserId", "DisplayName", "Created", "Active"))), false); + testColumnList(new TableSelector(ti, new HashSet<>(ti.getColumns("Email,UserId,DisplayName,Created,Active")), null, null), false); - // TableSelector to test a couple exception scenarios - TableSelector moduleSelector = new TableSelector(CoreSchema.getInstance().getTableInfoModules()); - moduleSelector.setLogLevel(Level.OFF); // Suppress auto-logging since we're intentionally causing SQLExceptions + testColumnList(new TableSelector(ti, PageFlowUtil.set("Email", "UserId", "DisplayName", "Created", "Active")), true); + testColumnList(new TableSelector(ti, new LinkedHashSet<>(Arrays.asList("Email", "UserId", "DisplayName", "Created", "Active"))), true); + testColumnList(new TableSelector(ti, new CsvSet("Email, UserId, DisplayName, Created, Active")), true); + testColumnList(new TableSelector(ti, PageFlowUtil.set(ti.getColumn("Email"), ti.getColumn("UserId"), ti.getColumn("DisplayName"), ti.getColumn("Created"), ti.getColumn("Active")), null, null), true); + testColumnList(new TableSelector(ti, ti.getColumns("Email,UserId,DisplayName,Created,Active"), null, null), true); - // Make sure that getObject() throws if more than one row is selected - try - { - moduleSelector.getObject(ModuleContext.class); - fail("getObject() should have thrown when returning multiple objects"); - } - catch (UncategorizedSQLException e) - { - String message = e.getMessage(); - // Verify that the exception message does not contain SQL (we don't want to display SQL to users... - assertFalse("Exception message " + message + " seems to contain SQL", message.contains("SELECT")); - // ...and that the exception is decorated, so the SQL does end up in mothership - String decoration = ExceptionUtil.getExceptionDecoration(e, ExceptionUtil.ExceptionInfo.DialectSQL); - assertNotNull("Exception was not decorated", decoration); + // Singleton column collections should always be considered "stable" + testColumnList(new TableSelector(ti, PageFlowUtil.set("Email")), true); + testColumnList(new TableSelector(ti, new CsvSet("Email")), true); + testColumnList(new TableSelector(ti, Collections.singleton("Email")), true); + testColumnList(new TableSelector(ti, ti.getColumns("Email"), null, null), true); + testColumnList(new TableSelector(ti, Collections.singleton(ti.getColumn("Email")), null, null), true); } - // Make sure that getObject() throws if pk == null, #20057 + @Test + public void testInClause() + { + TableInfo table = CoreSchema.getInstance().getTableInfoContainers(); + long rowCount = new TableSelector(table).getRowCount(); - // For now, null returns null to get DataReportsTest running again - ModuleContext ctx = moduleSelector.getObject(null, ModuleContext.class); - assertNull("getObject(null) should return null", ctx); + Container root = ContainerManager.getRoot(); + FilterClause rootClause = new InClause(FieldKey.fromParts("RowId"), Set.of(root.getRowId())); + assertEquals(1, new TableSelector(table, new SimpleFilter(rootClause), null).getRowCount()); + assertEquals(rowCount - 1, new TableSelector(table, new SimpleFilter(new NotClause(rootClause)), null).getRowCount()); -// try -// { -// moduleSelector.getObject(null, ModuleContext.class); -// fail("getObject() should have thrown with null pk"); -// } -// catch (IllegalStateException e) -// { -// assertEquals("PK on getObject() must not be null", e.getMessage()); -// } - } + FilterClause emptyClause = new InClause(FieldKey.fromParts("RowId"), Set.of()); + assertEquals(0, new TableSelector(table, new SimpleFilter(emptyClause), null).getRowCount()); + assertEquals(rowCount, new TableSelector(table, new SimpleFilter(new NotClause(emptyClause)), null).getRowCount()); + } - @Test - public void testColumnLists() throws SQLException - { - TableInfo ti = CoreSchema.getInstance().getTableInfoActiveUsers(); - - testColumnList(new TableSelector(ti, new HashSet<>(Arrays.asList("Email", "UserId", "DisplayName", "Created", "Active"))), false); - testColumnList(new TableSelector(ti, new HashSet<>(ti.getColumns("Email,UserId,DisplayName,Created,Active")), null, null), false); - - testColumnList(new TableSelector(ti, PageFlowUtil.set("Email", "UserId", "DisplayName", "Created", "Active")), true); - testColumnList(new TableSelector(ti, new LinkedHashSet<>(Arrays.asList("Email", "UserId", "DisplayName", "Created", "Active"))), true); - testColumnList(new TableSelector(ti, new CsvSet("Email, UserId, DisplayName, Created, Active")), true); - testColumnList(new TableSelector(ti, PageFlowUtil.set(ti.getColumn("Email"), ti.getColumn("UserId"), ti.getColumn("DisplayName"), ti.getColumn("Created"), ti.getColumn("Active")), null, null), true); - testColumnList(new TableSelector(ti, ti.getColumns("Email,UserId,DisplayName,Created,Active"), null, null), true); - - // Singleton column collections should always be considered "stable" - testColumnList(new TableSelector(ti, PageFlowUtil.set("Email")), true); - testColumnList(new TableSelector(ti, new CsvSet("Email")), true); - testColumnList(new TableSelector(ti, Collections.singleton("Email")), true); - testColumnList(new TableSelector(ti, ti.getColumns("Email"), null, null), true); - testColumnList(new TableSelector(ti, Collections.singleton(ti.getColumn("Email")), null, null), true); } - private void testColumnList(TableSelector selector, boolean stable) throws SQLException + protected void testColumnList(TableSelector selector, boolean stable) throws SQLException { // The following methods should succeed with both stable and unstable ordered column lists @@ -446,7 +334,7 @@ private void testColumnList(TableSelector selector, boolean stable) throws SQLEx } } - private void testTableSelector(TableInfo table, Class clazz) throws SQLException + protected void testTableSelector(TableInfo table, Class clazz) throws SQLException { DbSchema schema = table.getSchema(); LOG.info("Testing {}.{}.{}", schema.getScope().getDisplayName(), schema.getName(), table.getName()); @@ -550,19 +438,4 @@ private void test(TableSelector selector, Class clazz, int offset, int ro verifyResultSets(selector, rowCount, expectedComplete); } - @Test - public void testInClause() - { - TableInfo table = CoreSchema.getInstance().getTableInfoContainers(); - long rowCount = new TableSelector(table).getRowCount(); - - Container root = ContainerManager.getRoot(); - FilterClause rootClause = new InClause(FieldKey.fromParts("RowId"), Set.of(root.getRowId())); - assertEquals(1, new TableSelector(table, new SimpleFilter(rootClause), null).getRowCount()); - assertEquals(rowCount - 1, new TableSelector(table, new SimpleFilter(new NotClause(rootClause)), null).getRowCount()); - - FilterClause emptyClause = new InClause(FieldKey.fromParts("RowId"), Set.of()); - assertEquals(0, new TableSelector(table, new SimpleFilter(emptyClause), null).getRowCount()); - assertEquals(rowCount, new TableSelector(table, new SimpleFilter(new NotClause(emptyClause)), null).getRowCount()); - } } diff --git a/api/src/org/labkey/api/data/dialect/SqlDialect.java b/api/src/org/labkey/api/data/dialect/SqlDialect.java index d7525c0e081..805624000bf 100644 --- a/api/src/org/labkey/api/data/dialect/SqlDialect.java +++ b/api/src/org/labkey/api/data/dialect/SqlDialect.java @@ -58,6 +58,7 @@ import org.labkey.api.module.ModuleContext; import org.labkey.api.module.ModuleLoader; import org.labkey.api.query.FieldKey; +import org.labkey.api.test.TestWhen; import org.labkey.api.util.ExceptionUtil; import org.labkey.api.util.HtmlString; import org.labkey.api.util.MemTracker; @@ -2414,6 +2415,7 @@ public SQLFragment array_element_like(SQLFragment a, String... values) // TESTS // + @TestWhen(TestWhen.When.DBSCOPE) public static class DialectTestCase { DbScope s; diff --git a/api/src/org/labkey/api/test/TestWhen.java b/api/src/org/labkey/api/test/TestWhen.java index e83fd275cb6..62348acec3b 100644 --- a/api/src/org/labkey/api/test/TestWhen.java +++ b/api/src/org/labkey/api/test/TestWhen.java @@ -26,7 +26,7 @@ { enum When { - SMOKE, DRT, BVT, DAILY, WEEKLY, PERFORMANCE + SMOKE, DRT, DBSCOPE, BVT, DAILY, WEEKLY, PERFORMANCE } When value() default When.DRT; } diff --git a/query/src/org/labkey/query/sql/Method.java b/query/src/org/labkey/query/sql/Method.java index cb47eccc5ce..9e06a7bbac5 100644 --- a/query/src/org/labkey/query/sql/Method.java +++ b/query/src/org/labkey/query/sql/Method.java @@ -49,14 +49,15 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.settings.AppProps; +import org.labkey.api.test.TestWhen; import org.labkey.api.util.GUID; import org.labkey.query.QueryServiceImpl; import org.labkey.query.sql.antlr.SqlBaseLexer; -import java.util.Calendar; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.text.DecimalFormat; +import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -1999,6 +2000,7 @@ public SQLFragment getSQL(SqlDialect dialect, SQLFragment[] arguments) } } + @TestWhen(TestWhen.When.DBSCOPE) public static class TestCase extends Assert { void assertIsSimpleString(String expected, SQLFragment s)