Skip to content

Commit 9cf2827

Browse files
deckertron9000William Decker
andauthored
Added GetAttributesAsync to SftpClient (#1648)
* Added GetAttributesAsync to SftpClient * Adding integration tests + unit test * Address warnings in test classes. --------- Co-authored-by: William Decker <[email protected]>
1 parent 03e2821 commit 9cf2827

File tree

7 files changed

+238
-0
lines changed

7 files changed

+238
-0
lines changed

src/Renci.SshNet/ISftpClient.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,21 @@ public interface ISftpClient : IBaseClient
700700
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
701701
SftpFileAttributes GetAttributes(string path);
702702

703+
/// <summary>
704+
/// Gets the <see cref="SftpFileAttributes"/> of the file on the path.
705+
/// </summary>
706+
/// <param name="path">The path to the file.</param>
707+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
708+
/// <returns>
709+
/// A <see cref="Task{SftpFileAttributes}"/> that represents the attribute retrieval operation.
710+
/// The task result contains the <see cref="SftpFileAttributes"/> of the file on the path.
711+
/// </returns>
712+
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
713+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
714+
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
715+
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
716+
Task<SftpFileAttributes> GetAttributesAsync(string path, CancellationToken cancellationToken);
717+
703718
/// <summary>
704719
/// Returns the date and time the specified file or directory was last accessed.
705720
/// </summary>

src/Renci.SshNet/SftpClient.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2094,6 +2094,33 @@ public SftpFileAttributes GetAttributes(string path)
20942094
return _sftpSession.RequestLStat(fullPath);
20952095
}
20962096

2097+
/// <summary>
2098+
/// Gets the <see cref="SftpFileAttributes"/> of the file on the path.
2099+
/// </summary>
2100+
/// <param name="path">The path to the file.</param>
2101+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
2102+
/// <returns>
2103+
/// A <see cref="Task{SftpFileAttributes}"/> that represents the attribute retrieval operation.
2104+
/// The task result contains the <see cref="SftpFileAttributes"/> of the file on the path.
2105+
/// </returns>
2106+
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
2107+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
2108+
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
2109+
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
2110+
public async Task<SftpFileAttributes> GetAttributesAsync(string path, CancellationToken cancellationToken)
2111+
{
2112+
CheckDisposed();
2113+
2114+
if (_sftpSession is null)
2115+
{
2116+
throw new SshConnectionException("Client not connected.");
2117+
}
2118+
2119+
var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
2120+
2121+
return await _sftpSession.RequestLStatAsync(fullPath, cancellationToken).ConfigureAwait(false);
2122+
}
2123+
20972124
/// <summary>
20982125
/// Sets the specified <see cref="SftpFileAttributes"/> of the file on the specified path.
20992126
/// </summary>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using Renci.SshNet.Common;
2+
3+
namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
4+
{
5+
public partial class SftpClientTest
6+
{
7+
[TestMethod]
8+
[TestCategory("Sftp")]
9+
public void Test_Sftp_GetAttributes_Not_Exists()
10+
{
11+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
12+
{
13+
sftp.Connect();
14+
15+
Assert.ThrowsException<SftpPathNotFoundException>(() => sftp.GetAttributes("/asdfgh"));
16+
}
17+
}
18+
19+
[TestMethod]
20+
[TestCategory("Sftp")]
21+
public void Test_Sftp_GetAttributes_Null()
22+
{
23+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
24+
{
25+
sftp.Connect();
26+
27+
Assert.ThrowsException<ArgumentNullException>(() => sftp.GetAttributes(null));
28+
}
29+
}
30+
31+
[TestMethod]
32+
[TestCategory("Sftp")]
33+
public void Test_Sftp_GetAttributes_Current()
34+
{
35+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
36+
{
37+
sftp.Connect();
38+
39+
var attributes = sftp.GetAttributes(".");
40+
41+
Assert.IsNotNull(attributes);
42+
43+
sftp.Disconnect();
44+
}
45+
}
46+
}
47+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Renci.SshNet.Common;
2+
3+
namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
4+
{
5+
/// <summary>
6+
/// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
7+
/// </summary>
8+
public partial class SftpClientTest
9+
{
10+
[TestMethod]
11+
[TestCategory("Sftp")]
12+
public async Task Test_Sftp_GetAttributesAsync_Not_Exists()
13+
{
14+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
15+
{
16+
var cts = new CancellationTokenSource();
17+
cts.CancelAfter(TimeSpan.FromMinutes(1));
18+
19+
await sftp.ConnectAsync(cts.Token);
20+
21+
await Assert.ThrowsExceptionAsync<SftpPathNotFoundException>(async () => await sftp.GetAttributesAsync("/asdfgh", cts.Token));
22+
}
23+
}
24+
25+
[TestMethod]
26+
[TestCategory("Sftp")]
27+
public async Task Test_Sftp_GetAttributesAsync_Null()
28+
{
29+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
30+
{
31+
var cts = new CancellationTokenSource();
32+
cts.CancelAfter(TimeSpan.FromMinutes(1));
33+
34+
await sftp.ConnectAsync(cts.Token);
35+
36+
await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await sftp.GetAttributesAsync(null, cts.Token));
37+
}
38+
}
39+
40+
[TestMethod]
41+
[TestCategory("Sftp")]
42+
public async Task Test_Sftp_GetAttributesAsync_Current()
43+
{
44+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
45+
{
46+
var cts = new CancellationTokenSource();
47+
cts.CancelAfter(TimeSpan.FromMinutes(1));
48+
49+
await sftp.ConnectAsync(cts.Token);
50+
51+
var fileAttributes = await sftp.GetAttributesAsync(".", cts.Token);
52+
53+
Assert.IsNotNull(fileAttributes);
54+
55+
sftp.Disconnect();
56+
}
57+
}
58+
}
59+
}

