Skip to content
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

implement assert.not_state assertion function #403

Merged
merged 1 commit into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ We describe them following table and examples:
| assert.not_subroutine_called | FUNCTION | Assert subroutine has not called in testing subroutine |
| assert.restart | FUNCTION | Assert restart statement has called |
| assert.state | FUNCTION | Assert after state is expected one |
| assert.not_state | FUNCTION | Assert after state is not expected one |
| assert.error | FUNCTION | Assert error status code (and response) if error statement has called |
| assert.not_error | FUNCTION | Assert runtime state will not move to error status |

Expand Down Expand Up @@ -873,6 +874,22 @@ sub test_vcl {

----

### assert.not_state(ID state [, STRING message])

Assert current state is not expected one.

```vcl
sub test_vcl {
// vcl_recv will move state to lookup to lookup cache
testing.call_subroutine("vcl_recv");

// Assert state does not move to lookup
assert.not_state(lookup);
}
```

----

### assert.error(INTEGER status [, STRING response, STRING message])

Assert error status code (and response) if error statement has called.
Expand Down
51 changes: 51 additions & 0 deletions tester/function/assert_not_state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package function

import (
"fmt"

"github.com/ysugimoto/falco/interpreter"
"github.com/ysugimoto/falco/interpreter/context"
"github.com/ysugimoto/falco/interpreter/function/errors"
"github.com/ysugimoto/falco/interpreter/value"
)

const Assert_not_state_Name = "assert.not_state"

var Assert_not_state_ArgumentTypes = []value.Type{value.IdentType}

func Assert_not_state_Validate(args []value.Value) error {
if len(args) > 2 {
return errors.ArgumentNotInRange(Assert_not_state_Name, 1, 2, args)
}

for i := range Assert_not_state_ArgumentTypes {
if args[i].Type() != Assert_not_state_ArgumentTypes[i] {
return errors.TypeMismatch(Assert_not_state_Name, i+1, Assert_not_state_ArgumentTypes[i], args[i].Type())
}
}

return nil
}

func Assert_not_state(
ctx *context.Context,
i *interpreter.Interpreter,
args ...value.Value,
) (value.Value, error) {

if err := Assert_not_state_Validate(args); err != nil {
return nil, errors.NewTestingError("%s", err.Error())
}

state := value.Unwrap[*value.Ident](args[0])
expect := interpreter.StateFromString(state.Value)

var message string
if len(args) == 2 {
message = value.Unwrap[*value.String](args[0]).Value
} else {
message = fmt.Sprintf("state should not be %s", expect)
}

return assert_not(state, expect.String(), i.TestingState.String(), message)
}
58 changes: 58 additions & 0 deletions tester/function/assert_not_state_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package function

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/ysugimoto/falco/interpreter"
"github.com/ysugimoto/falco/interpreter/context"
"github.com/ysugimoto/falco/interpreter/function/errors"
"github.com/ysugimoto/falco/interpreter/value"
)

func Test_Assert_not_state(t *testing.T) {

tests := []struct {
args []value.Value
ip *interpreter.Interpreter
err error
expect *value.Boolean
}{
{
args: []value.Value{
&value.Ident{Value: "error"},
},
ip: &interpreter.Interpreter{
TestingState: interpreter.ERROR,
},
expect: &value.Boolean{Value: false},
err: &errors.AssertionError{},
},
{
args: []value.Value{
&value.Ident{Value: "error"},
},
ip: &interpreter.Interpreter{
TestingState: interpreter.LOOKUP,
},
expect: &value.Boolean{Value: true},
},
}

for i := range tests {
_, err := Assert_not_state(
&context.Context{},
tests[i].ip,
tests[i].args...,
)
if diff := cmp.Diff(
tests[i].err,
err,
cmpopts.IgnoreFields(errors.AssertionError{}, "Message", "Actual"),
cmpopts.IgnoreFields(errors.TestingError{}, "Message"),
); diff != "" {
t.Errorf("Assert_not_state()[%d] error: diff=%s", i, diff)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,26 @@ func assertionFunctions(i *interpreter.Interpreter, c Counter) Functions {
return false
},
},
"assert.not_state": {
Scope: allScope,
Call: func(ctx *context.Context, args ...value.Value) (value.Value, error) {
unwrapped, err := unwrapIdentArguments(i, args)
if err != nil {
return value.Null, errors.WithStack(err)
}
v, err := Assert_not_state(ctx, i, unwrapped...)
if err != nil {
c.Fail()
} else {
c.Pass()
}
return v, err
},
CanStatementCall: true,
IsIdentArgument: func(i int) bool {
return false
},
},
"assert.error": {
Scope: allScope,
Call: func(ctx *context.Context, args ...value.Value) (value.Value, error) {
Expand Down