Repository: kedacore/keda

Issue:
hashicorp vault pki integration wrong pkiData formatting
### Report

Hi team,
I think that keda tries to send wrong formatted data into vault. 
The issue seems to be the formatting of 
            pkiData: 
              commonName: host.com
              altNames: host.com

From what I see in vault log the are sent in camel case but Vault wanted those values with underscores like commont_name

### Expected Behavior

certificate created on vault

### Actual Behavior

error on vault and keda

### Steps to Reproduce the Problem

1. Create a hashicorp vault trigger authentication with these secrets 
```
          - key: certificate
            parameter: cert
            path: pki/issue/role            
            type: pki
            pkiData: 
              commonName: host.com
              altNames: host.com              
            ttl: 1h
```

### Logs from KEDA operator

Keda:
error establishing rabbitmq connection: tls: failed to verify certificate: x509: certificate signed by unknown authority


Vault logs
```
{"mount_point":"pki/","mount_type":"pki","mount_accessor":"pki_xxxxxxx","mount_running_plugin_version":"v1.15.4+builtin.vault","mount_class":"secret","data":{"error":"hmac"},"warnings":["Endpoint ignored these unrecognized parameters: [altNames commonName]"]}}
```


### KEDA Version

2.17.0

### Kubernetes Version

1.32

### Platform

Amazon Web Services

### Scaler Details

_No response_

### Anything else?

_No response_

Discussion:
@shysaApk: btw even the docs show two different approaches that doesn't seem right: 

https://keda.sh/docs/2.17/authentication-providers/hashicorp-vault/

The first block of code shows: 

    pkidata: {hashicorp-vault-secret-pkidata}                 # Optional. Data to be send with the secret  if `hashicorp-vault-secret-type` is pki request
      commonName: {hashicorp-vault-secret-pkidata-commonName} # Optional.
      altNames: {hashicorp-vault-secret-pkidata-altNames}     # Optional.
      ipSans: {hashicorp-vault-secret-pkidata-ipSans}         # Optional.
      uriSans: {hashicorp-vault-secret-pkidata-uriSans}       # Optional.
      otherSans: {hashicorp-vault-secret-pkidata-otherSans}   # Optional.
      ttl: {hashicorp-vault-secret-pkidata-ttl}               # Optional.
      format: {hashicorp-vault-secret-pkidata-format}         # Optional.


But the example shows: 

      - key: "certificate"
        parameter: "cert"
        path: {hashicorp-vault-secret-path}
        type: pki
        pki_data:
          common_name: {hashicorp-vault-secret-pkidata-commonName}
