Repository navigation
functions: Raise errors when +/- Infinity is returned from log or pow built-in functions
#39304
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| kind: BUG FIXES | ||
| body: 'funcs: The `log` and `pow` functions now raise errors when they return +/- Infinity, as this result is not usable in downstream code.' | ||
| time: 2026-09-29T16:51:30.773193+01:00 | ||
| custom: | ||
| Issue: "39304" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,146 +10,139 @@ import ( | |
| "github.com/zclconf/go-cty/cty" | ||
| ) | ||
|
|
||
| func TestLog(t *testing.T) { | ||
| func TestLog_valid(t *testing.T) { | ||
| tests := []struct { | ||
| Num cty.Value | ||
| Base cty.Value | ||
| Want cty.Value | ||
| Err bool | ||
| }{ | ||
| { | ||
| cty.NumberFloatVal(1), | ||
| cty.NumberFloatVal(10), | ||
| cty.NumberFloatVal(0), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(10), | ||
| cty.NumberFloatVal(10), | ||
| cty.NumberFloatVal(1), | ||
| false, | ||
| }, | ||
|
|
||
| { | ||
| cty.NumberFloatVal(0), | ||
| cty.NumberFloatVal(10), | ||
| cty.NegativeInfinity, | ||
| false, | ||
|
Comment on lines
-34
to
-37
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This old test case shows infinity being returned without an error, but I don't think this was part of an explicit decision to state that infinite values were valid returned values so we shouldn't be held back by it. For me, the fact downstream code cannot process this value is a clear sign that raising an error instead is the correct behaviour. |
||
| }, | ||
| { | ||
| cty.NumberFloatVal(10), | ||
| cty.NumberFloatVal(0), | ||
| cty.NumberFloatVal(-0), | ||
| false, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(fmt.Sprintf("log(%#v, %#v)", test.Num, test.Base), func(t *testing.T) { | ||
| got, err := LogFunc.Call([]cty.Value{test.Num, test.Base}) | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %s", err) | ||
| } | ||
|
|
||
| if !got.RawEquals(test.Want) { | ||
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestLog_invalid(t *testing.T) { | ||
| tests := []struct { | ||
| Num cty.Value | ||
| Base cty.Value | ||
| }{ | ||
| { | ||
| // NaN result | ||
| cty.NumberFloatVal(1), | ||
| cty.NumberFloatVal(1), | ||
| cty.NumberFloatVal(1), | ||
| true, | ||
| }, | ||
| { | ||
| // NaN result | ||
| cty.NumberFloatVal(-1), | ||
| cty.NumberFloatVal(10), | ||
| cty.NumberFloatVal(1), | ||
| true, | ||
| }, | ||
| { | ||
| // Negative infinity result | ||
| cty.NumberFloatVal(0), | ||
| cty.NumberFloatVal(10), | ||
| }, | ||
| { | ||
| // Positive infinity result | ||
| cty.PositiveInfinity, | ||
| cty.NumberFloatVal(2), | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(fmt.Sprintf("log(%#v, %#v)", test.Num, test.Base), func(t *testing.T) { | ||
| got, err := LogFunc.Call([]cty.Value{test.Num, test.Base}) | ||
| _, err := LogFunc.Call([]cty.Value{test.Num, test.Base}) | ||
|
|
||
| if test.Err { | ||
| if err == nil { | ||
| t.Fatal("succeeded; want error") | ||
| } | ||
| return | ||
| } else if err != nil { | ||
| t.Fatalf("unexpected error: %s", err) | ||
| if err == nil { | ||
| t.Fatal("expected error but got none") | ||
| } | ||
|
|
||
| if !got.RawEquals(test.Want) { | ||
| t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) | ||
| expectedErr := "result is not a number" | ||
| if err.Error() != expectedErr { | ||
| t.Fatal(err) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestPow(t *testing.T) { | ||
| func TestPow_valid(t *testing.T) { | ||
| tests := []struct { | ||
| Num cty.Value | ||
| Power cty.Value | ||
| Want cty.Value | ||
| Err bool | ||
| }{ | ||
| { | ||
| cty.NumberFloatVal(1), | ||
| cty.NumberFloatVal(0), | ||
| cty.NumberFloatVal(1), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(1), | ||
| cty.NumberFloatVal(1), | ||
| cty.NumberFloatVal(1), | ||
| false, | ||
| }, | ||
|
|
||
| { | ||
| cty.NumberFloatVal(2), | ||
| cty.NumberFloatVal(0), | ||
| cty.NumberFloatVal(1), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(2), | ||
| cty.NumberFloatVal(1), | ||
| cty.NumberFloatVal(2), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(3), | ||
| cty.NumberFloatVal(2), | ||
| cty.NumberFloatVal(9), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(-3), | ||
| cty.NumberFloatVal(2), | ||
| cty.NumberFloatVal(9), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(2), | ||
| cty.NumberFloatVal(-2), | ||
| cty.NumberFloatVal(0.25), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(0), | ||
| cty.NumberFloatVal(2), | ||
| cty.NumberFloatVal(0), | ||
| false, | ||
| }, | ||
| { | ||
| cty.NumberFloatVal(-2), | ||
| cty.NumberFloatVal(0.5), | ||
| cty.NumberFloatVal(0), | ||
| true, | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(fmt.Sprintf("pow(%#v, %#v)", test.Num, test.Power), func(t *testing.T) { | ||
| got, err := PowFunc.Call([]cty.Value{test.Num, test.Power}) | ||
|
|
||
| if test.Err { | ||
| if err == nil { | ||
| t.Fatal("succeeded; want error") | ||
| } | ||
| return | ||
| } else if err != nil { | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %s", err) | ||
| } | ||
|
|
||
|
|
@@ -159,3 +152,40 @@ func TestPow(t *testing.T) { | |
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestPow_invalid(t *testing.T) { | ||
| tests := []struct { | ||
| Num cty.Value | ||
| Power cty.Value | ||
| }{ | ||
| { | ||
| // NaN result | ||
| cty.NumberFloatVal(-2), | ||
| cty.NumberFloatVal(0.5), | ||
| }, | ||
| { | ||
| // Positive Infinity result | ||
| cty.NumberFloatVal(10), | ||
| cty.NumberFloatVal(1000), | ||
| }, | ||
| { | ||
| // Negative Infinity result | ||
| cty.NegativeInfinity, | ||
| cty.NumberFloatVal(1), | ||
| }, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| t.Run(fmt.Sprintf("pow(%#v, %#v)", test.Num, test.Power), func(t *testing.T) { | ||
| _, err := PowFunc.Call([]cty.Value{test.Num, test.Power}) | ||
| if err == nil { | ||
| t.Fatal("succeeded; want error") | ||
| } | ||
|
|
||
| expectedErr := "result is not a number" | ||
| if err.Error() != expectedErr { | ||
| t.Fatal(err) | ||
| } | ||
| }) | ||
| } | ||
| } | ||


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.
Reviewer: Should +/- infinity return a different error? I could update this to return a different
fmt.Errorf("result is not a finite number")or something.