Skip to content

Commit a34c217

Browse files
committed
Merge remote-tracking branch 'origin/develop' into fb_mail_transport_via_graph
2 parents be14467 + e203745 commit a34c217

16 files changed

Lines changed: 412 additions & 45 deletions

build.gradle

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ plugins {
55
id 'maven-publish'
66
}
77

8+
import org.labkey.gradle.task.RunTestSuite
9+
import org.labkey.gradle.task.RunUiTest
810
import org.labkey.gradle.util.BuildUtils
911
import org.labkey.gradle.util.GroupNames
1012
import org.labkey.gradle.util.PomFileHelper
@@ -170,8 +172,19 @@ project.tasks.register("convertHarToStressXml", JavaExec) {
170172
}
171173
}
172174

173-
project.tasks.uiTests.dependsOn(initPropertiesTask)
174-
project.parent.parent.tasks.ijConfigure.dependsOn(initPropertiesTask)
175+
project.tasks.register("testPackageLockJson", RunUiTest) {
176+
include "org/labkey/test/tests/PackageLockJsonTest.class"
177+
}
178+
179+
project.tasks.named("uiTests").configure {
180+
dependsOn(initPropertiesTask)
181+
}
182+
project.parent.parent.tasks.named("ijConfigure").configure {
183+
dependsOn(initPropertiesTask)
184+
}
185+
project.tasks.withType(RunTestSuite).configureEach { it ->
186+
scanForTestClasses = true // Required for Gradle 9.3
187+
}
175188

176189
if (project.hasProperty('doPublishing'))
177190
{

data/api/rlabkey-api-query.xml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,4 +625,45 @@
625625
]]>
626626
</response>
627627
</test>
628+
<test name="make filters" type="post">
629+
<url>
630+
<![CDATA[
631+
library(Rlabkey)
632+
print(makeFilter(
633+
c("foo/Reagent", "CONTAINS", "this or that"),
634+
c("Temp_C", "GREATER_THAN", "12"),
635+
c("SystolicBloodPressure", "MISSING", "")))
636+
]]>
637+
</url>
638+
<response>
639+
<![CDATA[
640+
[1] "query.foo%2FReagent~contains=this%20or%20that"
641+
[2] "query.Temp_C~gt=12"
642+
[3] "query.SystolicBloodPressure~isblank="
643+
]]>
644+
</response>
645+
</test>
646+
<test name="make filters as list" type="post">
647+
<url>
648+
<![CDATA[
649+
library(Rlabkey)
650+
print(makeFilter(
651+
c("foo/Reagent", "CONTAINS", "this or that"),
652+
c("Temp_C", "GREATER_THAN", "12"),
653+
c("SystolicBloodPressure", "MISSING", ""), asList=TRUE))
654+
]]>
655+
</url>
656+
<response>
657+
<![CDATA[
658+
$`query.foo/Reagent~contains`
659+
[1] "this or that"
660+
661+
$`query.Temp_C~gt`
662+
[1] "12"
663+
664+
$`query.SystolicBloodPressure~isblank`
665+
[1] ""
666+
]]>
667+
</response>
668+
</test>
628669
</ApiTests>

data/api/rlabkey-api.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<url>
3131
<![CDATA[
3232
library(Rlabkey)
33-
print(labkey.getFolders(labkey.url.base, "/%projectName%", includeEffectivePermissions=FALSE))
33+
print(labkey.getFolders(labkey.url.base, "/%projectName%", includeEffectivePermissions=FALSE, includeSubfolders=TRUE))
3434
]]>
3535
</url>
3636
<response>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.labkey.remoteapi;
2+
3+
import org.apache.hc.client5.http.classic.methods.HttpPost;
4+
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
5+
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
6+
import org.apache.hc.core5.http.NameValuePair;
7+
import org.apache.hc.core5.http.message.BasicNameValuePair;
8+
9+
import java.io.IOException;
10+
import java.net.URI;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
import java.util.Map;
14+
15+
public class SimpleFormCommand extends Command<CommandResponse, HttpUriRequest>
16+
{
17+
private final Map<String, String> _formData;
18+
19+
public SimpleFormCommand(String controller, String action, Map<String, String> formData)
20+
{
21+
super(controller, action);
22+
_formData = formData;
23+
}
24+
25+
public CommandResponse execute(Connection connection) throws IOException, CommandException
26+
{
27+
return execute(connection, null);
28+
}
29+
30+
@Override
31+
protected HttpUriRequest createRequest(URI uri)
32+
{
33+
HttpPost post = new HttpPost(uri);
34+
35+
List<NameValuePair> args = new ArrayList<>();
36+
for (Map.Entry<String, String> reportVal : _formData.entrySet())
37+
{
38+
args.add(new BasicNameValuePair(reportVal.getKey(), reportVal.getValue()));
39+
}
40+
post.setEntity(new UrlEncodedFormEntity(args));
41+
return post;
42+
}
43+
}

