/*
Copyright © The ESO Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package webhook

import (
	"bytes"
	"context"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"gopkg.in/yaml.v3"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
	"github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
)

type testCase struct {
	Case string `json:"case,omitempty"`
	Args args   `json:"args"`
	Want want   `json:"want"`
}

type secret struct {
	Name string            `json:"name"`
	Data map[string]string `json:"data"`
}

type args struct {
	URL        string `json:"url,omitempty"`
	Body       string `json:"body,omitempty"`
	Timeout    string `json:"timeout,omitempty"`
	Key        string `json:"key,omitempty"`
	SecretKey  string `json:"secretkey,omitempty"`
	Property   string `json:"property,omitempty"`
	Version    string `json:"version,omitempty"`
	JSONPath   string `json:"jsonpath,omitempty"`
	Response   string `json:"response,omitempty"`
	StatusCode int    `json:"statuscode,omitempty"`
	PushSecret bool   `json:"pushsecret,omitempty"`
	Secret     secret `json:"secret,omitempty"`
}

type want struct {
	Path      string            `json:"path,omitempty"`
	Body      *string           `json:"body,omitempty"`
	Err       string            `json:"err,omitempty"`
	Result    string            `json:"result,omitempty"`
	ResultMap map[string]string `json:"resultmap,omitempty"`
}

var testCases = `
case: error url
args:
  url: /api/getsecret?id={{ .unclosed.template
want:
  err: failed to parse url
---
case: error body
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  body: Body error {{ .unclosed.template
want:
  err: failed to parse body
---
case: error connection
args:
  url: 1/api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
want:
  err: failed to call endpoint
---
case: error no secret err
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  statuscode: 404
  response: not found
want:
  path: /api/getsecret?id=testkey&version=1
  err: ` + esv1.NoSecretErr.Error() + `
---
case: error server error
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  statuscode: 500
  response: server error
want:
  path: /api/getsecret?id=testkey&version=1
  err: endpoint gave error 500
---
case: error bad json
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result.thesecret
  response: '{"result":{"thesecret":"secret-value"}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: failed to parse response json
---
case: error bad jsonpath
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result.thesecret
  response: '{"result":{"nosecret":"secret-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: failed to get response path
---
case: pull data out of map
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result.thesecret
  response: '{"result":{"thesecret":{"one":"secret-value"}}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: '{"one":"secret-value"}'
---
case: error timeout
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  response: secret-value
  timeout: 0.01ms
want:
  path: /api/getsecret?id=testkey&version=1
  err: context deadline exceeded
---
case: good plaintext
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  response: secret-value
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: secret-value
---
case: good json
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result.thesecret
  response: '{"result":{"thesecret":"secret-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: secret-value
---
case: good json map
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result
  response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  resultmap:
    thesecret: secret-value
    alsosecret: another-value
---
case: templated jsonpath good json map
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.{{printf "result" }}
  response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  resultmap:
    thesecret: secret-value
    alsosecret: another-value
---
case: templated jsonpath invalid template
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.{{printf 'result' }}
  response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: "cannot get templated json path"
  resultmap:
    thesecret: secret-value
    alsosecret: another-value
---
case: good json map string
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  response: '{"thesecret":"secret-value","alsosecret":"another-value"}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  resultmap:
    thesecret: secret-value
    alsosecret: another-value
---
case: error json map string
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  response: 'some simple string'
want:
  path: /api/getsecret?id=testkey&version=1
  err: "failed to parse response json: invalid character"
  resultmap: {}
---
case: error json map
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result.thesecret
  response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: "failed to parse response json from jsonpath"
  resultmap: {}
---
case: good json with good templated jsonpath
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  property: thesecret
  version: 1
  jsonpath: $.result.{{ .remoteRef.property }}
  response: '{"result":{"thesecret":"secret-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: secret-value
---
case: good json with jsonpath filter
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.secrets[?@.name=="thesecret"].value
  response: '{"secrets": [{"name": "thesecret", "value": "secret-value"}, {"name": "alsosecret", "value": "another-value"}]}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: secret-value
---
case: good json with bad templated jsonpath
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  property: thesecret
  version: 1
  jsonpath: $.result.{{ .remoteRef.property }
  response: '{"result":{"thesecret":"secret-value"}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: 'template: webhooktemplate:1: unexpected "}" in operand'
---
case: error with jsonpath filter empty results
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.secrets[?@.name=="thebadsecret"].value
  response: '{"secrets": [{"name": "thesecret", "value": "secret-value"}, {"name": "alsosecret", "value": "another-value"}]}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: "filter worked but didn't get any result"
---
case: success with jsonpath filter and result array
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $..name
  response: '{"secrets": [{"name": "thesecret", "value": "secret-value"}, {"name": "alsosecret", "value": "another-value"}]}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: 'thesecret'
---
case: success with jsonpath filter and result array of ints
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $..name
  response: '{"secrets": [{"name": 123, "value": "secret-value"}, {"name": 456, "value": "another-value"}]}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: 123
---
case: support backslash
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.refresh_token
  response: '{"access_token":"REDACTED","refresh_token":"RE\/DACTED=="}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: "RE/DACTED=="
---
case: good json with mixed fields and jsonpath filter
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result.thesecret
  response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value", "id": 1234, "weight": 1.5}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  result: secret-value
---
case: good json with mixed fields to map
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
  key: testkey
  version: 1
  jsonpath: $.result
  response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value", "id": 1234, "weight": 1.5}}'
want:
  path: /api/getsecret?id=testkey&version=1
  err: ''
  resultmap:
    thesecret: secret-value
    alsosecret: another-value
    id: 1234
    weight: 1.5
---
case: only url encoding for url templates
args:
  url: /api/getsecrets?folder={{ .remoteRef.key }}
  body: '{"folder": "{{ .remoteRef.key }}"}'
  key: /myapp/secrets
want:
  path: /api/getsecrets?folder=%2Fmyapp%2Fsecrets
  body: '{"folder": "/myapp/secrets"}'
---
case: namespace template in headers
args:
  url: /api/getsecret?id={{ .remoteRef.key }}
  key: testkey
  response: secret-value
want:
  path: /api/getsecret?id=testkey
  err: ''
  result: secret-value
---
case: namespace template in url
args:
  url: /api/getsecret?id={{ .remoteRef.key }}&namespace={{ .remoteRef.namespace }}
  key: testkey
  response: secret-value
want:
  path: /api/getsecret?id=testkey&namespace=testnamespace
  err: ''
  result: secret-value
`

func TestWebhookGetSecret(t *testing.T) {
	ydec := yaml.NewDecoder(bytes.NewReader([]byte(testCases)))
	for {
		var tc testCase
		if err := ydec.Decode(&tc); err != nil {
			if !errors.Is(err, io.EOF) {
				t.Errorf("testcase decode error %v", err)
			}
			break
		}
		runTestCase(tc, t)
	}
}

var testCasesPushSecret = `
---
case: secret key not found
args:
  url: /api/pushsecret?id={{ .remoteRef.remoteKey }}&secret={{ .remoteRef.secretKey }}
  key: testkey
  secretkey: not-found
  pushsecret: true
  secret:
    name: test-secret
    data:
      secretkey: value
want:
  path: /api/pushsecret?id=testkey&secret=not-found
  err: 'failed to find secret key in secret with key: not-found'
---
case: default body good json
args:
  url: /api/pushsecret?id={{ .remoteRef.remoteKey }}&secret={{ .remoteRef.secretKey }}
  key: testkey
  secretkey: secretkey
  pushsecret: true
  secret:
    name: test-secret
    data:
      secretkey: value
want:
  path: /api/pushsecret?id=testkey&secret=secretkey
  body: 'value'
  err: ''
---
case: good json
args:
  url: /api/pushsecret?id={{ .remoteRef.remoteKey }}&secret={{ .remoteRef.secretKey }}
  key: testkey
  body: 'pre {{ .remoteRef.remoteKey }} {{ .remoteRef.testkey }} post'
  secretkey: secretkey
  pushsecret: true
  secret:
    name: test-secret
    data:
      secretkey: value
want:
  path: /api/pushsecret?id=testkey&secret=secretkey
  body: 'pre testkey value post'
  err: ''
---
case: empty body
args:
  url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  key: testkey
  body: '{{ "" }}'
  pushsecret: true
  secret:
    name: test-secret
    data:
      secretkey: value
want:
  path: /api/pushsecret?id=testkey
  body: ''
  err: ''
---
case: default body pushing without secret key
args:
  url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  key: testkey
  pushsecret: true
  secret:
    name: test-secret
    data:
      secretkey: value
want:
  path: /api/pushsecret?id=testkey
  body: '{"secretkey":"value"}'
  err: ''
---
case: pushing without secret key
args:
  url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  key: testkey
  body: 'pre {{ .remoteRef.remoteKey }} {{ index (.remoteRef.testkey | fromJson) "secretkey" }} post'
  pushsecret: true
  secret:
    name: test-secret
    data:
      secretkey: value
want:
  path: /api/pushsecret?id=testkey
  body: 'pre testkey value post'
  err: ''
---
case: pushing without secret key with dynamic resolution
args:
  url: /api/pushsecret?id={{ .remoteRef.remoteKey }}
  key: testkey
  body: 'pre {{ .remoteRef.remoteKey }} {{ index (index .remoteRef .remoteRef.remoteKey | fromJson) "secretkey" }} post'
  pushsecret: true
  secret:
    name: test-secret
    data:
      secretkey: value
want:
  path: /api/pushsecret?id=testkey
  body: 'pre testkey value post'
  err: ''
`

func TestWebhookPushSecret(t *testing.T) {
	ydec := yaml.NewDecoder(bytes.NewReader([]byte(testCasesPushSecret)))
	for {
		var tc testCase
		if err := ydec.Decode(&tc); err != nil {
			if !errors.Is(err, io.EOF) {
				t.Errorf("testcase decode error %v", err)
			}
			break
		}
		runTestCase(tc, t)
	}
}

func testCaseServer(tc testCase, t *testing.T) *httptest.Server {
	// Start a new server for every test case because the server wants to check the expected api path
	return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if tc.Want.Path != "" && req.URL.String() != tc.Want.Path {
			t.Errorf("%s: unexpected api path: %s, expected %s", tc.Case, req.URL.String(), tc.Want.Path)
		}
		if tc.Want.Body != nil {
			b, _ := io.ReadAll(req.Body)
			if tc.Want.Body != nil && string(b) != *tc.Want.Body {
				t.Errorf("%s: unexpected body: %s, expected %s", tc.Case, string(b), *tc.Want.Body)
			}
		}
		if tc.Args.StatusCode != 0 {
			rw.WriteHeader(tc.Args.StatusCode)
		}
		rw.Write([]byte(tc.Args.Response))
	}))
}

func parseTimeout(timeout string) (*metav1.Duration, error) {
	if timeout == "" {
		return nil, nil
	}
	dur, err := time.ParseDuration(timeout)
	if err != nil {
		return nil, err
	}
	return &metav1.Duration{Duration: dur}, nil
}

func runTestCase(tc testCase, t *testing.T) {
	t.Run(tc.Case, func(t *testing.T) {
		ts := testCaseServer(tc, t)
		defer ts.Close()

		testStore := makeClusterSecretStore(ts.URL, tc.Args)
		var err error
		timeout, err := parseTimeout(tc.Args.Timeout)
		if err != nil {
			t.Errorf("%s: error parsing timeout '%s': %s", tc.Case, tc.Args.Timeout, err.Error())
			return
		}
		testStore.Spec.Provider.Webhook.Timeout = timeout
		testProv := &Provider{}
		client, err := testProv.NewClient(context.Background(), testStore, nil, "testnamespace")
		if err != nil {
			t.Errorf("%s: error creating client: %s", tc.Case, err.Error())
			return
		}

		if tc.Want.ResultMap != nil && !tc.Args.PushSecret {
			testGetSecretMap(tc, t, client)
		} else if !tc.Args.PushSecret {
			testGetSecret(tc, t, client)
		} else {
			testPushSecret(tc, t, client)
		}
	})
}

func testGetSecretMap(tc testCase, t *testing.T, client esv1.SecretsClient) {
	testRef := esv1.ExternalSecretDataRemoteRef{
		Key:     tc.Args.Key,
		Version: tc.Args.Version,
	}
	secretmap, err := client.GetSecretMap(context.Background(), testRef)
	errStr := ""
	if err != nil {
		errStr = err.Error()
	}
	if (tc.Want.Err == "") != (errStr == "") || !strings.Contains(errStr, tc.Want.Err) {
		t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
	}
	if err == nil {
		for wantkey, wantval := range tc.Want.ResultMap {
			gotval, ok := secretmap[wantkey]
			if !ok {
				t.Errorf("%s: unexpected response: wanted key '%s' not found", tc.Case, wantkey)
			} else if string(gotval) != wantval {
				t.Errorf("%s: unexpected response: key '%s' = '%s' (expected '%s')", tc.Case, wantkey, wantval, gotval)
			}
		}
	}
}

func testGetSecret(tc testCase, t *testing.T, client esv1.SecretsClient) {
	testRef := esv1.ExternalSecretDataRemoteRef{
		Key:      tc.Args.Key,
		Property: tc.Args.Property,
		Version:  tc.Args.Version,
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	secret, err := client.GetSecret(ctx, testRef)
	errStr := ""
	if err != nil {
		errStr = err.Error()
	}
	if !strings.Contains(errStr, tc.Want.Err) {
		t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
	}
	if err == nil && string(secret) != tc.Want.Result {
		t.Errorf("%s: unexpected response: '%s' (expected '%s')", tc.Case, secret, tc.Want.Result)
	}
}

func testPushSecret(tc testCase, t *testing.T, client esv1.SecretsClient) {
	testRef := v1alpha1.PushSecretData{
		Match: v1alpha1.PushSecretMatch{
			SecretKey: tc.Args.SecretKey,
			RemoteRef: v1alpha1.PushSecretRemoteRef{
				RemoteKey: tc.Args.Key,
			},
		},
	}

	data := map[string][]byte{}
	for k, v := range tc.Args.Secret.Data {
		data[k] = []byte(v)
	}
	sec := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name: tc.Args.Secret.Name,
		},
		Data: data,
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	err := client.PushSecret(ctx, sec, testRef)
	errStr := ""
	if err != nil {
		errStr = err.Error()
	}
	if tc.Want.Err == "" && errStr != "" {
		t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
	}

	if !strings.Contains(errStr, tc.Want.Err) {
		t.Errorf("%s: unexpected error: '%s' (expected '%s')", tc.Case, errStr, tc.Want.Err)
	}
}

func makeClusterSecretStore(url string, args args) *esv1.ClusterSecretStore {
	store := &esv1.ClusterSecretStore{
		TypeMeta: metav1.TypeMeta{
			Kind: "ClusterSecretStore",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name:      "wehbook-store",
			Namespace: "default",
		},
		Spec: esv1.SecretStoreSpec{
			Provider: &esv1.SecretStoreProvider{
				Webhook: &esv1.WebhookProvider{
					URL:  url + args.URL,
					Body: args.Body,
					Headers: map[string]string{
						"Content-Type":           "application.json",
						"X-SecretKey":            "{{ .remoteRef.key }}",
						"X-Kubernetes-Namespace": "{{ .remoteRef.namespace }}",
					},
					Result: esv1.WebhookResult{
						JSONPath: args.JSONPath,
					},
				},
			},
		},
	}
	return store
}

func TestSecretExists(t *testing.T) {
	tests := []struct {
		name         string
		statusCode   int
		responseBody string
		wantExists   bool
		wantErr      bool
	}{
		{
			name:         "secret exists - returns 200",
			statusCode:   http.StatusOK,
			responseBody: `{"data":"secret-value"}`,
			wantExists:   true,
			wantErr:      false,
		},
		{
			name:         "secret does not exist - returns 404",
			statusCode:   http.StatusNotFound,
			responseBody: "not found",
			wantExists:   false,
			wantErr:      false,
		},
		{
			name:         "server error - returns 500",
			statusCode:   http.StatusInternalServerError,
			responseBody: "internal server error",
			wantExists:   false,
			wantErr:      true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				w.WriteHeader(tt.statusCode)
				w.Write([]byte(tt.responseBody))
			}))
			defer ts.Close()

			store := makeClusterSecretStore(ts.URL, args{URL: "/api/secret"})
			prov := &Provider{}
			client, err := prov.NewClient(context.Background(), store, nil, "testnamespace")
			if err != nil {
				t.Fatalf("failed to create client: %v", err)
			}

			remoteRef := v1alpha1.PushSecretRemoteRef{
				RemoteKey: "test-key",
			}

			exists, err := client.SecretExists(context.Background(), remoteRef)

			if (err != nil) != tt.wantErr {
				t.Errorf("SecretExists() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if exists != tt.wantExists {
				t.Errorf("SecretExists() = %v, want %v", exists, tt.wantExists)
			}
		})
	}
}

func TestDeleteSecret(t *testing.T) {
	tests := []struct {
		name         string
		statusCode   int
		wantErr      bool
		expectMethod string
	}{
		{
			name:         "successful delete - returns 200",
			statusCode:   http.StatusOK,
			wantErr:      false,
			expectMethod: http.MethodDelete,
		},
		{
			name:         "successful delete - returns 204",
			statusCode:   http.StatusNoContent,
			wantErr:      false,
			expectMethod: http.MethodDelete,
		},
		{
			name:         "secret not found - returns 404",
			statusCode:   http.StatusNotFound,
			wantErr:      false,
			expectMethod: http.MethodDelete,
		},
		{
			name:         "server error - returns 500",
			statusCode:   http.StatusInternalServerError,
			wantErr:      true,
			expectMethod: http.MethodDelete,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var receivedMethod string
			ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				receivedMethod = r.Method
				w.WriteHeader(tt.statusCode)
			}))
			defer ts.Close()

			store := makeClusterSecretStore(ts.URL, args{URL: "/api/secret"})
			prov := &Provider{}
			client, err := prov.NewClient(context.Background(), store, nil, "testnamespace")
			if err != nil {
				t.Fatalf("failed to create client: %v", err)
			}

			remoteRef := v1alpha1.PushSecretRemoteRef{
				RemoteKey: "test-key",
			}

			err = client.DeleteSecret(context.Background(), remoteRef)

			if (err != nil) != tt.wantErr {
				t.Errorf("DeleteSecret() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if receivedMethod != tt.expectMethod {
				t.Errorf("DeleteSecret() used method %v, want %v", receivedMethod, tt.expectMethod)
			}
		})
	}
}
