View Javadoc
1   package emissary.place;
2   
3   import emissary.core.DataObjectFactory;
4   import emissary.core.IBaseDataObject;
5   import emissary.test.core.junit5.UnitTest;
6   import emissary.util.io.ResourceReader;
7   import emissary.util.shell.Executrix;
8   
9   import org.apache.commons.io.IOUtils;
10  import org.junit.jupiter.api.AfterEach;
11  import org.junit.jupiter.api.BeforeEach;
12  import org.junit.jupiter.api.Test;
13  import org.slf4j.Logger;
14  import org.slf4j.LoggerFactory;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.io.OutputStream;
19  import java.nio.charset.StandardCharsets;
20  import java.nio.file.Files;
21  import java.nio.file.Path;
22  import java.nio.file.Paths;
23  import javax.annotation.Nullable;
24  
25  import static org.junit.jupiter.api.Assertions.assertEquals;
26  import static org.junit.jupiter.api.Assertions.assertNotNull;
27  import static org.junit.jupiter.api.Assertions.assertNull;
28  import static org.mockito.ArgumentMatchers.anyString;
29  import static org.mockito.ArgumentMatchers.eq;
30  import static org.mockito.ArgumentMatchers.isA;
31  import static org.mockito.ArgumentMatchers.isNull;
32  import static org.mockito.Mockito.mock;
33  import static org.mockito.Mockito.times;
34  import static org.mockito.Mockito.validateMockitoUsage;
35  import static org.mockito.Mockito.verify;
36  import static org.mockito.Mockito.when;
37  
38  class UnixCommandPlaceTest extends UnitTest {
39      @Nullable
40      private UnixCommandPlace place;
41      private static final Logger logger = LoggerFactory.getLogger(UnixCommandPlaceTest.class);
42      private Path scriptFile;
43      private static final String W = "Президент Буш";
44      @Nullable
45      private IBaseDataObject payload;
46      private static final String FORM = "TEST";
47  
48      @Override
49      @BeforeEach
50      public void setUp() throws Exception {
51          scriptFile = Paths.get(TMPDIR, "testUnixCommand.sh");
52          // read our default config for this place, not something else that got configured in
53          try (InputStream is = new ResourceReader().getConfigDataAsStream(this.getClass())) {
54              place = new UnixCommandPlace(is);
55              place.executrix.setTmpDir(TMPDIR);
56              place.executrix.setCommand(TMPDIR + "/testUnixCommand.sh <INPUT_NAME> <OUTPUT_NAME>");
57          } catch (Exception ex) {
58              logger.error("Cannot create UnixCommandPlace", ex);
59          }
60  
61          payload = DataObjectFactory.getInstance(new Object[] {"abcdefg".getBytes(), "myPayload", FORM});
62      }
63  
64      @Override
65      @AfterEach
66      public void tearDown() throws Exception {
67          super.tearDown();
68          place.shutDown();
69          place = null;
70          payload = null;
71          Files.deleteIfExists(scriptFile);
72          validateMockitoUsage();
73      }
74  
75      @Test
76      void testUnixCommandPlaceStdout() throws Exception {
77          assertNotNull(place, "Place must be created");
78          createScript(Executrix.OUTPUT_TYPE.STD);
79  
80          place.process(payload);
81          byte[] altView = payload.getAlternateView("TEST_VIEW");
82          assertNotNull(altView, "Alt view should have been created");
83          assertEquals(FORM, payload.currentForm(), "Payload should have same current form");
84          assertEquals(W, new String(altView).trim(), "Clean UTF-8 coming from the script must be maintained");
85      }
86  
87      @Test
88      void testUnixCommandPlaceFile() throws Exception {
89          assertNotNull(place, "Place must be created");
90          place.setFileOutputCommand();
91          createScript(Executrix.OUTPUT_TYPE.FILE);
92  
93          place.process(payload);
94          byte[] altView = payload.getAlternateView("TEST_VIEW");
95          assertNotNull(altView, "Alt view should have been created");
96          assertEquals(FORM, payload.currentForm(), "Payload should have same current form");
97          assertEquals(W, new String(altView).trim(), "Clean UTF-8 coming from the script must be maintained");
98      }
99  
100     @Test
101     void testUnixCommandPlaceLogging() throws Exception {
102         assertNotNull(place, "Place must be created");
103         Logger mockLogger = mock(Logger.class);
104         place.setLogger(mockLogger);
105         createLogScript();
106         place.process(payload);
107         verify(mockLogger, times(LOG_MSGS.length)).info(anyString());
108     }
109 
110     @Test
111     void testFileProcess() throws Exception {
112         Executrix e = mock(Executrix.class);
113 
114         // set up three possible scenarios and force return codes from the execute method
115         when(e.execute(eq(new String[] {"negative"}), (StringBuilder) isNull(), isA(StringBuilder.class))).thenReturn(-1);
116         when(e.execute(eq(new String[] {"zero"}), (StringBuilder) isNull(), isA(StringBuilder.class))).thenReturn(0);
117         when(e.execute(eq(new String[] {"positive"}), (StringBuilder) isNull(), isA(StringBuilder.class))).thenReturn(1);
118 
119         place.setExecutrix(e);
120 
121         // fake an output file and load it with some data
122         String DATA = "test-test";
123         Path outputFile = Paths.get(TMPDIR, "output.out");
124 
125         try {
126             IOUtils.write(DATA, Files.newOutputStream(outputFile), StandardCharsets.UTF_8);
127 
128             // null is returned in situations with a non-zero return code
129             assertNull(place.fileProcess(new String[] {"negative"}, outputFile.toAbsolutePath().toString()));
130             assertNull(place.fileProcess(new String[] {"positive"}, outputFile.toAbsolutePath().toString()));
131 
132             // a successful execution will return the bytes of the specified output file
133             assertEquals(DATA, new String(place.fileProcess(new String[] {"zero"}, outputFile.toAbsolutePath().toString())));
134         } finally {
135             Files.deleteIfExists(outputFile);
136         }
137     }
138 
139     @Test
140     void testStdOutProcess() {
141         Executrix e = mock(Executrix.class);
142 
143         // set up three possible scenarios and force return codes from the execute method
144         when(e.execute(eq(new String[] {"negative"}), isA(StringBuilder.class), isA(StringBuilder.class), eq(place.charset))).thenReturn(-1);
145         when(e.execute(eq(new String[] {"zero"}), isA(StringBuilder.class), isA(StringBuilder.class), eq(place.charset))).thenReturn(0);
146         when(e.execute(eq(new String[] {"positive"}), isA(StringBuilder.class), isA(StringBuilder.class), eq(place.charset))).thenReturn(1);
147 
148         place.setExecutrix(e);
149 
150         // null is returned in situations with a non-zero return code
151         assertNull(place.stdOutProcess(new String[] {"negative"}, false));
152         assertNull(place.stdOutProcess(new String[] {"positive"}, false));
153 
154         // we didn't actually execute anything, so the result is empty
155         assertEquals("", new String(place.stdOutProcess(new String[] {"zero"}, false)));
156     }
157 
158     private static final String[] LOG_MSGS = {"ERROR script error message", "WARN script warn message", "INFO script info message",
159             "DEBUG script debug message"};
160 
161     private void createLogScript() throws IOException {
162         try (OutputStream fos = startScript()) {
163 
164             // Add messages to the log file, name matched to serviceName from place key
165             for (String msg : LOG_MSGS) {
166                 fos.write(("echo '" + msg + "' >> UCP.log\n").getBytes());
167             }
168 
169             // Make some output
170             fos.write("cat ${1} > ${2}\n".getBytes());
171             scriptFile.toFile().setExecutable(true); // jdk 1.6+ only
172         }
173     }
174 
175     private OutputStream startScript() throws IOException {
176         Files.deleteIfExists(scriptFile);
177         OutputStream fos = Files.newOutputStream(scriptFile);
178         fos.write("#!/bin/bash\n".getBytes());
179         return fos;
180     }
181 
182     private void createScript(Executrix.OUTPUT_TYPE ot) throws IOException {
183         try (OutputStream fos = startScript()) {
184             fos.write(("echo '" + W + "'").getBytes());
185             if (ot == Executrix.OUTPUT_TYPE.FILE) {
186                 fos.write(" > ${2}".getBytes());
187             }
188             fos.write('\n');
189             scriptFile.toFile().setExecutable(true); // jdk 1.6+ only
190         }
191     }
192 
193 }