test/Renci.SshNet.IntegrationTests/SftpClientTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,33 @@ public async Task Create_file_and_delete_using_DeleteAsync()
177177

178178
Assert.IsFalse(await _sftpClient.ExistsAsync(testFileName).ConfigureAwait(false));
179179
}
180+
181+
[TestMethod]
182+
public void Create_file_and_use_GetAttributes()
183+
{
184+
var testFileName = "test-file.txt";
185+
var testContent = "file content";
186+
187+
using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
188+
_sftpClient.UploadFile(fileStream, testFileName);
189+
190+
var attributes = _sftpClient.GetAttributes(testFileName);
191+
Assert.IsNotNull(attributes);
192+
Assert.IsTrue(attributes.IsRegularFile);
193+
}
194+
195+
[TestMethod]
196+
public async Task Create_file_and_use_GetAttributesAsync()
197+
{
198+
var testFileName = "test-file.txt";
199+
var testContent = "file content";
200+
201+
using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
202+
await _sftpClient.UploadFileAsync(fileStream, testFileName).ConfigureAwait(false);
203+
204+
var attributes = await _sftpClient.GetAttributesAsync(testFileName, CancellationToken.None).ConfigureAwait(false);
205+
Assert.IsNotNull(attributes);
206+
Assert.IsTrue(attributes.IsRegularFile);
207+
}
180208
}
181209
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
5+
using Renci.SshNet.Common;
6+
using Renci.SshNet.Tests.Properties;
7+
8+
namespace Renci.SshNet.Tests.Classes
9+
{
10+
public partial class SftpClientTest
11+
{
12+
[TestMethod]
13+
public void GetAttributes_Throws_WhenNotConnected()
14+
{
15+
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
16+
{
17+
Assert.ThrowsException<SshConnectionException>(() => sftp.GetAttributes("."));
18+
}
19+
}
20+
21+
[TestMethod]
22+
public void GetAttributes_Throws_WhenDisposed()
23+
{
24+
var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD);
25+
sftp.Dispose();
26+
27+
Assert.ThrowsException<ObjectDisposedException>(() => sftp.GetAttributes("."));
28+
}
29+
}
30+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
using Microsoft.VisualStudio.TestTools.UnitTesting;
6+
7+
using Renci.SshNet.Common;
8+
using Renci.SshNet.Tests.Properties;
9+
10+
namespace Renci.SshNet.Tests.Classes
11+
{
12+
public partial class SftpClientTest
13+
{
14+
[TestMethod]
15+
public async Task GetAttributesAsync_Throws_WhenNotConnected()
16+
{
17+
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
18+
{
19+
await Assert.ThrowsExceptionAsync<SshConnectionException>(() => sftp.GetAttributesAsync(".", CancellationToken.None));
20+
}
21+
}
22+
23+
[TestMethod]
24+
public async Task GetAttributesAsync_Throws_WhenDisposed()
25+
{
26+
var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD);
27+
sftp.Dispose();
28+
29+
await Assert.ThrowsExceptionAsync<ObjectDisposedException>(() => sftp.GetAttributesAsync(".", CancellationToken.None));
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)