package config

import (
	"bytes"
	"context"
	"fmt"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
	"github.com/aws/aws-sdk-go-v2/internal/ini"
	"github.com/aws/smithy-go/logging"
	"github.com/aws/smithy-go/ptr"
)

var _ regionProvider = (*SharedConfig)(nil)

var (
	testConfigFilename      = filepath.Join("testdata", "shared_config")
	testConfigOtherFilename = filepath.Join("testdata", "shared_config_other")
	testCredentialsFilename = filepath.Join("testdata", "shared_credentials")
)

func TestNewSharedConfig(t *testing.T) {
	cases := map[string]struct {
		ConfigFilenames      []string
		CredentialsFilenames []string
		Profile              string
		Expected             SharedConfig
		Err                  error
	}{
		"file not exist": {
			ConfigFilenames: []string{"file_not_exist"},
			Profile:         "default",
			Err:             fmt.Errorf("failed to get shared config profile"),
		},
		"default profile": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "default",
			Expected: SharedConfig{
				Profile: "default",
				Region:  "default_region",
			},
		},
		"multiple config files": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "config_file_load_order",
			Expected: SharedConfig{
				Profile: "config_file_load_order",
				Region:  "shared_config_region",
				Credentials: aws.Credentials{
					AccessKeyID:     "shared_config_akid",
					SecretAccessKey: "shared_config_secret",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
				},
			},
		},
		"mutliple config files reverse order": {
			ConfigFilenames: []string{testConfigFilename, testConfigOtherFilename},
			Profile:         "config_file_load_order",
			Expected: SharedConfig{
				Profile: "config_file_load_order",
				Region:  "shared_config_other_region",
				Credentials: aws.Credentials{
					AccessKeyID:     "shared_config_other_akid",
					SecretAccessKey: "shared_config_other_secret",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigOtherFilename),
				},
			},
		},
		"Assume role": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "assume_role",
			Expected: SharedConfig{
				Profile:           "assume_role",
				RoleARN:           "assume_role_role_arn",
				SourceProfileName: "complete_creds",
				Source: &SharedConfig{
					Profile: "complete_creds",
					Credentials: aws.Credentials{
						AccessKeyID:     "complete_creds_akid",
						SecretAccessKey: "complete_creds_secret",
						Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
					},
				},
			},
		},
		"Assume role with invalid source profile": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "assume_role_invalid_source_profile",
			Err: SharedConfigAssumeRoleError{
				Profile: "profile_not_exists",
				RoleARN: "assume_role_invalid_source_profile_role_arn",
				Err: SharedConfigProfileNotExistError{
					Profile: "profile_not_exists",
					Err:     nil,
				},
			},
		},
		"Assume role with creds": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "assume_role_w_creds",
			Expected: SharedConfig{
				Profile:           "assume_role_w_creds",
				RoleARN:           "assume_role_w_creds_role_arn",
				ExternalID:        "1234",
				RoleSessionName:   "assume_role_w_creds_session_name",
				SourceProfileName: "assume_role_w_creds",
				Source: &SharedConfig{
					Profile: "assume_role_w_creds",
					Credentials: aws.Credentials{
						AccessKeyID:     "assume_role_w_creds_akid",
						SecretAccessKey: "assume_role_w_creds_secret",
						Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
					},
				},
			},
		},
		"Assume role without creds": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "assume_role_wo_creds",
			Expected: SharedConfig{
				Profile:           "assume_role_wo_creds",
				RoleARN:           "assume_role_wo_creds_role_arn",
				SourceProfileName: "assume_role_wo_creds",
			},
			Err: SharedConfigAssumeRoleError{
				Profile: "assume_role_wo_creds",
				RoleARN: "assume_role_wo_creds_role_arn",
			},
		},
		"Invalid INI file": {
			ConfigFilenames: []string{filepath.Join("testdata", "shared_config_invalid_ini")},
			Profile:         "profile_name",
			Err: SharedConfigProfileNotExistError{
				Filename: []string{filepath.Join("testdata", "shared_config_invalid_ini")},
				Profile:  "profile_name",
				Err:      nil,
			},
		},
		"S3UseARNRegion property on profile": {
			Profile:         "valid_arn_region",
			ConfigFilenames: []string{testConfigFilename},
			Expected: SharedConfig{
				Profile:        "valid_arn_region",
				S3UseARNRegion: ptr.Bool(true),
			},
		},
		"S3DisableMultiRegionAccessPoints property on profile": {
			Profile:         "disable_mrap",
			ConfigFilenames: []string{testConfigFilename},
			Expected: SharedConfig{
				Profile:                          "disable_mrap",
				S3DisableMultiRegionAccessPoints: ptr.Bool(true),
			},
		},
		"EndpointDiscovery property enabled on profile": {
			Profile:         "endpoint_discovery_enabled",
			ConfigFilenames: []string{testConfigFilename},
			Expected: SharedConfig{
				Profile:                 "endpoint_discovery_enabled",
				EnableEndpointDiscovery: aws.EndpointDiscoveryEnabled,
			},
		},
		"EndpointDiscovery property disabled on profile": {
			Profile:         "endpoint_discovery_disabled",
			ConfigFilenames: []string{testConfigFilename},
			Expected: SharedConfig{
				Profile:                 "endpoint_discovery_disabled",
				EnableEndpointDiscovery: aws.EndpointDiscoveryDisabled,
			},
		},
		"EndpointDiscovery property set as auto on profile": {
			Profile:         "endpoint_discovery_auto",
			ConfigFilenames: []string{testConfigFilename},
			Expected: SharedConfig{
				Profile:                 "endpoint_discovery_auto",
				EnableEndpointDiscovery: aws.EndpointDiscoveryAuto,
			},
		},
		"EndpointDiscovery property set as unknown on profile": {
			Profile:         "endpoint_discovery_unknown",
			ConfigFilenames: []string{testConfigFilename},
			Expected: SharedConfig{
				Profile:                 "endpoint_discovery_unknown",
				EnableEndpointDiscovery: aws.EndpointDiscoveryUnset,
			},
		},
		"Assume role with credential source Ec2Metadata": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "assume_role_with_credential_source",
			Expected: SharedConfig{
				Profile:          "assume_role_with_credential_source",
				RoleARN:          "assume_role_with_credential_source_role_arn",
				CredentialSource: credSourceEc2Metadata,
			},
		},
		"Assume role chained with creds": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "multiple_assume_role",
			Expected: SharedConfig{
				Profile:           "multiple_assume_role",
				RoleARN:           "multiple_assume_role_role_arn",
				SourceProfileName: "assume_role",
				Source: &SharedConfig{
					Profile:           "assume_role",
					RoleARN:           "assume_role_role_arn",
					SourceProfileName: "complete_creds",
					Source: &SharedConfig{
						Profile: "complete_creds",
						Credentials: aws.Credentials{
							AccessKeyID:     "complete_creds_akid",
							SecretAccessKey: "complete_creds_secret",
							Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
						},
					},
				},
			},
		},
		"Assume role chained with credential source": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "multiple_assume_role_with_credential_source",
			Expected: SharedConfig{
				Profile:           "multiple_assume_role_with_credential_source",
				RoleARN:           "multiple_assume_role_with_credential_source_role_arn",
				SourceProfileName: "assume_role_with_credential_source",
				Source: &SharedConfig{
					Profile:          "assume_role_with_credential_source",
					RoleARN:          "assume_role_with_credential_source_role_arn",
					CredentialSource: credSourceEc2Metadata,
				},
			},
		},
		"Assume role chained with credential source reversed order": {
			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
			Profile:         "multiple_assume_role_with_credential_source2",
			Expected: SharedConfig{
				Profile:           "multiple_assume_role_with_credential_source2",
				RoleARN:           "multiple_assume_role_with_credential_source2_role_arn",
				SourceProfileName: "multiple_assume_role_with_credential_source",
				Source: &SharedConfig{
					Profile:           "multiple_assume_role_with_credential_source",
					RoleARN:           "multiple_assume_role_with_credential_source_role_arn",
					SourceProfileName: "assume_role_with_credential_source",
					Source: &SharedConfig{
						Profile:          "assume_role_with_credential_source",
						RoleARN:          "assume_role_with_credential_source_role_arn",
						CredentialSource: credSourceEc2Metadata,
					},
				},
			},
		},
		"AWS SSO Profile": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "sso_creds",
			Expected: SharedConfig{
				Profile:      "sso_creds",
				SSOAccountID: "012345678901",
				SSORegion:    "us-west-2",
				SSORoleName:  "TestRole",
				SSOStartURL:  "https://127.0.0.1/start",
			},
		},
		"Assume Role with AWS SSO Credentials": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "source_sso_creds",
			Expected: SharedConfig{
				Profile:           "source_sso_creds",
				RoleARN:           "source_sso_creds_arn",
				SourceProfileName: "sso_creds",
				Source: &SharedConfig{
					Profile:      "sso_creds",
					SSOAccountID: "012345678901",
					SSORegion:    "us-west-2",
					SSORoleName:  "TestRole",
					SSOStartURL:  "https://127.0.0.1/start",
				},
			},
		},
		"AWS SSO Profile and Static Credentials": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "sso_and_static",
			Expected: SharedConfig{
				Profile: "sso_and_static",
				Credentials: aws.Credentials{
					AccessKeyID:     "sso_and_static_akid",
					SecretAccessKey: "sso_and_static_secret",
					SessionToken:    "sso_and_static_token",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
				},
				SSOAccountID: "012345678901",
				SSORegion:    "us-west-2",
				SSORoleName:  "TestRole",
				SSOStartURL:  "https://THIS_SHOULD_NOT_BE_IN_TESTDATA_CACHE/start",
			},
		},
		"Assume Role with AWS SSO Configuration and Source Profile": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "source_sso_and_assume",
			Expected: SharedConfig{
				Profile:           "source_sso_and_assume",
				RoleARN:           "source_sso_and_assume_arn",
				SourceProfileName: "sso_and_assume",
				Source: &SharedConfig{
					Profile:           "sso_and_assume",
					RoleARN:           "sso_with_assume_role_arn",
					SourceProfileName: "multiple_assume_role_with_credential_source",
					Source: &SharedConfig{
						Profile:           "multiple_assume_role_with_credential_source",
						RoleARN:           "multiple_assume_role_with_credential_source_role_arn",
						SourceProfileName: "assume_role_with_credential_source",
						Source: &SharedConfig{
							Profile:          "assume_role_with_credential_source",
							RoleARN:          "assume_role_with_credential_source_role_arn",
							CredentialSource: credSourceEc2Metadata,
						},
					},
				},
			},
		},
		"SSO Mixed with Additional Credential Providrer": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "sso_mixed_credproc",
			Expected: SharedConfig{
				Profile:           "sso_mixed_credproc",
				SSOAccountID:      "012345678901",
				SSORegion:         "us-west-2",
				SSORoleName:       "TestRole",
				SSOStartURL:       "https://127.0.0.1/start",
				CredentialProcess: "/path/to/process",
			},
		},
		"SSO Session success": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "sso-session-success",
			Expected: SharedConfig{
				Profile:        "sso-session-success",
				Region:         "us-east-1",
				SSOAccountID:   "123456789012",
				SSORoleName:    "testRole",
				SSOSessionName: "sso-session-success-dev",
				SSOSession: &SSOSession{
					Name:        "sso-session-success-dev",
					SSORegion:   "us-east-1",
					SSOStartURL: "https://d-123456789a.awsapps.com/start",
				},
			},
		},
		"profile names are case-sensitive (Mixed)": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "DoNotNormalize",
			Expected: SharedConfig{
				Profile: "DoNotNormalize",
				Credentials: aws.Credentials{
					AccessKeyID:     "DoNotNormalize_credentials_akid",
					SecretAccessKey: "DoNotNormalize_credentials_secret",
					SessionToken:    "DoNotNormalize_config_session_token",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
				},
				RoleDurationSeconds: func() *time.Duration { d := time.Minute * 20; return &d }(),
				Region:              "eu-west-1",
			},
		},
		"profile names are case-sensitive (lower)": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "donotnormalize",
			Expected: SharedConfig{
				Profile: "donotnormalize",
				Credentials: aws.Credentials{
					AccessKeyID:     "donotnormalize_credentials_akid",
					SecretAccessKey: "donotnormalize_credentials_secret",
					SessionToken:    "donotnormalize_config_session_token",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
				},
				RoleDurationSeconds: func() *time.Duration { d := time.Minute * 25; return &d }(),
				Region:              "eu-west-2",
			},
		},
		"profile names are case-sensitive (upper)": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "DONOTNORMALIZE",
			Expected: SharedConfig{
				Profile: "DONOTNORMALIZE",
				Credentials: aws.Credentials{
					AccessKeyID:     "DONOTNORMALIZE_credentials_akid",
					SecretAccessKey: "DONOTNORMALIZE_credentials_secret",
					SessionToken:    "DONOTNORMALIZE_config_session_token",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
				},
				RoleDurationSeconds: func() *time.Duration { d := time.Minute * 30; return &d }(),
				Region:              "eu-west-3",
			},
		},
		"source profile name is case-sensitive": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "AssumeWithDoNotNormalize",
			Expected: SharedConfig{
				Profile:           "AssumeWithDoNotNormalize",
				RoleARN:           "AssumeWithDoNotNormalize_role_arn",
				SourceProfileName: "DoNotNormalize",
				Source: &SharedConfig{
					Profile: "DoNotNormalize",
					Credentials: aws.Credentials{
						AccessKeyID:     "DoNotNormalize_credentials_akid",
						SecretAccessKey: "DoNotNormalize_credentials_secret",
						SessionToken:    "DoNotNormalize_config_session_token",
						Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
					},
					RoleDurationSeconds: func() *time.Duration { d := time.Minute * 20; return &d }(),
					Region:              "eu-west-1",
				},
			},
		},
		"profile with ec2_metadata_service_endpoint": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "EC2MetadataServiceEndpoint",
			Expected: SharedConfig{
				Profile:         "EC2MetadataServiceEndpoint",
				EC2IMDSEndpoint: "http://endpoint.localhost",
			},
		},
		"profile with ec2_metadata_service_endpoint_mode as IPv6": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "EC2MetadataServiceEndpointModeIPv6",
			Expected: SharedConfig{
				Profile:             "EC2MetadataServiceEndpointModeIPv6",
				EC2IMDSEndpointMode: imds.EndpointModeStateIPv6,
			},
		},
		"profile with ec2_metadata_service_endpoint_mode as IPv4": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "EC2MetadataServiceEndpointModeIPv4",
			Expected: SharedConfig{
				Profile:             "EC2MetadataServiceEndpointModeIPv4",
				EC2IMDSEndpointMode: imds.EndpointModeStateIPv4,
			},
		},
		"profile with ec2_metadata_service_endpoint_mode is unknown": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "EC2MetadataServiceEndpointModeUnknown",
			Expected: SharedConfig{
				Profile: "EC2MetadataServiceEndpointModeUnknown",
			},
			Err: fmt.Errorf("unknown EC2 IMDS endpoint mode"),
		},
		"profile with ec2_metadata_service_endpoint and ec2_metadata_service_endpoint_mode": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "EC2MetadataServiceEndpointAndModeMixed",
			Expected: SharedConfig{
				Profile:             "EC2MetadataServiceEndpointAndModeMixed",
				EC2IMDSEndpoint:     "http://endpoint.localhost",
				EC2IMDSEndpointMode: imds.EndpointModeStateIPv6,
			},
		},
		"dual-stack endpoint enabled": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "UseDualStackEndpointEnabled",
			Expected: SharedConfig{
				Profile:              "UseDualStackEndpointEnabled",
				Region:               "us-west-2",
				UseDualStackEndpoint: aws.DualStackEndpointStateEnabled,
			},
		},
		"dual-stack endpoint disabled": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "UseDualStackEndpointDisabled",
			Expected: SharedConfig{
				Profile:              "UseDualStackEndpointDisabled",
				Region:               "us-west-2",
				UseDualStackEndpoint: aws.DualStackEndpointStateDisabled,
			},
		},
		"dual-stack endpoint invalid": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "UseDualStackEndpointInvalid",
			Expected: SharedConfig{
				Profile:              "UseDualStackEndpointInvalid",
				Region:               "us-west-2",
				UseDualStackEndpoint: aws.DualStackEndpointStateDisabled,
			},
		},
		"fips endpoint enabled": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "UseFIPSEndpointEnabled",
			Expected: SharedConfig{
				Profile:         "UseFIPSEndpointEnabled",
				Region:          "us-west-2",
				UseFIPSEndpoint: aws.FIPSEndpointStateEnabled,
			},
		},
		"fips endpoint disabled": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "UseFIPSEndpointDisabled",
			Expected: SharedConfig{
				Profile:         "UseFIPSEndpointDisabled",
				Region:          "us-west-2",
				UseFIPSEndpoint: aws.FIPSEndpointStateDisabled,
			},
		},
		"fips endpoint unknown": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "UseFIPSEndpointInvalid",
			Expected: SharedConfig{
				Profile:         "UseFIPSEndpointInvalid",
				Region:          "us-west-2",
				UseFIPSEndpoint: aws.FIPSEndpointStateDisabled,
			},
		},
		"defaults mode auto": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "autodefaultsmode",
			Expected: SharedConfig{
				Profile:      "autodefaultsmode",
				DefaultsMode: aws.DefaultsModeAuto,
			},
		},
		"defaults mode standard": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "standarddefaultsmode",
			Expected: SharedConfig{
				Profile:      "standarddefaultsmode",
				DefaultsMode: aws.DefaultsModeStandard,
			},
		},
		"defaults mode invalid": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "invaliddefaultsmode",
			Err:                  fmt.Errorf("failed to load defaults_mode from shared config, invalid value: invalid"),
		},
		"retry options auto": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "retryunset",
			Expected: SharedConfig{
				Profile: "retryunset",
			},
		},
		"retry options standard": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "retrywithstandard",
			Expected: SharedConfig{
				Profile:          "retrywithstandard",
				RetryMode:        aws.RetryModeStandard,
				RetryMaxAttempts: 5,
			},
		},
		"retry options adaptive": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "retrywithadaptive",
			Expected: SharedConfig{
				Profile:          "retrywithadaptive",
				RetryMode:        aws.RetryModeAdaptive,
				RetryMaxAttempts: 4,
			},
		},
		"retry options invalid": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "retrywithinvalidmode",
			Err:                  fmt.Errorf("failed to load retry_mode from shared config, unknown RetryMode, invalid"),
		},
		"retry options invalid retry attempts": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "retrywithinvalidattempts",
			Err:                  fmt.Errorf("failed to load max_attempts from shared config, invalid value max_attempts=invalid, expect integer"),
		},
		"ca bundle options": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "with_ca_bundle",
			Expected: SharedConfig{
				Profile:        "with_ca_bundle",
				CustomCABundle: "custom_ca_bundle_file.pem",
			},
		},
		"merged profiles across files": {
			ConfigFilenames:      []string{testConfigFilename},
			CredentialsFilenames: []string{testCredentialsFilename},
			Profile:              "merged_profiles",
			Expected: SharedConfig{
				Profile:             "merged_profiles",
				RoleARN:             "creds_profile_arn",
				RoleDurationSeconds: aws.Duration(1023 * time.Second),
				SSOAccountID:        "0123456789",
				SSORegion:           "us-west-2",
				SSORoleName:         "CredProfileRole",
				SSOStartURL:         "https://my-sso-cred-profile-role.awsapps.com/start",
				CustomCABundle:      "/path/to/bundle.b",
			},
		},
		"merged profiles across config files": {
			ConfigFilenames:      []string{testConfigFilename, testConfigFilename},
			CredentialsFilenames: []string{},
			Profile:              "merged_profiles",
			Expected: SharedConfig{
				Profile:             "merged_profiles",
				RoleARN:             "config_profile_arn",
				RoleDurationSeconds: aws.Duration(3601 * time.Second),
				SSOAccountID:        "1234567890",
				SSORegion:           "us-east-1",
				SSORoleName:         "ConfigProfileRole",
				SSOStartURL:         "https://my-sso-config-profile-role.awsapps.com/start",
				CustomCABundle:      "/path/to/bundle.a",
			},
		},
		"merged profiles across credentials files": {
			ConfigFilenames:      []string{},
			CredentialsFilenames: []string{testCredentialsFilename, testCredentialsFilename},
			Profile:              "merged_profiles",
			Expected: SharedConfig{
				Profile:             "merged_profiles",
				RoleARN:             "creds_profile_arn",
				RoleDurationSeconds: aws.Duration(1023 * time.Second),
				SSOAccountID:        "0123456789",
				SSORegion:           "us-west-2",
				SSORoleName:         "CredProfileRole",
				SSOStartURL:         "https://my-sso-cred-profile-role.awsapps.com/start",
				CustomCABundle:      "/path/to/bundle.b",
			},
		},
		"Profile with app ID": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "sdk_app_id",
			Expected: SharedConfig{
				Profile: "sdk_app_id",
				AppID:   "12345",
			},
		},
		"endpoint config test": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "endpoint_config",
			Expected: SharedConfig{
				Profile:                   "endpoint_config",
				BaseEndpoint:              "https://example.com",
				IgnoreConfiguredEndpoints: ptr.Bool(true),
			},
		},
		"imdsv1 disabled = false": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "ec2-metadata-v1-disabled-false",
			Expected: SharedConfig{
				Profile:           "ec2-metadata-v1-disabled-false",
				EC2IMDSv1Disabled: aws.Bool(false),
			},
		},
		"imdsv1 disabled = true": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "ec2-metadata-v1-disabled-true",
			Expected: SharedConfig{
				Profile:           "ec2-metadata-v1-disabled-true",
				EC2IMDSv1Disabled: aws.Bool(true),
			},
		},
		"imdsv1 disabled = invalid": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "ec2-metadata-v1-disabled-invalid",
			Expected: SharedConfig{
				Profile:           "ec2-metadata-v1-disabled-invalid",
				EC2IMDSv1Disabled: aws.Bool(false),
			},
		},
		"profile configuring request compression": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "request_compression",
			Expected: SharedConfig{
				Profile:                     "request_compression",
				DisableRequestCompression:   aws.Bool(true),
				RequestMinCompressSizeBytes: aws.Int64(12345),
			},
		},
		"profile with invalid disableRequestCompression": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "request_compression_invalid_disable",
			Err: fmt.Errorf("invalid value for shared config profile field, %s=%s, need true or false",
				disableRequestCompression, "blabla"),
		},
		"profile with non-int requestMinCompressSizeBytes": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "request_compression_non_int_min_request",
			Err:             fmt.Errorf("invalid value for min request compression size bytes hahaha, need int64"),
		},
		"profile with requestMinCompressSizeBytes out of bounds": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "request_compression_min_request_out_of_bounds",
			Err:             fmt.Errorf("invalid range for min request compression size bytes 10485761, must be within 0 and 10485760 inclusively"),
		},
		"services section": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "service_endpoint_url",
			Expected: SharedConfig{
				Profile:             "service_endpoint_url",
				ServicesSectionName: "service_endpoint_url_services",
				Services: Services{
					ServiceValues: map[string]map[string]string{
						"s3": {
							"endpoint_url": "http://127.0.0.1",
							"other":        "foo",
						},
						"ec2": {
							"endpoint_url": "http://127.0.0.1:81",
						},
					},
				},
			},
		},
		"profile with aws account ID": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "account_id",
			Expected: SharedConfig{
				Profile: "account_id",
				Credentials: aws.Credentials{
					AccessKeyID:     "account_id_akid",
					SecretAccessKey: "account_id_secret",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
					AccountID:       "012345678901",
				},
			},
		},
		"profile with account ID endpoint mode": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "account_id_endpoint_mode",
			Expected: SharedConfig{
				Profile:               "account_id_endpoint_mode",
				AccountIDEndpointMode: aws.AccountIDEndpointModeDisabled,
			},
		},
		"profile with invalid account ID endpoint mode": {
			ConfigFilenames: []string{testConfigFilename},
			Profile:         "account_id_endpoint_mode_error",
			Expected: SharedConfig{
				Profile: "account_id_endpoint_mode_error",
			},
			Err: fmt.Errorf("invalid value for shared config profile field, account_id_endpoint_mode=blabla, must be preferred/required/disabled"),
		},
	}

	for name, c := range cases {
		t.Run(name, func(t *testing.T) {
			cfg, err := LoadSharedConfigProfile(context.TODO(), c.Profile, func(o *LoadSharedConfigOptions) {
				o.ConfigFiles = c.ConfigFilenames
				if c.CredentialsFilenames != nil {
					o.CredentialsFiles = c.CredentialsFilenames
				} else {
					o.CredentialsFiles = []string{filepath.Join("testdata", "empty_creds_config")}
				}
			})
			if c.Err != nil && err != nil {
				if e, a := c.Err.Error(), err.Error(); !strings.Contains(a, e) {
					t.Errorf("expect %q to be in %q", e, a)
				}
				return
			}
			if err != nil {
				t.Fatalf("expect no error, got %v", err)
			}
			if c.Err != nil {
				t.Errorf("expect error: %v, got none", c.Err)
			}
			if diff := cmpDiff(c.Expected, cfg); len(diff) > 0 {
				t.Error(diff)
			}
		})
	}
}