src/org/labkey/test/Runner.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@
4141
import org.jetbrains.annotations.NotNull;
4242
import org.junit.Ignore;
4343
import org.junit.runner.Description;
44+
import org.junit.runner.RunWith;
4445
import org.junit.runner.manipulation.Filter;
4546
import org.junit.runner.manipulation.Filterable;
4647
import org.junit.runner.manipulation.NoTestsRemainException;
48+
import org.junit.runners.AllTests;
4749
import org.labkey.junit.runner.WebTestProperties;
4850
import org.labkey.serverapi.reader.Readers;
4951
import org.labkey.serverapi.writer.PrintWriters;
@@ -92,6 +94,7 @@
9294

9395
import static org.labkey.test.WebTestHelper.logToServer;
9496

97+
@RunWith(AllTests.class)
9598
public class Runner extends TestSuite
9699
{
97100
static

src/org/labkey/test/components/core/ProjectMenu.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ public ProjectMenu open()
8989
openMenu.run();
9090
if (!Locator.tagWithClass("div", "folder-nav").existsIn(this))
9191
{
92+
// Menu opened but the folder list didn't load
93+
close();
9294
openMenu.run(); // retry
9395
}
9496
}

src/org/labkey/test/tests/AbstractAdminConsoleTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.List;
2626

2727
import static org.labkey.test.util.PermissionsHelper.APP_ADMIN_ROLE;
28+
import static org.labkey.test.util.PermissionsHelper.TROUBLESHOOTER_ROLE;
2829

2930
public abstract class AbstractAdminConsoleTest extends BaseWebDriverTest
3031
{
@@ -79,7 +80,7 @@ protected void createTestUsers()
7980

8081
int troubleshooterId = _userHelper.createUser(TROUBLESHOOTER_USER, true, false).getUserId();
8182
setInitialPassword(troubleshooterId);
82-
apiPermissionsHelper.addMemberToRole(TROUBLESHOOTER_USER, "Troubleshooter", PermissionsHelper.MemberType.user, "/");
83+
apiPermissionsHelper.addMemberToRole(TROUBLESHOOTER_USER, TROUBLESHOOTER_ROLE, PermissionsHelper.MemberType.user, "/");
8384
}
8485

8586
@Override

src/org/labkey/test/tests/AdminConsoleNavigationTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,13 @@
3333

3434
import static org.junit.Assert.assertFalse;
3535
import static org.junit.Assert.assertTrue;
36+
import static org.labkey.test.util.PermissionsHelper.TROUBLESHOOTER_ROLE;
3637

