Skip to content

Commit

Permalink
Merge pull request #344 from ysugimoto/feat/inject-value-for-tantitiv…
Browse files Browse the repository at this point in the history
…e-in-simulator

Feat/inject value for tentative in simulator
  • Loading branch information
ysugimoto committed Jul 31, 2024
2 parents 4681c1a + 57589fb commit 3151b1f
Show file tree
Hide file tree
Showing 25 changed files with 637 additions and 47 deletions.
2 changes: 0 additions & 2 deletions ast/codec/declaration_decode.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package codec

import (
"github.com/k0kubun/pp"
"github.com/pkg/errors"
"github.com/ysugimoto/falco/ast"
)
Expand Down Expand Up @@ -203,7 +202,6 @@ func (c *Decoder) decodeDirectorBackendObject() (*ast.DirectorBackendObject, err
}
backend.Values = append(backend.Values, prop)
default:
pp.Println("OK")
return nil, typeMismatch(DIRECTOR_PROPERTY, frame.Type())
}
}
Expand Down
42 changes: 42 additions & 0 deletions cmd/falco/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"net/http"
"os"
"strconv"
"strings"

"github.com/fatih/color"
Expand Down Expand Up @@ -469,6 +470,25 @@ func (r *Runner) Test(rslv resolver.Resolver) (*tester.TestFactory, error) {
options = append(options, icontext.WithOverrideHost(tc.OverrideHost))
}

// Factory override variables.
// The order is imporotant, should do yaml -> cli order because cli could override yaml configuration
overrides := make(map[string]any)
if tc.YamlOverrideVariables != nil {
for key, val := range tc.YamlOverrideVariables {
overrides[key] = val
}
}
if tc.CLIOverrideVariables != nil {
for _, v := range tc.CLIOverrideVariables {
key, val, parsed := r.parseOverrideVariables(v)
if !parsed {
continue
}
overrides[key] = val
}
}
options = append(options, icontext.WithOverrideVariales(overrides))

