2 * ============LICENSE_START======================================================================
3 * Copyright (C) 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
7 * except in compliance with the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software distributed under the
12 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 * either express or implied. See the License for the specific language governing permissions
14 * and limitations under the License.
15 * ============LICENSE_END========================================================================
18 package org.oran.datafile.ftp;
20 import static org.assertj.core.api.Assertions.assertThatThrownBy;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.ArgumentMatchers.anyString;
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.doThrow;
27 import static org.mockito.Mockito.spy;
28 import static org.mockito.Mockito.verify;
29 import static org.mockito.Mockito.verifyNoMoreInteractions;
30 import static org.mockito.Mockito.when;
32 import com.jcraft.jsch.ChannelSftp;
33 import com.jcraft.jsch.JSch;
34 import com.jcraft.jsch.JSchException;
35 import com.jcraft.jsch.Session;
36 import com.jcraft.jsch.SftpException;
38 import java.nio.file.Paths;
40 import org.junit.jupiter.api.Test;
41 import org.junit.jupiter.api.extension.ExtendWith;
42 import org.mockito.Mock;
43 import org.mockito.junit.jupiter.MockitoExtension;
44 import org.oran.datafile.commons.FileServerData;
45 import org.oran.datafile.configuration.SftpConfig;
46 import org.oran.datafile.exceptions.DatafileTaskException;
47 import org.oran.datafile.exceptions.NonRetryableDatafileTaskException;
49 @ExtendWith(MockitoExtension.class)
50 public class SftpClientTest {
52 private static final String HOST = "127.0.0.1";
53 private static final int SFTP_PORT = 1021;
54 private static final String USERNAME = "bob";
55 private static final String PASSWORD = "123";
58 private JSch jschMock;
61 private Session sessionMock;
64 private ChannelSftp channelMock;
67 public void openWithPort_success() throws Exception {
68 FileServerData expectedFileServerData = FileServerData.builder() //
69 .serverAddress(HOST) //
71 .password(PASSWORD) //
75 SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
77 doReturn(jschMock).when(sftpClientSpy).createJsch();
78 when(jschMock.getSession(anyString(), anyString(), anyInt())).thenReturn(sessionMock);
79 when(sessionMock.openChannel(anyString())).thenReturn(channelMock);
83 verify(jschMock).getSession(USERNAME, HOST, SFTP_PORT);
84 verify(sessionMock).setConfig("StrictHostKeyChecking", "no");
85 verify(sessionMock).setPassword(PASSWORD);
86 verify(sessionMock).connect();
87 verify(sessionMock).openChannel("sftp");
88 verifyNoMoreInteractions(sessionMock);
90 verify(channelMock).connect();
91 verifyNoMoreInteractions(channelMock);
95 public void openWithoutPort_success() throws Exception {
96 FileServerData expectedFileServerData = FileServerData.builder() //
97 .serverAddress(HOST) //
99 .password(PASSWORD) //
103 SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
105 doReturn(jschMock).when(sftpClientSpy).createJsch();
106 when(jschMock.getSession(anyString(), anyString(), anyInt())).thenReturn(sessionMock);
107 when(sessionMock.openChannel(anyString())).thenReturn(channelMock);
109 sftpClientSpy.open();
111 verify(jschMock).getSession(USERNAME, HOST, 22);
115 public void open_throwsExceptionWithRetry() throws Exception {
116 FileServerData expectedFileServerData = FileServerData.builder() //
117 .serverAddress(HOST) //
119 .password(PASSWORD) //
123 SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
125 doReturn(jschMock).when(sftpClientSpy).createJsch();
126 when(jschMock.getSession(anyString(), anyString(), anyInt())).thenThrow(new JSchException("Failed"));
128 DatafileTaskException exception = assertThrows(DatafileTaskException.class, () -> sftpClientSpy.open());
129 assertEquals("Could not open Sftp client. com.jcraft.jsch.JSchException: Failed", exception.getMessage());
133 public void openAuthFail_throwsExceptionWithoutRetry() throws Exception {
134 FileServerData expectedFileServerData = FileServerData.builder() //
135 .serverAddress(HOST) //
137 .password(PASSWORD) //
141 SftpClient sftpClientSpy = spy(new SftpClient(expectedFileServerData, createSampleSftpClientSettings()));
143 doReturn(jschMock).when(sftpClientSpy).createJsch();
144 when(jschMock.getSession(anyString(), anyString(), anyInt())).thenThrow(new JSchException("Auth fail"));
146 NonRetryableDatafileTaskException exception =
147 assertThrows(NonRetryableDatafileTaskException.class, () -> sftpClientSpy.open());
149 "Could not open Sftp client, no retry attempts will be done. com.jcraft.jsch.JSchException: Auth fail",
150 exception.getMessage());
153 @SuppressWarnings("resource")
155 public void collectFile_success() throws DatafileTaskException, SftpException {
156 FileServerData expectedFileServerData = FileServerData.builder() //
157 .serverAddress(HOST) //
159 .password(PASSWORD) //
162 SftpClient sftpClient = new SftpClient(expectedFileServerData, createSampleSftpClientSettings());
164 sftpClient.sftpChannel = channelMock;
166 sftpClient.collectFile("remote.xml", Paths.get("local.xml"));
168 verify(channelMock).get("remote.xml", "local.xml");
169 verifyNoMoreInteractions(channelMock);
173 public void collectFile_throwsExceptionWithRetry() throws SftpException {
174 FileServerData expectedFileServerData = FileServerData.builder() //
175 .serverAddress(HOST) //
177 .password(PASSWORD) //
181 try (SftpClient sftpClient = new SftpClient(expectedFileServerData, createSampleSftpClientSettings())) {
182 sftpClient.sftpChannel = channelMock;
183 doThrow(new SftpException(ChannelSftp.SSH_FX_BAD_MESSAGE, "Failed")).when(channelMock).get(anyString(),
186 assertThatThrownBy(() -> sftpClient.collectFile("remoteFile", Paths.get("localFile")))
187 .isInstanceOf(DatafileTaskException.class).hasMessageStartingWith("Unable to get file from xNF. ")
188 .hasMessageContaining(HOST);
193 public void collectFileFileMissing_throwsExceptionWithoutRetry() throws SftpException {
194 FileServerData expectedFileServerData = FileServerData.builder() //
195 .serverAddress(HOST) //
197 .password(PASSWORD) //
201 try (SftpClient sftpClient = new SftpClient(expectedFileServerData, createSampleSftpClientSettings())) {
202 sftpClient.sftpChannel = channelMock;
203 doThrow(new SftpException(ChannelSftp.SSH_FX_NO_SUCH_FILE, "Failed")).when(channelMock).get(anyString(),
206 assertThatThrownBy(() -> sftpClient.collectFile("remoteFile", Paths.get("localFile")))
207 .isInstanceOf(NonRetryableDatafileTaskException.class)
208 .hasMessageStartingWith("Unable to get file from xNF. No retry attempts will be done")
209 .hasMessageContaining("" + SFTP_PORT);
214 public void close_success() {
215 SftpClient sftpClient = new SftpClient(null, createSampleSftpClientSettings());
217 sftpClient.session = sessionMock;
218 sftpClient.sftpChannel = channelMock;
222 verify(sessionMock).disconnect();
223 verifyNoMoreInteractions(sessionMock);
225 verify(channelMock).exit();;
226 verifyNoMoreInteractions(channelMock);
229 private SftpClientSettings createSampleSftpClientSettings() {
230 return new SftpClientSettings(createSampleSftpConfigNoStrictHostChecking());
233 private SftpConfig createSampleSftpConfigNoStrictHostChecking() {
234 return SftpConfig.builder() //
235 .strictHostKeyChecking(false).knownHostsFilePath("N/A").build();