func TestLoadSharedConfigFromSection(t *testing.T) {
	filename := testConfigFilename
	sections, err := ini.OpenFile(filename)

	if err != nil {
		t.Fatalf("failed to load test config file, %s, %v", filename, err)
	}
	cases := map[string]struct {
		Profile  string
		Expected SharedConfig
		Err      error
	}{
		"Default as profile": {
			Profile:  "default",
			Expected: SharedConfig{Region: "default_region"},
		},
		"prefixed profile": {
			Profile:  "profile alt_profile_name",
			Expected: SharedConfig{Region: "alt_profile_name_region"},
		},
		"prefixed profile 2": {
			Profile:  "profile short_profile_name_first",
			Expected: SharedConfig{Region: "short_profile_name_first_alt"},
		},
		"profile with partial creds": {
			Profile:  "profile partial_creds",
			Expected: SharedConfig{},
		},
		"profile with role duration": {
			Profile: "profile with_role_duration",
			Expected: SharedConfig{
				RoleDurationSeconds: aws.Duration(3601 * time.Second),
			},
		},
		"profile with complete creds": {
			Profile: "profile complete_creds",
			Expected: SharedConfig{
				Credentials: aws.Credentials{
					AccessKeyID:     "complete_creds_akid",
					SecretAccessKey: "complete_creds_secret",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
				},
			},
		},
		"profile with complete creds and token": {
			Profile: "profile complete_creds_with_token",
			Expected: SharedConfig{
				Credentials: aws.Credentials{
					AccessKeyID:     "complete_creds_with_token_akid",
					SecretAccessKey: "complete_creds_with_token_secret",
					SessionToken:    "complete_creds_with_token_token",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
				},
			},
		},
		"complete profile": {
			Profile: "profile full_profile",
			Expected: SharedConfig{
				Credentials: aws.Credentials{
					AccessKeyID:     "full_profile_akid",
					SecretAccessKey: "full_profile_secret",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
				},
				Region: "full_profile_region",
			},
		},
		"profile with partial assume role": {
			Profile: "profile partial_assume_role",
			Expected: SharedConfig{
				RoleARN: "partial_assume_role_role_arn",
			},
		},
		"profile using assume role": {
			Profile: "profile assume_role",
			Expected: SharedConfig{
				RoleARN:           "assume_role_role_arn",
				SourceProfileName: "complete_creds",
			},
		},
		"profile with assume role and MFA": {
			Profile: "profile assume_role_w_mfa",
			Expected: SharedConfig{
				RoleARN:           "assume_role_role_arn",
				SourceProfileName: "complete_creds",
				MFASerial:         "0123456789",
			},
		},
		"does not exist": {
			Profile: "does_not_exist",
			Err: SharedConfigProfileNotExistError{
				Filename: []string{filename},
				Profile:  "does_not_exist",
				Err:      nil,
			},
		},
		"profile with mixed casing": {
			Profile: "profile with_mixed_case_keys",
			Expected: SharedConfig{
				Credentials: aws.Credentials{
					AccessKeyID:     "accessKey",
					SecretAccessKey: "secret",
					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
				},
			},
		},
	}

	for name, c := range cases {
		t.Run(name, func(t *testing.T) {
			var cfg SharedConfig

			section, ok := sections.GetSection(c.Profile)
			if !ok {
				if c.Err == nil {
					t.Fatalf("expected section to be present, was not")
				} else {
					if e, a := c.Err.Error(), "failed to get shared config profile"; !strings.Contains(e, a) {
						t.Fatalf("expect %q to be in %q", a, e)
					}
					return
				}
			}

			err := cfg.setFromIniSection(c.Profile, section)
			if c.Err != nil {
				if e, a := c.Err.Error(), err.Error(); !strings.Contains(a, e) {
					t.Errorf("expect %q to be in %q", e, a)
				}
				return
			}
			if err != nil {
				t.Fatalf("expect no error, got %v", err)
			}

			if diff := cmpDiff(c.Expected, cfg); diff != "" {
				t.Errorf("expect shared config match\n%s", diff)
			}
		})
	}
}