@dttung2905: Hi @shysaApk, thanks for reporting this to us. I found the issue. I'm working on a fix that will be available for review some time tomorrow. Stay tune!@shysaApk: Hi @dttung2905, Thanks a lot for your help!@dttung2905: @shysaApk if you have time, can you try my patches in the PR and deploy KEDA from your local machine with `make deploy` command from the root repo folder?@shysaApk: Hi @dttung2905, 
Sure I'll let you know as soon as possible. Thanks again@stale: This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions.


Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d184b6fa..7bc66056b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -78,7 +78,9 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio
 - **General**: Allow excluding labels from being propagated from ScaledObject and ScaledJob to generated HPA and Job objects ([#6849](https://github.com/kedacore/keda/issues/6849))
 - **General**: chore: only add webhook DNS names when webhook patching is enabled ([#7002](https://github.com/kedacore/keda/issues/7002))
 - **General**: Improve Events emitted from ScaledObject controller ([#6802](https://github.com/kedacore/keda/issues/6802))
+- **Azure Pipelines Scaler**: Add new `fetchUnfinishedJobsOnly` property to fetch only unfinished pipeline jobs for a pool ([#6819](https://github.com/kedacore/keda/issues/6819))
 - **Datadog Scaler**: Add a specific timeout configuration parameter for the Datadog trigger ([#6999](https://github.com/kedacore/keda/pull/6999))
+- **Datadog Scaler**: Fix bug with datadogNamespace config ([#6828](https://github.com/kedacore/keda/pull/6828))
 - **Datadog Scaler**: Improve Datadog scaler error messages ([#6999](https://github.com/kedacore/keda/pull/6999))
 - **Metrics API**: Support multiple auth methods simultaneously in Metrics API scaler ([#6642](https://github.com/kedacore/keda/issues/6642))
 - **Temporal Scaler**: Support custom tlsServerName ([#6820](https://github.com/kedacore/keda/pull/6820))
@@ -92,6 +94,7 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio
 - **General**: Remove klogr dependency and replace with zap ([#5732](https://github.com/kedacore/keda/issues/5732))
 - **General**: Sets hpaName in Status when ScaledObject adopts/finds an existing HPA ([#6336](https://github.com/kedacore/keda/issues/6336))
 - **Cron Scaler**: Fix cron scaler to return zero metric value by default([#6886](https://github.com/kedacore/keda/issues/6886))
+- **Hashicorp Vault**: Fix Vault PKI param using camel case param when making API call to Vault PKI endpoint ([#6864](https://github.com/kedacore/keda/issues/6864))
 - **RabbitMQ Scaler**: Fix incorrect URL encoding in RabbitMQ vhosts containing %2f ([#6963](https://github.com/kedacore/keda/issues/6963))
 
 ### Deprecations
diff --git a/pkg/scalers/azure_pipelines_scaler.go b/pkg/scalers/azure_pipelines_scaler.go
index a620c5c55..eaf6697fa 100644
--- a/pkg/scalers/azure_pipelines_scaler.go
+++ b/pkg/scalers/azure_pipelines_scaler.go
@@ -144,6 +144,7 @@ type azurePipelinesMetadata struct {
 	TargetPipelinesQueueLength           int64  `keda:"name=targetPipelinesQueueLength,          order=triggerMetadata, default=1, optional"`
 	ActivationTargetPipelinesQueueLength int64  `keda:"name=activationTargetPipelinesQueueLength,          order=triggerMetadata, default=0, optional"`
 	JobsToFetch                          int64  `keda:"name=jobsToFetch,          order=triggerMetadata, default=250, optional"`
+	FetchUnfinishedJobsOnly              bool   `keda:"name=fetchUnfinishedJobsOnly,          order=triggerMetadata, default=false, optional"`
 	triggerIndex                         int
 	RequireAllDemands                    bool `keda:"name=requireAllDemands,          order=triggerMetadata, default=false, optional"`
 	RequireAllDemandsAndIgnoreOthers     bool `keda:"name=requireAllDemandsAndIgnoreOthers,          order=triggerMetadata, default=false, optional"`
@@ -213,6 +214,13 @@ func getAuthMethod(logger logr.Logger, config *scalersconfig.ScalerConfig) (stri
 }
 
 func parseAzurePipelinesMetadata(ctx context.Context, logger logr.Logger, config *scalersconfig.ScalerConfig, httpClient *http.Client) (*azurePipelinesMetadata, kedav1alpha1.AuthPodIdentity, error) {
+	if config.TriggerMetadata["jobsToFetch"] != "" && config.TriggerMetadata["fetchUnfinishedJobsOnly"] != "" {
+		return nil, kedav1alpha1.AuthPodIdentity{}, fmt.Errorf("cannot specify both jobsToFetch and fetchUnfinishedJobsOnly at the same time")
+	}
+	if config.TriggerMetadata["jobsToFetch"] != "" && config.TriggerMetadata["parent"] != "" {
+		return nil, kedav1alpha1.AuthPodIdentity{}, fmt.Errorf("cannot specify both jobsToFetch and parent at the same time")
+	}
+
 	meta := &azurePipelinesMetadata{}
 	if err := config.TypedConfig(meta); err != nil {
 		return nil, kedav1alpha1.AuthPodIdentity{}, fmt.Errorf("error parsing azure pipeline metadata: %w", err)
@@ -368,14 +376,29 @@ func getAzurePipelineRequest(ctx context.Context, logger logr.Logger, urlString
 	return b, nil
 }
 
-func (s *azurePipelinesScaler) GetAzurePipelinesQueueLength(ctx context.Context) (int64, error) {
-	// HotFix Issue (#4387), $top changes the format of the returned JSON
+func (s *azurePipelinesScaler) GetAzurePipelinesQueueURL() (string, error) {
 	var urlString string
-	if s.metadata.Parent != "" {
+	if s.metadata.FetchUnfinishedJobsOnly {
+		// Because completedRequestCount=0 does not change the format of the returned JSON like $top does, we can also use this URL when a `Parent` agent is given
+		// However, in order to not force existing users of the `Parent` property to always send the `completedRequestCount=0` query parameter (which may change over time
+		//  due it it being an undocumented API), we only send the query parameter when `FetchUnfinishedJobsOnly` is explicitly set to true.
+		urlString = fmt.Sprintf("%s/_apis/distributedtask/pools/%d/jobrequests?completedRequestCount=0", s.metadata.OrganizationURL, s.metadata.PoolID)
+	} else if s.metadata.Parent != "" {
+		// HotFix Issue (#4387), $top changes the format of the returned JSON
 		urlString = fmt.Sprintf("%s/_apis/distributedtask/pools/%d/jobrequests", s.metadata.OrganizationURL, s.metadata.PoolID)
 	} else {
 		urlString = fmt.Sprintf("%s/_apis/distributedtask/pools/%d/jobrequests?$top=%d", s.metadata.OrganizationURL, s.metadata.PoolID, s.metadata.JobsToFetch)
 	}
+
+	return urlString, nil
+}
+
+func (s *azurePipelinesScaler) GetAzurePipelinesQueueLength(ctx context.Context) (int64, error) {
+	urlString, err := s.GetAzurePipelinesQueueURL()
+	if err != nil {
+		return -1, err
+	}
+
 	body, err := getAzurePipelineRequest(ctx, s.logger, urlString, s.metadata, s.podIdentity, s.httpClient)
 	if err != nil {
 		return -1, err
diff --git a/pkg/scaling/resolver/hashicorpvault_handler.go b/pkg/scaling/resolver/hashicorpvault_handler.go
index e80b7302c..7e57c7013 100644
--- a/pkg/scaling/resolver/hashicorpvault_handler.go
+++ b/pkg/scaling/resolver/hashicorpvault_handler.go
@@ -17,7 +17,6 @@ limitations under the License.
 package resolver
 
 import (
-	"encoding/json"
 	"errors"
 	"fmt"
 	"os"
@@ -29,7 +28,7 @@ import (
 	kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1"
 )
 
-// HashicorpVaultHandler is specification of Hashi Corp Vault
+// HashicorpVaultHandler is a specification of HashiCorp Vault
 type HashicorpVaultHandler struct {
 	vault  *kedav1alpha1.HashiCorpVault
 	client *vaultapi.Client
@@ -149,7 +148,7 @@ func (vh *HashicorpVaultHandler) renewToken(logger logr.Logger) {
 
 	renewer, err := vh.client.NewLifetimeWatcher(&vaultapi.RenewerInput{
 		Secret: secret,
-		//Grace:  time.Duration(15 * time.Second),
+		//Grace: time.Duration(15 * time.Second),
 		//Increment: 60,
 	})
 	if err != nil {
@@ -176,12 +175,12 @@ RenewWatcherLoop:
 	}
 }
 
-// Read is used to get a secret from vault Read api. (e.g. secret)
+// Read is used to get a secret from vault Read api. (e.g., secret)
 func (vh *HashicorpVaultHandler) Read(path string) (*vaultapi.Secret, error) {
 	return vh.client.Logical().Read(path)
 }
 
-// Write is used to get a secret from vault that needs to pass along data and uses the vault Write api. (e.g. pki)
+// Write is used to get a secret from vault that needs to pass along data and uses the vault Write api. (e.g., pki)
 func (vh *HashicorpVaultHandler) Write(path string, data map[string]interface{}) (*vaultapi.Secret, error) {
 	return vh.client.Logical().Write(path, data)
 }
@@ -194,17 +193,31 @@ func (vh *HashicorpVaultHandler) Stop() {
 }
 
 // getPkiRequest format the pkiData in a format that the vault sdk understands.
-func (vh *HashicorpVaultHandler) getPkiRequest(pkiData *kedav1alpha1.VaultPkiData) (map[string]interface{}, error) {
-	var data map[string]interface{}
-	a, err := json.Marshal(pkiData)
-	if err != nil {
-		return nil, err
+func (vh *HashicorpVaultHandler) getPkiRequest(pkiData *kedav1alpha1.VaultPkiData) map[string]interface{} {
+	data := make(map[string]interface{})
+	if pkiData.CommonName != "" {
+		data["common_name"] = pkiData.CommonName
 	}
-	err = json.Unmarshal(a, &data)
-	if err != nil {
-		return nil, err
+	if pkiData.AltNames != "" {
+		data["alt_names"] = pkiData.AltNames
+	}
+	if pkiData.IPSans != "" {
+		data["ip_sans"] = pkiData.IPSans
+	}
+	if pkiData.URISans != "" {
+		data["uri_sans"] = pkiData.URISans
+	}
+	if pkiData.OtherSans != "" {
+		data["other_sans"] = pkiData.OtherSans
 	}
-	return data, nil
+	if pkiData.TTL != "" {
+		data["ttl"] = pkiData.TTL
+	}
+	if pkiData.Format != "" {
+		data["format"] = pkiData.Format
+	}
+
+	return data
 }
 
 // getSecretValue extract the secret value from the vault api response. As the vault api returns us a map[string]interface{},
@@ -287,10 +300,7 @@ func (vh *HashicorpVaultHandler) fetchSecret(secretType kedav1alpha1.VaultSecret
 	var err error
 	switch secretType {
 	case kedav1alpha1.VaultSecretTypePki:
-		data, err := vh.getPkiRequest(vaultPkiData)
-		if err != nil {
-			return nil, err
-		}
+		data := vh.getPkiRequest(vaultPkiData)
 		vaultSecret, err = vh.Write(path, data)
 		if err != nil {
 			return nil, err
@@ -307,8 +317,8 @@ func (vh *HashicorpVaultHandler) fetchSecret(secretType kedav1alpha1.VaultSecret
 	return vaultSecret, nil
 }
 
-// ResolveSecrets allows to resolve a slice of secrets by vault. The function returns the list of secrets with the value updated.
-// If multiple secret refers to the same SecretGroup, the secret will be fetched only once.
+// ResolveSecrets allows resolving a slice of secrets by vault. The function returns the list of secrets with the value updated.
+// If multiple secrets refer to the same SecretGroup, the secret will be fetched only once.
 func (vh *HashicorpVaultHandler) ResolveSecrets(secrets []kedav1alpha1.VaultSecret) ([]kedav1alpha1.VaultSecret, error) {
 	// Group secret by path and type, this allows to fetch a path only once. This is useful for dynamic credentials
 	grouped := make(map[SecretGroup][]kedav1alpha1.VaultSecret)



Test patch:
diff --git a/pkg/scalers/azure_pipelines_scaler_test.go b/pkg/scalers/azure_pipelines_scaler_test.go
index 609759adf..4171db041 100644
--- a/pkg/scalers/azure_pipelines_scaler_test.go
+++ b/pkg/scalers/azure_pipelines_scaler_test.go
@@ -2,6 +2,7 @@ package scalers
 
 import (
 	"context"
+	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"strings"
@@ -46,6 +47,14 @@ var testAzurePipelinesMetadata = []parseAzurePipelinesMetadataTestData{
 	{"all properly formed", map[string]string{"organizationURLFromEnv": "AZP_URL", "personalAccessTokenFromEnv": "AZP_TOKEN", "poolID": "1", "targetPipelinesQueueLength": "1", "activationTargetPipelinesQueueLength": "A"}, true, testAzurePipelinesResolvedEnv, map[string]string{}},
 	// jobsToFetch malformed
 	{"jobsToFetch malformed", map[string]string{"organizationURLFromEnv": "AZP_URL", "personalAccessTokenFromEnv": "AZP_TOKEN", "poolID": "1", "targetPipelinesQueueLength": "1", "jobsToFetch": "test"}, true, testAzurePipelinesResolvedEnv, map[string]string{}},
+	// fetchUnfinishedJobsOnly malformed
+	{"fetchUnfinishedJobsOnly malformed", map[string]string{"organizationURLFromEnv": "AZP_URL", "personalAccessTokenFromEnv": "AZP_TOKEN", "poolID": "1", "targetPipelinesQueueLength": "1", "fetchUnfinishedJobsOnly": "test"}, true, testAzurePipelinesResolvedEnv, map[string]string{}},
+	// jobsToFetch and fetchUnfinishedJobsOnly given
+	{"jobsToFetch and fetchUnfinishedJobsOnly given", map[string]string{"organizationURLFromEnv": "AZP_URL", "personalAccessTokenFromEnv": "AZP_TOKEN", "poolID": "1", "targetPipelinesQueueLength": "1", "fetchUnfinishedJobsOnly": "true", "jobsToFetch": "42"}, true, testAzurePipelinesResolvedEnv, map[string]string{}},
+	// jobsToFetch and parent given
+	{"jobsToFetch and parent given", map[string]string{"organizationURLFromEnv": "AZP_URL", "personalAccessTokenFromEnv": "AZP_TOKEN", "poolID": "1", "targetPipelinesQueueLength": "1", "parent": "test-agent", "jobsToFetch": "42"}, true, testAzurePipelinesResolvedEnv, map[string]string{}},
+	// parent and fetchUnfinishedJobsOnly given
+	{"parent and fetchUnfinishedJobsOnly given", map[string]string{"organizationURLFromEnv": "AZP_URL", "personalAccessTokenFromEnv": "AZP_TOKEN", "poolID": "1", "targetPipelinesQueueLength": "1", "fetchUnfinishedJobsOnly": "true", "parent": "test-agent"}, false, testAzurePipelinesResolvedEnv, map[string]string{}},
 }
 
 var testJobRequestResponse = `{"count":2,"value":[{"requestId":890659,"queueTime":"2022-09-28T11:19:49.89Z","assignTime":"2022-09-28T11:20:29.5033333Z","receiveTime":"2022-09-28T11:20:32.0530499Z","lockedUntil":"2022-09-28T11:30:32.07Z","serviceOwner":"xxx","hostId":"xxx","scopeId":"xxx","planType":"Build","planId":"xxx","jobId":"xxx","demands":["kubectl","Agent.Version -gtVersion 2.182.1"],"reservedAgent":{"_links":{"self":{"href":"https://dev.azure.com/FOO/_apis/distributedtask/pools/44/agents/11735"},"web":{"href":"https://dev.azure.com/FOO/_settings/agentpools?view=jobs&poolId=44&agentId=11735"}},"id":11735,"name":"kube-scaledjob-5nlph-kzpgf","version":"2.210.1","osDescription":"Linux 5.4.0-1089-azure #94~18.04.1-Ubuntu SMP Fri Aug 5 12:34:50 UTC 2022","enabled":true,"status":"online","provisioningState":"Provisioned","accessPoint":"CodexAccessMapping"},"definition":{"_links":{"web":{"href":"https://dev.azure.com/FOO/1858395a-257e-4efd-bbc5-eb618128452b/_build/definition?definitionId=4869"},"self":{"href":"https://dev.azure.com/FOO/1858395a-257e-4efd-bbc5-eb618128452b/_apis/build/Definitions/4869"}},"id":4869,"name":"base - main"},"owner":{"_links":{"web":{"href":"https://dev.azure.com/FOO/1858395a-257e-4efd-bbc5-eb618128452b/_build/results?buildId=673584"},"self":{"href":"https://dev.azure.com/FOO/1858395a-257e-4efd-bbc5-eb618128452b/_apis/build/Builds/673584"}},"id":673584,"name":"20220928.2"},"data":{"ParallelismTag":"Private","IsScheduledKey":"False"},"poolId":44,"orchestrationId":"5c5c8ec9-786f-4e97-99d4-a29279befba3.build.__default","priority":0},{"requestId":890663,"queueTime":"2022-09-28T11:20:22.4633333Z","serviceOwner":"00025394-6065-48ca-87d9-7f5672854ef7","hostId":"41a18c7d-df5e-4032-a4df-d533b56bd2de","scopeId":"02696e26-a35b-424c-86b8-1f54e1b0b4b7","planType":"Build","planId":"b718cfed-493c-46be-a650-88fe762f75aa","jobId":"15b95994-59ec-5502-695d-0b93722883bd","demands":["dotnet60","java","cmake","Agent.Version -gtVersion 2.182.1"],"matchedAgents":[{"_links":{"self":{"href":"https://dev.azure.com/FOO/_apis/distributedtask/pools/44/agents/1755"},"web":{"href":"https://dev.azure.com/FOO/_settings/agentpools?view=jobs&poolId=44&agentId=1755"}},"id":1755,"name":"dotnet60-keda-template","version":"2.210.1","enabled":true,"status":"offline","provisioningState":"Provisioned"},{"_links":{"self":{"href":"https://dev.azure.com/FOO/_apis/distributedtask/pools/44/agents/11732"},"web":{"href":"https://dev.azure.com/FOO/_settings/agentpools?view=jobs&poolId=44&agentId=11732"}},"id":11732,"name":"dotnet60-scaledjob-5dsgc-pkqvm","version":"2.210.1","enabled":true,"status":"online","provisioningState":"Provisioned"},{"_links":{"self":{"href":"https://dev.azure.com/FOO/_apis/distributedtask/pools/44/agents/11733"},"web":{"href":"https://dev.azure.com/FOO/_settings/agentpools?view=jobs&poolId=44&agentId=11733"}},"id":11733,"name":"dotnet60-scaledjob-zgqnp-8h4z4","version":"2.210.1","enabled":true,"status":"online","provisioningState":"Provisioned"},{"_links":{"self":{"href":"https://dev.azure.com/FOO/_apis/distributedtask/pools/44/agents/11734"},"web":{"href":"https://dev.azure.com/FOO/_settings/agentpools?view=jobs&poolId=44&agentId=11734"}},"id":11734,"name":"dotnet60-scaledjob-wr65c-ff2cv","version":"2.210.1","enabled":true,"status":"online","provisioningState":"Provisioned"}],"definition":{"_links":{"web":{"href":"https://FOO.visualstudio.com/02696e26-a35b-424c-86b8-1f54e1b0b4b7/_build/definition?definitionId=3129"},"self":{"href":"https://FOO.visualstudio.com/02696e26-a35b-424c-86b8-1f54e1b0b4b7/_apis/build/Definitions/3129"}},"id":3129,"name":"Other Build CI"},"owner":{"_links":{"web":{"href":"https://FOO.visualstudio.com/02696e26-a35b-424c-86b8-1f54e1b0b4b7/_build/results?buildId=673585"},"self":{"href":"https://FOO.visualstudio.com/02696e26-a35b-424c-86b8-1f54e1b0b4b7/_apis/build/Builds/673585"}},"id":673585,"name":"20220928.11"},"data":{"ParallelismTag":"Private","IsScheduledKey":"False"},"poolId":44,"orchestrationId":"b718cfed-493c-46be-a650-88fe762f75aa.buildtest.build_and_test.__default","priority":0}]}`
@@ -413,3 +422,54 @@ func buildLoadJSON() []byte {
 
 	return []byte(output)
 }
+
+type validateAzurePipelinesQueueURLTestData struct {
+	testName    string
+	metadata    azurePipelinesMetadata
+	expectedURL string
+}
+
+var testValidateAzurePipelinesQueueURLData = []validateAzurePipelinesQueueURLTestData{
+	// parent agent defined
+	{"parent agent given", azurePipelinesMetadata{Parent: "test-agent"}, "%s/_apis/distributedtask/pools/%d/jobrequests"},
+	// jobsToFetch given
+	{"jobsToFetch given", azurePipelinesMetadata{JobsToFetch: 1250}, "%s/_apis/distributedtask/pools/%d/jobrequests?$top=1250"},
+	// fetchUnfinishedJobsOnly set to true
+	{"fetchUnfinishedJobsOnly set to true", azurePipelinesMetadata{FetchUnfinishedJobsOnly: true}, "%s/_apis/distributedtask/pools/%d/jobrequests?completedRequestCount=0"},
+	// fetchUnfinishedJobsOnly set to true and parent agent defined
+	{"fetchUnfinishedJobsOnly set to true and parent agent defined", azurePipelinesMetadata{FetchUnfinishedJobsOnly: true, Parent: "test-agent"}, "%s/_apis/distributedtask/pools/%d/jobrequests?completedRequestCount=0"},
+}
+
+func TestAzurePipelinesQueueURLTest(t *testing.T) {
+	for _, testData := range testValidateAzurePipelinesQueueURLData {
+		t.Run(testData.testName, func(t *testing.T) {
+			var apiStub = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+				w.WriteHeader(http.StatusOK)
+				_, _ = w.Write(buildLoadJSON())
+			}))
+
+			meta := testData.metadata
+			meta.OrganizationName = "testOrg"
+			meta.OrganizationURL = apiStub.URL
+			meta.authContext.pat = "testPAT"
+			meta.PoolID = 1
+			meta.TargetPipelinesQueueLength = 1
+
+			mockAzurePipelinesScaler := azurePipelinesScaler{
+				metadata:   &meta,
+				httpClient: http.DefaultClient,
+			}
+
+			queueURL, err := mockAzurePipelinesScaler.GetAzurePipelinesQueueURL()
+
+			if err != nil {
+				t.Fail()
+			}
+
+			expectedURL := fmt.Sprintf(testData.expectedURL, meta.OrganizationURL, meta.PoolID)
+			if queueURL != expectedURL {
+				t.Fail()
+			}
+		})
+	}
+}
diff --git a/pkg/scaling/resolver/hashicorpvault_handler_test.go b/pkg/scaling/resolver/hashicorpvault_handler_test.go
index 5051faeb3..919b29517 100644
--- a/pkg/scaling/resolver/hashicorpvault_handler_test.go
+++ b/pkg/scaling/resolver/hashicorpvault_handler_test.go
@@ -76,7 +76,6 @@ var (
 type pkiRequestTestData struct {
 	name     string
 	raw      string
-	isError  bool
 	secret   kedav1alpha1.VaultSecret
 	expected map[string]interface{}
 }
@@ -87,7 +86,30 @@ var pkiRequestTestDataset = []pkiRequestTestData{
 		raw:    `{ "commonName": "test" }`,
 		secret: kedav1alpha1.VaultSecret{},
 		expected: map[string]interface{}{
-			"commonName": "test",
+			"common_name": "test",
+		},
+	},
+	{
+		name: "full pki request with all fields",
+		secret: kedav1alpha1.VaultSecret{
+			PkiData: kedav1alpha1.VaultPkiData{
+				CommonName: "test",
+				AltNames:   "test2",
+				IPSans:     "192.168.1.1",
+				URISans:    "test.com",
+				OtherSans:  "othersans.com",
+				TTL:        "24h",
+				Format:     "pem",
+			},
+		},
+		expected: map[string]interface{}{
+			"common_name": "test",
+			"alt_names":   "test2",
+			"ip_sans":     "192.168.1.1",
+			"uri_sans":    "test.com",
+			"other_sans":  "othersans.com",
+			"ttl":         "24h",
+			"format":      "pem",
 		},
 	},
 }
@@ -106,12 +128,7 @@ func TestGetPkiRequest(t *testing.T) {
 		} else {
 			secret = testData.secret
 		}
-		data, err := vault.getPkiRequest(&secret.PkiData)
-		if testData.isError {
-			assert.NotNilf(t, err, "test %s: expected error but got success, testData - %+v", testData.name, testData)
-			continue
-		}
-		assert.Nilf(t, err, "test %s: expected success but got error - %s", testData.name, err)
+		data := vault.getPkiRequest(&secret.PkiData)
 		assert.Equalf(t, testData.expected, data, "test %s: expected data does not match given secret", testData.name)
 	}
 }
@@ -123,7 +140,7 @@ func mockVault(t *testing.T, useRootToken bool) *httptest.Server {
 		case "/v1/auth/token/lookup-self":
 			data = vaultTokenSelf
 			if useRootToken {
-				// remove renewable field
+				// remove the renewable field
 				delete(data, "renewable")
 			}
 		case "/v1/kv_v2/data/keda": //todo: more generic