Files
patterm/internal/harness/observe.go

93 lines
1.8 KiB
Go

package harness
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
func timeoutMS(ms int) time.Duration {
if ms <= 0 {
ms = 5000
}
return time.Duration(ms) * time.Millisecond
}
func valueAt(v any, path string) (any, bool) {
if path == "" {
return v, true
}
cur := v
for _, part := range strings.Split(path, ".") {
switch x := cur.(type) {
case map[string]any:
next, ok := x[part]
if !ok {
return nil, false
}
cur = next
case []any:
i, err := strconv.Atoi(part)
if err != nil || i < 0 || i >= len(x) {
return nil, false
}
cur = x[i]
default:
return nil, false
}
}
return cur, true
}
func assertJSONValue(raw json.RawMessage, path string, equals any, contains string, allowSubstring bool) error {
var v any
if err := json.Unmarshal(raw, &v); err != nil {
return err
}
got, ok := valueAt(v, path)
if !ok {
return fmt.Errorf("path %q not found in %s", path, string(raw))
}
if equals != nil {
if !reflect.DeepEqual(normalizeJSON(equals), normalizeJSON(got)) {
return fmt.Errorf("path %q = %#v, want %#v", path, got, equals)
}
}
if contains != "" {
switch x := got.(type) {
case string:
if !strings.Contains(x, contains) {
return fmt.Errorf("path %q = %q, does not contain %q", path, x, contains)
}
default:
b, _ := json.Marshal(x)
if allowSubstring && strings.Contains(string(b), contains) {
return nil
}
return fmt.Errorf("path %q is not a string: %#v", path, got)
}
}
return nil
}
func normalizeJSON(v any) any {
switch x := v.(type) {
case int:
return float64(x)
case int64:
return float64(x)
case []any:
for i := range x {
x[i] = normalizeJSON(x[i])
}
case map[string]any:
for k := range x {
x[k] = normalizeJSON(x[k])
}
}
return v
}