func TestLoadSharedConfig(t *testing.T) {
	origProf := defaultSharedConfigProfile
	origConfigFiles := DefaultSharedConfigFiles
	origCredentialFiles := DefaultSharedCredentialsFiles
	defer func() {
		defaultSharedConfigProfile = origProf
		DefaultSharedConfigFiles = origConfigFiles
		DefaultSharedCredentialsFiles = origCredentialFiles
	}()

	cases := []struct {
		LoadOptionFn func(*LoadOptions) error
		Files        []string
		Profile      string
		LoadFn       func(context.Context, configs) (Config, error)
		Expect       SharedConfig
		Err          string
	}{
		{
			LoadOptionFn: WithSharedConfigProfile("alt_profile_name"),
			Files: []string{
				filepath.Join("testdata", "shared_config"),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "alt_profile_name",
				Region:  "alt_profile_name_region",
			},
		},
		{
			LoadOptionFn: WithSharedConfigFiles([]string{
				filepath.Join("testdata", "shared_config"),
			}),
			Profile: "alt_profile_name",
			LoadFn:  loadSharedConfig,
			Expect: SharedConfig{
				Profile: "alt_profile_name",
				Region:  "alt_profile_name_region",
			},
		},
		{
			LoadOptionFn: WithSharedConfigProfile("default"),
			Files: []string{
				filepath.Join("file_not_exist"),
			},
			LoadFn: loadSharedConfig,
			Err:    "failed to get shared config profile",
		},
		{
			LoadOptionFn: WithSharedConfigProfile("profile_not_exist"),
			Files: []string{
				filepath.Join("testdata", "shared_config"),
			},
			LoadFn: loadSharedConfig,
			Err:    "failed to get shared config profile",
		},
		{
			LoadOptionFn: WithSharedConfigProfile("default"),
			Files: []string{
				filepath.Join("file_not_exist"),
			},
			LoadFn: loadSharedConfigIgnoreNotExist,
		},
		{
			LoadOptionFn: WithSharedConfigProfile("assume_role_invalid_source_profile"),
			Files: []string{
				testConfigOtherFilename, testConfigFilename,
			},
			LoadFn: loadSharedConfig,
			Err:    "failed to get shared config profile",
		},
		{
			LoadOptionFn: WithSharedConfigProfile("assume_role_invalid_source_profile"),
			Files: []string{
				testConfigOtherFilename, testConfigFilename,
			},
			LoadFn: loadSharedConfigIgnoreNotExist,
			Err:    "failed to get shared config profile",
		},
	}

	for i, c := range cases {
		t.Run(strconv.Itoa(i), func(t *testing.T) {
			defaultSharedConfigProfile = origProf
			DefaultSharedConfigFiles = origConfigFiles
			DefaultSharedCredentialsFiles = origCredentialFiles

			if len(c.Profile) > 0 {
				defaultSharedConfigProfile = c.Profile
			}
			if len(c.Files) > 0 {
				DefaultSharedConfigFiles = c.Files
			}

			DefaultSharedCredentialsFiles = []string{}

			var options LoadOptions
			c.LoadOptionFn(&options)

			cfg, err := c.LoadFn(context.Background(), configs{options})
			if len(c.Err) > 0 {
				if err == nil {
					t.Fatalf("expected error %v, got none", c.Err)
				}
				if e, a := c.Err, err.Error(); !strings.Contains(a, e) {
					t.Fatalf("expect %q to be in %q", e, a)
				}
				return
			} else if err != nil {
				t.Fatalf("expect no error, got %v", err)
			}

			if e, a := c.Expect, cfg; !reflect.DeepEqual(e, a) {
				t.Errorf("expect %v got %v", e, a)
			}
		})
	}
}

