-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathAssayTransformMissingParentDirTest.java
More file actions
144 lines (129 loc) · 7.3 KB
/
AssayTransformMissingParentDirTest.java
File metadata and controls
144 lines (129 loc) · 7.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package org.labkey.test.tests.assay;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.labkey.api.util.FileUtil;
import org.labkey.api.util.StringUtilsLabKey;
import org.labkey.test.Locator;
import org.labkey.test.TestFileUtils;
import org.labkey.test.categories.Assays;
import org.labkey.test.categories.Daily;
import org.labkey.test.components.assay.AssayConstants;
import org.labkey.test.pages.ReactAssayDesignerPage;
import org.labkey.test.pages.assay.AssayImportPage;
import org.labkey.test.pages.assay.AssayRunsPage;
import org.labkey.test.params.assay.GeneralAssayDesign;
import org.labkey.test.util.search.SearchAdminAPIHelper;
import java.io.File;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
/**
* Issue 54156: Regression test to ensure a reasonable error message is shown when an assay design references
* a transform script whose parent directory has since been deleted, and that the assay design can be fixed by removing the script.
*/
@Category({Assays.class, Daily.class})
public class AssayTransformMissingParentDirTest extends AbstractAssayTransformTest
{
@Test
public void testMissingParentDirectoryRegression() throws Exception
{
// Create a nested directory and an R transform script within it
String assayName = "missingParentDirAssay";
Path parentDir = Files.createTempDirectory("assay-transform-parent-");
Path nestedDir = FileUtil.createDirectories(parentDir.resolve("child"), false);
String scriptName = "transformMissingParent.R";
String transformContent = "library(Rlabkey);";
File transformFile = nestedDir.resolve(scriptName).toFile();
TestFileUtils.writeFile(transformFile, transformContent);
// Create a General assay and add the transform by absolute path (not upload)
var protocolResponse = new GeneralAssayDesign(assayName).createAssay(getProjectName(), createDefaultConnection());
var assayDesignerPage = ReactAssayDesignerPage.beginAt(this, getProjectName(), protocolResponse.getProtocolId(),
"general", getURL().toString());
assayDesignerPage.goToBatchFields().removeAllFields(true);
// add by path so the absolute path is stored; this allows reproducing the missing parent dir scenario
assayDesignerPage.addTransformScript(transformFile);
assayDesignerPage.clickSave();
// Now delete the parent dir to ensure we handle it reasonably.
// Sometimes on Windows the directory could be locked, maybe by an external process, or the child directory is
// readonly. Use a retry mechanism to set the writeable flag and then try to delete the parent directory.
for (int attempt = 1; attempt <= 10; attempt++) {
try
{
parentDir.toFile().setWritable(true, false);
// Wrap in a try to close the stream.
try (Stream<Path> files = Files.walk(parentDir)) {
files.forEach(p -> p.toFile().setWritable(true, false));
}
FileUtils.deleteDirectory(parentDir.toFile());
log(String.format("Deletion of directory %s was successful.", parentDir));
break;
} catch (AccessDeniedException deniedException) {
// Yes I know AccessDeniedException is a subset of an IOException, but I wanted to log explicitly a
// failure and retry because of an AccessDeniedException from some other IOException.
log(String.format("Access denied trying to delete directory %s. Error: %s. Waiting 10s and retrying. Attempt %d of 10.",
parentDir, deniedException.getMessage(), attempt));
if (attempt == 10) throw deniedException;
sleep(10_000);
}
catch (IOException ioException) {
log(String.format("IOException trying to delete directory %s. Error: %s. Waiting 10s and retrying. Attempt %d of 10.",
parentDir, ioException.getMessage(), attempt));
if (attempt == 10)
{
log("Dump the heap.");
dumpHeap();
if (SystemUtils.IS_OS_WINDOWS) {
try {
log("Lock diagnostic...");
ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process p = pb.start();
String output = new String(p.getInputStream().readAllBytes(), StringUtilsLabKey.DEFAULT_CHARSET);
log("Running processes:\n" + output);
} catch (IOException diagnosticException) {
log("Failed to run lock diagnostic: " + diagnosticException.getMessage());
}
}
throw ioException;
}
sleep(10_000);
}
}
// Attempt to import data and verify a reasonable error message is shown
String importData = """
VisitID\tParticipantID\tComment
1\tP1\timport after parent deleted
""";
clickAndWait(Locator.linkWithText(assayName));
new AssayRunsPage(getDriver()).getTable().clickHeaderButtonAndWait("Import Data");
var importPage = new AssayImportPage(getDriver());
importPage.setNamedInputText("Name", "missingParentImport");
importPage.setNamedTextAreaValue(AssayConstants.TEXT_AREA_DATA_COLLECTOR_TEXT_AREA_NAME, importData);
importPage.clickSaveAndFinish();
// Expect an error page/message indicating the transform script path cannot be used
// Be tolerant to platform-specific phrasing; assert any of these appear
String expectedPath = transformFile.getAbsolutePath();
checker().withScreenshot("missing-parent-error")
.verifyTrue("Expect an error message about the transform script path not being found",
isTextPresent("transformMissingParent.R, configured for this assay does not exist."));
// Fix the assay design by removing the transform script
goToProjectHome();
assayDesignerPage = ReactAssayDesignerPage.beginAt(this, getProjectName(), protocolResponse.getProtocolId(),
"general", getURL().toString());
assayDesignerPage.removeTransformScript(scriptName);
assayDesignerPage.clickSave();
// Retry the import and verify it succeeds without the transform
clickAndWait(Locator.linkWithText(assayName));
new AssayRunsPage(getDriver()).getTable().clickHeaderButtonAndWait("Import Data");
importPage = new AssayImportPage(getDriver());
importPage.setNamedInputText("Name", "fixedAssayImport");
importPage.setNamedTextAreaValue(AssayConstants.TEXT_AREA_DATA_COLLECTOR_TEXT_AREA_NAME, importData);
importPage.clickSaveAndFinish();
// Verify we land on the run details page and can see the run name (no transform needed)
new AssayRunsPage(getDriver()).clickAssayIdLink("fixedAssayImport");
}
}