github.com/anthropics/anthropic-sdk-go@v1.70.1/bedrock/bedrockmantle_test.go (about)

     1  package bedrock
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"io"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"strings"
    11  	"testing"
    12  
    13  	awssdk "github.com/aws/aws-sdk-go-v2/aws"
    14  	v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
    15  	"github.com/aws/aws-sdk-go-v2/credentials"
    16  
    17  	"github.com/anthropics/anthropic-sdk-go"
    18  	"github.com/anthropics/anthropic-sdk-go/internal/awsauth"
    19  	"github.com/anthropics/anthropic-sdk-go/option"
    20  )
    21  
    22  func makeStaticAWSConfig(region string) awssdk.Config {
    23  	return awssdk.Config{
    24  		Region: region,
    25  		Credentials: credentials.StaticCredentialsProvider{
    26  			Value: awssdk.Credentials{
    27  				AccessKeyID:     "test-access-key",
    28  				SecretAccessKey: "test-secret-key",
    29  			},
    30  		},
    31  	}
    32  }
    33  
    34  // writeMessagesResponse writes a canned successful Messages API response.
    35  func writeMessagesResponse(w http.ResponseWriter) {
    36  	w.Header().Set("Content-Type", "application/json")
    37  	json.NewEncoder(w).Encode(map[string]any{
    38  		"id":            "msg_test",
    39  		"type":          "message",
    40  		"role":          "assistant",
    41  		"content":       []map[string]any{{"type": "text", "text": "hi"}},
    42  		"model":         "claude-sonnet-4-6-20250514",
    43  		"stop_reason":   "end_turn",
    44  		"stop_sequence": nil,
    45  		"usage":         map[string]any{"input_tokens": 1, "output_tokens": 1},
    46  	})
    47  }
    48  
    49  type mantleCapturedRequest struct {
    50  	Headers http.Header
    51  	URL     string
    52  }
    53  
    54  func mantleMessagesHandler(captured *mantleCapturedRequest) http.HandlerFunc {
    55  	return func(w http.ResponseWriter, r *http.Request) {
    56  		captured.Headers = r.Header.Clone()
    57  		captured.URL = r.URL.String()
    58  		writeMessagesResponse(w)
    59  	}
    60  }
    61  
    62  // newTestMantleClient creates a MantleClient pointed at a test server and returns
    63  // the client and a struct that captures the request headers/URL.
    64  func newTestMantleClient(t *testing.T, cfg MantleClientConfig, opts ...option.RequestOption) (*MantleClient, *mantleCapturedRequest) {
    65  	t.Helper()
    66  	var captured mantleCapturedRequest
    67  	server := httptest.NewServer(mantleMessagesHandler(&captured))
    68  	t.Cleanup(server.Close)
    69  
    70  	cfg.BaseURL = server.URL
    71  	client, err := NewMantleClient(context.Background(), cfg, opts...)
    72  	if err != nil {
    73  		t.Fatalf("NewMantleClient failed: %v", err)
    74  	}
    75  	return client, &captured
    76  }
    77  
    78  func sendTestMantleRequest(t *testing.T, client *MantleClient) {
    79  	t.Helper()
    80  	_, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
    81  		Model:     "claude-sonnet-4-6-20250514",
    82  		MaxTokens: 1,
    83  		Messages: []anthropic.MessageParam{
    84  			anthropic.NewUserMessage(anthropic.NewTextBlock("hi")),
    85  		},
    86  	})
    87  	if err != nil {
    88  		t.Fatalf("request failed: %v", err)
    89  	}
    90  }
    91  
    92  // --- Validation tests ---
    93  
    94  func TestMantleRequiresBaseURLOrRegion(t *testing.T) {
    95  	t.Setenv("AWS_REGION", "")
    96  	t.Setenv("ANTHROPIC_BEDROCK_MANTLE_BASE_URL", "")
    97  
    98  	_, err := NewMantleClient(context.Background(), MantleClientConfig{
    99  		APIKey: "my-key",
   100  	})
   101  	if err == nil {
   102  		t.Fatal("expected error when neither base URL nor region is available")
   103  	}
   104  }
   105  
   106  func TestMantleRegionRequiredForSigV4(t *testing.T) {
   107  	t.Setenv("AWS_REGION", "")
   108  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   109  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   110  
   111  	_, err := NewMantleClient(context.Background(), MantleClientConfig{
   112  		AWSAccessKey:       "key",
   113  		AWSSecretAccessKey: "secret",
   114  	})
   115  	if err == nil {
   116  		t.Fatal("expected error when region is missing and SigV4 is used")
   117  	}
   118  }
   119  
   120  // --- API key mode tests ---
   121  
   122  func TestMantleAPIKeyModeHeaders(t *testing.T) {
   123  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   124  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   125  
   126  	client, captured := newTestMantleClient(t, MantleClientConfig{
   127  		APIKey:    "my-api-key",
   128  		AWSRegion: "us-east-1",
   129  	})
   130  	sendTestMantleRequest(t, client)
   131  
   132  	if got := captured.Headers.Get("Authorization"); got != "Bearer my-api-key" {
   133  		t.Errorf("expected Authorization %q, got %q", "Bearer my-api-key", got)
   134  	}
   135  	if captured.Headers.Get("X-Api-Key") != "" {
   136  		t.Error("expected no X-Api-Key header in bearer auth mode")
   137  	}
   138  }
   139  
   140  // --- SigV4 mode tests ---
   141  
   142  func TestMantleSigV4ModeHeaders(t *testing.T) {
   143  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   144  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   145  	t.Setenv("ANTHROPIC_API_KEY", "")
   146  
   147  	client, captured := newTestMantleClient(t, MantleClientConfig{
   148  		AWSRegion:          "us-east-1",
   149  		AWSAccessKey:       "test-access-key",
   150  		AWSSecretAccessKey: "test-secret-key",
   151  	})
   152  	sendTestMantleRequest(t, client)
   153  
   154  	auth := captured.Headers.Get("Authorization")
   155  	if auth == "" {
   156  		t.Fatal("expected Authorization header in SigV4 mode")
   157  	}
   158  	if !strings.HasPrefix(auth, "AWS4-HMAC-SHA256") {
   159  		t.Errorf("expected AWS4 signature, got: %s", auth)
   160  	}
   161  	if !strings.Contains(auth, mantleServiceName) {
   162  		t.Errorf("expected service name %q in Authorization, got: %s", mantleServiceName, auth)
   163  	}
   164  	if captured.Headers.Get("X-Amz-Date") == "" {
   165  		t.Error("expected X-Amz-Date header in SigV4 mode")
   166  	}
   167  }
   168  
   169  // --- SigV4 middleware service name test ---
   170  
   171  func TestMantleSigV4ServiceName(t *testing.T) {
   172  	cfg := makeStaticAWSConfig("us-east-1")
   173  	signer := v4.NewSigner()
   174  	middleware := awsauth.SigV4Middleware(signer, cfg, mantleServiceName)
   175  
   176  	req, err := http.NewRequest("POST", "https://bedrock-mantle.us-east-1.api.aws/anthropic/v1/messages", bytes.NewReader([]byte(`{}`)))
   177  	if err != nil {
   178  		t.Fatalf("failed to create request: %v", err)
   179  	}
   180  
   181  	var gotAuth string
   182  	_, err = middleware(req, func(r *http.Request) (*http.Response, error) {
   183  		gotAuth = r.Header.Get("Authorization")
   184  		return &http.Response{StatusCode: 200, Body: http.NoBody}, nil
   185  	})
   186  	if err != nil {
   187  		t.Fatalf("middleware failed: %v", err)
   188  	}
   189  
   190  	if gotAuth == "" {
   191  		t.Fatal("expected non-empty Authorization header")
   192  	}
   193  	expectedServiceFragment := "/" + mantleServiceName + "/"
   194  	if !bytes.Contains([]byte(gotAuth), []byte(expectedServiceFragment)) {
   195  		t.Errorf("expected Authorization to contain service %q, got: %s", mantleServiceName, gotAuth)
   196  	}
   197  }
   198  
   199  // --- Env var fallback tests ---
   200  
   201  func TestMantleAPIKeyFallbackToAWSEnv(t *testing.T) {
   202  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   203  	t.Setenv("ANTHROPIC_AWS_API_KEY", "aws-fallback-key")
   204  	t.Setenv("AWS_REGION", "us-east-1")
   205  
   206  	client, captured := newTestMantleClient(t, MantleClientConfig{})
   207  	sendTestMantleRequest(t, client)
   208  
   209  	if got := captured.Headers.Get("Authorization"); got != "Bearer aws-fallback-key" {
   210  		t.Errorf("expected Authorization %q (AWS fallback), got %q", "Bearer aws-fallback-key", got)
   211  	}
   212  }
   213  
   214  func TestMantleAPIKeyMantleEnvOverridesAWSEnv(t *testing.T) {
   215  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "mantle-key")
   216  	t.Setenv("ANTHROPIC_AWS_API_KEY", "aws-key")
   217  	t.Setenv("AWS_REGION", "us-east-1")
   218  
   219  	client, captured := newTestMantleClient(t, MantleClientConfig{})
   220  	sendTestMantleRequest(t, client)
   221  
   222  	if got := captured.Headers.Get("Authorization"); got != "Bearer mantle-key" {
   223  		t.Errorf("expected Authorization %q (mantle-specific), got %q", "Bearer mantle-key", got)
   224  	}
   225  }
   226  
   227  // --- Base URL tests ---
   228  
   229  func TestMantleBaseURLDerivedFromRegion(t *testing.T) {
   230  	t.Setenv("ANTHROPIC_BEDROCK_MANTLE_BASE_URL", "")
   231  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   232  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   233  
   234  	resolved, err := awsauth.ResolveConfig(mantleToInternalConfig(MantleClientConfig{
   235  		AWSRegion: "us-west-2",
   236  		APIKey:    "my-key",
   237  	}), mantleResolveParams())
   238  	if err != nil {
   239  		t.Fatalf("unexpected error: %v", err)
   240  	}
   241  	expected := "https://bedrock-mantle.us-west-2.api.aws/anthropic"
   242  	if resolved.BaseURL != expected {
   243  		t.Errorf("expected base URL %q, got %q", expected, resolved.BaseURL)
   244  	}
   245  }
   246  
   247  func TestMantleBaseURLFromEnv(t *testing.T) {
   248  	t.Setenv("ANTHROPIC_BEDROCK_MANTLE_BASE_URL", "https://custom.mantle.example.com")
   249  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "my-key")
   250  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   251  
   252  	resolved, err := awsauth.ResolveConfig(mantleToInternalConfig(MantleClientConfig{}), mantleResolveParams())
   253  	if err != nil {
   254  		t.Fatalf("unexpected error: %v", err)
   255  	}
   256  	if resolved.BaseURL != "https://custom.mantle.example.com" {
   257  		t.Errorf("expected base URL from env, got %q", resolved.BaseURL)
   258  	}
   259  }
   260  
   261  func TestMantleBaseURLExplicitOverridesRegion(t *testing.T) {
   262  	t.Setenv("ANTHROPIC_BEDROCK_MANTLE_BASE_URL", "")
   263  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   264  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   265  
   266  	resolved, err := awsauth.ResolveConfig(mantleToInternalConfig(MantleClientConfig{
   267  		BaseURL:   "https://explicit.example.com",
   268  		AWSRegion: "us-east-1",
   269  		APIKey:    "my-key",
   270  	}), mantleResolveParams())
   271  	if err != nil {
   272  		t.Fatalf("unexpected error: %v", err)
   273  	}
   274  	if resolved.BaseURL != "https://explicit.example.com" {
   275  		t.Errorf("expected explicit base URL, got %q", resolved.BaseURL)
   276  	}
   277  }
   278  
   279  // --- skipAuth tests ---
   280  
   281  func TestMantleSkipAuthNoAuthRequired(t *testing.T) {
   282  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   283  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   284  
   285  	_, err := NewMantleClient(context.Background(), MantleClientConfig{
   286  		BaseURL:  "https://proxy.example.com",
   287  		SkipAuth: true,
   288  	})
   289  	if err != nil {
   290  		t.Fatalf("expected no error with skipAuth, got: %v", err)
   291  	}
   292  }
   293  
   294  func TestMantleSkipAuthNoAuthHeaders(t *testing.T) {
   295  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   296  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   297  
   298  	client, captured := newTestMantleClient(t, MantleClientConfig{
   299  		SkipAuth: true,
   300  	})
   301  	sendTestMantleRequest(t, client)
   302  
   303  	if captured.Headers.Get("Authorization") != "" {
   304  		t.Error("expected no Authorization header with skipAuth")
   305  	}
   306  	if captured.Headers.Get("X-Amz-Date") != "" {
   307  		t.Error("expected no X-Amz-Date header with skipAuth")
   308  	}
   309  }
   310  
   311  // --- NewMantleClient tests ---
   312  
   313  func TestNewMantleClientMessages(t *testing.T) {
   314  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   315  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   316  
   317  	client, captured := newTestMantleClient(t, MantleClientConfig{
   318  		APIKey:    "test-key",
   319  		AWSRegion: "us-east-1",
   320  	})
   321  	sendTestMantleRequest(t, client)
   322  
   323  	if got := captured.Headers.Get("Authorization"); got != "Bearer test-key" {
   324  		t.Errorf("expected Authorization %q, got %q", "Bearer test-key", got)
   325  	}
   326  }
   327  
   328  func TestNewMantleClientBetaMessages(t *testing.T) {
   329  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   330  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   331  
   332  	var captured mantleCapturedRequest
   333  	server := httptest.NewServer(mantleMessagesHandler(&captured))
   334  	t.Cleanup(server.Close)
   335  
   336  	client, err := NewMantleClient(context.Background(), MantleClientConfig{
   337  		APIKey:    "test-key",
   338  		AWSRegion: "us-east-1",
   339  		BaseURL:   server.URL,
   340  	})
   341  	if err != nil {
   342  		t.Fatalf("unexpected error: %v", err)
   343  	}
   344  
   345  	_, err = client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
   346  		Model:     "claude-sonnet-4-6-20250514",
   347  		MaxTokens: 1,
   348  		Messages: []anthropic.BetaMessageParam{
   349  			anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("hi")),
   350  		},
   351  	})
   352  	if err != nil {
   353  		t.Fatalf("beta messages request failed: %v", err)
   354  	}
   355  }
   356  
   357  // --- RequestOption tests ---
   358  
   359  func TestMantleClientRequestOptionsCustomHeader(t *testing.T) {
   360  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   361  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   362  
   363  	client, captured := newTestMantleClient(t, MantleClientConfig{
   364  		APIKey:    "test-key",
   365  		AWSRegion: "us-east-1",
   366  	}, option.WithHeader("X-Custom-Header", "custom-value"))
   367  	sendTestMantleRequest(t, client)
   368  
   369  	if got := captured.Headers.Get("X-Custom-Header"); got != "custom-value" {
   370  		t.Errorf("expected X-Custom-Header %q, got %q", "custom-value", got)
   371  	}
   372  }
   373  
   374  func TestMantleClientRequestOptionsOverrideInternal(t *testing.T) {
   375  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   376  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   377  
   378  	// User-provided opts should override internal opts (e.g. override the Authorization header)
   379  	client, captured := newTestMantleClient(t, MantleClientConfig{
   380  		APIKey:    "internal-key",
   381  		AWSRegion: "us-east-1",
   382  	}, option.WithHeader("Authorization", "Bearer override-key"))
   383  	sendTestMantleRequest(t, client)
   384  
   385  	if got := captured.Headers.Get("Authorization"); got != "Bearer override-key" {
   386  		t.Errorf("expected Authorization %q (from RequestOption override), got %q", "Bearer override-key", got)
   387  	}
   388  }
   389  
   390  func TestMantleClientPerRequestOptionsOverrideClientOptions(t *testing.T) {
   391  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   392  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   393  
   394  	client, captured := newTestMantleClient(t, MantleClientConfig{
   395  		APIKey:    "test-key",
   396  		AWSRegion: "us-east-1",
   397  	}, option.WithHeader("X-Custom-Header", "client-level"))
   398  
   399  	// Send request with per-request option that overrides the client-level header
   400  	_, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
   401  		Model:     "claude-sonnet-4-6-20250514",
   402  		MaxTokens: 1,
   403  		Messages: []anthropic.MessageParam{
   404  			anthropic.NewUserMessage(anthropic.NewTextBlock("hi")),
   405  		},
   406  	}, option.WithHeader("X-Custom-Header", "request-level"))
   407  	if err != nil {
   408  		t.Fatalf("request failed: %v", err)
   409  	}
   410  
   411  	if got := captured.Headers.Get("X-Custom-Header"); got != "request-level" {
   412  		t.Errorf("expected X-Custom-Header %q (per-request override), got %q", "request-level", got)
   413  	}
   414  }
   415  
   416  // TestMantleUserMiddlewareRunsBeforeSigV4Signing verifies that SigV4 signing
   417  // runs after (inside) middleware registered through client options, so the
   418  // signature covers the middleware's request mutations.
   419  func TestMantleUserMiddlewareRunsBeforeSigV4Signing(t *testing.T) {
   420  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   421  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   422  	t.Setenv("ANTHROPIC_API_KEY", "")
   423  
   424  	var observedAuth, observedAmzDate string
   425  	mutator := func(r *http.Request, next option.MiddlewareNext) (*http.Response, error) {
   426  		observedAuth = r.Header.Get("Authorization")
   427  		observedAmzDate = r.Header.Get("X-Amz-Date")
   428  
   429  		body, err := io.ReadAll(r.Body)
   430  		if err != nil {
   431  			return nil, err
   432  		}
   433  		var parsed map[string]any
   434  		if err := json.Unmarshal(body, &parsed); err != nil {
   435  			return nil, err
   436  		}
   437  		parsed["metadata"] = map[string]any{"user_id": "injected-by-middleware"}
   438  		mutated, err := json.Marshal(parsed)
   439  		if err != nil {
   440  			return nil, err
   441  		}
   442  		r.Body = io.NopCloser(bytes.NewReader(mutated))
   443  		r.ContentLength = int64(len(mutated))
   444  		return next(r)
   445  	}
   446  
   447  	var wireBody map[string]any
   448  	var wireAuth string
   449  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   450  		wireAuth = r.Header.Get("Authorization")
   451  		if err := json.NewDecoder(r.Body).Decode(&wireBody); err != nil {
   452  			t.Errorf("failed to decode wire body: %v", err)
   453  		}
   454  		writeMessagesResponse(w)
   455  	}))
   456  	t.Cleanup(server.Close)
   457  
   458  	client, err := NewMantleClient(context.Background(), MantleClientConfig{
   459  		BaseURL:            server.URL,
   460  		AWSRegion:          "us-east-1",
   461  		AWSAccessKey:       "test-access-key",
   462  		AWSSecretAccessKey: "test-secret-key",
   463  	}, option.WithMiddleware(mutator))
   464  	if err != nil {
   465  		t.Fatalf("NewMantleClient failed: %v", err)
   466  	}
   467  	sendTestMantleRequest(t, client)
   468  
   469  	// The user middleware runs before signing, so no signature exists yet.
   470  	if observedAuth != "" {
   471  		t.Errorf("expected user middleware to run before signing, but saw Authorization %q", observedAuth)
   472  	}
   473  	if observedAmzDate != "" {
   474  		t.Errorf("expected user middleware to run before signing, but saw X-Amz-Date %q", observedAmzDate)
   475  	}
   476  
   477  	// The wire request is signed and carries the middleware's mutation.
   478  	if !strings.HasPrefix(wireAuth, "AWS4-HMAC-SHA256") {
   479  		t.Errorf("expected SigV4 Authorization on the wire, got %q", wireAuth)
   480  	}
   481  	metadata, _ := wireBody["metadata"].(map[string]any)
   482  	if metadata["user_id"] != "injected-by-middleware" {
   483  		t.Errorf("expected middleware mutation in the signed wire body, got metadata %v", wireBody["metadata"])
   484  	}
   485  }
   486  
   487  func TestMantleClientRequestOptionsMiddleware(t *testing.T) {
   488  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   489  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   490  
   491  	middlewareCalled := false
   492  	mw := func(r *http.Request, next option.MiddlewareNext) (*http.Response, error) {
   493  		middlewareCalled = true
   494  		r.Header.Set("X-From-Middleware", "true")
   495  		return next(r)
   496  	}
   497  
   498  	client, captured := newTestMantleClient(t, MantleClientConfig{
   499  		APIKey:    "test-key",
   500  		AWSRegion: "us-east-1",
   501  	}, option.WithMiddleware(mw))
   502  	sendTestMantleRequest(t, client)
   503  
   504  	if !middlewareCalled {
   505  		t.Error("expected middleware to be called")
   506  	}
   507  	if got := captured.Headers.Get("X-From-Middleware"); got != "true" {
   508  		t.Errorf("expected X-From-Middleware %q, got %q", "true", got)
   509  	}
   510  }
   511  
   512  // --- ANTHROPIC_API_KEY isolation test ---
   513  
   514  func TestMantleDoesNotLeakAnthropicAPIKey(t *testing.T) {
   515  	t.Setenv("ANTHROPIC_API_KEY", "should-not-appear")
   516  	t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "")
   517  	t.Setenv("ANTHROPIC_AWS_API_KEY", "")
   518  
   519  	client, captured := newTestMantleClient(t, MantleClientConfig{
   520  		AWSRegion:          "us-east-1",
   521  		AWSAccessKey:       "test-access-key",
   522  		AWSSecretAccessKey: "test-secret-key",
   523  	})
   524  	sendTestMantleRequest(t, client)
   525  
   526  	if got := captured.Headers.Get("X-Api-Key"); got != "" {
   527  		t.Errorf("expected no X-Api-Key header when using SigV4, got %q", got)
   528  	}
   529  	// Authorization header should be SigV4, not Bearer
   530  	if auth := captured.Headers.Get("Authorization"); !strings.HasPrefix(auth, "AWS4-HMAC-SHA256") {
   531  		t.Errorf("expected SigV4 Authorization header, got %q", auth)
   532  	}
   533  }