Skip to content

Commit

Permalink
🐛 parse time formats case-insensitive (#3265)
Browse files Browse the repository at this point in the history
* 🐛 parse time formats case-insensitive

* 🧹 handle case where compiler cannot check the args type for parse.date
  • Loading branch information
chris-rock authored Feb 11, 2024
1 parent 2defef5 commit 06b62cd
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 3 deletions.
15 changes: 12 additions & 3 deletions llx/builtin_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,25 @@ func resourceDateV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (
return nil, rref, err
}

timestamp, ok := args[0].(string)
if !ok {
return nil, 0, errors.New("failed to parse time, timestamp needs to be a string")
}

var format string
if len(args) >= 2 {
format = args[1].(string)
format, ok = args[1].(string)
if !ok {
return nil, 0, errors.New("provided time format is not provided as string")
}
format = strings.ToLower(format)
if f, ok := timeFormats[format]; ok {
format = f
}
}

if format != "" {
parsed, err := time.Parse(format, args[0].(string))
parsed, err := time.Parse(format, timestamp)
if err != nil {
return nil, 0, errors.New("failed to parse time: " + err.Error())
}
Expand All @@ -275,7 +284,7 @@ func resourceDateV2(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (
// Note: Yes, this approach is much slower than giving us a hint
// about which time format is used.
for _, format := range defaultTimeFormatsOrder {
parsed, err := time.Parse(format, args[0].(string))
parsed, err := time.Parse(format, timestamp)
if err != nil {
continue
}
Expand Down
25 changes: 25 additions & 0 deletions providers-sdk/v1/testutils/testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ func (ctx *tester) ExecuteCode(bundle *llx.CodeBundle, props map[string]*llx.Pri
return mql.ExecuteCode(ctx.Runtime, bundle, props, Features)
}

func (ctx *tester) TestQueryPWithError(t *testing.T, query string, props map[string]*llx.Primitive) ([]*llx.RawResult, error) {
t.Helper()
bundle, err := mqlc.Compile(query, props, mqlc.NewConfig(ctx.Runtime.Schema(), Features))
if err != nil {
return nil, fmt.Errorf("failed to compile code: %w", err)
}
err = mqlc.Invariants.Check(bundle)
if err != nil {
return nil, fmt.Errorf("failed to check invariants: %w", err)
}
return ctx.TestMqlc(t, bundle, props), nil
}

func (ctx *tester) TestQueryP(t *testing.T, query string, props map[string]*llx.Primitive) []*llx.RawResult {
t.Helper()
bundle, err := mqlc.Compile(query, props, mqlc.NewConfig(ctx.Runtime.Schema(), Features))
Expand Down Expand Up @@ -358,6 +371,18 @@ func (ctx *tester) TestSimpleErrors(t *testing.T, tests []SimpleTest) {
}
}

func (ctx *tester) TestCompileErrors(t *testing.T, tests []SimpleTest) {
for i := range tests {
cur := tests[i]
t.Run(cur.Code, func(t *testing.T) {
res := ctx.TestQuery(t, cur.Code)
assert.NotEmpty(t, res)
assert.Equal(t, cur.Expectation, res[cur.ResultIndex].Result().Error)
assert.Nil(t, res[cur.ResultIndex].Data.Value)
})
}
}

func TestNoResultErrors(t *testing.T, r []*llx.RawResult) bool {
var found bool
for i := range r {
Expand Down
50 changes: 50 additions & 0 deletions providers/core/resources/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"go.mondoo.com/cnquery/v10/llx"
"go.mondoo.com/cnquery/v10/providers-sdk/v1/testutils"
)
Expand All @@ -33,9 +34,58 @@ func TestParse_Date(t *testing.T) {
ResultIndex: 0,
Expectation: &simpleDate,
},
{
Code: "parse.date('2023-12-23T00:00:00Z', 'rfc3339')",
ResultIndex: 0,
Expectation: &simpleDate,
},
{
Code: "parse.date('2023-12-23T00:00:00Z', 'RFC3339')", // ensure the format finding is case-insensitive
ResultIndex: 0,
Expectation: &simpleDate,
},
})
}

func TestParse_DateError(t *testing.T) {
tests := []testutils.SimpleTest{
{
Code: "parse.date('2023-12-23T00:00:00Z', 1234)", // handle invalid format
ResultIndex: 0,
Expectation: "failed to compile code: Incorrect type on argument 1 in parse.date: expected string, got: int",
},
{
Code: "parse.date(123456)", // handle invalid timestamp format
ResultIndex: 0,
Expectation: "failed to compile code: Incorrect type on argument 0 in parse.date: expected string, got: int",
},
{
Code: "dict = { 'x': 'y'}; parse.date(dict)", // handle invalid input that is a dict
ResultIndex: 0,
Expectation: "failed to compile code: Incorrect type on argument 0 in parse.date: expected string, got: map[string]string",
},
{
Code: "parse.date(mondoo.jobEnvironment)", // handle invalid input that is a dict and cannot be handled during compile time
ResultIndex: 0,
Expectation: "failed to parse time, timestamp needs to be a string",
},
}
for i := range tests {
cur := tests[i]
t.Run(cur.Code, func(t *testing.T) {
res, err := x.TestQueryPWithError(t, cur.Code, nil)
var errMsg string
if err != nil {
errMsg = err.Error()
}
if res != nil && res[0].Data.Error != nil {
errMsg = res[0].Data.Error.Error()
}
assert.Equal(t, cur.Expectation, errMsg)
})
}
}

func TestParse_Duration(t *testing.T) {
twoSecs := llx.DurationToTime(2)
tenMin := llx.DurationToTime(10 * 60)
Expand Down

0 comments on commit 06b62cd

Please sign in to comment.