568 lines
16 KiB
Go
568 lines
16 KiB
Go
package governance
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCustomerResponsesIncludeAssignedVirtualKeys(t *testing.T) {
|
|
t.Parallel()
|
|
testData := NewGlobalTestData()
|
|
defer testData.Cleanup(t)
|
|
|
|
customerName := "test-customer-vk-response-" + generateRandomID()
|
|
createCustomerResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/customers",
|
|
Body: CreateCustomerRequest{
|
|
Name: customerName,
|
|
},
|
|
})
|
|
|
|
if createCustomerResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create customer: status %d", createCustomerResp.StatusCode)
|
|
}
|
|
|
|
customerID := ExtractIDFromResponse(t, createCustomerResp)
|
|
testData.AddCustomer(customerID)
|
|
|
|
customerData, ok := createCustomerResp.Body["customer"].(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("Expected 'customer' in response body, got: %v", createCustomerResp.Body)
|
|
}
|
|
assertJSONArrayField(t, customerData, "teams", 0)
|
|
assertJSONArrayField(t, customerData, "virtual_keys", 0)
|
|
|
|
vkName := "test-vk-customer-response-" + generateRandomID()
|
|
createVKResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/virtual-keys",
|
|
Body: CreateVirtualKeyRequest{
|
|
Name: vkName,
|
|
CustomerID: &customerID,
|
|
},
|
|
})
|
|
|
|
if createVKResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create VK: status %d", createVKResp.StatusCode)
|
|
}
|
|
|
|
vkID := ExtractIDFromResponse(t, createVKResp)
|
|
testData.AddVirtualKey(vkID)
|
|
|
|
customerDetailReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers/" + customerID,
|
|
}
|
|
customerListReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers",
|
|
}
|
|
customerMemoryDetailReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers/" + customerID + "?from_memory=true",
|
|
}
|
|
customerMemoryListReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers?from_memory=true",
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerDetailReq, func(resp *APIResponse) bool {
|
|
return customerHasVirtualKey(resp, customerID, vkID)
|
|
}, 5*time.Second, "db customer detail shows assigned virtual key"); !ok {
|
|
t.Fatalf("Customer detail never showed assigned virtual key")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerListReq, func(resp *APIResponse) bool {
|
|
return customerHasVirtualKey(resp, customerID, vkID)
|
|
}, 5*time.Second, "db customer list shows assigned virtual key"); !ok {
|
|
t.Fatalf("Customer list never showed assigned virtual key")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerMemoryDetailReq, func(resp *APIResponse) bool {
|
|
return customerHasVirtualKey(resp, customerID, vkID)
|
|
}, 5*time.Second, "in-memory customer detail shows assigned virtual key"); !ok {
|
|
t.Fatalf("In-memory customer detail never showed assigned virtual key")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerMemoryListReq, func(resp *APIResponse) bool {
|
|
return customerHasVirtualKey(resp, customerID, vkID)
|
|
}, 5*time.Second, "in-memory customer list shows assigned virtual key"); !ok {
|
|
t.Fatalf("In-memory customer list never showed assigned virtual key")
|
|
}
|
|
}
|
|
|
|
func TestVirtualKeyResponsesEmbedConsistentCustomerRelations(t *testing.T) {
|
|
t.Parallel()
|
|
testData := NewGlobalTestData()
|
|
defer testData.Cleanup(t)
|
|
|
|
customerName := "test-vk-embedded-customer-" + generateRandomID()
|
|
createCustomerResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/customers",
|
|
Body: CreateCustomerRequest{
|
|
Name: customerName,
|
|
},
|
|
})
|
|
|
|
if createCustomerResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create customer: status %d", createCustomerResp.StatusCode)
|
|
}
|
|
|
|
customerID := ExtractIDFromResponse(t, createCustomerResp)
|
|
testData.AddCustomer(customerID)
|
|
|
|
vkName := "test-vk-embedded-customer-" + generateRandomID()
|
|
createVKResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/virtual-keys",
|
|
Body: CreateVirtualKeyRequest{
|
|
Name: vkName,
|
|
CustomerID: &customerID,
|
|
},
|
|
})
|
|
|
|
if createVKResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create VK: status %d", createVKResp.StatusCode)
|
|
}
|
|
|
|
vkID := ExtractIDFromResponse(t, createVKResp)
|
|
testData.AddVirtualKey(vkID)
|
|
|
|
getVKReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/virtual-keys/" + vkID,
|
|
}
|
|
getVKMemoryReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/virtual-keys/" + vkID + "?from_memory=true",
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, getVKReq, func(resp *APIResponse) bool {
|
|
return embeddedCustomerHasExpectedRelations(resp, customerID, vkID) && responseJSONIsMarshalable(resp)
|
|
}, 5*time.Second, "db virtual key embeds normalized customer"); !ok {
|
|
t.Fatalf("Virtual key detail never returned embedded customer with normalized relations")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, getVKMemoryReq, func(resp *APIResponse) bool {
|
|
return embeddedCustomerHasExpectedRelations(resp, customerID, vkID) && responseJSONIsMarshalable(resp)
|
|
}, 5*time.Second, "in-memory virtual key embeds normalized customer"); !ok {
|
|
t.Fatalf("In-memory virtual key detail never returned embedded customer with normalized relations")
|
|
}
|
|
}
|
|
|
|
func TestCustomerResponsesIncludeMultipleAssignedVirtualKeys(t *testing.T) {
|
|
t.Parallel()
|
|
testData := NewGlobalTestData()
|
|
defer testData.Cleanup(t)
|
|
|
|
customerName := "test-customer-multi-vk-" + generateRandomID()
|
|
createCustomerResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/customers",
|
|
Body: CreateCustomerRequest{
|
|
Name: customerName,
|
|
},
|
|
})
|
|
|
|
if createCustomerResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create customer: status %d", createCustomerResp.StatusCode)
|
|
}
|
|
|
|
customerID := ExtractIDFromResponse(t, createCustomerResp)
|
|
testData.AddCustomer(customerID)
|
|
|
|
vk1ID := createCustomerVirtualKeyForTest(t, testData, customerID, "test-customer-multi-vk-1-")
|
|
vk2ID := createCustomerVirtualKeyForTest(t, testData, customerID, "test-customer-multi-vk-2-")
|
|
|
|
customerDetailReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers/" + customerID,
|
|
}
|
|
customerListReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers",
|
|
}
|
|
customerMemoryDetailReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers/" + customerID + "?from_memory=true",
|
|
}
|
|
customerMemoryListReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers?from_memory=true",
|
|
}
|
|
|
|
expectedVKs := []string{vk1ID, vk2ID}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerDetailReq, func(resp *APIResponse) bool {
|
|
return customerHasExactVirtualKeys(resp, customerID, expectedVKs)
|
|
}, 5*time.Second, "db customer detail shows both assigned virtual keys"); !ok {
|
|
t.Fatalf("Customer detail never showed both assigned virtual keys")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerListReq, func(resp *APIResponse) bool {
|
|
return customerHasExactVirtualKeys(resp, customerID, expectedVKs)
|
|
}, 5*time.Second, "db customer list shows both assigned virtual keys"); !ok {
|
|
t.Fatalf("Customer list never showed both assigned virtual keys")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerMemoryDetailReq, func(resp *APIResponse) bool {
|
|
return customerHasExactVirtualKeys(resp, customerID, expectedVKs)
|
|
}, 5*time.Second, "in-memory customer detail shows both assigned virtual keys"); !ok {
|
|
t.Fatalf("In-memory customer detail never showed both assigned virtual keys")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerMemoryListReq, func(resp *APIResponse) bool {
|
|
return customerHasExactVirtualKeys(resp, customerID, expectedVKs)
|
|
}, 5*time.Second, "in-memory customer list shows both assigned virtual keys"); !ok {
|
|
t.Fatalf("In-memory customer list never showed both assigned virtual keys")
|
|
}
|
|
}
|
|
|
|
func TestCustomerResponsesExcludeTeamScopedVirtualKeys(t *testing.T) {
|
|
t.Parallel()
|
|
testData := NewGlobalTestData()
|
|
defer testData.Cleanup(t)
|
|
|
|
customerName := "test-customer-team-vk-" + generateRandomID()
|
|
createCustomerResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/customers",
|
|
Body: CreateCustomerRequest{
|
|
Name: customerName,
|
|
},
|
|
})
|
|
|
|
if createCustomerResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create customer: status %d", createCustomerResp.StatusCode)
|
|
}
|
|
|
|
customerID := ExtractIDFromResponse(t, createCustomerResp)
|
|
testData.AddCustomer(customerID)
|
|
|
|
teamName := "test-team-team-vk-" + generateRandomID()
|
|
createTeamResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/teams",
|
|
Body: CreateTeamRequest{
|
|
Name: teamName,
|
|
CustomerID: &customerID,
|
|
},
|
|
})
|
|
|
|
if createTeamResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create team: status %d", createTeamResp.StatusCode)
|
|
}
|
|
|
|
teamID := ExtractIDFromResponse(t, createTeamResp)
|
|
testData.AddTeam(teamID)
|
|
|
|
vkName := "test-team-scoped-vk-" + generateRandomID()
|
|
createVKResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/virtual-keys",
|
|
Body: CreateVirtualKeyRequest{
|
|
Name: vkName,
|
|
TeamID: &teamID,
|
|
},
|
|
})
|
|
|
|
if createVKResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create team-scoped VK: status %d", createVKResp.StatusCode)
|
|
}
|
|
|
|
vkID := ExtractIDFromResponse(t, createVKResp)
|
|
testData.AddVirtualKey(vkID)
|
|
|
|
customerDetailReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers/" + customerID,
|
|
}
|
|
customerListReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers",
|
|
}
|
|
customerMemoryDetailReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers/" + customerID + "?from_memory=true",
|
|
}
|
|
customerMemoryListReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/customers?from_memory=true",
|
|
}
|
|
getVKReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/virtual-keys/" + vkID,
|
|
}
|
|
getVKMemoryReq := APIRequest{
|
|
Method: "GET",
|
|
Path: "/api/governance/virtual-keys/" + vkID + "?from_memory=true",
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerDetailReq, func(resp *APIResponse) bool {
|
|
return customerExcludesVirtualKey(resp, customerID, vkID, 0)
|
|
}, 5*time.Second, "db customer detail excludes team-scoped virtual key"); !ok {
|
|
t.Fatalf("Customer detail incorrectly included team-scoped virtual key")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerListReq, func(resp *APIResponse) bool {
|
|
return customerExcludesVirtualKey(resp, customerID, vkID, 0)
|
|
}, 5*time.Second, "db customer list excludes team-scoped virtual key"); !ok {
|
|
t.Fatalf("Customer list incorrectly included team-scoped virtual key")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerMemoryDetailReq, func(resp *APIResponse) bool {
|
|
return customerExcludesVirtualKey(resp, customerID, vkID, 0)
|
|
}, 5*time.Second, "in-memory customer detail excludes team-scoped virtual key"); !ok {
|
|
t.Fatalf("In-memory customer detail incorrectly included team-scoped virtual key")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, customerMemoryListReq, func(resp *APIResponse) bool {
|
|
return customerExcludesVirtualKey(resp, customerID, vkID, 0)
|
|
}, 5*time.Second, "in-memory customer list excludes team-scoped virtual key"); !ok {
|
|
t.Fatalf("In-memory customer list incorrectly included team-scoped virtual key")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, getVKReq, func(resp *APIResponse) bool {
|
|
return virtualKeyHasExpectedTeam(resp, vkID, teamID) && responseJSONIsMarshalable(resp)
|
|
}, 5*time.Second, "db virtual key retains team relationship"); !ok {
|
|
t.Fatalf("DB virtual key detail never returned expected team relationship")
|
|
}
|
|
|
|
if _, ok := WaitForAPICondition(t, getVKMemoryReq, func(resp *APIResponse) bool {
|
|
return virtualKeyHasExpectedTeam(resp, vkID, teamID) && responseJSONIsMarshalable(resp)
|
|
}, 5*time.Second, "in-memory virtual key retains team relationship"); !ok {
|
|
t.Fatalf("In-memory virtual key detail never returned expected team relationship")
|
|
}
|
|
}
|
|
|
|
func createCustomerVirtualKeyForTest(t *testing.T, testData *GlobalTestData, customerID, namePrefix string) string {
|
|
t.Helper()
|
|
|
|
vkName := namePrefix + generateRandomID()
|
|
createVKResp := MakeRequest(t, APIRequest{
|
|
Method: "POST",
|
|
Path: "/api/governance/virtual-keys",
|
|
Body: CreateVirtualKeyRequest{
|
|
Name: vkName,
|
|
CustomerID: &customerID,
|
|
},
|
|
})
|
|
|
|
if createVKResp.StatusCode != 200 {
|
|
t.Fatalf("Failed to create VK: status %d", createVKResp.StatusCode)
|
|
}
|
|
|
|
vkID := ExtractIDFromResponse(t, createVKResp)
|
|
testData.AddVirtualKey(vkID)
|
|
return vkID
|
|
}
|
|
|
|
func extractCustomerFromResponse(body map[string]interface{}, customerID string) map[string]interface{} {
|
|
if customer, ok := body["customer"].(map[string]interface{}); ok {
|
|
id, _ := customer["id"].(string)
|
|
if id == customerID {
|
|
return customer
|
|
}
|
|
}
|
|
|
|
if customers, ok := body["customers"].([]interface{}); ok {
|
|
for _, item := range customers {
|
|
customer, ok := item.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
id, _ := customer["id"].(string)
|
|
if id == customerID {
|
|
return customer
|
|
}
|
|
}
|
|
}
|
|
|
|
if customers, ok := body["customers"].(map[string]interface{}); ok {
|
|
if customer, ok := customers[customerID].(map[string]interface{}); ok {
|
|
return customer
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func customerHasVirtualKey(resp *APIResponse, customerID, vkID string) bool {
|
|
if resp.StatusCode != 200 {
|
|
return false
|
|
}
|
|
|
|
customer := extractCustomerFromResponse(resp.Body, customerID)
|
|
if customer == nil {
|
|
return false
|
|
}
|
|
|
|
if !arrayFieldContainsID(customer, "virtual_keys", vkID) {
|
|
return false
|
|
}
|
|
|
|
return fieldIsJSONArray(customer, "teams")
|
|
}
|
|
|
|
func customerHasExactVirtualKeys(resp *APIResponse, customerID string, expectedVKIDs []string) bool {
|
|
if resp.StatusCode != 200 {
|
|
return false
|
|
}
|
|
|
|
customer := extractCustomerFromResponse(resp.Body, customerID)
|
|
if customer == nil || !fieldIsJSONArray(customer, "teams") {
|
|
return false
|
|
}
|
|
|
|
values, ok := customer["virtual_keys"].([]interface{})
|
|
if !ok || len(values) != len(expectedVKIDs) {
|
|
return false
|
|
}
|
|
|
|
for _, expectedID := range expectedVKIDs {
|
|
if !arrayFieldContainsID(customer, "virtual_keys", expectedID) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func customerExcludesVirtualKey(resp *APIResponse, customerID, vkID string, expectedLen int) bool {
|
|
if resp.StatusCode != 200 {
|
|
return false
|
|
}
|
|
|
|
customer := extractCustomerFromResponse(resp.Body, customerID)
|
|
if customer == nil || !fieldIsJSONArray(customer, "teams") {
|
|
return false
|
|
}
|
|
|
|
values, ok := customer["virtual_keys"].([]interface{})
|
|
if !ok || len(values) != expectedLen {
|
|
return false
|
|
}
|
|
|
|
return !arrayFieldContainsID(customer, "virtual_keys", vkID)
|
|
}
|
|
|
|
func embeddedCustomerHasExpectedRelations(resp *APIResponse, customerID, vkID string) bool {
|
|
if resp.StatusCode != 200 {
|
|
return false
|
|
}
|
|
|
|
virtualKey, ok := resp.Body["virtual_key"].(map[string]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
customer, ok := virtualKey["customer"].(map[string]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
id, _ := customer["id"].(string)
|
|
if id != customerID {
|
|
return false
|
|
}
|
|
|
|
if !fieldIsJSONArray(customer, "teams") {
|
|
return false
|
|
}
|
|
|
|
if !arrayFieldContainsID(customer, "virtual_keys", vkID) {
|
|
return false
|
|
}
|
|
|
|
values, ok := customer["virtual_keys"].([]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
for _, item := range values {
|
|
entry, ok := item.(map[string]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
if nestedCustomer, exists := entry["customer"]; exists && nestedCustomer != nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func virtualKeyHasExpectedTeam(resp *APIResponse, vkID, teamID string) bool {
|
|
if resp.StatusCode != 200 {
|
|
return false
|
|
}
|
|
|
|
virtualKey, ok := resp.Body["virtual_key"].(map[string]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
id, _ := virtualKey["id"].(string)
|
|
if id != vkID {
|
|
return false
|
|
}
|
|
|
|
team, ok := virtualKey["team"].(map[string]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
actualTeamID, _ := team["id"].(string)
|
|
if actualTeamID != teamID {
|
|
return false
|
|
}
|
|
|
|
customer, exists := virtualKey["customer"]
|
|
return !exists || customer == nil
|
|
}
|
|
|
|
func arrayFieldContainsID(parent map[string]interface{}, field, id string) bool {
|
|
values, ok := parent[field].([]interface{})
|
|
if !ok {
|
|
return false
|
|
}
|
|
for _, item := range values {
|
|
entry, ok := item.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
entryID, _ := entry["id"].(string)
|
|
if entryID == id {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func fieldIsJSONArray(parent map[string]interface{}, field string) bool {
|
|
_, ok := parent[field].([]interface{})
|
|
return ok
|
|
}
|
|
|
|
func assertJSONArrayField(t *testing.T, parent map[string]interface{}, field string, expectedLen int) {
|
|
t.Helper()
|
|
|
|
values, ok := parent[field].([]interface{})
|
|
if !ok {
|
|
t.Fatalf("Expected %q to be an array, got %T", field, parent[field])
|
|
}
|
|
if len(values) != expectedLen {
|
|
t.Fatalf("Expected %q length %d, got %d", field, expectedLen, len(values))
|
|
}
|
|
}
|
|
|
|
func responseJSONIsMarshalable(resp *APIResponse) bool {
|
|
if !json.Valid(resp.RawBody) {
|
|
return false
|
|
}
|
|
_, err := json.Marshal(resp.Body)
|
|
return err == nil
|
|
}
|