|
9 | 9 | "testing"
|
10 | 10 |
|
11 | 11 | "github.com/apparentlymart/go-dump/dump"
|
| 12 | + "github.com/google/go-cmp/cmp" |
| 13 | + "github.com/zclconf/go-cty-debug/ctydebug" |
12 | 14 | "github.com/zclconf/go-cty/cty"
|
13 | 15 |
|
14 | 16 | "github.com/hashicorp/hcl/v2"
|
@@ -210,3 +212,72 @@ foo = "invalid"
|
210 | 212 | })
|
211 | 213 | }
|
212 | 214 | }
|
| 215 | + |
| 216 | +func TestRefineValueSpec(t *testing.T) { |
| 217 | + config := ` |
| 218 | +foo = "hello" |
| 219 | +bar = unk |
| 220 | +` |
| 221 | + |
| 222 | + f, diags := hclsyntax.ParseConfig([]byte(config), "", hcl.InitialPos) |
| 223 | + if diags.HasErrors() { |
| 224 | + t.Fatal(diags.Error()) |
| 225 | + } |
| 226 | + |
| 227 | + attrSpec := func(name string) Spec { |
| 228 | + return &RefineValueSpec{ |
| 229 | + // RefineValueSpec should typically have a ValidateSpec wrapped |
| 230 | + // inside it to catch any values that are outside of the required |
| 231 | + // range and return a helpful error message about it. In this |
| 232 | + // case our refinement is .NotNull so the validation function |
| 233 | + // must reject null values. |
| 234 | + Wrapped: &ValidateSpec{ |
| 235 | + Wrapped: &AttrSpec{ |
| 236 | + Name: name, |
| 237 | + Required: true, |
| 238 | + Type: cty.String, |
| 239 | + }, |
| 240 | + Func: func(value cty.Value) hcl.Diagnostics { |
| 241 | + var diags hcl.Diagnostics |
| 242 | + if value.IsNull() { |
| 243 | + diags = diags.Append(&hcl.Diagnostic{ |
| 244 | + Severity: hcl.DiagError, |
| 245 | + Summary: "Cannot be null", |
| 246 | + Detail: "Argument is required.", |
| 247 | + }) |
| 248 | + } |
| 249 | + return diags |
| 250 | + }, |
| 251 | + }, |
| 252 | + Refine: func(rb *cty.RefinementBuilder) *cty.RefinementBuilder { |
| 253 | + return rb.NotNull() |
| 254 | + }, |
| 255 | + } |
| 256 | + } |
| 257 | + spec := &ObjectSpec{ |
| 258 | + "foo": attrSpec("foo"), |
| 259 | + "bar": attrSpec("bar"), |
| 260 | + } |
| 261 | + |
| 262 | + got, diags := Decode(f.Body, spec, &hcl.EvalContext{ |
| 263 | + Variables: map[string]cty.Value{ |
| 264 | + "unk": cty.UnknownVal(cty.String), |
| 265 | + }, |
| 266 | + }) |
| 267 | + if diags.HasErrors() { |
| 268 | + t.Fatal(diags.Error()) |
| 269 | + } |
| 270 | + |
| 271 | + want := cty.ObjectVal(map[string]cty.Value{ |
| 272 | + // This argument had a known value, so it's unchanged but the |
| 273 | + // RefineValueSpec still checks that it isn't null to catch |
| 274 | + // bugs in the application's validation function. |
| 275 | + "foo": cty.StringVal("hello"), |
| 276 | + |
| 277 | + // The final value of bar is unknown but refined as non-null. |
| 278 | + "bar": cty.UnknownVal(cty.String).RefineNotNull(), |
| 279 | + }) |
| 280 | + if diff := cmp.Diff(want, got, ctydebug.CmpOptions); diff != "" { |
| 281 | + t.Errorf("wrong result\n%s", diff) |
| 282 | + } |
| 283 | +} |
0 commit comments