diff --git a/.github/workflows/dotnet_test.yml b/.github/workflows/dotnet_test.yml index 84c57e2..f7daf84 100644 --- a/.github/workflows/dotnet_test.yml +++ b/.github/workflows/dotnet_test.yml @@ -14,13 +14,13 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore + dotnet-version: 10.0.x - name: Test - run: dotnet test --no-build --verbosity normal + run: | + dotnet test test/FileParty.Core.Tests/FileParty.Core.Tests.csproj --configuration Release --verbosity normal + dotnet test test/FileParty.Handlers.FileSystem.Tests/FileParty.Handlers.FileSystem.Tests.csproj --configuration Release --verbosity normal + dotnet test test/FileParty.Handlers.AWS.S3.Tests/FileParty.Handlers.AWS.S3.Tests.csproj --configuration Release-V3 --verbosity normal + dotnet test test/FileParty.Handlers.AWS.S3.Tests/FileParty.Handlers.AWS.S3.Tests.csproj --configuration Release-V4 --verbosity normal env: "fileparty_s3_region": ${{ secrets.TEST_S3_REGION }} "fileparty_s3_bucket": ${{ secrets.TEST_S3_BUCKET }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bda256e..2b29ade 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,7 +13,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 8.0.x + dotnet-version: 10.0.x - name: Build and Publish Nuget Packages id: csharp_nuget_publish uses: JankwareDotCom/nuget_publish@0.4.0 diff --git a/FileParty.sln b/FileParty.sln index d0bd762..ccd1f3a 100644 --- a/FileParty.sln +++ b/FileParty.sln @@ -27,36 +27,58 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Debug-V4|Any CPU = Debug-V4|Any CPU + Release-V4|Any CPU = Release-V4|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Debug|Any CPU.Build.0 = Debug|Any CPU {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Release|Any CPU.ActiveCfg = Release|Any CPU {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Release|Any CPU.Build.0 = Release|Any CPU + {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Debug-V4|Any CPU.ActiveCfg = Debug|Any CPU + {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Debug-V4|Any CPU.Build.0 = Debug|Any CPU + {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Release-V4|Any CPU.ActiveCfg = Release|Any CPU + {F4F0963C-2998-4B87-8BFD-2CF12A5ABDA4}.Release-V4|Any CPU.Build.0 = Release|Any CPU {792B430A-5277-44B8-9284-2D0C256DF22D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {792B430A-5277-44B8-9284-2D0C256DF22D}.Debug|Any CPU.Build.0 = Debug|Any CPU {792B430A-5277-44B8-9284-2D0C256DF22D}.Release|Any CPU.ActiveCfg = Release|Any CPU {792B430A-5277-44B8-9284-2D0C256DF22D}.Release|Any CPU.Build.0 = Release|Any CPU + {792B430A-5277-44B8-9284-2D0C256DF22D}.Debug-V4|Any CPU.ActiveCfg = Debug|Any CPU + {792B430A-5277-44B8-9284-2D0C256DF22D}.Debug-V4|Any CPU.Build.0 = Debug|Any CPU + {792B430A-5277-44B8-9284-2D0C256DF22D}.Release-V4|Any CPU.ActiveCfg = Release|Any CPU + {792B430A-5277-44B8-9284-2D0C256DF22D}.Release-V4|Any CPU.Build.0 = Release|Any CPU {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Debug|Any CPU.Build.0 = Debug|Any CPU {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Release|Any CPU.ActiveCfg = Release|Any CPU {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Release|Any CPU.Build.0 = Release|Any CPU - {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Release|Any CPU.Build.0 = Release|Any CPU + {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Debug-V4|Any CPU.ActiveCfg = Debug-V4|Any CPU + {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Debug-V4|Any CPU.Build.0 = Debug-V4|Any CPU + {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Release-V4|Any CPU.ActiveCfg = Release-V4|Any CPU + {739F3D2D-5541-47A3-9C97-5329FB8583CE}.Release-V4|Any CPU.Build.0 = Release-V4|Any CPU {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Debug|Any CPU.Build.0 = Debug|Any CPU {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Release|Any CPU.ActiveCfg = Release|Any CPU {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Release|Any CPU.Build.0 = Release|Any CPU + {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Debug-V4|Any CPU.ActiveCfg = Debug|Any CPU + {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Debug-V4|Any CPU.Build.0 = Debug|Any CPU + {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Release-V4|Any CPU.ActiveCfg = Release|Any CPU + {C17D1DB1-7C20-413E-BC69-A0656720FA33}.Release-V4|Any CPU.Build.0 = Release|Any CPU {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Debug|Any CPU.Build.0 = Debug|Any CPU {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Release|Any CPU.ActiveCfg = Release|Any CPU {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Release|Any CPU.Build.0 = Release|Any CPU - {22ED881A-4D94-4A4E-B908-BD1F4CCFB026}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {22ED881A-4D94-4A4E-B908-BD1F4CCFB026}.Debug|Any CPU.Build.0 = Debug|Any CPU - {22ED881A-4D94-4A4E-B908-BD1F4CCFB026}.Release|Any CPU.ActiveCfg = Release|Any CPU - {22ED881A-4D94-4A4E-B908-BD1F4CCFB026}.Release|Any CPU.Build.0 = Release|Any CPU + {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Debug-V4|Any CPU.ActiveCfg = Debug|Any CPU + {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Debug-V4|Any CPU.Build.0 = Debug|Any CPU + {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Release-V4|Any CPU.ActiveCfg = Release|Any CPU + {4ADB9737-3274-4E6C-ABA0-FE214D89807D}.Release-V4|Any CPU.Build.0 = Release|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Debug|Any CPU.ActiveCfg = Debug-V3|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Debug|Any CPU.Build.0 = Debug-V3|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Debug-V4|Any CPU.ActiveCfg = Debug-V4|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Debug-V4|Any CPU.Build.0 = Debug-V4|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Release|Any CPU.ActiveCfg = Release-V3|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Release|Any CPU.Build.0 = Release-V3|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Release-V4|Any CPU.ActiveCfg = Release-V4|Any CPU + {E557D9D3-A8B9-4D31-BD7A-631C13025ABA}.Release-V4|Any CPU.Build.0 = Release-V4|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {4ADB9737-3274-4E6C-ABA0-FE214D89807D} = {5C3F1ED6-EBB2-443C-8222-CC938FF2EB21} diff --git a/src/FileParty.Core/FileParty.Core.csproj b/src/FileParty.Core/FileParty.Core.csproj index 3be2eda..25955c4 100644 --- a/src/FileParty.Core/FileParty.Core.csproj +++ b/src/FileParty.Core/FileParty.Core.csproj @@ -1,15 +1,17 @@  - 1.3.0 + 2.0.0 Jankware Agnostic File Storage, Core Project, Service Registration https://github.com/JankwareDotCom/FileParty https://github.com/JankwareDotCom/FileParty - net6.0;net8.0;netstandard2.0;netstandard2.1 + net8.0;net10.0;netstandard2.0;netstandard2.1 true Jankware FileParty https://raw.githubusercontent.com/JankwareDotCom/FileParty/main/README.md + Debug;Release;Debug-V4;Release-V4 + AnyCPU @@ -24,7 +26,7 @@ - + diff --git a/src/FileParty.Providers.AWS.S3/AWS.S3Module.cs b/src/FileParty.Providers.AWS.S3/AWS.S3Module.cs index 08a2f11..a48b163 100644 --- a/src/FileParty.Providers.AWS.S3/AWS.S3Module.cs +++ b/src/FileParty.Providers.AWS.S3/AWS.S3Module.cs @@ -1,4 +1,5 @@ -using FileParty.Core; +using System; +using FileParty.Core; using FileParty.Core.Registration; using FileParty.Providers.AWS.S3.Interfaces; @@ -6,6 +7,8 @@ namespace FileParty.Providers.AWS.S3 { public class AWS_S3Module : BaseFilePartyModule { + public static readonly bool IsAwsSdkV4 = Type.GetType("Amazon.Runtime.Credentials.DefaultAWSCredentialsIdentityResolver, AWSSDK.Core") != null; + public AWS_S3Module() { this.RegisterModuleDependency AssumeRoleAsync(IFilePartyAWSCredentialFacto ? nameof(FileParty) + "_" + nameof(AWS_S3Module) + "_" + _internalIdentifier : RoleSessionName; - using (var stsClient = new AmazonSecurityTokenServiceClient(credFactory.GetAmazonCredentials(_baseConfig))) + var creds = credFactory.GetAmazonCredentials(_baseConfig); + var config = new AmazonSecurityTokenServiceConfig + { + RegionEndpoint = this.GetRegionEndpoint() + }; + + using (var stsClient = new AmazonSecurityTokenServiceClient(creds, config)) { try { diff --git a/src/FileParty.Providers.AWS.S3/Config/AWSSessionCredentials.cs b/src/FileParty.Providers.AWS.S3/Config/AWSSessionCredentials.cs index 5beb8f8..c11ae5b 100644 --- a/src/FileParty.Providers.AWS.S3/Config/AWSSessionCredentials.cs +++ b/src/FileParty.Providers.AWS.S3/Config/AWSSessionCredentials.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Amazon; using Amazon.Runtime; using Amazon.SecurityToken; using Amazon.SecurityToken.Model; @@ -43,7 +44,14 @@ internal async Task GetTemporaryCredentialsAsync( return _sessionCredentials; } - using (var stsClient = new AmazonSecurityTokenServiceClient(credFactory.GetAmazonCredentials(_baseConfig))) + var creds = credFactory.GetAmazonCredentials(_baseConfig); + + var config = new AmazonSecurityTokenServiceConfig + { + RegionEndpoint = this.GetRegionEndpoint() + }; + + using (var stsClient = new AmazonSecurityTokenServiceClient(creds, config)) { var getSessionTokenRequest = new GetSessionTokenRequest { diff --git a/src/FileParty.Providers.AWS.S3/Config/AWSStoredProfileConfiguration.cs b/src/FileParty.Providers.AWS.S3/Config/AWSStoredProfileConfiguration.cs index 4399303..f99c8ef 100644 --- a/src/FileParty.Providers.AWS.S3/Config/AWSStoredProfileConfiguration.cs +++ b/src/FileParty.Providers.AWS.S3/Config/AWSStoredProfileConfiguration.cs @@ -33,16 +33,5 @@ public AWSStoredProfileConfiguration(string profileName, string profileLocation) public string ProfileName { get; set; } = "default"; public string ProfileLocation { get; set; } - - internal StoredProfileAWSCredentials GetConfig() - { - return string.IsNullOrWhiteSpace(ProfileLocation) - ? string.IsNullOrWhiteSpace(ProfileName) - ? new StoredProfileAWSCredentials() - : new StoredProfileAWSCredentials(ProfileName) - : string.IsNullOrWhiteSpace(ProfileName) - ? new StoredProfileAWSCredentials("default", ProfileLocation) - : new StoredProfileAWSCredentials(ProfileName, ProfileLocation); - } } } \ No newline at end of file diff --git a/src/FileParty.Providers.AWS.S3/FileParty.Providers.AWS.S3.csproj b/src/FileParty.Providers.AWS.S3/FileParty.Providers.AWS.S3.csproj index cef3887..e491904 100644 --- a/src/FileParty.Providers.AWS.S3/FileParty.Providers.AWS.S3.csproj +++ b/src/FileParty.Providers.AWS.S3/FileParty.Providers.AWS.S3.csproj @@ -1,22 +1,49 @@  - net6.0;net8.0;netstandard2.0;netstandard2.1 - 1.3.0 + net8.0;net10.0;netstandard2.0;netstandard2.1 + 2.0.0 Agnostic File Storage, AWS S3 Module https://github.com/JankwareDotCom/FileParty https://github.com/JankwareDotCom/FileParty true + Debug;Release;Debug-V3;Release-V3;Debug-V4;Release-V4 + AnyCPU + + $(DefineConstants);AWS_SDK_ANY + + + + $(DefineConstants);AWS_SDK_V3 + + + + $(DefineConstants);AWS_SDK_V4 + + - - + + + + + + + + + + + + + + + diff --git a/src/FileParty.Providers.AWS.S3/FilePartyAWSCredentialFactory.cs b/src/FileParty.Providers.AWS.S3/FilePartyAWSCredentialFactory.cs index 4529364..1ffde02 100644 --- a/src/FileParty.Providers.AWS.S3/FilePartyAWSCredentialFactory.cs +++ b/src/FileParty.Providers.AWS.S3/FilePartyAWSCredentialFactory.cs @@ -1,5 +1,10 @@ using System; +using System.IO; +using System.Linq; +using System.Reflection; using Amazon.Runtime; +using Amazon.Runtime.CredentialManagement; +using Amazon.Runtime.Credentials; using Amazon.S3; using Amazon.SecurityToken; using FileParty.Core.Exceptions; @@ -31,9 +36,27 @@ public virtual AWSCredentials GetAmazonCredentials(StorageProviderConfiguration< .GetAwaiter() .GetResult(); case AWSDefaultConfiguration _: - return FallbackCredentialsFactory.GetCredentials(false); + return GetDefaultCredentials(); case AWSStoredProfileConfiguration storedProfileConfiguration: - return storedProfileConfiguration.GetConfig(); + { + var profileLocation = string.IsNullOrWhiteSpace(storedProfileConfiguration.ProfileLocation) + ? null + : Directory.Exists(storedProfileConfiguration.ProfileLocation) + ? Path.Combine(storedProfileConfiguration.ProfileLocation, "credentials") + : storedProfileConfiguration.ProfileLocation; + + var profileName = string.IsNullOrWhiteSpace(storedProfileConfiguration.ProfileName) + ? "default" + : storedProfileConfiguration.ProfileName; + + var chain = string.IsNullOrWhiteSpace(profileLocation) + ? new CredentialProfileStoreChain() + : new CredentialProfileStoreChain(profileLocation); + + return chain.TryGetAWSCredentials(profileName, out var creds) + ? creds + : throw Errors.InvalidConfiguration; + } case AWSInstanceProfileConfiguration instanceConfiguration: return new InstanceProfileAWSCredentials(instanceConfiguration.Role); } @@ -57,5 +80,31 @@ public virtual AWSCredentials GetAmazonCredentials(StorageProviderConfiguration< throw Errors.InvalidConfiguration; } + + private static AWSCredentials GetDefaultCredentials() + { + return !AWS_S3Module.IsAwsSdkV4 + ? (AWSCredentials) V3GetCredentialsMethod + ?.Invoke(null, new object[]{false}) + ?? throw new InvalidOperationException("Unable to get v3 credentials") + : (AWSCredentials) V4GetCredentialsMethod + ?.Invoke(null, V4GetCredentialsMethod.GetParameters() + .Select(s => Convert.ChangeType(null, s.ParameterType)).ToArray()) + ?? throw new InvalidOperationException("Unable to get credentials"); + } + + + + private static readonly MethodInfo V4GetCredentialsMethod = + AWS_S3Module.IsAwsSdkV4 + ? Type.GetType("Amazon.Runtime.Credentials.DefaultAWSCredentialsIdentityResolver, AWSSDK.Core") + ?.GetMethod("GetCredentials") + : null; + + private static readonly MethodInfo V3GetCredentialsMethod = + !AWS_S3Module.IsAwsSdkV4 + ? Type.GetType("Amazon.Runtime.FallbackCredentialsFactory, AWSSDK.Core") + ?.GetMethod("GetCredentials", new[]{typeof(bool)}) + : null; } } \ No newline at end of file diff --git a/src/FileParty.Providers.AWS.S3/S3StorageProvider.cs b/src/FileParty.Providers.AWS.S3/S3StorageProvider.cs index ce12270..0e802fe 100644 --- a/src/FileParty.Providers.AWS.S3/S3StorageProvider.cs +++ b/src/FileParty.Providers.AWS.S3/S3StorageProvider.cs @@ -142,7 +142,7 @@ public virtual async Task ExistsAsync(string storagePointer, using (var s3Wrapper = new FilePartyS3ClientWrapper(_s3ClientFactory)) { var _ = await s3Wrapper.ExecuteAsync( - (s3Client) => GetInformationAsync(s3Client, storagePointer, cancellationToken)); + async (s3Client) => await GetInformationAsync(s3Client, storagePointer, cancellationToken)); } return true; @@ -153,12 +153,12 @@ public virtual async Task ExistsAsync(string storagePointer, } } - public virtual Task> ExistsAsync(IEnumerable storagePointers, + public virtual async Task> ExistsAsync(IEnumerable storagePointers, CancellationToken cancellationToken = default) { using (var s3Wrapper = new FilePartyS3ClientWrapper(_s3ClientFactory)) { - return s3Wrapper.ExecuteAsync((s3Client) => + return await s3Wrapper.ExecuteAsync((s3Client) => { IDictionary result = storagePointers .ToDictionary( @@ -170,12 +170,12 @@ public virtual Task> ExistsAsync(IEnumerable s } } - public Task TryGetStoredItemTypeAsync(string storagePointer, + public async Task TryGetStoredItemTypeAsync(string storagePointer, CancellationToken cancellationToken = default) { using (var s3Wrapper = new FilePartyS3ClientWrapper(_s3ClientFactory)) { - return s3Wrapper.ExecuteAsync((s3Client) => + return await s3Wrapper.ExecuteAsync((s3Client) => TryGetStoredItemTypeAsync(s3Client, storagePointer, cancellationToken)); } } @@ -276,7 +276,7 @@ protected virtual async Task GetFileInformation(AmazonS3C result.StoredType = StoredItemType.File; result.Size = omInfo.ContentLength; - result.LastModifiedTimestamp = omInfo.LastModified.ToUniversalTime(); + result.LastModifiedTimestamp = (omInfo.LastModified as DateTime?).GetValueOrDefault().ToUniversalTime(); result.StoragePointer = storagePointer; return result; } @@ -452,9 +452,9 @@ protected virtual async Task DeleteAsync(AmazonS3Client s3Client, IEnumerable s.Key).ToArray(), cancellationToken); + await DeleteAsync(directoryContents.S3Objects?.Select(s => s.Key).ToArray(), cancellationToken); } } } diff --git a/src/FileParty.Providers.FileSystem/FileParty.Providers.FileSystem.csproj b/src/FileParty.Providers.FileSystem/FileParty.Providers.FileSystem.csproj index bb7fef4..9ac7ed5 100644 --- a/src/FileParty.Providers.FileSystem/FileParty.Providers.FileSystem.csproj +++ b/src/FileParty.Providers.FileSystem/FileParty.Providers.FileSystem.csproj @@ -1,12 +1,14 @@  - net6.0;net8.0;netstandard2.0;netstandard2.1 - 1.3.0 + net8.0;net10.0;netstandard2.0;netstandard2.1 + 2.0.0 Agnostic File Storage, Filesystem Module https://github.com/JankwareDotCom/FileParty https://github.com/JankwareDotCom/FileParty true + Debug;Release;Debug-V4;Release-V4 + AnyCPU diff --git a/test/FileParty.Core.Tests/FileParty.Core.Tests.csproj b/test/FileParty.Core.Tests/FileParty.Core.Tests.csproj index e169b08..8dc5f1e 100644 --- a/test/FileParty.Core.Tests/FileParty.Core.Tests.csproj +++ b/test/FileParty.Core.Tests/FileParty.Core.Tests.csproj @@ -2,24 +2,26 @@ false - net8.0 + net10.0 FileParty.Core.Tests FileParty.Core.Tests FileParty.Core.Tests FileParty.Core.Tests FileParty.Core.Tests FileParty.Core.Tests + Debug;Release;Debug-V4;Release-V4 + AnyCPU - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/FileParty.Handlers.AWS.S3.Tests/AwsSdkVersionTest.cs b/test/FileParty.Handlers.AWS.S3.Tests/AwsSdkVersionTest.cs new file mode 100644 index 0000000..9f49dad --- /dev/null +++ b/test/FileParty.Handlers.AWS.S3.Tests/AwsSdkVersionTest.cs @@ -0,0 +1,28 @@ +using System.Reflection; +using Xunit; + +namespace FileParty.Handlers.AWS.S3.Tests; + +public class AwsSdkVersionTest +{ + [Theory] +#if AWS_SDK_V3 + [InlineData(3)] +#endif +#if AWS_SDK_V4 + [InlineData(4)] +#endif + public void ShouldUseExpectedAwsSdkVersion(int expectedVersion) + { + var awsS3Assembly = Assembly.Load("AWSSDK.S3"); + + Assert.NotNull(awsS3Assembly); + + var version = awsS3Assembly.GetName().Version; + + // Assert expected version + Assert.True( + version?.Major == expectedVersion, + $"Expected AWS SDK v{expectedVersion}, but got v{version?.Major ?? 0}"); + } +} \ No newline at end of file diff --git a/test/FileParty.Handlers.AWS.S3.Tests/CredentialFactoryShould.cs b/test/FileParty.Handlers.AWS.S3.Tests/CredentialFactoryShould.cs index c1771ac..7791f7e 100644 --- a/test/FileParty.Handlers.AWS.S3.Tests/CredentialFactoryShould.cs +++ b/test/FileParty.Handlers.AWS.S3.Tests/CredentialFactoryShould.cs @@ -12,6 +12,7 @@ using FileParty.Providers.AWS.S3.Config; using FileParty.Providers.AWS.S3.Interfaces; using Microsoft.Extensions.DependencyInjection; +using Moq; using Xunit; namespace FileParty.Handlers.AWS.S3.Tests; @@ -116,20 +117,34 @@ public async Task CreateCredentials_UsingSession() await EnsureFileCreationAndDeletion(cfg); } - [Fact(Skip = "Long Running Test")] + [Fact(Skip = "Long Running Test")] // last manually verified 2025-11-20 public async Task CreateCredentials_ButThrowDueToExpired_UsingSession() { + var sc = new ServiceCollection(); + sc.AddFileParty(c => c.AddModule(null)); + await using var sp = sc.BuildServiceProvider(); + var cfg = new AWSSessionCredentials(_accessKey, _secretKey) { Region = _regionName, Name = _bucketName, - DurationSeconds = 15 * 60 + DurationSeconds = 15 * 60 // 15 minute duration is minimum }; - - await _credFactory.GetAmazonCredentials(cfg).GetCredentialsAsync(); - - await Task.Delay(TimeSpan.FromSeconds(cfg.DurationSeconds), CancellationToken.None); - await Assert.ThrowsAnyAsync(async () => { await EnsureFileCreationAndDeletion(cfg); }); + + var clientFactory = sp.GetRequiredService(); + + var client = clientFactory.GetClient(cfg); + + Assert.True((await client.ListObjectsAsync(cfg.Name)).HttpStatusCode == System.Net.HttpStatusCode.OK); + + await Task.Delay(TimeSpan.FromSeconds(cfg.DurationSeconds + 10), CancellationToken.None); + + var exc = await Assert.ThrowsAsync(async () => + { + await client.ListObjectsAsync(cfg.Name); + }); + + Assert.Equal("The provided token has expired.", exc.Message); } [Fact] @@ -150,6 +165,7 @@ public async Task CreateCredentials_UsingRole() } } + //[Fact] // last manually verified 2025-11-22 [Fact(Skip = "Requires External AWS Account")] public async Task CreateCredentials_UsingRole_ExternalAccount() { @@ -160,7 +176,7 @@ public async Task CreateCredentials_UsingRole_ExternalAccount() var cfg = new AWSRoleBasedConfiguration { Name = externalConfig[0], - Region = _regionName, + Region = externalConfig[3], RoleArn = externalConfig[1], ExternalId = externalConfig[2] }; diff --git a/test/FileParty.Handlers.AWS.S3.Tests/FileParty.Handlers.AWS.S3.Tests.csproj b/test/FileParty.Handlers.AWS.S3.Tests/FileParty.Handlers.AWS.S3.Tests.csproj index ec8d85b..24754da 100644 --- a/test/FileParty.Handlers.AWS.S3.Tests/FileParty.Handlers.AWS.S3.Tests.csproj +++ b/test/FileParty.Handlers.AWS.S3.Tests/FileParty.Handlers.AWS.S3.Tests.csproj @@ -1,18 +1,36 @@  - net8.0 + net10.0 + Debug-V3;Debug-V4;Release-V3;Release-V4 + + + + $(DefineConstants);AWS_SDK_V3 + + + + $(DefineConstants);AWS_SDK_V4 - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all + + + + + + + + diff --git a/test/FileParty.Handlers.FileSystem.Tests/FileParty.Handlers.FileSystem.Tests.csproj b/test/FileParty.Handlers.FileSystem.Tests/FileParty.Handlers.FileSystem.Tests.csproj index 89c6205..dfc809b 100644 --- a/test/FileParty.Handlers.FileSystem.Tests/FileParty.Handlers.FileSystem.Tests.csproj +++ b/test/FileParty.Handlers.FileSystem.Tests/FileParty.Handlers.FileSystem.Tests.csproj @@ -4,17 +4,19 @@ false FileParty.Providers.FileSystem.Tests FileParty.Providers.FileSystem.Tests - net8.0 + net10.0 + Debug;Release;Debug-V4;Release-V4 + AnyCPU - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all