r.message(white, "Running tests...")
factory, err := tester.New(tc, options).Run(r.config.Commands.At(1))
if err != nil {
Expand All @@ -480,6 +500,28 @@ func (r *Runner) Test(rslv resolver.Resolver) (*tester.TestFactory, error) {
return factory, nil
}

func (r *Runner) parseOverrideVariables(v string) (string, any, bool) {
sep := strings.SplitN(v, "=", 2)
if len(sep) != 2 {
return "", "", false
}
key := strings.TrimSpace(sep[0])
val := strings.TrimSpace(sep[1])

// Simple type assertion of primitive value
if strings.EqualFold(val, "true") {
return key, true, true // bool:true
} else if strings.EqualFold(val, "false") {
return key, false, true // bool:true
} else if v, err := strconv.ParseInt(val, 10, 64); err != nil {
return key, v, true // integer
} else if v, err := strconv.ParseFloat(val, 64); err != nil {
return key, v, true // float
} else {
return key, val, true // string
}
}

func (r *Runner) Format(rslv resolver.Resolver) error {
main, err := rslv.MainVCL()
if err != nil {
Expand Down
15 changes: 15 additions & 0 deletions cmd/falco/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ func TestTester(t *testing.T) {
filter: "*mock_subroutine.test.vcl",
passes: 6,
},
{
name: "overriding variables test",
main: "../../examples/testing/override_variables.vcl",
filter: "*override_variables.test.vcl",
passes: 6,
},
}

for _, tt := range tests {
Expand All @@ -367,6 +373,15 @@ func TestTester(t *testing.T) {
},
Testing: &config.TestConfig{
Filter: tt.filter,
YamlOverrideVariables: map[string]any{
"tls.client.certificate.is_cert_missing": true,
"client.geo.area_code": 100,
"req.digest.ratio": 0.8,
"client.as.name": "Foobar",
},
CLIOverrideVariables: []string{
"client.geo.area_code=200", // will be overridden
},
},
Commands: config.Commands{"test", main},
}
Expand Down
7 changes: 7 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ type SimulatorConfig struct {

// Override Request configuration
OverrideRequest *RequestConfig

// Inject values that the simulator returns tentative value
InjectValues map[string]any `yaml:"values"`
}

// Testing configuration
Expand All @@ -67,6 +70,10 @@ type TestConfig struct {

// Override Request configuration
OverrideRequest *RequestConfig

// Override tentative variable values
CLIOverrideVariables []string `cli:"o,override"` // from CLI
YamlOverrideVariables map[string]any `yaml:"overrides"` // from .falco.yaml
}

// Console configuration
Expand Down
2 changes: 1 addition & 1 deletion docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,7 @@ Failed to load include target module.

## regex/matched-value-override

Regex matched operator `re.group.N` value will be overriden.
Regex matched operator `re.group.N` value will be overridden.

These variables could use if(else) block statement when condition has regex operator like `~` or `!~`.
Note that group matched variable has potential of making bugs due to its spec:
Expand Down
33 changes: 28 additions & 5 deletions docs/variables.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
# Variables in simulator

Following table describes variables that will return tentative values.
Will be updated when we find or implement a way to get accurate values.
Following table describes variables that will return tentative values,
but you can override these values by configuration or cli arguments for testing.

## Overide by Configuration

Put override configuration in `testing.overrides` section as map.

```yaml
...
testing:
overrides:
client.class.checker: true
client.as.name: "overridden"
...
```

## CLI

Provide `-o, --override` option on the CLI command, accepts multiple options.

```shell
falco test /path/to/main.vcl -o "client.class.checker=true" -o "client.as.name=overridden"
```


| Variable | Tentative Value |
Expand All @@ -15,8 +36,8 @@ Will be updated when we find or implement a way to get accurate values.
| client.platform.mediaplayer | false |
| client.geo.latitude | 37.7786941 |
| client.geo.longitude | -122.3981452 |
| client.as_number | 4294967294 |
| client.as_name | "Reserved" |
| client.as.number | 4294967294 |
| client.as.name | "Reserved" |
| client.display.height | -1 |
| client.display.ppi | -1 |
| client.display.width | -1 |
Expand Down Expand Up @@ -67,7 +88,7 @@ Will be updated when we find or implement a way to get accurate values.
| obj.is_pci | false |
| req.backend.is_cluster | false |
| resp.is_locally_generated | false |
| req.digest_ratio | 0.4 |
| req.digest.ratio | 0.4 |
| obj.stale_white_revalidate | 60s |
| backend.socket.congestion_algorithm | "cubic" |
| backend.socket.cwnd | 60 |
Expand Down Expand Up @@ -184,3 +205,5 @@ Will be updated when we find or implement a way to get accurate values.

"TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:TLS_ECDHE_ECDSA_WITH_AES_256_CCM:TLS_DHE_RSA_WITH_AES_256_CCM_8:TLS_DHE_RSA_WITH_AES_256_CCM:TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384:TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384:TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384:TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:TLS_ECDHE_ECDSA_WITH_AES_128_CCM:TLS_DHE_RSA_WITH_AES_128_CCM_8:TLS_DHE_RSA_WITH_AES_128_CCM:TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256:TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256:TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:TLS_DHE_RSA_WITH_AES_256_CBC_SHA:TLS_DHE_DSS_WITH_AES_256_CBC_SHA:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:TLS_DHE_RSA_WITH_AES_128_CBC_SHA:TLS_DHE_DSS_WITH_AES_128_CBC_SHA:TLS_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES_256_CCM_8:TLS_RSA_WITH_AES_256_CCM:TLS_RSA_WITH_ARIA_256_GCM_SHA384:TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_128_CCM_8:TLS_RSA_WITH_AES_128_CCM:TLS_RSA_WITH_ARIA_128_GCM_SHA256:TLS_RSA_WITH_AES_256_CBC_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA256:TLS_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_AES_128_CBC_SHA:TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
```
These values will be updated when we find or implement a way to get accurate values.
32 changes: 32 additions & 0 deletions examples/testing/override_variables.test.vcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @scope: recv
// @suite: Default variable via function
sub test_default_server_region {
testing.call_subroutine("vcl_recv");

assert.equal(req.http.Region, "US");
}

// @scope: recv
// @suite: Override variable via function
sub test_override_server_region {
testing.inject_variable("server.region", "ASIA");
testing.call_subroutine("vcl_recv");

assert.equal(req.http.Region, "ASIA");
}

// @scope: deliver
// @suite: Assert overridden variables via configuration
sub test_override_via_configuration {
testing.call_subroutine("overrides");

// bool
assert.equal(req.http.Is-Cert-Bad, "1");
// integer
assert.equal(req.http.Geo-Area-Code, "200");
// float
assert.equal(req.http.Digest-Ratio, "0.800");
// string
assert.equal(req.http.Client-As-Name, "Foobar");
}

13 changes: 13 additions & 0 deletions examples/testing/override_variables.vcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
sub vcl_recv {
#Fastly recv
set req.http.Region = server.region;
}


sub overrides {
// Following variables could be overridden by configuration
set req.http.Is-Cert-Bad = tls.client.certificate.is_cert_missing;
set req.http.Geo-Area-Code = client.geo.area_code;
set req.http.Digest-Ratio = req.digest.ratio;
set req.http.Client-As-Name = client.as.name;
}
4 changes: 4 additions & 0 deletions interpreter/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ type Context struct {
// However, Fastly document says the esi will be triggered when esi statement is executed in FETCH directive.
// see: https://developer.fastly.com/reference/vcl/statements/esi/
TriggerESI bool

OverrideVariables map[string]value.Value
}

func New(options ...Option) *Context {
Expand Down Expand Up @@ -258,6 +260,8 @@ func New(options ...Option) *Context {

RegexMatchedValues: make(map[string]*value.String),
SubroutineCalls: make(map[string]int),

OverrideVariables: make(map[string]value.Value),
}

// collect options
Expand Down
18 changes: 18 additions & 0 deletions interpreter/context/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package context

import (
"github.com/ysugimoto/falco/config"
"github.com/ysugimoto/falco/interpreter/value"
"github.com/ysugimoto/falco/resolver"
"github.com/ysugimoto/falco/snippets"
)
Expand Down Expand Up @@ -61,3 +62,20 @@ func WithActualResponse(is bool) Option {
c.IsActualResponse = is
}
}

func WithOverrideVariales(variables map[string]any) Option {
return func(c *Context) {
for k, v := range variables {
switch t := v.(type) {
case int:
c.OverrideVariables[k] = &value.Integer{Value: int64(t)}
case string:
c.OverrideVariables[k] = &value.String{Value: t}
case float64:
c.OverrideVariables[k] = &value.Float{Value: float64(t)}
case bool:
c.OverrideVariables[k] = &value.Boolean{Value: t}
}
}
}
}
4 changes: 0 additions & 4 deletions interpreter/statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"io"
"strings"

"github.com/k0kubun/pp"
"github.com/pkg/errors"
"github.com/ysugimoto/falco/ast"
"github.com/ysugimoto/falco/interpreter/assign"
Expand Down Expand Up @@ -37,9 +36,6 @@ func (i *Interpreter) ProcessBlockStatement(
}

// Find process marker and add flow if found
if stmt.GetMeta() == nil {
pp.Println(stmt)
}
if name, found := findProcessMark(stmt.GetMeta().Leading); found {
i.process.Flows = append(
i.process.Flows,
Expand Down
Loading

0 comments on commit 3151b1f

Please sign in to comment.