func TestSharedConfigLoading(t *testing.T) {
	// initialize a logger
	var loggerBuf bytes.Buffer
	logger := logging.NewStandardLogger(&loggerBuf)

	cases := map[string]struct {
		LoadOptionFns []func(*LoadOptions) error
		LoadFn        func(context.Context, configs) (Config, error)
		Expect        SharedConfig
		ExpectLog     string
		Err           string
	}{
		"duplicate profiles in the configuration files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("duplicate-profile"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "duplicate-profile",
				Region:  "us-west-2",
			},
			ExpectLog: "For profile: profile duplicate-profile, overriding region value, with a region value found in a " +
				"duplicate profile defined later in the same file",
		},

		"profile prefix not used in the configuration files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("no-such-profile"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{},
			Err:    "failed to get shared config profile",
		},

		"profile prefix overrides default": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "default",
				Region:  "ap-north-1",
			},
			ExpectLog: "non-default profile not prefixed with `profile `",
		},

		"duplicate profiles in credentials file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("duplicate-profile"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "duplicate-profile",
				Region:  "us-west-2",
			},
			ExpectLog: "overriding region value, with a region value found in a duplicate profile defined later in the same file",
			Err:       "",
		},

		"profile prefix used in credentials files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("unused-profile"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			ExpectLog: "profile defined with name `profile unused-profile` is ignored.",
			Err:       "failed to get shared config profile, unused-profile",
		},
		"partial credentials in configuration files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("partial-creds-1"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "partial-creds-1",
			},
			Err: "partial credentials",
		},
		"parital credentials in the credentials files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("partial-creds-1"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "partial-creds-1",
			},
			Err: "partial credentials found for profile partial-creds-1",
		},
		"credentials override configuration profile": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("complete"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "complete",
				Credentials: aws.Credentials{
					AccessKeyID:     "credsAccessKey",
					SecretAccessKey: "credsSecretKey",
					Source: fmt.Sprintf("SharedConfigCredentials: %v",
						filepath.Join("testdata", "load_credentials")),
				},
				Region: "us-west-2",
			},
		},
		"credentials profile has complete credentials": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("complete"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "complete",
				Credentials: aws.Credentials{
					AccessKeyID:     "credsAccessKey",
					SecretAccessKey: "credsSecretKey",
					Source:          fmt.Sprintf("SharedConfigCredentials: %v", filepath.Join("testdata", "load_credentials")),
				},
			},
		},
		"credentials split between multiple credentials files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("partial-creds-1"),
				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
					filepath.Join("testdata", "load_credentials_secondary"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "partial-creds-1",
			},
			Err: "partial credentials",
		},
		"credentials split between multiple configuration files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("partial-creds-1"),
				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
					filepath.Join("testdata", "load_config_secondary"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "partial-creds-1",
				Region:  "us-west-2",
			},
			ExpectLog: "",
			Err:       "partial credentials",
		},
		"credentials split between credentials and config files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("partial-creds-1"),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "partial-creds-1",
			},
			ExpectLog: "",
			Err:       "partial credentials",
		},
		"replaced profile with prefixed profile in config files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("replaced-profile"),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "replaced-profile",
				Region:  "eu-west-1",
			},
			ExpectLog: "non-default profile not prefixed with `profile `",
		},
		"replaced profile with prefixed profile in credentials files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("replaced-profile"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "replaced-profile",
				Region:  "us-west-2",
			},
			ExpectLog: "profile defined with name `profile replaced-profile` is ignored.",
		},
		"ignored profiles w/o prefixed profile across credentials and config files": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("replaced-profile"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "replaced-profile",
				Region:  "us-west-2",
			},
			ExpectLog: "profile defined with name `profile replaced-profile` is ignored.",
		},
		"1. profile with name as `profile` in config file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("profile"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			Err:       "failed to get shared config profile, profile",
			ExpectLog: "profile defined with name `profile` is ignored",
		},
		"2. profile with name as `profile ` in config file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("profile "),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			Err:       "failed to get shared config profile, profile",
			ExpectLog: "profile defined with name `profile` is ignored",
		},
		"3. profile with name as `profile\t` in config file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("profile"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			Err:       "failed to get shared config profile, profile",
			ExpectLog: "profile defined with name `profile` is ignored",
		},
		"profile with tabs as delimiter for profile prefix in config file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("with-tab"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "with-tab",
				Region:  "cn-north-1",
			},
		},
		"profile with tabs as delimiter for profile prefix in credentials file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("with-tab"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			Err:       "failed to get shared config profile, with-tab",
			ExpectLog: "profile defined with name `profile with-tab` is ignored",
		},
		"profile with name as `profile profile` in credentials file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("profile"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			Err:       "failed to get shared config profile, profile",
			ExpectLog: "profile defined with name `profile profile` is ignored",
		},
		"profile with name profile-bar in credentials file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("profile-bar"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile: "profile-bar",
				Region:  "us-west-2",
			},
		},
		"profile with name profile-bar in config file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("profile-bar"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "empty_creds_config"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			Err:       "failed to get shared config profile, profile-bar",
			ExpectLog: "profile defined with name `profile-bar` is ignored",
		},
		"profile ignored in credentials and config file": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("ignored-profile"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn:    loadSharedConfig,
			Err:       "failed to get shared config profile, ignored-profile",
			ExpectLog: "profile defined with name `ignored-profile` is ignored.",
		},
		"profile with sso_session": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedConfigProfile("sso-session-test"),
				WithSharedCredentialsFiles([]string{
					filepath.Join("testdata", "load_credentials"),
				}),
				WithSharedConfigFiles([]string{
					filepath.Join("testdata", "load_config"),
				}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Expect: SharedConfig{
				Profile:        "sso-session-test",
				SSOSessionName: "dev-session",
				SSOSession: &SSOSession{
					Name:        "dev-session",
					SSORegion:   "us-west-2",
					SSOStartURL: "https://example.aws/start",
				},
			},
		},
		"config and creds files explicitly set to empty slice": {
			LoadOptionFns: []func(*LoadOptions) error{
				WithSharedCredentialsFiles([]string{}),
				WithSharedConfigFiles([]string{}),
				WithLogConfigurationWarnings(true),
				WithLogger(logger),
			},
			LoadFn: loadSharedConfig,
			Err:    "failed to get shared config profile, default",
		},
	}

	for name, c := range cases {
		t.Run(name, func(t *testing.T) {
			defer loggerBuf.Reset()

			var options LoadOptions

			for _, fn := range c.LoadOptionFns {
				fn(&options)
			}

			cfg, err := c.LoadFn(context.Background(), configs{options})

			if e, a := c.ExpectLog, loggerBuf.String(); !strings.Contains(a, e) {
				t.Errorf("expect %v logged in %v", e, a)
			}
			if loggerBuf.Len() == 0 && len(c.ExpectLog) != 0 {
				t.Errorf("expected log, got none")
			}

			if len(c.Err) > 0 {
				if err == nil {
					t.Fatalf("expected error %v, got none", c.Err)
				}
				if e, a := c.Err, err.Error(); !strings.Contains(a, e) {
					t.Fatalf("expect %q to be in %q", e, a)
				}
				return
			} else if err != nil {
				t.Fatalf("expect no error, got %v", err)
			}

			if diff := cmpDiff(c.Expect, cfg); diff != "" {
				t.Errorf("expect shared config match\n%s", diff)
			}
		})
	}
}
