FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: add MockNow option for deterministic now() in tests by ChrisJr404 · Pull Request #986 · expr-lang/expr · GitHub

/ expr Public
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .go  (3) .md  (1) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
14 changes: 14 additions & 0 deletions docs/configuration.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,17 @@ The timezone is used for the following functions:
date("2024-11-23 12:00:00") // parses the date in the specified timezone
now() // returns the current time in the specified timezone
```

## Mock now()

Expressions that depend on `now()` are hard to test because the result changes
on every run. The [`MockNow`](https://pkg.go.dev/github.com/expr-lang/expr#MockNow)
option replaces `now()` with a fixed time, so the compiled program always
returns the same value.

```go
program, err := expr.Compile(code, expr.MockNow(time.Date(2024, 11, 23, 12, 0, 0, 0, time.UTC)))
```

`MockNow` can be combined with [`Timezone`](#timezone) to return the fixed time
in the specified timezone.
10 changes: 10 additions & 0 deletions expr.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ func Timezone(name string) Option {
})
}

// MockNow replaces now() calls with the given time. This is useful to get
// deterministic results when testing expressions that depend on now(). The
// replacement happens at compile time, so the compiled program always returns
// the given time from now().
func MockNow(t time.Time) Option {
return Patch(patcher.WithNow{
Now: t,
})
}

// MaxNodes sets the maximum number of nodes allowed in the expression.
// By default, the maximum number of nodes is conf.DefaultMaxNodes.
// If MaxNodes is set to 0, the node budget check is disabled.
Expand Down
35 changes: 35 additions & 0 deletions patcher/with_now.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package patcher

import (
"time"

"github.com/expr-lang/expr/ast"
)

// WithNow replaces now() calls with a constant time. This is useful to get
// deterministic results when testing expressions that depend on the current
// time. The replacement happens while patching the AST, so the compiled
// program always returns the same time from now().
type WithNow struct {
Now time.Time
}

func (p WithNow) Visit(node *ast.Node) {
btin, ok := (*node).(*ast.BuiltinNode)
if !ok || btin.Name != "now" {
return
}
now := p.Now
// Preserve the location injected by WithTimezone, if any, so that
// Timezone and MockNow can be combined.
for _, arg := range btin.Arguments {
c, ok := arg.(*ast.ConstantNode)
if !ok {
continue
}
if loc, ok := c.Value.(*time.Location); ok {
now = now.In(loc)
}
}
ast.Patch(node, &ast.ConstantNode{Value: now})
}
44 changes: 44 additions & 0 deletions patcher/with_now_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package patcher_test

import (
"testing"
"time"

"github.com/expr-lang/expr/internal/testify/require"

"github.com/expr-lang/expr"
)

func TestWithNow(t *testing.T) {
now := time.Date(2024, 5, 7, 23, 0, 0, 0, time.UTC)

program, err := expr.Compile(`now()`, expr.MockNow(now))
require.NoError(t, err)

out, err := expr.Run(program, nil)
require.NoError(t, err)
require.Equal(t, now, out)
}

func TestWithNow_is_deterministic(t *testing.T) {
now := time.Date(2024, 5, 7, 23, 0, 0, 0, time.UTC)

program, err := expr.Compile(`now().Year()`, expr.MockNow(now))
require.NoError(t, err)

out, err := expr.Run(program, nil)
require.NoError(t, err)
require.Equal(t, 2024, out)
}

func TestWithNow_combined_with_timezone(t *testing.T) {
now := time.Date(2024, 5, 7, 23, 0, 0, 0, time.UTC)

program, err := expr.Compile(`now()`, expr.Timezone("Asia/Kamchatka"), expr.MockNow(now))
require.NoError(t, err)

out, err := expr.Run(program, nil)
require.NoError(t, err)
require.Equal(t, "Asia/Kamchatka", out.(time.Time).Location().String())
require.True(t, out.(time.Time).Equal(now))
}

Back | FazBrowse Home | New Git URL