Improve Test coverage of DFC
[nonrtric/plt/ranpm.git] / datafilecollector / src / test / java / org / oran / datafile / ftp / FtpesClientTest.java
1 /*
2  * ============LICENSE_START======================================================================
3  * Copyright (C) 2018-2023 Nordix Foundation. All rights reserved.
4  * Copyright (C) 2020 Nokia. All rights reserved.
5  * ===============================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7  * in compliance with the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software distributed under the License
12  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing permissions and limitations under
14  * the License.
15  * ============LICENSE_END========================================================================
16  */
17
18 package org.oran.datafile.ftp;
19
20 import static org.assertj.core.api.Assertions.assertThatThrownBy;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.Mockito.doReturn;
27 import static org.mockito.Mockito.doThrow;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.spy;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.verifyNoMoreInteractions;
33 import static org.mockito.Mockito.when;
34
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 import java.net.URISyntaxException;
39 import java.nio.file.Path;
40 import java.nio.file.Paths;
41 import java.security.KeyStoreException;
42 import java.security.NoSuchAlgorithmException;
43 import java.security.cert.CertificateException;
44 import javax.net.ssl.KeyManager;
45 import javax.net.ssl.TrustManager;
46 import org.apache.commons.net.ftp.FTP;
47 import org.apache.commons.net.ftp.FTPSClient;
48 import org.junit.jupiter.api.BeforeEach;
49 import org.junit.jupiter.api.Test;
50 import org.mockito.ArgumentMatchers;
51 import org.oran.datafile.exceptions.DatafileTaskException;
52 import org.oran.datafile.exceptions.NonRetryableDatafileTaskException;
53 import org.oran.datafile.model.FileServerData;
54 import org.springframework.http.HttpStatus;
55
56 class FtpesClientTest {
57
58     private static final String REMOTE_FILE_PATH = "/dir/sample.txt";
59     private static final Path LOCAL_FILE_PATH = Paths.get("target/sample.txt");
60     private static final String XNF_ADDRESS = "127.0.0.1";
61     private static final int PORT = 8021;
62     private static final String FTP_KEY_PATH = "ftpKeyPath";
63     private static final String FTP_KEY_PASSWORD_PATH = "ftpKeyPasswordPath";
64     private static final Path TRUSTED_CA_PATH = Paths.get("trustedCaPath");
65     private static final String TRUSTED_CA_PASSWORD = "trustedCaPassword";
66
67     private static final String USERNAME = "bob";
68     private static final String PASSWORD = "123";
69
70     private FTPSClient ftpsClientMock = mock(FTPSClient.class);
71     private KeyManager keyManagerMock = mock(KeyManager.class);
72     private TrustManager trustManagerMock = mock(TrustManager.class);
73     private InputStream inputStreamMock = mock(InputStream.class);
74     private OutputStream outputStreamMock = mock(OutputStream.class);
75
76     FtpesClient clientUnderTestSpy;
77
78     private FileServerData createFileServerData() {
79         return FileServerData.builder() //
80             .serverAddress(XNF_ADDRESS) //
81             .userId(USERNAME).password(PASSWORD) //
82             .port(PORT) //
83             .build();
84     }
85
86     @BeforeEach
87     protected void setUp() throws Exception {
88         clientUnderTestSpy = spy(new FtpesClient(createFileServerData(), Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH,
89             TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD));
90         clientUnderTestSpy.realFtpsClient = ftpsClientMock;
91     }
92
93     private void verifyFtpsClientMock_openOk() throws Exception {
94         doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
95
96         when(ftpsClientMock.retrieveFile(eq(REMOTE_FILE_PATH),
97             ArgumentMatchers.any(OutputStream.class))).thenReturn(true);
98         verify(ftpsClientMock).setNeedClientAuth(true);
99         verify(ftpsClientMock).setKeyManager(keyManagerMock);
100         verify(ftpsClientMock).setTrustManager(trustManagerMock);
101         verify(ftpsClientMock).connect(XNF_ADDRESS, PORT);
102         verify(ftpsClientMock).login(USERNAME, PASSWORD);
103         verify(ftpsClientMock).getReplyCode();
104         verify(ftpsClientMock, times(1)).enterLocalPassiveMode();
105         verify(ftpsClientMock).execPBSZ(0);
106         verify(ftpsClientMock).execPROT("P");
107         verify(ftpsClientMock).setFileType(FTP.BINARY_FILE_TYPE);
108         verify(ftpsClientMock).setBufferSize(1024 * 1024);
109     }
110
111     @Test
112     void collectFile_allOk() throws Exception {
113
114         doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH);
115         doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
116         doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
117         doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
118         doReturn(HttpStatus.OK.value()).when(ftpsClientMock).getReplyCode();
119
120         clientUnderTestSpy.open();
121
122         doReturn(true).when(ftpsClientMock).retrieveFile(REMOTE_FILE_PATH, outputStreamMock);
123         clientUnderTestSpy.collectFile(REMOTE_FILE_PATH, LOCAL_FILE_PATH);
124
125         doReturn(true).when(ftpsClientMock).isConnected();
126         clientUnderTestSpy.close();
127
128         verifyFtpsClientMock_openOk();
129         verify(ftpsClientMock, times(1)).isConnected();
130         verify(ftpsClientMock, times(1)).logout();
131         verify(ftpsClientMock, times(1)).disconnect();
132         verify(ftpsClientMock, times(1)).retrieveFile(eq(REMOTE_FILE_PATH), any());
133         verifyNoMoreInteractions(ftpsClientMock);
134     }
135
136     @Test
137     void collectFileFaultyOwnKey_shouldFail() throws Exception {
138
139         doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
140         assertThatThrownBy(() -> clientUnderTestSpy.open()).hasMessageContaining("Could not open connection:");
141
142         verify(ftpsClientMock).setNeedClientAuth(true);
143
144         doReturn(false).when(ftpsClientMock).isConnected();
145         clientUnderTestSpy.close();
146         verify(ftpsClientMock).isConnected();
147         verifyNoMoreInteractions(ftpsClientMock);
148     }
149
150     @Test
151     void collectFileFaultTrustedCA_shouldFail_no_trustedCA_file() throws Exception {
152
153         doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH);
154         doThrow(new IOException("problem")).when(clientUnderTestSpy).createInputStream(TRUSTED_CA_PATH);
155
156         assertThatThrownBy(() -> clientUnderTestSpy.open()).hasMessageContaining("Could not open connection:");
157
158     }
159
160     @Test
161     void collectFileFaultTrustedCA_shouldFail_empty_trustedCA_file() throws Exception {
162
163         doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH);
164         doReturn(inputStreamMock).when(clientUnderTestSpy).createInputStream(TRUSTED_CA_PATH);
165
166         assertThatThrownBy(() -> clientUnderTestSpy.open()).hasMessageContaining("Could not open connection: ");
167     }
168
169     @Test
170     void collectFileFaultyLogin_shouldFail() throws Exception {
171
172         doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH);
173         doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
174         doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
175         doReturn(false).when(ftpsClientMock).login(USERNAME, PASSWORD);
176
177         assertThatThrownBy(() -> clientUnderTestSpy.open()).hasMessage("Unable to log in to xNF. 127.0.0.1");
178
179         verify(ftpsClientMock).setNeedClientAuth(true);
180         verify(ftpsClientMock).setKeyManager(keyManagerMock);
181         verify(ftpsClientMock).setTrustManager(trustManagerMock);
182         verify(ftpsClientMock).connect(XNF_ADDRESS, PORT);
183         verify(ftpsClientMock).login(USERNAME, PASSWORD);
184     }
185
186     @Test
187     void collectFileBadRequestResponse_shouldFail() throws Exception {
188         doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH);
189         doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
190         doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
191         doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
192         doReturn(503).when(ftpsClientMock).getReplyCode();
193
194         assertThatThrownBy(() -> clientUnderTestSpy.open())
195             .hasMessage("Unable to connect to xNF. 127.0.0.1 xNF reply code: 503");
196
197         verify(ftpsClientMock).setNeedClientAuth(true);
198         verify(ftpsClientMock).setKeyManager(keyManagerMock);
199         verify(ftpsClientMock).setTrustManager(trustManagerMock);
200         verify(ftpsClientMock).connect(XNF_ADDRESS, PORT);
201         verify(ftpsClientMock).login(USERNAME, PASSWORD);
202         verify(ftpsClientMock, times(2)).getReplyCode();
203         verifyNoMoreInteractions(ftpsClientMock);
204     }
205
206     @Test
207     void collectFile_shouldFail() throws Exception {
208         doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH);
209         doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
210         doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
211         doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
212         doReturn(HttpStatus.OK.value()).when(ftpsClientMock).getReplyCode();
213         clientUnderTestSpy.open();
214
215         doReturn(false).when(ftpsClientMock).retrieveFile(REMOTE_FILE_PATH, outputStreamMock);
216
217         assertThatThrownBy(() -> clientUnderTestSpy.collectFile(REMOTE_FILE_PATH, LOCAL_FILE_PATH))
218             .hasMessageContaining(REMOTE_FILE_PATH).hasMessageContaining("No retry");
219
220         verifyFtpsClientMock_openOk();
221         verify(ftpsClientMock, times(1)).retrieveFile(eq(REMOTE_FILE_PATH), any());
222         verifyNoMoreInteractions(ftpsClientMock);
223     }
224
225     @Test
226     void collectFile_shouldFail_ioexception() throws Exception {
227         doReturn(keyManagerMock).when(clientUnderTestSpy).getKeyManager(Paths.get(FTP_KEY_PATH), FTP_KEY_PASSWORD_PATH);
228         doReturn(trustManagerMock).when(clientUnderTestSpy).getTrustManager(TRUSTED_CA_PATH, TRUSTED_CA_PASSWORD);
229         doReturn(outputStreamMock).when(clientUnderTestSpy).createOutputStream(LOCAL_FILE_PATH);
230         doReturn(true).when(ftpsClientMock).login(USERNAME, PASSWORD);
231         doReturn(HttpStatus.OK.value()).when(ftpsClientMock).getReplyCode();
232         clientUnderTestSpy.open();
233         when(ftpsClientMock.isConnected()).thenReturn(false);
234
235         doThrow(new IOException("problem")).when(ftpsClientMock).retrieveFile(REMOTE_FILE_PATH, outputStreamMock);
236
237         assertThatThrownBy(() -> clientUnderTestSpy.collectFile(REMOTE_FILE_PATH, LOCAL_FILE_PATH))
238             .hasMessage("Could not fetch file: java.io.IOException: problem");
239
240         verifyFtpsClientMock_openOk();
241         verify(ftpsClientMock, times(1)).retrieveFile(eq(REMOTE_FILE_PATH), any());
242         verifyNoMoreInteractions(ftpsClientMock);
243     }
244
245     @Test
246     void testCreateInputStream() throws IOException, URISyntaxException {
247         Path trustCaPath = Paths.get(getClass().getResource("/org/oran/datafile/datastore/file.txt").toURI());
248         InputStream actualCreateInputStreamResult = clientUnderTestSpy.createInputStream(trustCaPath);
249         assertNotNull(actualCreateInputStreamResult);
250     }
251
252     @Test
253     void testCreateOutputStream() throws IOException, URISyntaxException, DatafileTaskException {
254         Path trustCaPath = Paths.get(getClass().getResource("/org/oran/datafile/datastore/file.txt").toURI());
255         assertThrows(NonRetryableDatafileTaskException.class, () -> clientUnderTestSpy.createOutputStream(trustCaPath));
256     }
257
258     @Test
259     void testGetTrustManager2() throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
260         FileServerData fileServerData = FileServerData.builder()
261             .password("password123")
262             .port(8080)
263             .serverAddress("42 Main St")
264             .userId("42")
265             .build();
266         assertNull((new FtpesClient(fileServerData, Paths.get(System.getProperty("java.io.tmpdir"), "test.txt"),
267             "Key Cert Password Path", Paths.get(System.getProperty("java.io.tmpdir"), "test.txt"),
268             "Trusted Ca Password Path")).getTrustManager(null, "foo"));
269     }
270 }