3738
@Category({Git.class})
3839
@BaseWebDriverTest.ClassTimeout(minutes = 6)
3940
public class AdminConsoleNavigationTest extends BaseWebDriverTest
4041
{
41-
private static final String TROUBLESHOOTER = "troubleshooter@adminconsolelinks.test";
42+
private static final String TROUBLESHOOTER_USER = "troubleshooter@adminconsolelinks.test";
4243
private static final String NON_ADMIN = "nonadmin@adminconsolelinks.test";
4344

4445
public ApiPermissionsHelper _apiPermissionsHelper = new ApiPermissionsHelper(this);
@@ -52,16 +53,16 @@ public static void setupProject()
5253

5354
private void doSetup()
5455
{
55-
_userHelper.createUser(TROUBLESHOOTER);
56-
_apiPermissionsHelper.addMemberToRole(TROUBLESHOOTER, "Troubleshooter", PermissionsHelper.MemberType.user, "/");
56+
_userHelper.createUser(TROUBLESHOOTER_USER);
57+
_apiPermissionsHelper.addMemberToRole(TROUBLESHOOTER_USER, TROUBLESHOOTER_ROLE, PermissionsHelper.MemberType.user, "/");
5758

5859
_userHelper.createUser(NON_ADMIN);
5960
}
6061

6162
@Override
6263
protected void doCleanup(boolean afterTest)
6364
{
64-
_userHelper.deleteUsers(false, TROUBLESHOOTER, NON_ADMIN);
65+
_userHelper.deleteUsers(false, TROUBLESHOOTER_USER, NON_ADMIN);
6566
}
6667

6768
@Test
@@ -120,7 +121,7 @@ public void testTroubleshooterLinkAccess()
120121
"Profiler" //Profiler can be edited by the troubleshooter
121122
));
122123
ShowAdminPage adminConsole = goToAdminConsole();
123-
impersonate(TROUBLESHOOTER);
124+
impersonate(TROUBLESHOOTER_USER);
124125
Map<String, String> linkHrefs = new LinkedHashMap<>();
125126
List<WebElement> troubleshooterLinks = adminConsole.getAllAdminConsoleLinks();
126127
assertTrue(String.format("Failed sanity check. Only found %s admin links. There should be more.", troubleshooterLinks.size()), troubleshooterLinks.size() > 10);
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package org.labkey.test.tests;
2+
3+
import org.apache.commons.lang3.CharUtils;
4+
import org.json.JSONObject;
5+
import org.json.JSONTokener;
6+
import org.junit.Assert;
7+
import org.junit.Test;
8+
import org.junit.experimental.categories.Category;
9+
import org.junit.runner.RunWith;
10+
import org.junit.runners.Parameterized;
11+
import org.labkey.serverapi.reader.Readers;
12+
import org.labkey.test.TestFileUtils;
13+
import org.labkey.test.util.TestLogger;
14+
15+
import java.io.File;
16+
import java.io.Reader;
17+
import java.net.URI;
18+
import java.net.URISyntaxException;
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.Collection;
22+
import java.util.List;
23+
import java.util.Objects;
24+
import java.util.Set;
25+
26+
@Category({})
27+
@RunWith(Parameterized.class)
28+
public class PackageLockJsonTest
29+
{
30+
private static final Set<String> ALLOWED_DEPENDENCY_HOSTS = Set.of("registry.npmjs.org", "labkey.jfrog.io");
31+
// Allow-list of '@isaacs/cliui' dependencies
32+
private static final Set<String> ALLOWED_NONSTANDARD_VERSIONS = Set.of("npm:string-width@^4.2.0", "npm:strip-ansi@^6.0.1", "npm:wrap-ansi@^7.0.0");
33+
34+
private final List<String> errors = new ArrayList<>();
35+
private final File moduleDir;
36+
37+
public PackageLockJsonTest(File moduleDir, String name)
38+
{
39+
this.moduleDir = moduleDir;
40+
}
41+
42+
@Parameterized.Parameters(name = "{1}")
43+
public static Collection<Object[]> data()
44+
{
45+
List<File> allModules = new ArrayList<>();
46+
47+
File modulesDir = new File(TestFileUtils.getLabKeyRoot(), "server/modules");
48+
File[] files = modulesDir.listFiles();
49+
if (files == null)
50+
{
51+
throw new RuntimeException("No files found in modules directory: " + modulesDir.getAbsolutePath());
52+
}
53+
for (File file : files)
54+
{
55+
if (file.isDirectory())
56+
{
57+
if (new File(file, "module.properties").exists())
58+
{
59+
allModules.add(file);
60+
}
61+
else
62+
{
63+
allModules.addAll(Arrays.stream(Objects.requireNonNull(file.listFiles(), () -> "No files found in " + file.getAbsolutePath()))
64+
.filter(f -> f.isDirectory() && new File(f, "module.properties").isFile()).toList());
65+
}
66+
}
67+
}
68+
69+
return allModules.stream().filter(file -> new File(file, "package-lock.json").exists())
70+
.map(f -> new Object[]{f, f.getName()}).toList();
71+
}
72+
73+
@Test
74+
public void testPackageLock() throws Exception
75+
{
76+
File packageLock = new File(moduleDir, "package-lock.json");
77+
Assert.assertTrue("No package-lock.json found in module: " + moduleDir.getAbsolutePath(), packageLock.isFile());
78+
79+
TestLogger.log("Testing module: " + moduleDir.getAbsolutePath());
80+
81+
JSONObject packages;
82+
try (Reader reader = Readers.getReader(packageLock))
83+
{
84+
JSONObject jsonObject = new JSONObject(new JSONTokener(reader));
85+
packages = jsonObject.optJSONObject("packages");
86+
if (packages == null)
87+
packages = jsonObject.getJSONObject("dependencies"); // old lockfile version
88+
}
89+
90+
for (String packageName : packages.keySet())
91+
{
92+
if (!packageName.isBlank())
93+
{
94+
JSONObject packageJson = packages.getJSONObject(packageName);
95+
verifyPackage(packageName, packageJson, packageLock);
96+
}
97+
}
98+
99+
Assert.assertTrue("Bad sources: " + errors, errors.isEmpty());
100+
}
101+
102+
/// Verify that a package reference in a package-lock.json file only resolves to known hosts and has a valid version
103+
/// Also checks sub-dependencies
104+
private void verifyPackage(String packageName, JSONObject packageJson, File packageLockFile)
105+
{
106+
String resolved = packageJson.optString("resolved");
107+
if (resolved.isBlank())
108+
{
109+
TestLogger.debug("Resolved field is blank for package " + packageName + " in " + packageLockFile.getAbsolutePath());
110+
}
111+
else
112+
{
113+
try
114+
{
115+
URI resolvedURL = new URI(resolved);
116+
String host = resolvedURL.getHost();
117+
if (!ALLOWED_DEPENDENCY_HOSTS.contains(host))
118+
{
119+
String message = "Package " + packageName + " resolved to unrecognized host [" + host + "] in " + packageLockFile.getAbsolutePath();
120+
errors.add(message);
121+
TestLogger.error(message);
122+
}
123+
}
124+
catch (URISyntaxException e)
125+
{
126+
String message = "Package " + packageName + " resolved to an invalid location [" + resolved + "] in " + packageLockFile.getAbsolutePath();
127+
errors.add(message);
128+
TestLogger.error(message);
129+
}
130+
}
131+
132+
String version = packageJson.optString("version");
133+
if (version.isBlank() || !CharUtils.isAsciiNumeric(version.charAt(0)))
134+
{
135+
String message = "Package " + packageName + " has bad version [" + version + "] in " + packageLockFile.getAbsolutePath();
136+
errors.add(message);
137+
TestLogger.error(message);
138+
}
139+
140+
JSONObject transitiveDeps = packageJson.optJSONObject("dependencies", new JSONObject());
141+
for (String tDep : transitiveDeps.keySet())
142+
{
143+
JSONObject packageJsonDep = transitiveDeps.optJSONObject(tDep);
144+
if (packageJsonDep != null)
145+
{
146+
verifyPackage(tDep, packageJsonDep, packageLockFile); // belt and suspenders
147+
}
148+
else
149+
{
150+
String tVer = transitiveDeps.optString(tDep);
151+
if (tVer == null || tVer.contains(":") && !ALLOWED_NONSTANDARD_VERSIONS.contains(tVer)) // URL, file, or workspace dependency
152+
{
153+
String message = "Package " + packageName + " has bad transitive dependency [" + tVer + "] in " + packageLockFile.getAbsolutePath();
154+
errors.add(message);
155+
TestLogger.error(message);
156+
}
157+
}
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)