-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementing expect unknown and null output value plan checks #220
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this looks great! I've added minor comments mostly around documentation.
More generally, I think we should come up with common language to differentiate between attribute state and attribute output/value. Especially with expectUnknownValue()
and expectUnknownOutputValue()
where the language is a bit too similar. Maybe we can describe expectUnknownValue()
and expectSensitiveValue()
as "asserting that an attribute is being marked as unknown/sensitive."
} | ||
|
||
case SliceStep: | ||
steps = append(steps, fmt.Sprint(s)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a use case for using fmt.Sprint(s)
instead of string(s)
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fmt.Sprint(s)
is used as golangci-lint
raises the following issue if string(s)
is used:
Error: stringintconv: conversion from SliceStep (int) to string yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?) (govet)
} | ||
} | ||
|
||
// OutputValueParams is used during the creation of a plan check for output values, and specifies |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it better to move this information to the website documentation since it's directly exposed to provider developers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We generally prefer to document terraform-plugin-* functionality that is exposed to developers via both Go package documentation and website documentation. The Go package documentation shows up on https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing as well as code editor integrations, such as the Go extension for VS Code, meaning that we can reduce developers need to context switch to a web browser while writing code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the documentation for OutputValueParams
to the website docs. It now appears in both the website docs and in the Go package docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general this is getting there -- just some larger things to potentially discuss/handle before getting into smaller implementation details.
} | ||
} | ||
|
||
// OutputValueParams is used during the creation of a plan check for output values, and specifies |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We generally prefer to document terraform-plugin-* functionality that is exposed to developers via both Go package documentation and website documentation. The Go package documentation shows up on https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing as well as code editor integrations, such as the Go extension for VS Code, meaning that we can reduce developers need to context switch to a web browser while writing code.
type OutputValueParams struct { | ||
OutputAddress string | ||
AttributePath tfjsonpath.Path | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👋 I think it might be good to discuss whether a separate struct is necessary or if we should expose separate functions instead (one without path, one with path). At the very least we should likely call the path information Path
or ValuePath
-- attribute paths don't exist within outputs since an output is a value. That value may have came from a resource with attributes, but that's not guaranteed nor the only valid usage for complex output values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated to ValuePath
for now, but let's discuss potentially splitting into separate functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following out-of-band discussion, the Expect<Null|Unknown>OutputValue
funcs have been split into:
Expect<Null|Unknown>OutputValue
Expect<Null|Unknown>OutputValueAtPath
| [`plancheck.ExpectResourceAction(address, operation)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectResourceAction) | Asserts the given resource has the specified operation for apply. | | ||
| [`plancheck.ExpectUnknownValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectUnknownValue) | Asserts the specified attribute at the given resource has an unknown value. | | ||
| [`plancheck.ExpectSensitiveValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectSensitiveValue) | Asserts the specified attribute at the given resource has a sensitive value. | | ||
| Check | Description | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be good to split up this table (and maybe the page) to be resource attribute checks versus output value checks to reduce potential confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have split the page into Plan Checks - Overview, Resource, Output, and Custom.
The docs for ExpectEmptyPlan
and ExpectNonEmptyPlan
currently appear on the Resource page. Perhaps these should be on a separate page or on the Overview page?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To answer your question directly -- I think choosing the atypical option of documenting those on the Overview page is okay here since they should theoretically apply to the whole plan. ExpectEmptyPlan
/ExpectNonEmptyPlan
currently only check resource changes, not output changes, at the moment though. I'm guessing that most developers would also expect them to also account for other/all plan changes given the naming, so I'm going to file a bug report to adjust the existing implementation. I think if developers need more granular "only no resource changes" then we can introduce general resource vs output (non-)empty plan checks when requested. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also ❤️ for thinking ahead to just split up the pages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have added a General Plan Checks
section to the Overview page to include plan checks for ExpectEmptyPlan
/ExpectNonEmptyPlan
.
Co-authored-by: Selena Goods <selena.goods@hashicorp.com>
I've modified the wording on
Do you think this provides enough distinction/clarity? |
…Unknown>OutputValue and Expect<Null|Unknown>OutputValueAtPath (#219)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, this is wonderful. Looks good to me 🚀
}, | ||
Steps: []r.TestStep{ | ||
{ | ||
Config: `resource "time_static" "one" {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 If we want, we can skip using a real provider and either implement code-based provider or maybe preferably use the terraform_data
resource (with requisite Terraform 1.4+ version checking) as its output
attribute is automatically an unknown value of input
. I personally think its okay to use terraform_data
and skip the test on older Terraform versions just to simplify and speed up this testing. If there happened to be an earlier Terraform version bug with its plan output, it would not be fixed at this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, this is fun. We validate things a little too well currently if you fully omit referencing some provider in the test case/step configuration.
Test validation error: TestStep 1/2 validation error: Providers must be specified at the TestCase level, or in all TestStep, or in TestStep.ConfigDirectory or TestStep.ConfigFile
I'll create a separate issue to see if we can make it possible to create testing with just the built-in terraform
provider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ha -- found a workaround based on a special provider discovery rule:
ExternalProviders: map[string]resource.ExternalProvider{
"terraform": {Source: "terraform.io/builtin/terraform"},
},
That works as expected although it calls terraform init
unnecessarily in this very specific case -- I'll look to see if it makes sense to potentially have the init
calling logic skip if terraform.io/builtin/terraform
is the only external provider and source.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I've replaced the usage of the time provider with the built-in terraform_data resource in all of the ExpectUnknownOutputValue
, and ExpectUnknownOutputValueAtPath
plan check tests.
- Available plan phases for **Refresh** mode are defined in the [`TestStep.RefreshPlanChecks`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/helper/resource#TestStep) struct | ||
- **Import** mode currently does not run any plan operations, and therefore does not support plan checks. | ||
|
||
There are built-in [resource plan checks](/terraform/plugin/testing/acceptance-tests/plan-checks/resource), and [output plan checks](/terraform/plugin/testing/acceptance-tests/plan-checks/output). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource is definitely the correct word to use for both managed resources and data resources (colloquially called data sources), however I wonder if we should explicitly mention "managed resources and data resources" when we have textual room so folks aren't confused about how to test data sources?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the Overview and Resource pages to explicitly mention "managed resources, and data sources". I went with "data sources" rather than "data resources" as it seems likely that the former might be more familiar terminology given the documentation on the terraform language for Data Sources page. Does that seem ok?
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions. |
Closes: #219