From fcffc531cce9e42367b5dd4ac2ae4326a03a3110 Mon Sep 17 00:00:00 2001 From: Eliise Date: Tue, 2 Jun 2020 00:04:59 +0100 Subject: [PATCH] Add KeyVault reference feature for VariableGroups Resource (#282) * Add required env vars and flags for vscode testing * Add key vault var group test * Add key vault variable groups * Add conflicts with * Update go-sdk version to v1.0.0-b3 * Fix build errors * Fix unit test * Split acceptance and unit tests * Fix linter issues * Remove comment * Fixing issue with HCL used in acceptance tests * Add make install * Remove incorrect variable tests * Fix tests * Fix integration test * Stop using decode * Remove redudant conflicts * inline declaration * Refactor * Refactor into method * Refactor error messages * Update docs * Refactor variable groups * Refactor tests Co-authored-by: Nick Iodice --- .devcontainer/devcontainer.json | 4 +- GNUmakefile | 5 + README.md.lengacy => README.md.legacy | 0 azuredevops/resource_variable_group.go | 261 ++++-- ...resource_variable_group_acceptance_test.go | 212 +++++ azuredevops/resource_variable_group_test.go | 260 ++---- azuredevops/utils/testhelper/hcl.go | 25 + docs/contributing.md | 30 +- go.mod | 7 +- go.sum | 13 +- .../azure-devops-go-api/azuredevops/client.go | 2 +- .../azuredevops/profile/models.go | 16 + .../azuredevops/taskagent/models.go | 62 +- .../testify/assert/assertion_format.go | 82 ++ .../testify/assert/assertion_forward.go | 164 ++++ .../testify/assert/assertion_order.go | 309 ++++++++ .../stretchr/testify/assert/assertions.go | 96 ++- .../stretchr/testify/require/require.go | 750 +++++++++++------- .../stretchr/testify/require/require.go.tmpl | 2 +- .../testify/require/require_forward.go | 164 ++++ vendor/gopkg.in/yaml.v2/decode.go | 38 + vendor/gopkg.in/yaml.v2/resolve.go | 2 +- vendor/gopkg.in/yaml.v2/scannerc.go | 16 + vendor/modules.txt | 6 +- 24 files changed, 1954 insertions(+), 572 deletions(-) rename README.md.lengacy => README.md.legacy (100%) create mode 100644 azuredevops/resource_variable_group_acceptance_test.go create mode 100644 vendor/github.com/stretchr/testify/assert/assertion_order.go diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5f0ff5a4c..ab7bb3896 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -33,6 +33,7 @@ "-v", "-tags=all" ], + "go.testTimeout": "90s", "go.testEnvVars": { "TF_ACC": "1" }, @@ -58,6 +59,7 @@ "ms-vsliveshare.vsliveshare", "streetsidesoftware.code-spell-checker", "eamodio.gitlens", - "yzhang.markdown-all-in-one" + "yzhang.markdown-all-in-one", + "davidanson.vscode-markdownlint" ] } \ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile index 3911a52dc..2020f4cf0 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -54,6 +54,9 @@ test-compile: fi go test -c $(TEST) $(TESTARGS) +install: + ./scripts/build.sh --SkipTests --Install + check-vendor-vs-mod: ## Check that go modules and vendored code are on par @echo "==> Checking that go modules and vendored dependencies match..." go mod vendor @@ -71,6 +74,8 @@ vet: exit 1; \ fi +ci: check-vendor-vs-mod lint test + website: ifeq (,$(wildcard $(GOPATH)/src/$(WEBSITE_REPO))) echo "$(WEBSITE_REPO) not found in your GOPATH (necessary for layouts and assets), get-ting..." diff --git a/README.md.lengacy b/README.md.legacy similarity index 100% rename from README.md.lengacy rename to README.md.legacy diff --git a/azuredevops/resource_variable_group.go b/azuredevops/resource_variable_group.go index 94badbd3e..759b9fb3b 100644 --- a/azuredevops/resource_variable_group.go +++ b/azuredevops/resource_variable_group.go @@ -1,9 +1,11 @@ package azuredevops import ( + "encoding/json" "fmt" "strconv" + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/microsoft/azure-devops-go-api/azuredevops/build" "github.com/microsoft/azure-devops-go-api/azuredevops/taskagent" @@ -15,17 +17,25 @@ import ( ) const ( - vgProjectID = "project_id" - vgName = "name" - vgDescription = "description" - vgAllowAccess = "allow_access" - vgVariable = "variable" - vgValue = "value" - vgIsSecret = "is_secret" + vgProjectID = "project_id" + vgName = "name" + vgDescription = "description" + vgAllowAccess = "allow_access" + vgVariable = "variable" + vgValue = "value" + vgIsSecret = "is_secret" + vgKeyVault = "key_vault" + vgServiceEndpointId = "service_endpoint_id" + vgContentType = "content_type" + vgEnabled = "enabled" + vgExpires = "expires" ) const ( - invalidVarGroupIDErrorMessageFormat = "Error parsing the variable group ID from the Terraform resource data: %v" + azureKeyVaultType = "AzureKeyVault" + invalidVariableGroupIDErrorMessageFormat = "Error parsing the variable group ID from the Terraform resource data: %v" + flatteningVariableGroupErrorMessageFormat = "Error flattening variable group: %v" + expandingVariableGroupErrorMessageFormat = "Error expanding variable group resource data: %+v" ) func resourceVariableGroup() *schema.Resource { @@ -58,7 +68,9 @@ func resourceVariableGroup() *schema.Resource { Default: false, }, vgVariable: { - Type: schema.TypeSet, + Type: schema.TypeSet, + Required: true, + MinItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ vgName: { @@ -66,19 +78,50 @@ func resourceVariableGroup() *schema.Resource { Required: true, }, vgValue: { - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Optional: true, + Default: "", + ConflictsWith: []string{vgKeyVault}, }, vgIsSecret: { + Type: schema.TypeBool, + Optional: true, + Default: false, + ConflictsWith: []string{vgKeyVault}, + }, + vgContentType: { + Type: schema.TypeString, + Computed: true, + }, + vgEnabled: { Type: schema.TypeBool, - Optional: true, - Default: false, + Computed: true, + }, + vgExpires: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + vgKeyVault: { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + vgName: { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + vgServiceEndpointId: { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.UUID, }, }, }, - Required: true, - MinItems: 1, }, }, } @@ -86,17 +129,24 @@ func resourceVariableGroup() *schema.Resource { func resourceVariableGroupCreate(d *schema.ResourceData, m interface{}) error { clients := m.(*config.AggregatedClient) - variableGroupParameters, projectID := expandVariableGroupParameters(d) + variableGroupParameters, projectID, err := expandVariableGroupParameters(d) + if err != nil { + return fmt.Errorf(expandingVariableGroupErrorMessageFormat, err) + } addedVariableGroup, err := createVariableGroup(clients, variableGroupParameters, projectID) if err != nil { return fmt.Errorf("Error creating variable group in Azure DevOps: %+v", err) } - flattenVariableGroup(d, addedVariableGroup, projectID) + err = flattenVariableGroup(d, addedVariableGroup, projectID) + + if err != nil { + return fmt.Errorf(flatteningVariableGroupErrorMessageFormat, err) + } // Update Allow Access with definition Reference - definitionResourceReferenceArgs := expandDefinitionResourceAuth(d, addedVariableGroup) + definitionResourceReferenceArgs := expandAllowAccess(d, addedVariableGroup) definitionResourceReference, err := updateDefinitionResourceAuth(clients, definitionResourceReferenceArgs, projectID) if err != nil { return fmt.Errorf("Error creating definitionResourceReference Azure DevOps object: %+v", err) @@ -112,7 +162,7 @@ func resourceVariableGroupRead(d *schema.ResourceData, m interface{}) error { projectID, variableGroupID, err := tfhelper.ParseProjectIDAndResourceID(d) if err != nil { - return fmt.Errorf(invalidVarGroupIDErrorMessageFormat, err) + return fmt.Errorf(invalidVariableGroupIDErrorMessageFormat, err) } variableGroup, err := clients.TaskAgentClient.GetVariableGroup( @@ -130,7 +180,11 @@ func resourceVariableGroupRead(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("Error looking up variable group given ID (%v) and project ID (%v): %v", variableGroupID, projectID, err) } - flattenVariableGroup(d, variableGroup, &projectID) + err = flattenVariableGroup(d, variableGroup, &projectID) + + if err != nil { + return fmt.Errorf(flatteningVariableGroupErrorMessageFormat, err) + } //Read the Authorization Resource for get allow access property resourceRefType := "variablegroup" @@ -155,11 +209,15 @@ func resourceVariableGroupRead(d *schema.ResourceData, m interface{}) error { func resourceVariableGroupUpdate(d *schema.ResourceData, m interface{}) error { clients := m.(*config.AggregatedClient) - variableGroupParams, projectID := expandVariableGroupParameters(d) + + variableGroupParams, projectID, err := expandVariableGroupParameters(d) + if err != nil { + return fmt.Errorf(expandingVariableGroupErrorMessageFormat, err) + } _, variableGroupID, err := tfhelper.ParseProjectIDAndResourceID(d) if err != nil { - return fmt.Errorf(invalidVarGroupIDErrorMessageFormat, err) + return fmt.Errorf(invalidVariableGroupIDErrorMessageFormat, err) } updatedVariableGroup, err := updateVariableGroup(clients, variableGroupParams, &variableGroupID, projectID) @@ -167,10 +225,14 @@ func resourceVariableGroupUpdate(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("Error updating variable group in Azure DevOps: %+v", err) } - flattenVariableGroup(d, updatedVariableGroup, projectID) + err = flattenVariableGroup(d, updatedVariableGroup, projectID) + + if err != nil { + return fmt.Errorf(flatteningVariableGroupErrorMessageFormat, err) + } // Update Allow Access - definitionResourceReferenceArgs := expandDefinitionResourceAuth(d, updatedVariableGroup) + definitionResourceReferenceArgs := expandAllowAccess(d, updatedVariableGroup) definitionResourceReference, err := updateDefinitionResourceAuth(clients, definitionResourceReferenceArgs, projectID) if err != nil { return fmt.Errorf("Error updating definitionResourceReference Azure DevOps object: %+v", err) @@ -185,7 +247,7 @@ func resourceVariableGroupDelete(d *schema.ResourceData, m interface{}) error { clients := m.(*config.AggregatedClient) projectID, variableGroupID, err := tfhelper.ParseProjectIDAndResourceID(d) if err != nil { - return fmt.Errorf(invalidVarGroupIDErrorMessageFormat, err) + return fmt.Errorf(invalidVariableGroupIDErrorMessageFormat, err) } //delete the definition resource (allow access) varGroupID := strconv.Itoa(variableGroupID) @@ -234,11 +296,11 @@ func deleteVariableGroup(clients *config.AggregatedClient, project *string, vari } // Convert internal Terraform data structure to an AzDO data structure -func expandVariableGroupParameters(d *schema.ResourceData) (*taskagent.VariableGroupParameters, *string) { +func expandVariableGroupParameters(d *schema.ResourceData) (*taskagent.VariableGroupParameters, *string, error) { projectID := converter.String(d.Get(vgProjectID).(string)) variables := d.Get(vgVariable).(*schema.Set).List() - variableMap := make(map[string]taskagent.VariableValue) + variableMap := make(map[string]interface{}) for _, variable := range variables { asMap := variable.(map[string]interface{}) @@ -254,51 +316,156 @@ func expandVariableGroupParameters(d *schema.ResourceData) (*taskagent.VariableG Variables: &variableMap, } - return variableGroup, projectID + key_vault := d.Get(vgKeyVault).([]interface{}) + + // Note: this will be of length 1 based on the schema definition above. + if len(key_vault) == 1 { + keyVaultValues := key_vault[0].(map[string]interface{}) + + serviceEndpointId, err := uuid.Parse(keyVaultValues[vgServiceEndpointId].(string)) + if err != nil { + return nil, nil, err + } + + variableGroup.ProviderData = taskagent.AzureKeyVaultVariableGroupProviderData{ + ServiceEndpointId: &serviceEndpointId, + Vault: converter.String(keyVaultValues[vgName].(string)), + } + + variableGroup.Type = converter.String(azureKeyVaultType) + } + + return variableGroup, projectID, nil } // Convert AzDO data structure to internal Terraform data structure -func flattenVariableGroup(d *schema.ResourceData, variableGroup *taskagent.VariableGroup, projectID *string) { +func flattenVariableGroup(d *schema.ResourceData, variableGroup *taskagent.VariableGroup, projectID *string) error { d.SetId(fmt.Sprintf("%d", *variableGroup.Id)) d.Set(vgName, *variableGroup.Name) d.Set(vgDescription, *variableGroup.Description) - d.Set(vgVariable, flattenVariables(d, variableGroup)) d.Set(vgProjectID, projectID) + + variables, err := flattenVariables(d, variableGroup) + + if err != nil { + return err + } + + if err = d.Set(vgVariable, variables); err != nil { + return err + } + + if isKeyVaultVariableGroupType(variableGroup.Type) { + keyVault, err := flattenKeyVault(d, variableGroup) + + if err != nil { + return err + } + + if err = d.Set(vgKeyVault, keyVault); err != nil { + return err + } + } + return nil +} + +func isKeyVaultVariableGroupType(variableGrouptype *string) bool { + return variableGrouptype != nil && *variableGrouptype == azureKeyVaultType } // Convert AzDO Variables data structure to Terraform TypeSet // // Note: The AzDO API does not return the value for variables marked as a secret. For this reason // variables marked as secret will need to be pulled from the state itself -func flattenVariables(d *schema.ResourceData, variableGroup *taskagent.VariableGroup) interface{} { - // Preallocate list of variable prop maps +func flattenVariables(d *schema.ResourceData, variableGroup *taskagent.VariableGroup) (interface{}, error) { variables := make([]map[string]interface{}, len(*variableGroup.Variables)) index := 0 for varName, varVal := range *variableGroup.Variables { - var variable map[string]interface{} - if converter.ToBool(varVal.IsSecret, false) { - variable = tfhelper.FindMapInSetWithGivenKeyValue(d, vgVariable, vgName, varName) + variableAsJSON, err := json.Marshal(varVal) + if err != nil { + return nil, fmt.Errorf("Unable to marshal variable into JSON: %+v", err) + } + + if isKeyVaultVariableGroupType(variableGroup.Type) { + variables[index], err = flattenKeyVaultVariable(d, variableAsJSON, varName) } else { - variable = map[string]interface{}{ - vgName: varName, - vgValue: converter.ToString(varVal.Value, ""), - vgIsSecret: false, - } + variables[index], err = flattenVariable(d, variableAsJSON, varName) + } + + if err != nil { + return nil, err } - variables[index] = variable + index = index + 1 } - return variables + return variables, nil +} + +func flattenKeyVaultVariable(d *schema.ResourceData, variableAsJSON []byte, varName string) (map[string]interface{}, error) { + var variable taskagent.AzureKeyVaultVariableValue + err := json.Unmarshal(variableAsJSON, &variable) + if err != nil { + return nil, fmt.Errorf("Unable to unmarshal variable (%+v): %+v", variable, err) + } + + variableMap := map[string]interface{}{ + vgName: varName, + vgValue: nil, + vgIsSecret: false, + vgEnabled: converter.ToBool(variable.Enabled, false), + vgContentType: converter.ToString(variable.ContentType, ""), + } + if variable.Expires != nil { + variableMap[vgExpires] = variable.Expires.String() + } + return variableMap, nil +} + +func flattenVariable(d *schema.ResourceData, variableAsJSON []byte, varName string) (map[string]interface{}, error) { + var variable taskagent.AzureKeyVaultVariableValue + err := json.Unmarshal(variableAsJSON, &variable) + if err != nil { + return nil, fmt.Errorf("Unable to unmarshal variable (%+v): %+v", variable, err) + } + + if converter.ToBool(variable.IsSecret, false) { + return tfhelper.FindMapInSetWithGivenKeyValue(d, vgVariable, vgName, varName), nil + } + return map[string]interface{}{ + vgName: varName, + vgValue: converter.ToString(variable.Value, ""), + vgIsSecret: false, + }, nil +} + +func flattenKeyVault(d *schema.ResourceData, variableGroup *taskagent.VariableGroup) (interface{}, error) { + providerDataAsJSON, err := json.Marshal(variableGroup.ProviderData) + if err != nil { + return nil, fmt.Errorf("Unable to marshal provider data into JSON: %+v", err) + } + + var providerData taskagent.AzureKeyVaultVariableGroupProviderData + err = json.Unmarshal(providerDataAsJSON, &providerData) + if err != nil { + return nil, fmt.Errorf("Unable to unmarshal provider data (%+v): %+v", providerData, err) + } + + keyVault := []map[string]interface{}{{ + vgName: providerData.Vault, + vgServiceEndpointId: providerData.ServiceEndpointId.String(), + }} + + return keyVault, nil } // Convert internal Terraform data structure to an AzDO data structure for Allow Access -func expandDefinitionResourceAuth(d *schema.ResourceData, createdVariableGroup *taskagent.VariableGroup) []build.DefinitionResourceReference { +func expandAllowAccess(d *schema.ResourceData, createdVariableGroup *taskagent.VariableGroup) []build.DefinitionResourceReference { resourceRefType := "variablegroup" variableGroupID := strconv.Itoa(*createdVariableGroup.Id) - var ArrayDefinitionResourceReference []build.DefinitionResourceReference + var arrayDefinitionResourceReference []build.DefinitionResourceReference defResourceRef := build.DefinitionResourceReference{ Type: &resourceRefType, @@ -307,9 +474,9 @@ func expandDefinitionResourceAuth(d *schema.ResourceData, createdVariableGroup * Id: &variableGroupID, } - ArrayDefinitionResourceReference = append(ArrayDefinitionResourceReference, defResourceRef) + arrayDefinitionResourceReference = append(arrayDefinitionResourceReference, defResourceRef) - return ArrayDefinitionResourceReference + return arrayDefinitionResourceReference } // Make the Azure DevOps API call to update the Definition resource = Allow Access diff --git a/azuredevops/resource_variable_group_acceptance_test.go b/azuredevops/resource_variable_group_acceptance_test.go new file mode 100644 index 000000000..a6c87dbb6 --- /dev/null +++ b/azuredevops/resource_variable_group_acceptance_test.go @@ -0,0 +1,212 @@ +// +build all resource_variable_group + +package azuredevops + +// The tests in this file use the mock clients in mock_client.go to mock out +// the Azure DevOps client operations. + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/microsoft/azure-devops-go-api/azuredevops/build" + "github.com/microsoft/azure-devops-go-api/azuredevops/taskagent" + "github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/config" + "github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/converter" + "github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/testhelper" +) + +func TestAccVariableGroup_CreateAndUpdate(t *testing.T) { + projectName := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + + vargroupNameFirst := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + vargroupNameSecond := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + vargroupNameNoSecret := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + + allowAccessTrue := true + allowAccessFalse := false + + tfVarGroupNode := "azuredevops_variable_group.vg" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testhelper.TestAccPreCheck(t, nil) }, + Providers: testAccProviders, + CheckDestroy: testAccVariableGroupCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testhelper.TestAccVariableGroupResource(projectName, vargroupNameFirst, allowAccessTrue), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(tfVarGroupNode, "project_id"), + resource.TestCheckResourceAttr(tfVarGroupNode, "name", vargroupNameFirst), + testAccCheckVariableGroupResourceExists(vargroupNameFirst, allowAccessTrue), + ), + }, { + Config: testhelper.TestAccVariableGroupResource(projectName, vargroupNameSecond, allowAccessFalse), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(tfVarGroupNode, "project_id"), + resource.TestCheckResourceAttr(tfVarGroupNode, "name", vargroupNameSecond), + testAccCheckVariableGroupResourceExists(vargroupNameSecond, allowAccessFalse), + ), + }, { + Config: testhelper.TestAccVariableGroupResourceNoSecrets(projectName, vargroupNameNoSecret, allowAccessFalse), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(tfVarGroupNode, "project_id"), + resource.TestCheckResourceAttr(tfVarGroupNode, "name", vargroupNameNoSecret), + testAccCheckVariableGroupResourceExists(vargroupNameNoSecret, allowAccessFalse), + ), + }, { + // Resource Acceptance Testing https://www.terraform.io/docs/extend/resources/import.html#resource-acceptance-testing-implementation + ResourceName: tfVarGroupNode, + ImportStateIdFunc: testAccImportStateIDFunc(tfVarGroupNode), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccVariableGroupKeyVault_CreateAndUpdate(t *testing.T) { + projectName := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + + vargroupKeyvault := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + keyVaultName := "key-vault-name" + allowAccessFalse := false + tfVarGroupNode := "azuredevops_variable_group.vg" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testhelper.TestAccPreCheck(t, nil) }, + Providers: testAccProviders, + CheckDestroy: testAccVariableGroupCheckDestroy, + Steps: []resource.TestStep{ + { + Config: testhelper.TestAccVariableGroupResourceKeyVaultWithProject(projectName, vargroupKeyvault, allowAccessFalse, keyVaultName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(tfVarGroupNode, "project_id"), + resource.TestCheckResourceAttr(tfVarGroupNode, "name", vargroupKeyvault), + testAccCheckVariableGroupResourceExists(vargroupKeyvault, allowAccessFalse), + ), + }, { + // Resource Acceptance Testing https://www.terraform.io/docs/extend/resources/import.html#resource-acceptance-testing-implementation + ResourceName: tfVarGroupNode, + ImportStateIdFunc: testAccImportStateIDFunc(tfVarGroupNode), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +// Given an AzDO variable group name, this will return a function that will check whether +// or not the definition (1) exists in the state, (2) exists in AzDO, and (3) has the correct +// or expected name +func testAccCheckVariableGroupResourceExists(expectedName string, expectedAllowAccess bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + varGroup, ok := s.RootModule().Resources["azuredevops_variable_group.vg"] + if !ok { + return fmt.Errorf("Did not find a variable group in the TF state") + } + + variableGroup, err := getVariableGroupFromResource(varGroup) + if err != nil { + return err + } + + if *variableGroup.Name != expectedName { + return fmt.Errorf("Variable Group has Name=%s, but expected %s", *variableGroup.Name, expectedName) + } + + // testing Allow access with definition reference AzDo object + definitionReference, err := getDefinitionResourceFromVariableGroupResource(varGroup) + if err != nil { + return err + } + + if expectedAllowAccess { + if len(*definitionReference) == 0 { + return fmt.Errorf("Definition reference should be not empty for allow access true") + } + if len(*definitionReference) > 0 && *(*definitionReference)[0].Authorized != expectedAllowAccess { + return fmt.Errorf("Variable Group has Allow_access=%t, but expected %t", *(*definitionReference)[0].Authorized, expectedAllowAccess) + } + } else { + if len(*definitionReference) > 0 { + return fmt.Errorf("Definition reference should be empty for allow access false") + } + } + return nil + } +} + +// Verifies that all variable groups referenced in the state are destroyed. This will be +// invoked *after* Terraform destroys the resource but *before* the state is wiped clean. +func testAccVariableGroupCheckDestroy(s *terraform.State) error { + for _, resource := range s.RootModule().Resources { + if resource.Type != "azuredevops_variable_group" { + continue + } + + // Indicates the variable group still exists -- this should fail the test + if _, err := getVariableGroupFromResource(resource); err == nil { + return fmt.Errorf("Unexpectedly found a variable group that should be deleted") + } + + // Indicates the definition reference still exists -- this should fail the test + if _, err := getDefinitionResourceFromVariableGroupResource(resource); err == nil { + return fmt.Errorf("Unexpectedly found a definition reference for allow access that should be deleted") + } + } + + return nil +} + +// Given a resource from the state, return a variable group (and error) +func getVariableGroupFromResource(resource *terraform.ResourceState) (*taskagent.VariableGroup, error) { + variableGroupID, err := strconv.Atoi(resource.Primary.ID) + if err != nil { + return nil, err + } + + projectID := resource.Primary.Attributes["project_id"] + clients := testAccProvider.Meta().(*config.AggregatedClient) + return clients.TaskAgentClient.GetVariableGroup( + clients.Ctx, + taskagent.GetVariableGroupArgs{ + GroupId: &variableGroupID, + Project: &projectID, + }, + ) +} + +// Given a resource from the state, return a definition Reference (and error) +func getDefinitionResourceFromVariableGroupResource(resource *terraform.ResourceState) (*[]build.DefinitionResourceReference, error) { + projectID := resource.Primary.Attributes["project_id"] + clients := testAccProvider.Meta().(*config.AggregatedClient) + + return clients.BuildClient.GetProjectResources( + clients.Ctx, + build.GetProjectResourcesArgs{ + Project: &projectID, + Type: converter.String("variablegroup"), + Id: &resource.Primary.ID, + }, + ) +} + +// Set the Imported ID +func testAccImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["project_id"], rs.Primary.Attributes["id"]), nil + } +} + +func init() { + InitProvider() +} diff --git a/azuredevops/resource_variable_group_test.go b/azuredevops/resource_variable_group_test.go index b3773deae..cef919103 100644 --- a/azuredevops/resource_variable_group_test.go +++ b/azuredevops/resource_variable_group_test.go @@ -1,232 +1,102 @@ -// +build all resource_variable_group - package azuredevops // The tests in this file use the mock clients in mock_client.go to mock out // the Azure DevOps client operations. import ( - "fmt" - "strconv" + "encoding/json" "testing" "github.com/google/uuid" - "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/microsoft/azure-devops-go-api/azuredevops/build" "github.com/microsoft/azure-devops-go-api/azuredevops/taskagent" - "github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/config" "github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/converter" - "github.com/microsoft/terraform-provider-azuredevops/azuredevops/utils/testhelper" "github.com/stretchr/testify/require" ) -var testVarGroupProjectID = uuid.New().String() - -// This definition matches the overall structure of what a configured git repository would -// look like. Note that the ID and Name attributes match -- this is the service-side behavior -// when configuring a GitHub repo. -var testVariableGroup = taskagent.VariableGroup{ - Id: converter.Int(100), - Name: converter.String("Name"), - Description: converter.String("This is a test variable group."), - Variables: &map[string]taskagent.VariableValue{ - "var1": { - Value: converter.String("value1"), - IsSecret: converter.Bool(false), - }, - }, -} -var resourceRefType = "variablegroup" -var testDefinitionResource = build.DefinitionResourceReference{ - Type: &resourceRefType, - Authorized: converter.Bool(true), - Name: testVariableGroup.Name, - Id: converter.String("100"), -} - -/** - * Begin unit tests - */ -// verifies that the flatten/expand round trip yields the same build definition -func TestAzureDevOpsVariableGroup_ExpandFlatten_Roundtrip(t *testing.T) { +func TestVariableGroupAllowAccess_ExpandFlatten_Roundtrip(t *testing.T) { + testVariableGroup := taskagent.VariableGroup{ + Id: converter.Int(100), + Name: converter.String("Name"), + } + resourceRefType := "variablegroup" + testDefinitionResource := build.DefinitionResourceReference{ + Type: &resourceRefType, + Authorized: converter.Bool(true), + Name: testVariableGroup.Name, + Id: converter.String("100"), + } resourceData := schema.TestResourceDataRaw(t, resourceVariableGroup().Schema, nil) - flattenVariableGroup(resourceData, &testVariableGroup, &testVarGroupProjectID) - var testArrayDefinitionResourceReference []build.DefinitionResourceReference - testArrayDefinitionResourceReference = append(testArrayDefinitionResourceReference, testDefinitionResource) - flattenAllowAccess(resourceData, &testArrayDefinitionResourceReference) - variableGroupParams, projectID := expandVariableGroupParameters(resourceData) - definitionResourceReferenceArgs := expandDefinitionResourceAuth(resourceData, &testVariableGroup) - - require.Equal(t, *testVariableGroup.Name, *variableGroupParams.Name) - require.Equal(t, *testVariableGroup.Description, *variableGroupParams.Description) - require.Equal(t, *testVariableGroup.Variables, *variableGroupParams.Variables) - require.Equal(t, testVarGroupProjectID, *projectID) + testArrayDefinitionResourceReference := []build.DefinitionResourceReference{testDefinitionResource} + flattenAllowAccess(resourceData, &testArrayDefinitionResourceReference) + definitionResourceReferenceArgs := expandAllowAccess(resourceData, &testVariableGroup) require.Equal(t, testDefinitionResource.Authorized, definitionResourceReferenceArgs[0].Authorized) require.Equal(t, testDefinitionResource.Id, definitionResourceReferenceArgs[0].Id) } -/** - * Begin acceptance tests - */ - -func TestAccAccAzureDevOpsVariableGroup_CreateAndUpdate(t *testing.T) { - projectName := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) - vargroupNameFirst := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) - vargroupNameSecond := testhelper.TestAccResourcePrefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) - allowAccessFirst := true - allowAccessSecond := false - - tfVarGroupNode := "azuredevops_variable_group.vg" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testhelper.TestAccPreCheck(t, nil) }, - Providers: testAccProviders, - CheckDestroy: testAccVariableGroupCheckDestroy, - Steps: []resource.TestStep{ - { - Config: testhelper.TestAccVariableGroupResource(projectName, vargroupNameFirst, allowAccessFirst), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(tfVarGroupNode, "project_id"), - resource.TestCheckResourceAttr(tfVarGroupNode, "name", vargroupNameFirst), - testAccCheckVariableGroupResourceExists(vargroupNameFirst, allowAccessFirst), - ), - }, { - Config: testhelper.TestAccVariableGroupResource(projectName, vargroupNameSecond, allowAccessSecond), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(tfVarGroupNode, "project_id"), - resource.TestCheckResourceAttr(tfVarGroupNode, "name", vargroupNameSecond), - testAccCheckVariableGroupResourceExists(vargroupNameSecond, allowAccessSecond), - ), - }, { - Config: testhelper.TestAccVariableGroupResourceNoSecrets(projectName, vargroupNameSecond, allowAccessSecond), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(tfVarGroupNode, "project_id"), - resource.TestCheckResourceAttr(tfVarGroupNode, "name", vargroupNameSecond), - testAccCheckVariableGroupResourceExists(vargroupNameSecond, allowAccessSecond), - ), - }, - { - // Resource Acceptance Testing https://www.terraform.io/docs/extend/resources/import.html#resource-acceptance-testing-implementation - ResourceName: tfVarGroupNode, - ImportStateIdFunc: testAccImportStateIDFunc(tfVarGroupNode), - ImportState: true, - ImportStateVerify: true, +func TestVariableGroup_ExpandFlatten_Roundtrip(t *testing.T) { + testVariableGroup := taskagent.VariableGroup{ + Id: converter.Int(100), + Name: converter.String("Name"), + Description: converter.String("This is a test variable group."), + Variables: &map[string]interface{}{ + "var1": map[string]interface{}{ + "value": converter.String("value1"), + "isSecret": converter.Bool(false), }, }, - }) -} - -// Given an AzDO variable group name, this will return a function that will check whether -// or not the definition (1) exists in the state, (2) exists in AzDO, and (3) has the correct -// or expected name -func testAccCheckVariableGroupResourceExists(expectedName string, expectedAllowAccess bool) resource.TestCheckFunc { - return func(s *terraform.State) error { - varGroup, ok := s.RootModule().Resources["azuredevops_variable_group.vg"] - if !ok { - return fmt.Errorf("Did not find a variable group in the TF state") - } - - variableGroup, err := getVariableGroupFromResource(varGroup) - if err != nil { - return err - } - - if *variableGroup.Name != expectedName { - return fmt.Errorf("Variable Group has Name=%s, but expected %s", *variableGroup.Name, expectedName) - } - - // testing Allow access with definition reference AzDo object - definitionReference, err := getDefinitionResourceFromVariableGroupResource(varGroup) - if err != nil { - return err - } - - if !expectedAllowAccess { - if len(*definitionReference) > 0 { - return fmt.Errorf("Definition reference should be empty for allow access false") - } - } else { - if len(*definitionReference) == 0 { - return fmt.Errorf("Definition reference should be not empty for allow access true") - } - if len(*definitionReference) > 0 && *(*definitionReference)[0].Authorized != expectedAllowAccess { - return fmt.Errorf("Variable Group has Allow_access=%t, but expected %t", *(*definitionReference)[0].Authorized, expectedAllowAccess) - } - } - return nil } -} - -// Verifies that all variable groups referenced in the state are destroyed. This will be -// invoked *after* Terraform destroys the resource but *before* the state is wiped clean. -func testAccVariableGroupCheckDestroy(s *terraform.State) error { - for _, resource := range s.RootModule().Resources { - if resource.Type != "azuredevops_variable_group" { - continue - } + resourceData := schema.TestResourceDataRaw(t, resourceVariableGroup().Schema, nil) + testVarGroupProjectID := uuid.New().String() - // Indicates the variable group still exists -- this should fail the test - if _, err := getVariableGroupFromResource(resource); err == nil { - return fmt.Errorf("Unexpectedly found a variable group that should be deleted") - } + err := flattenVariableGroup(resourceData, &testVariableGroup, &testVarGroupProjectID) + require.Equal(t, nil, err) - // Indicates the definition reference still exists -- this should fail the test - if _, err := getDefinitionResourceFromVariableGroupResource(resource); err == nil { - return fmt.Errorf("Unexpectedly found a definition reference for allow access that should be deleted") - } - } + variableGroupParams, projectID, _ := expandVariableGroupParameters(resourceData) + require.Equal(t, *testVariableGroup.Name, *variableGroupParams.Name) + require.Equal(t, *testVariableGroup.Description, *variableGroupParams.Description) + require.Equal(t, testVarGroupProjectID, *projectID) - return nil + variablesExpected, _ := json.Marshal(testVariableGroup.Variables) + variableActual, _ := json.Marshal(variableGroupParams.Variables) + require.Equal(t, variablesExpected, variableActual) } -// Given a resource from the state, return a variable group (and error) -func getVariableGroupFromResource(resource *terraform.ResourceState) (*taskagent.VariableGroup, error) { - variableGroupID, err := strconv.Atoi(resource.Primary.ID) - if err != nil { - return nil, err - } - - projectID := resource.Primary.Attributes["project_id"] - clients := testAccProvider.Meta().(*config.AggregatedClient) - return clients.TaskAgentClient.GetVariableGroup( - clients.Ctx, - taskagent.GetVariableGroupArgs{ - GroupId: &variableGroupID, - Project: &projectID, +func TestVariableGroupKeyVault_ExpandFlatten_Roundtrip(t *testing.T) { + testVariableGroupKeyvault := taskagent.VariableGroup{ + Id: converter.Int(100), + Name: converter.String("Name"), + Description: converter.String("This is a test variable group."), + Variables: &map[string]interface{}{ + "var1": map[string]interface{}{ + "isSecret": converter.Bool(false), + }, }, - ) -} + ProviderData: map[string]interface{}{ + "serviceEndpointId": converter.String(uuid.New().String()), + "vault": converter.String("VaultName"), + }, + Type: converter.String(azureKeyVaultType), + } + resourceData := schema.TestResourceDataRaw(t, resourceVariableGroup().Schema, nil) + testVarGroupProjectID := uuid.New().String() -// Given a resource from the state, return a definition Reference (and error) -func getDefinitionResourceFromVariableGroupResource(resource *terraform.ResourceState) (*[]build.DefinitionResourceReference, error) { - projectID := resource.Primary.Attributes["project_id"] - clients := testAccProvider.Meta().(*config.AggregatedClient) + err := flattenVariableGroup(resourceData, &testVariableGroupKeyvault, &testVarGroupProjectID) + require.Equal(t, nil, err) - return clients.BuildClient.GetProjectResources( - clients.Ctx, - build.GetProjectResourcesArgs{ - Project: &projectID, - Type: &resourceRefType, - Id: &resource.Primary.ID, - }, - ) -} + variableGroupParams, projectID, _ := expandVariableGroupParameters(resourceData) + require.Equal(t, *testVariableGroupKeyvault.Name, *variableGroupParams.Name) + require.Equal(t, *testVariableGroupKeyvault.Description, *variableGroupParams.Description) + require.Equal(t, testVarGroupProjectID, *projectID) -// Set the Imported ID -func testAccImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not found: %s", resourceName) - } - return fmt.Sprintf("%s/%s", rs.Primary.Attributes["project_id"], rs.Primary.Attributes["id"]), nil - } -} + variablesExpected, _ := json.Marshal(testVariableGroupKeyvault.Variables) + variableActual, _ := json.Marshal(variableGroupParams.Variables) + require.Equal(t, variablesExpected, variableActual) -func init() { - InitProvider() + providerDataExpected, _ := json.Marshal(testVariableGroupKeyvault.ProviderData) + providerDataActual, _ := json.Marshal(variableGroupParams.ProviderData) + require.Equal(t, providerDataExpected, providerDataActual) } diff --git a/azuredevops/utils/testhelper/hcl.go b/azuredevops/utils/testhelper/hcl.go index 545bff8db..dfe6f520d 100644 --- a/azuredevops/utils/testhelper/hcl.go +++ b/azuredevops/utils/testhelper/hcl.go @@ -276,6 +276,31 @@ resource "azuredevops_variable_group" "vg" { return fmt.Sprintf("%s\n%s", projectResource, variableGroupResource) } +// TestAccVariableGroupResourceKeyVaultWithProject HCL describing an AzDO project and variable group with key vault +func TestAccVariableGroupResourceKeyVaultWithProject(projectName string, variableGroupName string, allowAccess bool, keyVaultName string) string { + projectAndServiceEndpoint := TestAccServiceEndpointAzureRMResource(projectName, "test-service-connection") + + return fmt.Sprintf("%s\n%s", projectAndServiceEndpoint, TestAccVariableGroupResourceKeyVault(variableGroupName, allowAccess, keyVaultName)) +} + +// TestAccVariableGroupResourceKeyVault HCL describing an AzDO variable group with key vault +func TestAccVariableGroupResourceKeyVault(variableGroupName string, allowAccess bool, keyVaultName string) string { + return fmt.Sprintf(` +resource "azuredevops_variable_group" "vg" { + project_id = azuredevops_project.project.id + name = "%s" + description = "A sample variable group." + allow_access = %t + key_vault { + name = "%s" + service_endpoint_id = azuredevops_serviceendpoint_azurerm.serviceendpointrm.id + } + variable { + name = "key1" + } +}`, variableGroupName, allowAccess, keyVaultName) +} + // TestAccAgentPoolResource HCL describing an AzDO Agent Pool func TestAccAgentPoolResource(poolName string) string { return fmt.Sprintf(` diff --git a/docs/contributing.md b/docs/contributing.md index 6c03c5130..855807435 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -125,9 +125,17 @@ $ cd terraform-provider-azuredevops/ ## 3. Build & Install Provider +To build and install the provider locally execute the `make install` command which will execute the `build.sh --Install --SkipTests` command for you. + +```bash +$ make install +``` + +For more information about the process see below. + **Build & test the azure devops provider** -Running the next command will orchestrate a few things for you: +Running the `build.sh` script will orchestrate a few things for you: - Verify that all required Go packages are installed. This may take a few minutes the first time you run the script as the packages will be cached locally. Subsequent runs will be much faster - Run all unit tests @@ -147,7 +155,7 @@ The `build.sh` suppports the following parameters: `-s | --SkipTests` : Skip executing the unit tests `-d | --DebugBuild`: Execute _go build_ with flags that disables optimization and adds debug symbols to the created binary -`-i | --Install` : After successful build perform a local install of the provider +`-i | --Install` : After successful build perform a local install of the provider by executing `local-install.sh` ```bash $ ls -lah ./bin/ @@ -443,12 +451,12 @@ There is a lot of context to cover here, so check out our dedicated [debugging d Most changes should involve updates to the client-facing reference documentation. Please update the existing documentation to reflect any changes you have made to the codebase. -| Name | Description | Link | -| ---- | ----------- | ---- | -| Index | Table of contents | [index.md](../website/docs/index.html.markdown) | -| Resources | Resources reference | [resources](../website/docs/r) | -| Data Sources | Data Sources reference | [data sources](../website/docs/d) | -| Guides | Guide and tutorial docs | [guides](../website/docs/guides) | +| Name | Description | Link | +| ------------ | ----------------------- | ----------------------------------------------- | +| Index | Table of contents | [index.md](../website/docs/index.html.markdown) | +| Resources | Resources reference | [resources](../website/docs/r) | +| Data Sources | Data Sources reference | [data sources](../website/docs/d) | +| Guides | Guide and tutorial docs | [guides](../website/docs/guides) | ## 7. Explore with postman @@ -461,6 +469,12 @@ After the feature or bug is completed, you'll want to post a PR to get it review ### Tips for PR authors - The steps run by the CI are located [here](./../.azdo/ci.yml). You should manually execute these commands to make sure that they pass before you post your PR. + - The below command runs everything, but the acceptance tests: + + ```bash + $ make ci + ``` + - The PR template for the repository is located [here](../.github/PULL_REQUEST_TEMPLATE.md). Please be ready to fill this template out when posting the PR. - If your PR is a draft, please mark it as a draft and put `WIP` (stands for "Work In Progress") in the title. This helps reviewers prioritize which reviews to look at. - If your PR contains changes to the `go.mod` or `go.sum` files, or the `vendor/` directory, please elaborate on why the change is being made. diff --git a/go.mod b/go.mod index 85acc5314..5115acf01 100644 --- a/go.mod +++ b/go.mod @@ -9,10 +9,11 @@ require ( github.com/hashicorp/go-uuid v1.0.1 github.com/hashicorp/terraform v0.12.23 github.com/hashicorp/terraform-plugin-sdk v1.8.0 - github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b2 - github.com/stretchr/testify v1.3.0 + github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b3 + github.com/sergi/go-diff v1.1.0 // indirect + github.com/stretchr/testify v1.4.0 golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect golang.org/x/sys v0.0.0-20200428200454-593003d681fa // indirect - gopkg.in/yaml.v2 v2.2.2 + gopkg.in/yaml.v2 v2.2.4 ) diff --git a/go.sum b/go.sum index 8da5b0e0d..067eaad11 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,7 @@ github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRY github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= @@ -256,8 +257,8 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b2 h1:BF9jJAspnyzig7c8e1Ka3sxaYRJLC9HntYH2jE3f9C8= -github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b2/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= +github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b3 h1:5iyKm9Mzp0NbKLVHP6PZbigCAzvOYq/pAaMyc8KpNLs= +github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b3/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= github.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -313,6 +314,8 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= @@ -327,6 +330,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw= github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk= @@ -475,6 +480,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -482,6 +489,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/client.go b/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/client.go index 903e1f55c..8547dc58a 100644 --- a/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/client.go +++ b/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/client.go @@ -44,7 +44,7 @@ var SessionId = uuid.New().String() var apiResourceLocationCache = make(map[string]*map[uuid.UUID]ApiResourceLocation) var apiResourceLocationCacheLock = sync.RWMutex{} -var version = "5.1.0-b1" // todo: remove hardcoded version +var version = "1.1.0-b3" // todo: remove hardcoded version var versionSuffix = "" // Base user agent string. The UserAgent set on the connection will be appended to this. diff --git a/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/profile/models.go b/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/profile/models.go index b248e3c13..ee68a9d00 100644 --- a/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/profile/models.go +++ b/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/profile/models.go @@ -55,6 +55,14 @@ var AvatarSizeValues = avatarSizeValuesType{ // A profile attribute which always has a value for each profile. type CoreProfileAttribute struct { + // The descriptor of the attribute. + Descriptor *AttributeDescriptor `json:"descriptor,omitempty"` + // The revision number of the attribute. + Revision *int `json:"revision,omitempty"` + // The time the attribute was last changed. + TimeStamp *azuredevops.Time `json:"timeStamp,omitempty"` + // The value of the attribute. + Value interface{} `json:"value,omitempty"` } type CreateProfileContext struct { @@ -94,6 +102,14 @@ type Profile struct { // A named object associated with a profile. type ProfileAttribute struct { + // The descriptor of the attribute. + Descriptor *AttributeDescriptor `json:"descriptor,omitempty"` + // The revision number of the attribute. + Revision *int `json:"revision,omitempty"` + // The time the attribute was last changed. + TimeStamp *azuredevops.Time `json:"timeStamp,omitempty"` + // The value of the attribute. + Value interface{} `json:"value,omitempty"` } type ProfileAttributeBase struct { diff --git a/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/taskagent/models.go b/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/taskagent/models.go index defe9b396..edfa370ae 100644 --- a/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/taskagent/models.go +++ b/vendor/github.com/microsoft/azure-devops-go-api/azuredevops/taskagent/models.go @@ -2465,33 +2465,33 @@ type TimelineAttempt struct { } type TimelineRecord struct { - Attempt *int `json:"attempt,omitempty"` - ChangeId *int `json:"changeId,omitempty"` - CurrentOperation *string `json:"currentOperation,omitempty"` - Details *TimelineReference `json:"details,omitempty"` - ErrorCount *int `json:"errorCount,omitempty"` - FinishTime *azuredevops.Time `json:"finishTime,omitempty"` - Id *uuid.UUID `json:"id,omitempty"` - Identifier *string `json:"identifier,omitempty"` - Issues *[]Issue `json:"issues,omitempty"` - LastModified *azuredevops.Time `json:"lastModified,omitempty"` - Location *string `json:"location,omitempty"` - Log *TaskLogReference `json:"log,omitempty"` - Name *string `json:"name,omitempty"` - Order *int `json:"order,omitempty"` - ParentId *uuid.UUID `json:"parentId,omitempty"` - PercentComplete *int `json:"percentComplete,omitempty"` - PreviousAttempts *[]TimelineAttempt `json:"previousAttempts,omitempty"` - RefName *string `json:"refName,omitempty"` - Result *TaskResult `json:"result,omitempty"` - ResultCode *string `json:"resultCode,omitempty"` - StartTime *azuredevops.Time `json:"startTime,omitempty"` - State *TimelineRecordState `json:"state,omitempty"` - Task *TaskReference `json:"task,omitempty"` - Type *string `json:"type,omitempty"` - Variables *map[string]VariableValue `json:"variables,omitempty"` - WarningCount *int `json:"warningCount,omitempty"` - WorkerName *string `json:"workerName,omitempty"` + Attempt *int `json:"attempt,omitempty"` + ChangeId *int `json:"changeId,omitempty"` + CurrentOperation *string `json:"currentOperation,omitempty"` + Details *TimelineReference `json:"details,omitempty"` + ErrorCount *int `json:"errorCount,omitempty"` + FinishTime *azuredevops.Time `json:"finishTime,omitempty"` + Id *uuid.UUID `json:"id,omitempty"` + Identifier *string `json:"identifier,omitempty"` + Issues *[]Issue `json:"issues,omitempty"` + LastModified *azuredevops.Time `json:"lastModified,omitempty"` + Location *string `json:"location,omitempty"` + Log *TaskLogReference `json:"log,omitempty"` + Name *string `json:"name,omitempty"` + Order *int `json:"order,omitempty"` + ParentId *uuid.UUID `json:"parentId,omitempty"` + PercentComplete *int `json:"percentComplete,omitempty"` + PreviousAttempts *[]TimelineAttempt `json:"previousAttempts,omitempty"` + RefName *string `json:"refName,omitempty"` + Result *TaskResult `json:"result,omitempty"` + ResultCode *string `json:"resultCode,omitempty"` + StartTime *azuredevops.Time `json:"startTime,omitempty"` + State *TimelineRecordState `json:"state,omitempty"` + Task *TaskReference `json:"task,omitempty"` + Type *string `json:"type,omitempty"` + Variables *map[string]interface{} `json:"variables,omitempty"` + WarningCount *int `json:"warningCount,omitempty"` + WorkerName *string `json:"workerName,omitempty"` } type TimelineRecordFeedLinesWrapper struct { @@ -2550,11 +2550,11 @@ type VariableGroup struct { // Gets or sets name of the variable group. Name *string `json:"name,omitempty"` // Gets or sets provider data. - ProviderData *VariableGroupProviderData `json:"providerData,omitempty"` + ProviderData interface{} `json:"providerData,omitempty"` // Gets or sets type of the variable group. Type *string `json:"type,omitempty"` // Gets or sets variables contained in the variable group. - Variables *map[string]VariableValue `json:"variables,omitempty"` + Variables *map[string]interface{} `json:"variables,omitempty"` } // [Flags] @@ -2578,11 +2578,11 @@ type VariableGroupParameters struct { // Sets name of the variable group. Name *string `json:"name,omitempty"` // Sets provider data. - ProviderData *VariableGroupProviderData `json:"providerData,omitempty"` + ProviderData interface{} `json:"providerData,omitempty"` // Sets type of the variable group. Type *string `json:"type,omitempty"` // Sets variables contained in the variable group. - Variables *map[string]VariableValue `json:"variables,omitempty"` + Variables *map[string]interface{} `json:"variables,omitempty"` } // Defines provider data of the variable group. diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index aa1c2b95c..e0364e9e7 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -113,6 +113,17 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { return Error(t, err, append([]interface{}{msg}, args...)...) } +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) @@ -157,6 +168,31 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool return FileExists(t, path, append([]interface{}{msg}, args...)...) } +// Greaterf asserts that the first element is greater than the second +// +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Greater(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) +} + // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // @@ -289,6 +325,14 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) } +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // @@ -300,6 +344,31 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf return Len(t, object, length, append([]interface{}{msg}, args...)...) } +// Lessf asserts that the first element is less than the second +// +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) +// assert.Lessf(t, "a", "b", "error message %s", "formatted") +func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Less(t, e1, e2, append([]interface{}{msg}, args...)...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) +} + // Nilf asserts that the specified object is nil. // // assert.Nilf(t, err, "error message %s", "formatted") @@ -444,6 +513,19 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) } +// Samef asserts that two pointers reference the same object. +// +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Same(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index de39f794e..26830403a 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -215,6 +215,28 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { return Errorf(a.t, err, msg, args...) } +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Eventuallyf(a.t, condition, waitFor, tick, msg, args...) +} + // Exactly asserts that two objects are equal in value and type. // // a.Exactly(int32(123), int64(123)) @@ -303,6 +325,56 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b return FileExistsf(a.t, path, msg, args...) } +// Greater asserts that the first element is greater than the second +// +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") +func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Greater(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") +func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return GreaterOrEqualf(a.t, e1, e2, msg, args...) +} + +// Greaterf asserts that the first element is greater than the second +// +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) +// a.Greaterf("b", "a", "error message %s", "formatted") +func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Greaterf(a.t, e1, e2, msg, args...) +} + // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // @@ -567,6 +639,22 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. return JSONEqf(a.t, expected, actual, msg, args...) } +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return YAMLEqf(a.t, expected, actual, msg, args...) +} + // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // @@ -589,6 +677,56 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in return Lenf(a.t, object, length, msg, args...) } +// Less asserts that the first element is less than the second +// +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") +func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Less(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") +func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return LessOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return LessOrEqualf(a.t, e1, e2, msg, args...) +} + +// Lessf asserts that the first element is less than the second +// +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) +// a.Lessf("a", "b", "error message %s", "formatted") +func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Lessf(a.t, e1, e2, msg, args...) +} + // Nil asserts that the specified object is nil. // // a.Nil(err) @@ -877,6 +1015,32 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . return Regexpf(a.t, rx, str, msg, args...) } +// Same asserts that two pointers reference the same object. +// +// a.Same(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Same(a.t, expected, actual, msgAndArgs...) +} + +// Samef asserts that two pointers reference the same object. +// +// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Samef(a.t, expected, actual, msg, args...) +} + // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go new file mode 100644 index 000000000..15a486ca6 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -0,0 +1,309 @@ +package assert + +import ( + "fmt" + "reflect" +) + +func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { + switch kind { + case reflect.Int: + { + intobj1 := obj1.(int) + intobj2 := obj2.(int) + if intobj1 > intobj2 { + return -1, true + } + if intobj1 == intobj2 { + return 0, true + } + if intobj1 < intobj2 { + return 1, true + } + } + case reflect.Int8: + { + int8obj1 := obj1.(int8) + int8obj2 := obj2.(int8) + if int8obj1 > int8obj2 { + return -1, true + } + if int8obj1 == int8obj2 { + return 0, true + } + if int8obj1 < int8obj2 { + return 1, true + } + } + case reflect.Int16: + { + int16obj1 := obj1.(int16) + int16obj2 := obj2.(int16) + if int16obj1 > int16obj2 { + return -1, true + } + if int16obj1 == int16obj2 { + return 0, true + } + if int16obj1 < int16obj2 { + return 1, true + } + } + case reflect.Int32: + { + int32obj1 := obj1.(int32) + int32obj2 := obj2.(int32) + if int32obj1 > int32obj2 { + return -1, true + } + if int32obj1 == int32obj2 { + return 0, true + } + if int32obj1 < int32obj2 { + return 1, true + } + } + case reflect.Int64: + { + int64obj1 := obj1.(int64) + int64obj2 := obj2.(int64) + if int64obj1 > int64obj2 { + return -1, true + } + if int64obj1 == int64obj2 { + return 0, true + } + if int64obj1 < int64obj2 { + return 1, true + } + } + case reflect.Uint: + { + uintobj1 := obj1.(uint) + uintobj2 := obj2.(uint) + if uintobj1 > uintobj2 { + return -1, true + } + if uintobj1 == uintobj2 { + return 0, true + } + if uintobj1 < uintobj2 { + return 1, true + } + } + case reflect.Uint8: + { + uint8obj1 := obj1.(uint8) + uint8obj2 := obj2.(uint8) + if uint8obj1 > uint8obj2 { + return -1, true + } + if uint8obj1 == uint8obj2 { + return 0, true + } + if uint8obj1 < uint8obj2 { + return 1, true + } + } + case reflect.Uint16: + { + uint16obj1 := obj1.(uint16) + uint16obj2 := obj2.(uint16) + if uint16obj1 > uint16obj2 { + return -1, true + } + if uint16obj1 == uint16obj2 { + return 0, true + } + if uint16obj1 < uint16obj2 { + return 1, true + } + } + case reflect.Uint32: + { + uint32obj1 := obj1.(uint32) + uint32obj2 := obj2.(uint32) + if uint32obj1 > uint32obj2 { + return -1, true + } + if uint32obj1 == uint32obj2 { + return 0, true + } + if uint32obj1 < uint32obj2 { + return 1, true + } + } + case reflect.Uint64: + { + uint64obj1 := obj1.(uint64) + uint64obj2 := obj2.(uint64) + if uint64obj1 > uint64obj2 { + return -1, true + } + if uint64obj1 == uint64obj2 { + return 0, true + } + if uint64obj1 < uint64obj2 { + return 1, true + } + } + case reflect.Float32: + { + float32obj1 := obj1.(float32) + float32obj2 := obj2.(float32) + if float32obj1 > float32obj2 { + return -1, true + } + if float32obj1 == float32obj2 { + return 0, true + } + if float32obj1 < float32obj2 { + return 1, true + } + } + case reflect.Float64: + { + float64obj1 := obj1.(float64) + float64obj2 := obj2.(float64) + if float64obj1 > float64obj2 { + return -1, true + } + if float64obj1 == float64obj2 { + return 0, true + } + if float64obj1 < float64obj2 { + return 1, true + } + } + case reflect.String: + { + stringobj1 := obj1.(string) + stringobj2 := obj2.(string) + if stringobj1 > stringobj2 { + return -1, true + } + if stringobj1 == stringobj2 { + return 0, true + } + if stringobj1 < stringobj2 { + return 1, true + } + } + } + + return 0, false +} + +// Greater asserts that the first element is greater than the second +// +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") +func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != -1 { + return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") +func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != -1 && res != 0 { + return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} + +// Less asserts that the first element is less than the second +// +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") +func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != 1 { + return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") +func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + e1Kind := reflect.ValueOf(e1).Kind() + e2Kind := reflect.ValueOf(e2).Kind() + if e1Kind != e2Kind { + return Fail(t, "Elements should be the same type", msgAndArgs...) + } + + res, isComparable := compare(e1, e2, e1Kind) + if !isComparable { + return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) + } + + if res != 1 && res != 0 { + return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) + } + + return true +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 9bd4a80e4..044da8b01 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -18,6 +18,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" + yaml "gopkg.in/yaml.v2" ) //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl @@ -350,6 +351,37 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) } +// Same asserts that two pointers reference the same object. +// +// assert.Same(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual) + if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr { + return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...) + } + + expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual) + if expectedType != actualType { + return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v", + expectedType, actualType), msgAndArgs...) + } + + if expected != actual { + return Fail(t, fmt.Sprintf("Not same: \n"+ + "expected: %p %#v\n"+ + "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) + } + + return true +} + // formatUnequalValues takes two values of arbitrary types and returns string // representations appropriate to be presented to the user. // @@ -479,14 +511,14 @@ func isEmpty(object interface{}) bool { // collection types are empty when they have no element case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 - // pointers are empty if nil or if the value they point to is empty + // pointers are empty if nil or if the value they point to is empty case reflect.Ptr: if objValue.IsNil() { return true } deref := objValue.Elem().Interface() return isEmpty(deref) - // for all other types, compare against the zero value + // for all other types, compare against the zero value default: zero := reflect.Zero(objValue.Type()) return reflect.DeepEqual(object, zero.Interface()) @@ -629,7 +661,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ func includeElement(list interface{}, element interface{}) (ok, found bool) { listValue := reflect.ValueOf(list) - elementValue := reflect.ValueOf(element) + listKind := reflect.TypeOf(list).Kind() defer func() { if e := recover(); e != nil { ok = false @@ -637,11 +669,12 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) { } }() - if reflect.TypeOf(list).Kind() == reflect.String { + if listKind == reflect.String { + elementValue := reflect.ValueOf(element) return true, strings.Contains(listValue.String(), elementValue.String()) } - if reflect.TypeOf(list).Kind() == reflect.Map { + if listKind == reflect.Map { mapKeys := listValue.MapKeys() for i := 0; i < len(mapKeys); i++ { if ObjectsAreEqual(mapKeys[i].Interface(), element) { @@ -1337,6 +1370,24 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) } +// YAMLEq asserts that two YAML strings are equivalent. +func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + var expectedYAMLAsInterface, actualYAMLAsInterface interface{} + + if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) + } + + if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) + } + + return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) +} + func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { t := reflect.TypeOf(v) k := t.Kind() @@ -1371,8 +1422,8 @@ func diff(expected interface{}, actual interface{}) string { e = spewConfig.Sdump(expected) a = spewConfig.Sdump(actual) } else { - e = expected.(string) - a = actual.(string) + e = reflect.ValueOf(expected).String() + a = reflect.ValueOf(actual).String() } diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ @@ -1414,3 +1465,34 @@ var spewConfig = spew.ConfigState{ type tHelper interface { Helper() } + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + timer := time.NewTimer(waitFor) + ticker := time.NewTicker(tick) + checkPassed := make(chan bool) + defer timer.Stop() + defer ticker.Stop() + defer close(checkPassed) + for { + select { + case <-timer.C: + return Fail(t, "Condition never satisfied", msgAndArgs...) + case result := <-checkPassed: + if result { + return true + } + case <-ticker.C: + go func() { + checkPassed <- condition() + }() + } + } +} diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 535f29349..c5903f5db 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -14,23 +14,23 @@ import ( // Condition uses a Comparison to assert a complex condition. func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { - if assert.Condition(t, comp, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Condition(t, comp, msgAndArgs...) { + return + } t.FailNow() } // Conditionf uses a Comparison to assert a complex condition. func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) { - if assert.Conditionf(t, comp, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Conditionf(t, comp, msg, args...) { + return + } t.FailNow() } @@ -41,12 +41,12 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // assert.Contains(t, ["Hello", "World"], "World") // assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if assert.Contains(t, s, contains, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Contains(t, s, contains, msgAndArgs...) { + return + } t.FailNow() } @@ -57,34 +57,34 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") // assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { - if assert.Containsf(t, s, contains, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Containsf(t, s, contains, msg, args...) { + return + } t.FailNow() } // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { - if assert.DirExists(t, path, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.DirExists(t, path, msgAndArgs...) { + return + } t.FailNow() } // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { - if assert.DirExistsf(t, path, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.DirExistsf(t, path, msg, args...) { + return + } t.FailNow() } @@ -94,12 +94,12 @@ func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { // // assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { - if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { + return + } t.FailNow() } @@ -109,12 +109,12 @@ func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs // // assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { - if assert.ElementsMatchf(t, listA, listB, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.ElementsMatchf(t, listA, listB, msg, args...) { + return + } t.FailNow() } @@ -123,12 +123,12 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // // assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.Empty(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Empty(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -137,12 +137,12 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // // assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.Emptyf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Emptyf(t, object, msg, args...) { + return + } t.FailNow() } @@ -154,12 +154,12 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.Equal(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Equal(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -169,12 +169,12 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i // actualObj, err := SomeFunction() // assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { - if assert.EqualError(t, theError, errString, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualError(t, theError, errString, msgAndArgs...) { + return + } t.FailNow() } @@ -184,12 +184,12 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // actualObj, err := SomeFunction() // assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { - if assert.EqualErrorf(t, theError, errString, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualErrorf(t, theError, errString, msg, args...) { + return + } t.FailNow() } @@ -198,12 +198,12 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args // // assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.EqualValues(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualValues(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -212,12 +212,12 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg // // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.EqualValuesf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.EqualValuesf(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -229,12 +229,12 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.Equalf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Equalf(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -245,12 +245,12 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // assert.Equal(t, expectedError, err) // } func Error(t TestingT, err error, msgAndArgs ...interface{}) { - if assert.Error(t, err, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Error(t, err, msgAndArgs...) { + return + } t.FailNow() } @@ -261,9 +261,37 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) { // assert.Equal(t, expectedErrorf, err) // } func Errorf(t TestingT, err error, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.Errorf(t, err, msg, args...) { return } + t.FailNow() +} + +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { + return + } + if h, ok := t.(tHelper); ok { + h.Helper() + } + t.FailNow() +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { + return + } if h, ok := t.(tHelper); ok { h.Helper() } @@ -274,12 +302,12 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // // assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.Exactly(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Exactly(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -287,56 +315,56 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.Exactlyf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Exactlyf(t, expected, actual, msg, args...) { + return + } t.FailNow() } // Fail reports a failure through func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if assert.Fail(t, failureMessage, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Fail(t, failureMessage, msgAndArgs...) { + return + } t.FailNow() } // FailNow fails test func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if assert.FailNow(t, failureMessage, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.FailNow(t, failureMessage, msgAndArgs...) { + return + } t.FailNow() } // FailNowf fails test func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) { - if assert.FailNowf(t, failureMessage, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.FailNowf(t, failureMessage, msg, args...) { + return + } t.FailNow() } // Failf reports a failure through func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { - if assert.Failf(t, failureMessage, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Failf(t, failureMessage, msg, args...) { + return + } t.FailNow() } @@ -344,12 +372,12 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // // assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { - if assert.False(t, value, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.False(t, value, msgAndArgs...) { + return + } t.FailNow() } @@ -357,34 +385,96 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // // assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { - if assert.Falsef(t, value, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Falsef(t, value, msg, args...) { + return + } t.FailNow() } // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { - if assert.FileExists(t, path, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.FileExists(t, path, msgAndArgs...) { + return + } t.FailNow() } // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.FileExistsf(t, path, msg, args...) { return } + t.FailNow() +} + +// Greater asserts that the first element is greater than the second +// +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") +func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Greater(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") +func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) { + return + } + t.FailNow() +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.GreaterOrEqualf(t, e1, e2, msg, args...) { + return + } + t.FailNow() +} + +// Greaterf asserts that the first element is greater than the second +// +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Greaterf(t, e1, e2, msg, args...) { + return + } t.FailNow() } @@ -395,12 +485,12 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } t.FailNow() } @@ -411,12 +501,12 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { + return + } t.FailNow() } @@ -427,12 +517,12 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { + return + } t.FailNow() } @@ -443,12 +533,12 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { + return + } t.FailNow() } @@ -458,12 +548,12 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { + return + } t.FailNow() } @@ -473,12 +563,12 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { + return + } t.FailNow() } @@ -488,12 +578,12 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { + return + } t.FailNow() } @@ -503,12 +593,12 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { + return + } t.FailNow() } @@ -518,12 +608,12 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { + return + } t.FailNow() } @@ -533,12 +623,12 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { + return + } t.FailNow() } @@ -546,12 +636,12 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // // assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - if assert.Implements(t, interfaceObject, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Implements(t, interfaceObject, object, msgAndArgs...) { + return + } t.FailNow() } @@ -559,12 +649,12 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { - if assert.Implementsf(t, interfaceObject, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Implementsf(t, interfaceObject, object, msg, args...) { + return + } t.FailNow() } @@ -572,56 +662,56 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } // InDeltaSlice is the same as InDelta, except it compares two slices. func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } // InDeltaSlicef is the same as InDelta, except it compares two slices. func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } @@ -629,132 +719,216 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if assert.InDeltaf(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InDeltaf(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } // InEpsilon asserts that expected and actual have a relative error less than epsilon func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { + return + } t.FailNow() } // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { + return + } t.FailNow() } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { + return + } t.FailNow() } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { + return + } t.FailNow() } // IsType asserts that the specified objects are of the same type. func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { - if assert.IsType(t, expectedType, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.IsType(t, expectedType, object, msgAndArgs...) { + return + } t.FailNow() } // IsTypef asserts that the specified objects are of the same type. func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.IsTypef(t, expectedType, object, msg, args...) { return } + t.FailNow() +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.JSONEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// YAMLEq asserts that two YAML strings are equivalent. +func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEq(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.YAMLEqf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3) +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Len(t, object, length, msgAndArgs...) { + return + } + t.FailNow() +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Lenf(t, object, length, msg, args...) { + return + } t.FailNow() } -// JSONEq asserts that two JSON strings are equivalent. +// Less asserts that the first element is less than the second // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { - if assert.JSONEq(t, expected, actual, msgAndArgs...) { - return - } +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") +func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Less(t, e1, e2, msgAndArgs...) { + return + } t.FailNow() } -// JSONEqf asserts that two JSON strings are equivalent. +// LessOrEqual asserts that the first element is less than or equal to the second // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") -func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { - if assert.JSONEqf(t, expected, actual, msg, args...) { - return - } +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") +func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.LessOrEqual(t, e1, e2, msgAndArgs...) { + return + } t.FailNow() } -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. +// LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.Len(t, mySlice, 3) -func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { - if assert.Len(t, object, length, msgAndArgs...) { - return - } +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.LessOrEqualf(t, e1, e2, msg, args...) { + return + } t.FailNow() } -// Lenf asserts that the specified object has specific length. -// Lenf also fails if the object has a type that len() not accept. +// Lessf asserts that the first element is less than the second // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") -func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { - if assert.Lenf(t, object, length, msg, args...) { - return - } +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) +// assert.Lessf(t, "a", "b", "error message %s", "formatted") +func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Lessf(t, e1, e2, msg, args...) { + return + } t.FailNow() } @@ -762,12 +936,12 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // // assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.Nil(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Nil(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -775,12 +949,12 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // // assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.Nilf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Nilf(t, object, msg, args...) { + return + } t.FailNow() } @@ -791,12 +965,12 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { // assert.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { - if assert.NoError(t, err, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NoError(t, err, msgAndArgs...) { + return + } t.FailNow() } @@ -807,12 +981,12 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { // assert.Equal(t, expectedObj, actualObj) // } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { - if assert.NoErrorf(t, err, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NoErrorf(t, err, msg, args...) { + return + } t.FailNow() } @@ -823,12 +997,12 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { // assert.NotContains(t, ["Hello", "World"], "Earth") // assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if assert.NotContains(t, s, contains, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotContains(t, s, contains, msgAndArgs...) { + return + } t.FailNow() } @@ -839,12 +1013,12 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") // assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { - if assert.NotContainsf(t, s, contains, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotContainsf(t, s, contains, msg, args...) { + return + } t.FailNow() } @@ -855,12 +1029,12 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // assert.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.NotEmpty(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEmpty(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -871,12 +1045,12 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // assert.Equal(t, "two", obj[1]) // } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.NotEmptyf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEmptyf(t, object, msg, args...) { + return + } t.FailNow() } @@ -887,12 +1061,12 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if assert.NotEqual(t, expected, actual, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEqual(t, expected, actual, msgAndArgs...) { + return + } t.FailNow() } @@ -903,12 +1077,12 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if assert.NotEqualf(t, expected, actual, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotEqualf(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -916,12 +1090,12 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // // assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if assert.NotNil(t, object, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotNil(t, object, msgAndArgs...) { + return + } t.FailNow() } @@ -929,12 +1103,12 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // // assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { - if assert.NotNilf(t, object, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotNilf(t, object, msg, args...) { + return + } t.FailNow() } @@ -942,12 +1116,12 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // // assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if assert.NotPanics(t, f, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotPanics(t, f, msgAndArgs...) { + return + } t.FailNow() } @@ -955,12 +1129,12 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // // assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { - if assert.NotPanicsf(t, f, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotPanicsf(t, f, msg, args...) { + return + } t.FailNow() } @@ -969,12 +1143,12 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if assert.NotRegexp(t, rx, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotRegexp(t, rx, str, msgAndArgs...) { + return + } t.FailNow() } @@ -983,12 +1157,12 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { - if assert.NotRegexpf(t, rx, str, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotRegexpf(t, rx, str, msg, args...) { + return + } t.FailNow() } @@ -997,12 +1171,12 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // // assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if assert.NotSubset(t, list, subset, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotSubset(t, list, subset, msgAndArgs...) { + return + } t.FailNow() } @@ -1011,34 +1185,34 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { - if assert.NotSubsetf(t, list, subset, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotSubsetf(t, list, subset, msg, args...) { + return + } t.FailNow() } // NotZero asserts that i is not the zero value for its type. func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if assert.NotZero(t, i, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotZero(t, i, msgAndArgs...) { + return + } t.FailNow() } // NotZerof asserts that i is not the zero value for its type. func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { - if assert.NotZerof(t, i, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.NotZerof(t, i, msg, args...) { + return + } t.FailNow() } @@ -1046,12 +1220,12 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // // assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if assert.Panics(t, f, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Panics(t, f, msgAndArgs...) { + return + } t.FailNow() } @@ -1060,12 +1234,12 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // // assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { + return + } t.FailNow() } @@ -1074,12 +1248,12 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // // assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { - if assert.PanicsWithValuef(t, expected, f, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.PanicsWithValuef(t, expected, f, msg, args...) { + return + } t.FailNow() } @@ -1087,12 +1261,12 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // // assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { - if assert.Panicsf(t, f, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Panicsf(t, f, msg, args...) { + return + } t.FailNow() } @@ -1101,12 +1275,12 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if assert.Regexp(t, rx, str, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Regexp(t, rx, str, msgAndArgs...) { + return + } t.FailNow() } @@ -1115,12 +1289,44 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } if assert.Regexpf(t, rx, str, msg, args...) { return } + t.FailNow() +} + +// Same asserts that two pointers reference the same object. +// +// assert.Same(t, ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.Same(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// Samef asserts that two pointers reference the same object. +// +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Samef(t, expected, actual, msg, args...) { + return + } t.FailNow() } @@ -1129,12 +1335,12 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // // assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if assert.Subset(t, list, subset, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Subset(t, list, subset, msgAndArgs...) { + return + } t.FailNow() } @@ -1143,12 +1349,12 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte // // assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { - if assert.Subsetf(t, list, subset, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Subsetf(t, list, subset, msg, args...) { + return + } t.FailNow() } @@ -1156,12 +1362,12 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // // assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { - if assert.True(t, value, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.True(t, value, msgAndArgs...) { + return + } t.FailNow() } @@ -1169,12 +1375,12 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // // assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { - if assert.Truef(t, value, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Truef(t, value, msg, args...) { + return + } t.FailNow() } @@ -1182,12 +1388,12 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // // assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { - if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { + return + } t.FailNow() } @@ -1195,33 +1401,33 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // // assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { - if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { + return + } t.FailNow() } // Zero asserts that i is the zero value for its type. func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if assert.Zero(t, i, msgAndArgs...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Zero(t, i, msgAndArgs...) { + return + } t.FailNow() } // Zerof asserts that i is the zero value for its type. func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) { - if assert.Zerof(t, i, msg, args...) { - return - } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.Zerof(t, i, msg, args...) { + return + } t.FailNow() } diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl index 6ffc751b5..55e42ddeb 100644 --- a/vendor/github.com/stretchr/testify/require/require.go.tmpl +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -1,6 +1,6 @@ {{.Comment}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { - if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } if h, ok := t.(tHelper); ok { h.Helper() } + if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } t.FailNow() } diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 9fe41dbdc..804fae035 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -216,6 +216,28 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { Errorf(a.t, err, msg, args...) } +// Eventually asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventually(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// Eventuallyf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. +// +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Eventuallyf(a.t, condition, waitFor, tick, msg, args...) +} + // Exactly asserts that two objects are equal in value and type. // // a.Exactly(int32(123), int64(123)) @@ -304,6 +326,56 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { FileExistsf(a.t, path, msg, args...) } +// Greater asserts that the first element is greater than the second +// +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") +func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greater(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqual asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") +func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// GreaterOrEqualf asserts that the first element is greater than or equal to the second +// +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + GreaterOrEqualf(a.t, e1, e2, msg, args...) +} + +// Greaterf asserts that the first element is greater than the second +// +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) +// a.Greaterf("b", "a", "error message %s", "formatted") +func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Greaterf(a.t, e1, e2, msg, args...) +} + // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // @@ -568,6 +640,22 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. JSONEqf(a.t, expected, actual, msg, args...) } +// YAMLEq asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEq(a.t, expected, actual, msgAndArgs...) +} + +// YAMLEqf asserts that two YAML strings are equivalent. +func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + YAMLEqf(a.t, expected, actual, msg, args...) +} + // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // @@ -590,6 +678,56 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in Lenf(a.t, object, length, msg, args...) } +// Less asserts that the first element is less than the second +// +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") +func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Less(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqual asserts that the first element is less than or equal to the second +// +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") +func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqual(a.t, e1, e2, msgAndArgs...) +} + +// LessOrEqualf asserts that the first element is less than or equal to the second +// +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") +func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + LessOrEqualf(a.t, e1, e2, msg, args...) +} + +// Lessf asserts that the first element is less than the second +// +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) +// a.Lessf("a", "b", "error message %s", "formatted") +func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Lessf(a.t, e1, e2, msg, args...) +} + // Nil asserts that the specified object is nil. // // a.Nil(err) @@ -878,6 +1016,32 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . Regexpf(a.t, rx, str, msg, args...) } +// Same asserts that two pointers reference the same object. +// +// a.Same(ptr1, ptr2) +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Same(a.t, expected, actual, msgAndArgs...) +} + +// Samef asserts that two pointers reference the same object. +// +// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// +// Both arguments must be pointer variables. Pointer variable sameness is +// determined based on the equality of both type and value. +func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + Samef(a.t, expected, actual, msg, args...) +} + // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go index e4e56e28e..531087655 100644 --- a/vendor/gopkg.in/yaml.v2/decode.go +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -229,6 +229,10 @@ type decoder struct { mapType reflect.Type terrors []string strict bool + + decodeCount int + aliasCount int + aliasDepth int } var ( @@ -314,7 +318,39 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm return out, false, false } +const ( + // 400,000 decode operations is ~500kb of dense object declarations, or ~5kb of dense object declarations with 10000% alias expansion + alias_ratio_range_low = 400000 + // 4,000,000 decode operations is ~5MB of dense object declarations, or ~4.5MB of dense object declarations with 10% alias expansion + alias_ratio_range_high = 4000000 + // alias_ratio_range is the range over which we scale allowed alias ratios + alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) +) + +func allowedAliasRatio(decodeCount int) float64 { + switch { + case decodeCount <= alias_ratio_range_low: + // allow 99% to come from alias expansion for small-to-medium documents + return 0.99 + case decodeCount >= alias_ratio_range_high: + // allow 10% to come from alias expansion for very large documents + return 0.10 + default: + // scale smoothly from 99% down to 10% over the range. + // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. + // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). + return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) + } +} + func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { + d.decodeCount++ + if d.aliasDepth > 0 { + d.aliasCount++ + } + if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { + failf("document contains excessive aliasing") + } switch n.kind { case documentNode: return d.document(n, out) @@ -353,7 +389,9 @@ func (d *decoder) alias(n *node, out reflect.Value) (good bool) { failf("anchor '%s' value contains itself", n.value) } d.aliases[n] = true + d.aliasDepth++ good = d.unmarshal(n.alias, out) + d.aliasDepth-- delete(d.aliases, n) return good } diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go index 6c151db6f..4120e0c91 100644 --- a/vendor/gopkg.in/yaml.v2/resolve.go +++ b/vendor/gopkg.in/yaml.v2/resolve.go @@ -81,7 +81,7 @@ func resolvableTag(tag string) bool { return false } -var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) +var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) func resolve(tag string, in string) (rtag string, out interface{}) { if !resolvableTag(tag) { diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go index 077fd1dd2..570b8ecd1 100644 --- a/vendor/gopkg.in/yaml.v2/scannerc.go +++ b/vendor/gopkg.in/yaml.v2/scannerc.go @@ -906,6 +906,9 @@ func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { return true } +// max_flow_level limits the flow_level +const max_flow_level = 10000 + // Increase the flow level and resize the simple key list if needed. func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Reset the simple key on the next level. @@ -913,6 +916,11 @@ func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Increase the flow level. parser.flow_level++ + if parser.flow_level > max_flow_level { + return yaml_parser_set_scanner_error(parser, + "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_flow_level)) + } return true } @@ -925,6 +933,9 @@ func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { return true } +// max_indents limits the indents stack size +const max_indents = 10000 + // Push the current indentation level to the stack and set the new level // the current column is greater than the indentation level. In this case, // append or insert the specified token into the token queue. @@ -939,6 +950,11 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml // indentation level. parser.indents = append(parser.indents, parser.indent) parser.indent = column + if len(parser.indents) > max_indents { + return yaml_parser_set_scanner_error(parser, + "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark, + fmt.Sprintf("exceeded max depth of %d", max_indents)) + } // Create a token and insert it into the queue. token := yaml_token_t{ diff --git a/vendor/modules.txt b/vendor/modules.txt index 5bf66f8a9..7772dc3a9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -193,7 +193,7 @@ github.com/jmespath/go-jmespath github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.5 github.com/mattn/go-isatty -# github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b2 +# github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b3 github.com/microsoft/azure-devops-go-api/azuredevops github.com/microsoft/azure-devops-go-api/azuredevops/accounts github.com/microsoft/azure-devops-go-api/azuredevops/build @@ -244,7 +244,7 @@ github.com/posener/complete/match # github.com/spf13/afero v1.2.2 github.com/spf13/afero github.com/spf13/afero/mem -# github.com/stretchr/testify v1.3.0 +# github.com/stretchr/testify v1.4.0 github.com/stretchr/testify/assert github.com/stretchr/testify/require # github.com/ulikunitz/xz v0.5.5 @@ -392,5 +392,5 @@ google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap google.golang.org/grpc/test/bufconn -# gopkg.in/yaml.v2 v2.2.2 +# gopkg.in/yaml.v2 v2.2.4 gopkg.in/yaml.v2