@voxgig/sdkgen 0.26.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/Feature.js +11 -9
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/cmp/Main.js +6 -2
- package/dist/cmp/Main.js.map +1 -1
- package/model/sdkgen.jsonic +1 -1
- package/package.json +3 -3
- package/project/.sdk/model/feature/log.jsonic +1 -1
- package/project/.sdk/model/feature/test.jsonic +1 -1
- package/project/.sdk/model/target/go.jsonic +19 -4
- package/project/.sdk/model/target/ts.jsonic +3 -3
- package/project/.sdk/src/cmp/go/Config_go.ts +119 -0
- package/project/.sdk/src/cmp/go/EntityOperation_go.ts +48 -0
- package/project/.sdk/src/cmp/go/Entity_go.ts +67 -0
- package/project/.sdk/src/cmp/go/MainEntity_go.ts +22 -0
- package/project/.sdk/src/cmp/go/Main_go.ts +209 -0
- package/project/.sdk/src/cmp/go/Package_go.ts +89 -0
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +373 -0
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +341 -0
- package/project/.sdk/src/cmp/go/Test_go.ts +37 -0
- package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +146 -0
- package/project/.sdk/src/cmp/go/fragment/EntityCreateOp.fragment.go +35 -0
- package/project/.sdk/src/cmp/go/fragment/EntityListOp.fragment.go +26 -0
- package/project/.sdk/src/cmp/go/fragment/EntityLoadOp.fragment.go +38 -0
- package/project/.sdk/src/cmp/go/fragment/EntityRemoveOp.fragment.go +38 -0
- package/project/.sdk/src/cmp/go/fragment/EntityUpdateOp.fragment.go +38 -0
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +250 -0
- package/project/.sdk/src/cmp/go/fragment/SdkError.fragment.go +22 -0
- package/project/.sdk/src/cmp/go/tsconfig.json +15 -0
- package/project/.sdk/src/cmp/go/utility_go.ts +88 -0
- package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +6 -6
- package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +6 -6
- package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +6 -6
- package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +6 -6
- package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +6 -6
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +85 -1
- package/project/.sdk/src/cmp/ts/EntityOperation_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/Main_ts.ts +4 -4
- package/project/.sdk/src/cmp/ts/SdkError_ts.ts +42 -0
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +288 -0
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +14 -6
- package/project/.sdk/src/cmp/ts/Test_ts.ts +2 -0
- package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +30 -0
- package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +12 -12
- package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +12 -12
- package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +12 -12
- package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +13 -13
- package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +12 -12
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +102 -6
- package/project/.sdk/src/cmp/ts/fragment/SdkError.fragment.ts +25 -0
- package/project/.sdk/tm/go/Makefile +10 -0
- package/project/.sdk/tm/go/core/context.go +267 -0
- package/project/.sdk/tm/go/core/control.go +7 -0
- package/project/.sdk/tm/go/core/error.go +25 -0
- package/project/.sdk/tm/go/core/helpers.go +33 -0
- package/project/.sdk/tm/go/core/operation.go +61 -0
- package/project/.sdk/tm/go/core/response.go +55 -0
- package/project/.sdk/tm/go/core/result.go +73 -0
- package/project/.sdk/tm/go/core/spec.go +97 -0
- package/project/.sdk/tm/go/core/target.go +102 -0
- package/project/.sdk/tm/go/core/types.go +44 -0
- package/project/.sdk/tm/go/core/utility_type.go +54 -0
- package/project/.sdk/tm/go/feature/base_feature.go +40 -0
- package/project/.sdk/tm/go/feature/test_feature.go +196 -0
- package/project/.sdk/tm/go/src/feature/README.md +1 -0
- package/project/.sdk/tm/go/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/go/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/go/test/custom_utility_test.go +80 -0
- package/project/.sdk/tm/go/test/exists_test.go +16 -0
- package/project/.sdk/tm/go/test/primary_utility_test.go +899 -0
- package/project/.sdk/tm/go/test/runner_test.go +428 -0
- package/project/.sdk/tm/go/test/struct_runner_test.go +1094 -0
- package/project/.sdk/tm/go/test/struct_utility_test.go +1423 -0
- package/project/.sdk/tm/go/utility/clean.go +7 -0
- package/project/.sdk/tm/go/utility/done.go +20 -0
- package/project/.sdk/tm/go/utility/feature_add.go +10 -0
- package/project/.sdk/tm/go/utility/feature_hook.go +30 -0
- package/project/.sdk/tm/go/utility/feature_init.go +30 -0
- package/project/.sdk/tm/go/utility/fetcher.go +102 -0
- package/project/.sdk/tm/go/utility/make_context.go +7 -0
- package/project/.sdk/tm/go/utility/make_error.go +69 -0
- package/project/.sdk/tm/go/utility/make_fetch_def.go +44 -0
- package/project/.sdk/tm/go/utility/make_options.go +130 -0
- package/project/.sdk/tm/go/utility/make_request.go +59 -0
- package/project/.sdk/tm/go/utility/make_response.go +46 -0
- package/project/.sdk/tm/go/utility/make_result.go +55 -0
- package/project/.sdk/tm/go/utility/make_spec.go +68 -0
- package/project/.sdk/tm/go/utility/make_target.go +95 -0
- package/project/.sdk/tm/go/utility/make_url.go +41 -0
- package/project/.sdk/tm/go/utility/param.go +66 -0
- package/project/.sdk/tm/go/utility/prepare_auth.go +40 -0
- package/project/.sdk/tm/go/utility/prepare_body.go +14 -0
- package/project/.sdk/tm/go/utility/prepare_headers.go +22 -0
- package/project/.sdk/tm/go/utility/prepare_method.go +21 -0
- package/project/.sdk/tm/go/utility/prepare_params.go +41 -0
- package/project/.sdk/tm/go/utility/prepare_path.go +21 -0
- package/project/.sdk/tm/go/utility/prepare_query.go +47 -0
- package/project/.sdk/tm/go/utility/register.go +39 -0
- package/project/.sdk/tm/go/utility/result_basic.go +31 -0
- package/project/.sdk/tm/go/utility/result_body.go +17 -0
- package/project/.sdk/tm/go/utility/result_headers.go +22 -0
- package/project/.sdk/tm/go/utility/struct/go.mod +3 -0
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +4891 -0
- package/project/.sdk/tm/go/utility/transform_request.go +32 -0
- package/project/.sdk/tm/go/utility/transform_response.go +45 -0
- package/project/.sdk/tm/js/src/feature/log/LogFeature.js +2 -2
- package/project/.sdk/tm/ts/src/Context.ts +144 -0
- package/project/.sdk/tm/ts/src/Control.ts +20 -0
- package/project/.sdk/tm/ts/src/Operation.ts +24 -0
- package/project/.sdk/tm/ts/src/Response.ts +26 -0
- package/project/.sdk/tm/ts/src/Result.ts +30 -0
- package/project/.sdk/tm/ts/src/Spec.ts +40 -0
- package/project/.sdk/tm/ts/src/Target.ts +36 -0
- package/project/.sdk/tm/ts/src/feature/base/BaseFeature.ts +1 -1
- package/project/.sdk/tm/ts/src/feature/log/LogFeature.ts +2 -2
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +7 -7
- package/project/.sdk/tm/ts/src/types.ts +15 -205
- package/project/.sdk/tm/ts/src/utility/DoneUtility.ts +2 -3
- package/project/.sdk/tm/ts/src/utility/{AddfeatureUtility.ts → FeatureAddUtility.ts} +2 -2
- package/project/.sdk/tm/ts/src/utility/{InitfeatureUtility.ts → FeatureInitUtility.ts} +2 -2
- package/project/.sdk/tm/ts/src/utility/FetcherUtility.ts +5 -3
- package/project/.sdk/tm/ts/src/utility/{ErrorUtility.ts → MakeErrorUtility.ts} +4 -4
- package/project/.sdk/tm/ts/src/utility/MakeFetchDefUtility.ts +46 -0
- package/project/.sdk/tm/ts/src/utility/{OptionsUtility.ts → MakeOptionsUtility.ts} +10 -7
- package/project/.sdk/tm/ts/src/utility/MakeRequestUtility.ts +66 -0
- package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +61 -0
- package/project/.sdk/tm/ts/src/utility/{ResultUtility.ts → MakeResultUtility.ts} +6 -6
- package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +61 -0
- package/project/.sdk/tm/ts/src/utility/{SelectionUtility.ts → MakeTargetUtility.ts} +22 -24
- package/project/.sdk/tm/ts/src/utility/{FullurlUtility.ts → MakeUrlUtility.ts} +7 -8
- package/project/.sdk/tm/ts/src/utility/{FindparamUtility.ts → ParamUtility.ts} +21 -8
- package/project/.sdk/tm/ts/src/utility/{AuthUtility.ts → PrepareAuthUtility.ts} +4 -4
- package/project/.sdk/tm/ts/src/utility/{BodyUtility.ts → PrepareBodyUtility.ts} +7 -7
- package/project/.sdk/tm/ts/src/utility/{HeadersUtility.ts → PrepareHeadersUtility.ts} +5 -7
- package/project/.sdk/tm/ts/src/utility/{MethodUtility.ts → PrepareMethodUtility.ts} +6 -6
- package/project/.sdk/tm/ts/src/utility/PrepareParamsUtility.ts +37 -0
- package/project/.sdk/tm/ts/src/utility/PreparePathUtility.ts +3 -4
- package/project/.sdk/tm/ts/src/utility/PrepareQueryUtility.ts +30 -0
- package/project/.sdk/tm/ts/src/utility/{ResbasicUtility.ts → ResultBasicUtility.ts} +11 -6
- package/project/.sdk/tm/ts/src/utility/{ResbodyUtility.ts → ResultBodyUtility.ts} +4 -3
- package/project/.sdk/tm/ts/src/utility/{ResheadersUtility.ts → ResultHeadersUtility.ts} +4 -3
- package/project/.sdk/tm/ts/src/utility/{ReqformUtility.ts → TransformRequestUtility.ts} +9 -7
- package/project/.sdk/tm/ts/src/utility/{ResformUtility.ts → TransformResponseUtility.ts} +10 -7
- package/project/.sdk/tm/ts/src/utility/Utility.ts +47 -62
- package/project/.sdk/tm/ts/test/runner.ts +25 -4
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +386 -175
- package/src/cmp/Feature.ts +11 -9
- package/src/cmp/Main.ts +8 -2
- package/project/.sdk/tm/ts/src/utility/JoinurlUtility.ts +0 -15
- package/project/.sdk/tm/ts/src/utility/OperationUtility.ts +0 -23
- package/project/.sdk/tm/ts/src/utility/ParamsUtility.ts +0 -37
- package/project/.sdk/tm/ts/src/utility/QueryUtility.ts +0 -27
- package/project/.sdk/tm/ts/src/utility/RequestUtility.ts +0 -83
- package/project/.sdk/tm/ts/src/utility/ResponseUtility.ts +0 -61
- package/project/.sdk/tm/ts/src/utility/SpecUtility.ts +0 -68
- /package/project/.sdk/tm/ts/src/utility/{FeaturehookUtility.ts → FeatureHookUtility.ts} +0 -0
- /package/project/.sdk/tm/ts/src/utility/{ContextUtility.ts → MakeContextUtility.ts} +0 -0
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
// Vendored from github.com/voxgig/struct/go/testutil
|
|
2
|
+
// Test runner that uses the test model in build/test.
|
|
3
|
+
|
|
4
|
+
package sdktest
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"fmt"
|
|
8
|
+
|
|
9
|
+
voxgigstruct "github.com/voxgig/struct"
|
|
10
|
+
|
|
11
|
+
"encoding/json"
|
|
12
|
+
"errors"
|
|
13
|
+
"os"
|
|
14
|
+
"path/filepath"
|
|
15
|
+
"reflect"
|
|
16
|
+
"regexp"
|
|
17
|
+
"strings"
|
|
18
|
+
"testing"
|
|
19
|
+
"unicode"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// StructClient interface defines the minimum needed to work with the runner
|
|
23
|
+
type StructClient interface {
|
|
24
|
+
Utility() StructUtilityIF
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type StructUtilityIF interface {
|
|
28
|
+
Struct() *StructUtility
|
|
29
|
+
Check(ctx map[string]any) map[string]any
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type StructUtility struct {
|
|
33
|
+
IsNode func(val any) bool
|
|
34
|
+
Clone func(val any) any
|
|
35
|
+
CloneFlags func(val any, flags map[string]bool) any
|
|
36
|
+
GetPath func(path any, store any, injdefs ...*voxgigstruct.Injection) any
|
|
37
|
+
Inject func(val any, store any, injdefs ...*voxgigstruct.Injection) any
|
|
38
|
+
Items func(val any) [][2]any
|
|
39
|
+
Stringify func(val any, maxlen ...int) string
|
|
40
|
+
Walk func(val any, apply voxgigstruct.WalkApply, opts ...any) any
|
|
41
|
+
|
|
42
|
+
DelProp func(parent any, key any) any
|
|
43
|
+
EscRe func(s string) string
|
|
44
|
+
EscUrl func(s string) string
|
|
45
|
+
Filter func(val any, check func([2]any) bool) []any
|
|
46
|
+
Flatten func(list any, depths ...int) any
|
|
47
|
+
GetDef func(val any, alt any) any
|
|
48
|
+
GetElem func(val any, key any, alts ...any) any
|
|
49
|
+
GetProp func(val any, key any, alts ...any) any
|
|
50
|
+
HasKey func(val any, key any) bool
|
|
51
|
+
IsEmpty func(val any) bool
|
|
52
|
+
IsFunc func(val any) bool
|
|
53
|
+
IsKey func(val any) bool
|
|
54
|
+
IsList func(val any) bool
|
|
55
|
+
IsMap func(val any) bool
|
|
56
|
+
Join func(arr []any, args ...any) string
|
|
57
|
+
Jsonify func(val any, flags ...map[string]any) string
|
|
58
|
+
KeysOf func(val any) []string
|
|
59
|
+
Merge func(val any, maxdepths ...int) any
|
|
60
|
+
Pad func(str any, args ...any) string
|
|
61
|
+
Pathify func(val any, from ...int) string
|
|
62
|
+
Select func(children any, query any) []any
|
|
63
|
+
SetPath func(store any, path any, val any, injdefs ...map[string]any) any
|
|
64
|
+
SetProp func(parent any, key any, newval any) any
|
|
65
|
+
Size func(val any) int
|
|
66
|
+
Slice func(val any, args ...any) any
|
|
67
|
+
StrKey func(key any) string
|
|
68
|
+
Transform func(data any, spec any, injdefs ...*voxgigstruct.Injection) any
|
|
69
|
+
Typify func(value any) int
|
|
70
|
+
Typename func(t int) string
|
|
71
|
+
Validate func(data any, spec any, injdefs ...*voxgigstruct.Injection) (any, error)
|
|
72
|
+
|
|
73
|
+
SKIP any
|
|
74
|
+
DELETE any
|
|
75
|
+
|
|
76
|
+
Jo func(kv ...any) map[string]any
|
|
77
|
+
Ja func(v ...any) []any
|
|
78
|
+
|
|
79
|
+
CheckPlacement func(modes int, ijname string, parentTypes int, inj *voxgigstruct.Injection) bool
|
|
80
|
+
InjectorArgs func(argTypes []int, args []any) []any
|
|
81
|
+
InjectChild func(child any, store any, inj *voxgigstruct.Injection) *voxgigstruct.Injection
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type Subject func(args ...any) (any, error)
|
|
85
|
+
|
|
86
|
+
type RunSet func(
|
|
87
|
+
t *testing.T,
|
|
88
|
+
testspec any,
|
|
89
|
+
testsubject any,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
type RunSetFlags func(
|
|
93
|
+
t *testing.T,
|
|
94
|
+
testspec any,
|
|
95
|
+
flags map[string]bool,
|
|
96
|
+
testsubject any,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
type RunPack struct {
|
|
100
|
+
Spec map[string]any
|
|
101
|
+
RunSet RunSet
|
|
102
|
+
RunSetFlags RunSetFlags
|
|
103
|
+
Subject Subject
|
|
104
|
+
Client StructClient
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
type TestPack struct {
|
|
108
|
+
Name string
|
|
109
|
+
Client StructClient
|
|
110
|
+
Subject Subject
|
|
111
|
+
Utility StructUtilityIF
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var (
|
|
115
|
+
NULLMARK = "__NULL__"
|
|
116
|
+
UNDEFMARK = "__UNDEF__"
|
|
117
|
+
EXISTSMARK = "__EXISTS__"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
// MakeRunner creates a runner function that can be used to run tests
|
|
121
|
+
func MakeRunner(testfile string, client StructClient) func(name string, store any) (*RunPack, error) {
|
|
122
|
+
|
|
123
|
+
return func(name string, store any) (*RunPack, error) {
|
|
124
|
+
utility := client.Utility()
|
|
125
|
+
structUtil := utility.Struct()
|
|
126
|
+
|
|
127
|
+
spec := resolveSpec(name, testfile)
|
|
128
|
+
|
|
129
|
+
clients, err := resolveClients(spec, store, structUtil, client)
|
|
130
|
+
if err != nil {
|
|
131
|
+
return nil, err
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
subject, err := resolveSubject(name, utility)
|
|
135
|
+
if err != nil {
|
|
136
|
+
return nil, err
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
var runsetFlags RunSetFlags = func(
|
|
140
|
+
t *testing.T,
|
|
141
|
+
testspec any,
|
|
142
|
+
flags map[string]bool,
|
|
143
|
+
testsubject any,
|
|
144
|
+
) {
|
|
145
|
+
if testsubject != nil {
|
|
146
|
+
subject = subjectify(testsubject)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
flags = resolveFlags(flags)
|
|
150
|
+
|
|
151
|
+
var testspecmap = fixJSON(
|
|
152
|
+
testspec.(map[string]any),
|
|
153
|
+
flags,
|
|
154
|
+
).(map[string]any)
|
|
155
|
+
|
|
156
|
+
testset, ok := testspecmap["set"].([]any)
|
|
157
|
+
if !ok {
|
|
158
|
+
panic(fmt.Sprintf("No test set in %v", name))
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for _, entryVal := range testset {
|
|
162
|
+
entry := resolveEntry(entryVal, flags)
|
|
163
|
+
|
|
164
|
+
// Go cannot distinguish absent values from nil (JSON null).
|
|
165
|
+
// Skip entries where "in" or "out" is missing and the expected
|
|
166
|
+
// result is T_noval, as this represents a concept (undefined)
|
|
167
|
+
// that does not exist in Go.
|
|
168
|
+
_, hasIn := entry["in"]
|
|
169
|
+
_, hasOut := entry["out"]
|
|
170
|
+
if !hasIn || !hasOut {
|
|
171
|
+
if outVal, ok := entry["out"]; ok {
|
|
172
|
+
if outNum, ok := outVal.(int); ok && outNum == voxgigstruct.T_noval {
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// When null flag is false, skip entries where in values are nil,
|
|
179
|
+
// since Go cannot distinguish absent/undefined from nil.
|
|
180
|
+
if !flags["null"] {
|
|
181
|
+
if inMap, ok := entry["in"].(map[string]any); ok {
|
|
182
|
+
skipEntry := false
|
|
183
|
+
for _, v := range inMap {
|
|
184
|
+
if v == nil {
|
|
185
|
+
skipEntry = true
|
|
186
|
+
break
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if skipEntry {
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Also skip when out is nil (nil/undefined distinction).
|
|
194
|
+
if entry["out"] == nil {
|
|
195
|
+
continue
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
testpack, err := resolveTestPack(name, entry, subject, client, clients)
|
|
200
|
+
if err != nil {
|
|
201
|
+
// No debug output
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
args := resolveArgs(entry, testpack)
|
|
206
|
+
entry["args"] = args
|
|
207
|
+
|
|
208
|
+
res, err := testpack.Subject(args...)
|
|
209
|
+
|
|
210
|
+
res = fixJSON(res, flags)
|
|
211
|
+
|
|
212
|
+
entry["res"] = res
|
|
213
|
+
entry["thrown"] = err
|
|
214
|
+
|
|
215
|
+
if nil == err {
|
|
216
|
+
checkResult(t, entry, res, structUtil)
|
|
217
|
+
} else {
|
|
218
|
+
handleError(t, entry, err, structUtil)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
var runsetFn RunSet = func(
|
|
224
|
+
t *testing.T,
|
|
225
|
+
testspec any,
|
|
226
|
+
testsubject any,
|
|
227
|
+
) {
|
|
228
|
+
runsetFlags(t, testspec, nil, testsubject)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return &RunPack{
|
|
232
|
+
Spec: spec,
|
|
233
|
+
RunSet: runsetFn,
|
|
234
|
+
RunSetFlags: runsetFlags,
|
|
235
|
+
Subject: subject,
|
|
236
|
+
}, nil
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
func resolveSpec(
|
|
241
|
+
name string,
|
|
242
|
+
testfile string,
|
|
243
|
+
) map[string]any {
|
|
244
|
+
|
|
245
|
+
data, err := os.ReadFile(filepath.Join(".", testfile))
|
|
246
|
+
if err != nil {
|
|
247
|
+
panic(err)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
var alltests map[string]any
|
|
251
|
+
if err := json.Unmarshal(data, &alltests); err != nil {
|
|
252
|
+
panic(err)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
var spec map[string]any
|
|
256
|
+
|
|
257
|
+
// Check if there's a "primary" key that is a map, and if it has our 'name'
|
|
258
|
+
if primaryRaw, hasPrimary := alltests["primary"]; hasPrimary {
|
|
259
|
+
if primaryMap, ok := primaryRaw.(map[string]any); ok {
|
|
260
|
+
if found, ok := primaryMap[name]; ok {
|
|
261
|
+
spec = found.(map[string]any)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if spec == nil {
|
|
267
|
+
if found, ok := alltests[name]; ok {
|
|
268
|
+
spec = found.(map[string]any)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if spec == nil {
|
|
273
|
+
spec = alltests
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return spec
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
func resolveClients(
|
|
280
|
+
spec map[string]any,
|
|
281
|
+
store any,
|
|
282
|
+
structUtil *StructUtility,
|
|
283
|
+
baseClient StructClient,
|
|
284
|
+
) (map[string]StructClient, error) {
|
|
285
|
+
clients := make(map[string]StructClient)
|
|
286
|
+
|
|
287
|
+
defRaw, hasDef := spec["DEF"]
|
|
288
|
+
if !hasDef {
|
|
289
|
+
return clients, nil
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
defMap, ok := defRaw.(map[string]any)
|
|
293
|
+
if !ok {
|
|
294
|
+
return clients, nil
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
clientRaw, hasClient := defMap["client"]
|
|
298
|
+
if !hasClient {
|
|
299
|
+
return clients, nil
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
clientMap, ok := clientRaw.(map[string]any)
|
|
303
|
+
if !ok {
|
|
304
|
+
return clients, nil
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Check if the client has a Tester method using reflection
|
|
308
|
+
baseClientValue := reflect.ValueOf(baseClient)
|
|
309
|
+
testerMethod := baseClientValue.MethodByName("Tester")
|
|
310
|
+
if !testerMethod.IsValid() {
|
|
311
|
+
return clients, nil
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
for _, cdef := range structUtil.Items(clientMap) {
|
|
315
|
+
key, _ := cdef[0].(string)
|
|
316
|
+
valMap, _ := cdef[1].(map[string]any)
|
|
317
|
+
|
|
318
|
+
if valMap == nil {
|
|
319
|
+
continue
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
testRaw, _ := valMap["test"].(map[string]any)
|
|
323
|
+
opts, _ := testRaw["options"].(map[string]any)
|
|
324
|
+
if opts == nil {
|
|
325
|
+
opts = make(map[string]any)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Inject store values into options
|
|
329
|
+
if store != nil && structUtil.Inject != nil {
|
|
330
|
+
structUtil.Inject(opts, store)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Call the client's Tester method using reflection
|
|
334
|
+
results := testerMethod.Call([]reflect.Value{reflect.ValueOf(opts)})
|
|
335
|
+
if len(results) != 2 {
|
|
336
|
+
return nil, fmt.Errorf("resolveClients: Tester method must return (Client, error)")
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Check for error
|
|
340
|
+
if !results[1].IsNil() {
|
|
341
|
+
err := results[1].Interface().(error)
|
|
342
|
+
return nil, err
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Get the new client instance
|
|
346
|
+
newClientValue := results[0].Interface()
|
|
347
|
+
newClient, ok := newClientValue.(StructClient)
|
|
348
|
+
if !ok {
|
|
349
|
+
return nil, fmt.Errorf("resolveClients: Tester method did not return a StructClient")
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
clients[key] = newClient
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return clients, nil
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
func resolveSubject(
|
|
359
|
+
name string,
|
|
360
|
+
container any,
|
|
361
|
+
) (Subject, error) {
|
|
362
|
+
name = uppercaseFirstLetter(name)
|
|
363
|
+
|
|
364
|
+
val := reflect.ValueOf(container)
|
|
365
|
+
|
|
366
|
+
if _, ok := container.(StructUtilityIF); ok {
|
|
367
|
+
subjectVal := val.MethodByName(name)
|
|
368
|
+
subjectIF := subjectVal.Interface()
|
|
369
|
+
subject := subjectify(subjectIF)
|
|
370
|
+
return subject, nil
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if val.Kind() == reflect.Ptr {
|
|
374
|
+
val = val.Elem()
|
|
375
|
+
}
|
|
376
|
+
if val.Kind() != reflect.Struct {
|
|
377
|
+
return nil, errors.New("resolveSubject: not a struct or struct pointer")
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
fieldVal := val.FieldByName(name)
|
|
381
|
+
|
|
382
|
+
if !fieldVal.IsValid() {
|
|
383
|
+
return nil, fmt.Errorf("resolveSubject: field %q is not a func", name)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if fieldVal.Kind() != reflect.Func {
|
|
387
|
+
return nil, fmt.Errorf("resolveSubject: field %q is not a func", name)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
fn := fieldVal.Interface()
|
|
391
|
+
var sfn Subject
|
|
392
|
+
|
|
393
|
+
sfn, ok := fn.(Subject)
|
|
394
|
+
if !ok {
|
|
395
|
+
sfn = subjectify(fn)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return sfn, nil
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
func resolveFlags(flags map[string]bool) map[string]bool {
|
|
402
|
+
|
|
403
|
+
if nil == flags {
|
|
404
|
+
flags = map[string]bool{}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if _, ok := flags["null"]; !ok {
|
|
408
|
+
flags["null"] = true
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return flags
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
func resolveEntry(entryVal any, flags map[string]bool) map[string]any {
|
|
415
|
+
entry := entryVal.(map[string]any)
|
|
416
|
+
|
|
417
|
+
if flags["null"] {
|
|
418
|
+
|
|
419
|
+
// Where `out` is missing in the test spec, set it to the special null symbol __NULL__
|
|
420
|
+
_, has := entry["out"]
|
|
421
|
+
if !has {
|
|
422
|
+
entry["out"] = NULLMARK
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return entry
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
func checkResult(
|
|
430
|
+
t *testing.T,
|
|
431
|
+
entry map[string]any,
|
|
432
|
+
res any,
|
|
433
|
+
structUtils *StructUtility,
|
|
434
|
+
) {
|
|
435
|
+
// Check if this test expects an output or an error
|
|
436
|
+
_, hasExpectedErr := entry["err"]
|
|
437
|
+
|
|
438
|
+
// Special case for array tests
|
|
439
|
+
if hasExpectedErr && entry["err"] != nil {
|
|
440
|
+
errStr, isStr := entry["err"].(string)
|
|
441
|
+
if isStr && strings.Contains(errStr, "null:") {
|
|
442
|
+
return
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if entry["match"] == nil || entry["out"] != nil {
|
|
447
|
+
var cleanRes any
|
|
448
|
+
if res != nil {
|
|
449
|
+
flags := map[string]bool{"func": false}
|
|
450
|
+
cleanRes = structUtils.CloneFlags(res, flags)
|
|
451
|
+
} else {
|
|
452
|
+
cleanRes = res
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if !reflect.DeepEqual(cleanRes, entry["out"]) {
|
|
456
|
+
t.Error(outFail(entry, cleanRes, entry["out"]))
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if entry["match"] != nil {
|
|
462
|
+
pass, err := MatchNode(
|
|
463
|
+
entry["match"],
|
|
464
|
+
map[string]any{
|
|
465
|
+
"in": entry["in"],
|
|
466
|
+
"out": entry["res"],
|
|
467
|
+
"ctx": entry["ctx"],
|
|
468
|
+
"args": entry["args"],
|
|
469
|
+
},
|
|
470
|
+
structUtils,
|
|
471
|
+
)
|
|
472
|
+
if err != nil {
|
|
473
|
+
t.Error(fmt.Sprintf("match error: %v", err))
|
|
474
|
+
return
|
|
475
|
+
}
|
|
476
|
+
if !pass {
|
|
477
|
+
t.Error(fmt.Sprintf("match fail: %v", err))
|
|
478
|
+
return
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
func outFail(entry any, res any, out any) string {
|
|
484
|
+
return fmt.Sprintf("Entry:\n%s\nExpected:\n%s\nGot:\n%s\n",
|
|
485
|
+
inspect(entry), inspect(out), inspect(res))
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
func inspect(val any) string {
|
|
489
|
+
return inspectIndent(val, "")
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
func inspectIndent(val any, indent string) string {
|
|
493
|
+
result := ""
|
|
494
|
+
|
|
495
|
+
switch v := val.(type) {
|
|
496
|
+
case map[string]any:
|
|
497
|
+
result += indent + "{\n"
|
|
498
|
+
for key, value := range v {
|
|
499
|
+
result += fmt.Sprintf("%s \"%s\": %s", indent, key, inspectIndent(value, indent+" "))
|
|
500
|
+
}
|
|
501
|
+
result += indent + "}\n"
|
|
502
|
+
|
|
503
|
+
case []any:
|
|
504
|
+
result += indent + "[\n"
|
|
505
|
+
for _, value := range v {
|
|
506
|
+
result += fmt.Sprintf("%s - %s", indent, inspectIndent(value, indent+" "))
|
|
507
|
+
}
|
|
508
|
+
result += indent + "]\n"
|
|
509
|
+
|
|
510
|
+
default:
|
|
511
|
+
result += fmt.Sprintf("%v (%s)\n", v, reflect.TypeOf(v))
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return result
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
func handleError(
|
|
518
|
+
t *testing.T,
|
|
519
|
+
entry map[string]any,
|
|
520
|
+
testerr error,
|
|
521
|
+
structUtils *StructUtility,
|
|
522
|
+
) {
|
|
523
|
+
// Record the error in the entry
|
|
524
|
+
entry["thrown"] = testerr
|
|
525
|
+
entryErr := entry["err"]
|
|
526
|
+
|
|
527
|
+
// Special cases for testing
|
|
528
|
+
if nil == entryErr && entry["out"] != nil {
|
|
529
|
+
errStr := testerr.Error()
|
|
530
|
+
if strings.Contains(errStr, "null:") &&
|
|
531
|
+
strings.Contains(structUtils.Stringify(entry["in"]), "q:[") {
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if nil == entryErr {
|
|
537
|
+
t.Error(fmt.Sprintf("%s\n\nENTRY: %s", testerr.Error(), structUtils.Stringify(entry)))
|
|
538
|
+
return
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
boolErr, hasBoolErr := entryErr.(bool)
|
|
542
|
+
if hasBoolErr && !boolErr {
|
|
543
|
+
t.Error(fmt.Sprintf("%s\n\nENTRY: %s", testerr.Error(), structUtils.Stringify(entry)))
|
|
544
|
+
return
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Handle special cases
|
|
548
|
+
errStr := testerr.Error()
|
|
549
|
+
entryErrStr, isStr := entryErr.(string)
|
|
550
|
+
if isStr {
|
|
551
|
+
if strings.Contains(errStr, "null:") && strings.Contains(entryErrStr, "null:") {
|
|
552
|
+
return
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
matchErr, err := MatchNode(entryErr, errStr, structUtils)
|
|
557
|
+
|
|
558
|
+
if err != nil {
|
|
559
|
+
t.Error(fmt.Sprintf("match error: %v", err))
|
|
560
|
+
return
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if boolErr || matchErr {
|
|
564
|
+
if entry["match"] != nil {
|
|
565
|
+
flags := map[string]bool{"null": true}
|
|
566
|
+
matchErr, err := MatchNode(
|
|
567
|
+
entry["match"],
|
|
568
|
+
map[string]any{
|
|
569
|
+
"in": entry["in"],
|
|
570
|
+
"out": entry["res"],
|
|
571
|
+
"ctx": entry["ctx"],
|
|
572
|
+
"err": fixJSON(testerr, flags),
|
|
573
|
+
},
|
|
574
|
+
structUtils,
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
if !matchErr {
|
|
578
|
+
t.Error(fmt.Sprintf("match failed: %v", matchErr))
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if nil != err {
|
|
582
|
+
t.Error(fmt.Sprintf("match failed: %v", err))
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
} else {
|
|
587
|
+
t.Error(fmt.Sprintf("ERROR MATCH: [%s] <=> [%s]",
|
|
588
|
+
structUtils.Stringify(entryErr),
|
|
589
|
+
errStr,
|
|
590
|
+
))
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
func resolveArgs(entry map[string]any, testpack TestPack) []any {
|
|
595
|
+
structUtils := testpack.Utility.Struct()
|
|
596
|
+
|
|
597
|
+
var args []any
|
|
598
|
+
if inVal, ok := entry["in"]; ok {
|
|
599
|
+
args = []any{structUtils.Clone(inVal)}
|
|
600
|
+
} else {
|
|
601
|
+
args = []any{}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if ctx, exists := entry["ctx"]; exists && ctx != nil {
|
|
605
|
+
args = []any{ctx}
|
|
606
|
+
} else if rawArgs, exists := entry["args"]; exists && rawArgs != nil {
|
|
607
|
+
if slice, ok := rawArgs.([]any); ok {
|
|
608
|
+
args = slice
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if entry["ctx"] != nil || entry["args"] != nil {
|
|
613
|
+
if len(args) > 0 {
|
|
614
|
+
first := args[0]
|
|
615
|
+
if firstMap, ok := first.(map[string]any); ok && first != nil {
|
|
616
|
+
clonedFirst := structUtils.Clone(firstMap)
|
|
617
|
+
args[0] = clonedFirst
|
|
618
|
+
entry["ctx"] = clonedFirst
|
|
619
|
+
if m, ok := clonedFirst.(map[string]any); ok {
|
|
620
|
+
m["client"] = testpack.Client
|
|
621
|
+
m["utility"] = testpack.Utility
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return args
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
func resolveTestPack(
|
|
631
|
+
name string,
|
|
632
|
+
entry any,
|
|
633
|
+
testsubject any,
|
|
634
|
+
client StructClient,
|
|
635
|
+
clients map[string]StructClient,
|
|
636
|
+
) (TestPack, error) {
|
|
637
|
+
|
|
638
|
+
subject, ok := testsubject.(Subject)
|
|
639
|
+
if !ok {
|
|
640
|
+
panic("Bad subject")
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
testpack := TestPack{
|
|
644
|
+
Name: name,
|
|
645
|
+
Client: client,
|
|
646
|
+
Subject: subject,
|
|
647
|
+
Utility: client.Utility(),
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
var err error
|
|
651
|
+
|
|
652
|
+
if e, ok := entry.(map[string]any); ok {
|
|
653
|
+
if rawClient, exists := e["client"]; exists {
|
|
654
|
+
if clientKey, ok := rawClient.(string); ok {
|
|
655
|
+
if cl, found := clients[clientKey]; found {
|
|
656
|
+
testpack.Client = cl
|
|
657
|
+
testpack.Utility = cl.Utility()
|
|
658
|
+
testpack.Subject, err = resolveSubject(name, testpack.Utility.Struct())
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return testpack, err
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
func MatchNode(
|
|
668
|
+
check any,
|
|
669
|
+
base any,
|
|
670
|
+
structUtil *StructUtility,
|
|
671
|
+
) (bool, error) {
|
|
672
|
+
pass := true
|
|
673
|
+
var err error = nil
|
|
674
|
+
|
|
675
|
+
// Clone the base object to avoid modifying the original
|
|
676
|
+
base = structUtil.Clone(base)
|
|
677
|
+
|
|
678
|
+
structUtil.Walk(
|
|
679
|
+
check,
|
|
680
|
+
func(key *string, val any, _parent any, path []string) any {
|
|
681
|
+
scalar := !structUtil.IsNode(val)
|
|
682
|
+
|
|
683
|
+
if scalar {
|
|
684
|
+
baseval := structUtil.GetPath(path, base)
|
|
685
|
+
if !MatchScalar(val, baseval, structUtil) {
|
|
686
|
+
pass = false
|
|
687
|
+
err = fmt.Errorf(
|
|
688
|
+
"MATCHX: %s: [%s] <=> [%s]",
|
|
689
|
+
strings.Join(path, "."),
|
|
690
|
+
structUtil.Stringify(val),
|
|
691
|
+
structUtil.Stringify(baseval),
|
|
692
|
+
)
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return val
|
|
696
|
+
},
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
return pass, err
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
func MatchScalar(check, base any, structUtil *StructUtility) bool {
|
|
703
|
+
// Handle special cases for undefined and null values
|
|
704
|
+
if s, ok := check.(string); ok && s == UNDEFMARK {
|
|
705
|
+
return base == nil || reflect.ValueOf(base).IsZero()
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Handle EXISTSMARK - value exists and is not undefined
|
|
709
|
+
if s, ok := check.(string); ok && s == EXISTSMARK {
|
|
710
|
+
return base != nil
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
pass := (check == base)
|
|
714
|
+
|
|
715
|
+
if !pass {
|
|
716
|
+
if checkStr, ok := check.(string); ok {
|
|
717
|
+
basestr := structUtil.Stringify(base)
|
|
718
|
+
|
|
719
|
+
if len(checkStr) > 2 && checkStr[0] == '/' && checkStr[len(checkStr)-1] == '/' {
|
|
720
|
+
pat := checkStr[1 : len(checkStr)-1]
|
|
721
|
+
if rx, err := regexp.Compile(pat); err == nil {
|
|
722
|
+
pass = rx.MatchString(basestr)
|
|
723
|
+
} else {
|
|
724
|
+
pass = false
|
|
725
|
+
}
|
|
726
|
+
} else {
|
|
727
|
+
basenorm := strings.ToLower(basestr)
|
|
728
|
+
checknorm := strings.ToLower(structUtil.Stringify(checkStr))
|
|
729
|
+
pass = strings.Contains(
|
|
730
|
+
basenorm,
|
|
731
|
+
checknorm,
|
|
732
|
+
)
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
cv := reflect.ValueOf(check)
|
|
736
|
+
isf := cv.Kind() == reflect.Func
|
|
737
|
+
if isf {
|
|
738
|
+
pass = true
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return pass
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
func subjectify(fn any) Subject {
|
|
747
|
+
v := reflect.ValueOf(fn)
|
|
748
|
+
if v.Kind() != reflect.Func {
|
|
749
|
+
panic("subjectify: not a function")
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
sfn, ok := v.Interface().(Subject)
|
|
753
|
+
if ok {
|
|
754
|
+
return sfn
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
fnType := v.Type()
|
|
758
|
+
|
|
759
|
+
return func(args ...any) (any, error) {
|
|
760
|
+
|
|
761
|
+
argCount := fnType.NumIn()
|
|
762
|
+
|
|
763
|
+
if len(args) < argCount {
|
|
764
|
+
extended := make([]any, argCount)
|
|
765
|
+
copy(extended, args)
|
|
766
|
+
args = extended
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Build reflect.Value slice for call
|
|
770
|
+
in := make([]reflect.Value, fnType.NumIn())
|
|
771
|
+
for i := 0; i < fnType.NumIn(); i++ {
|
|
772
|
+
paramType := fnType.In(i)
|
|
773
|
+
arg := args[i]
|
|
774
|
+
|
|
775
|
+
if arg == nil {
|
|
776
|
+
in[i] = reflect.Zero(paramType)
|
|
777
|
+
} else {
|
|
778
|
+
val := reflect.ValueOf(arg)
|
|
779
|
+
|
|
780
|
+
// Check compatibility so we don't panic on invalid type
|
|
781
|
+
if !val.Type().AssignableTo(paramType) {
|
|
782
|
+
return nil, fmt.Errorf(
|
|
783
|
+
"subjectify: argument %d type %T not assignable to parameter type %s",
|
|
784
|
+
i, arg, paramType,
|
|
785
|
+
)
|
|
786
|
+
}
|
|
787
|
+
in[i] = val
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Call the original function
|
|
792
|
+
out := v.Call(in)
|
|
793
|
+
|
|
794
|
+
// Interpret results
|
|
795
|
+
switch len(out) {
|
|
796
|
+
case 0:
|
|
797
|
+
return nil, nil
|
|
798
|
+
case 1:
|
|
799
|
+
return out[0].Interface(), nil
|
|
800
|
+
case 2:
|
|
801
|
+
errVal := out[1].Interface()
|
|
802
|
+
var err error
|
|
803
|
+
if errVal != nil {
|
|
804
|
+
err = errVal.(error)
|
|
805
|
+
}
|
|
806
|
+
return out[0].Interface(), err
|
|
807
|
+
default:
|
|
808
|
+
return nil, fmt.Errorf("subjectify: function returns too many values (%d)", len(out))
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
func fixJSON(data any, flags map[string]bool) any {
|
|
814
|
+
// Ensure flags is initialized
|
|
815
|
+
if flags == nil {
|
|
816
|
+
flags = map[string]bool{"null": true}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Handle nil data
|
|
820
|
+
if nil == data && flags["null"] {
|
|
821
|
+
return NULLMARK
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Handle error objects specially
|
|
825
|
+
if err, ok := data.(error); ok {
|
|
826
|
+
errorMap := map[string]any{
|
|
827
|
+
"name": reflect.TypeOf(err).String(),
|
|
828
|
+
"message": err.Error(),
|
|
829
|
+
}
|
|
830
|
+
return errorMap
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
v := reflect.ValueOf(data)
|
|
834
|
+
|
|
835
|
+
switch v.Kind() {
|
|
836
|
+
case reflect.Float64:
|
|
837
|
+
if v.Float() == float64(int(v.Float())) {
|
|
838
|
+
return int(v.Float())
|
|
839
|
+
}
|
|
840
|
+
return data
|
|
841
|
+
|
|
842
|
+
case reflect.Map:
|
|
843
|
+
fixedMap := make(map[string]any)
|
|
844
|
+
for _, key := range v.MapKeys() {
|
|
845
|
+
strKey, ok := key.Interface().(string)
|
|
846
|
+
if ok {
|
|
847
|
+
value := v.MapIndex(key).Interface()
|
|
848
|
+
if value == nil && flags["null"] {
|
|
849
|
+
fixedMap[strKey] = NULLMARK
|
|
850
|
+
} else {
|
|
851
|
+
fixedMap[strKey] = fixJSON(value, flags)
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return fixedMap
|
|
856
|
+
|
|
857
|
+
case reflect.Slice:
|
|
858
|
+
length := v.Len()
|
|
859
|
+
fixedSlice := make([]any, length)
|
|
860
|
+
for i := 0; i < length; i++ {
|
|
861
|
+
value := v.Index(i).Interface()
|
|
862
|
+
if value == nil && flags["null"] {
|
|
863
|
+
fixedSlice[i] = NULLMARK
|
|
864
|
+
} else {
|
|
865
|
+
fixedSlice[i] = fixJSON(value, flags)
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return fixedSlice
|
|
869
|
+
|
|
870
|
+
case reflect.Array:
|
|
871
|
+
length := v.Len()
|
|
872
|
+
fixedSlice := make([]any, length)
|
|
873
|
+
for i := 0; i < length; i++ {
|
|
874
|
+
value := v.Index(i).Interface()
|
|
875
|
+
if value == nil && flags["null"] {
|
|
876
|
+
fixedSlice[i] = NULLMARK
|
|
877
|
+
} else {
|
|
878
|
+
fixedSlice[i] = fixJSON(value, flags)
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return fixedSlice
|
|
882
|
+
|
|
883
|
+
default:
|
|
884
|
+
return data
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
func NullModifier(
|
|
889
|
+
val any,
|
|
890
|
+
key any,
|
|
891
|
+
parent any,
|
|
892
|
+
inj *voxgigstruct.Injection,
|
|
893
|
+
store any,
|
|
894
|
+
) {
|
|
895
|
+
switch v := val.(type) {
|
|
896
|
+
case string:
|
|
897
|
+
if NULLMARK == v {
|
|
898
|
+
_ = voxgigstruct.SetProp(parent, key, nil)
|
|
899
|
+
} else if UNDEFMARK == v {
|
|
900
|
+
_ = voxgigstruct.SetProp(parent, key, nil)
|
|
901
|
+
} else if EXISTSMARK == v {
|
|
902
|
+
// For EXISTSMARK, no transform needed
|
|
903
|
+
} else {
|
|
904
|
+
_ = voxgigstruct.SetProp(parent, key,
|
|
905
|
+
strings.ReplaceAll(v, NULLMARK, "null"))
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
func Fdt(data any) string {
|
|
911
|
+
return fdti(data, "")
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
func fdti(data any, indent string) string {
|
|
915
|
+
result := ""
|
|
916
|
+
|
|
917
|
+
switch v := data.(type) {
|
|
918
|
+
case map[string]any:
|
|
919
|
+
result += indent + "{\n"
|
|
920
|
+
for key, value := range v {
|
|
921
|
+
result += fmt.Sprintf("%s \"%s\": %s", indent, key, fdti(value, indent+" "))
|
|
922
|
+
}
|
|
923
|
+
result += indent + "}\n"
|
|
924
|
+
|
|
925
|
+
case []any:
|
|
926
|
+
result += indent + "[\n"
|
|
927
|
+
for _, value := range v {
|
|
928
|
+
result += fmt.Sprintf("%s - %s", indent, fdti(value, indent+" "))
|
|
929
|
+
}
|
|
930
|
+
result += indent + "]\n"
|
|
931
|
+
|
|
932
|
+
default:
|
|
933
|
+
result += fmt.Sprintf("%v (%s)\n", v, reflect.TypeOf(v))
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return result
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
func ToJSONString(data any) string {
|
|
940
|
+
jsonBytes, err := json.Marshal(data)
|
|
941
|
+
if err != nil {
|
|
942
|
+
return ""
|
|
943
|
+
}
|
|
944
|
+
return string(jsonBytes)
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
func uppercaseFirstLetter(s string) string {
|
|
948
|
+
if len(s) == 0 {
|
|
949
|
+
return s
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
runes := []rune(s)
|
|
953
|
+
runes[0] = unicode.ToUpper(runes[0])
|
|
954
|
+
return string(runes)
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// StructSDK is the test SDK client for the struct runner
|
|
958
|
+
type StructSDK struct {
|
|
959
|
+
opts map[string]any
|
|
960
|
+
utility *StructSDKUtility
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// StructSDKUtility implements the StructUtilityIF interface
|
|
964
|
+
type StructSDKUtility struct {
|
|
965
|
+
sdk *StructSDK
|
|
966
|
+
structu *StructUtility
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// Struct returns the StructUtility
|
|
970
|
+
func (u *StructSDKUtility) Struct() *StructUtility {
|
|
971
|
+
return u.structu
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Contextify implements the contextify function
|
|
975
|
+
func (u *StructSDKUtility) Contextify(ctxmap map[string]any) map[string]any {
|
|
976
|
+
return ctxmap
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// Check implements the check function
|
|
980
|
+
func (u *StructSDKUtility) Check(ctx map[string]any) map[string]any {
|
|
981
|
+
zed := "ZED"
|
|
982
|
+
if u.sdk.opts != nil {
|
|
983
|
+
if foo, ok := u.sdk.opts["foo"]; ok && foo != nil {
|
|
984
|
+
zed += fmt.Sprint(foo)
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
zed += "_"
|
|
988
|
+
|
|
989
|
+
if ctx == nil {
|
|
990
|
+
zed += "0"
|
|
991
|
+
} else if meta, ok := ctx["meta"].(map[string]any); ok && meta != nil {
|
|
992
|
+
if bar, ok := meta["bar"]; ok && bar != nil {
|
|
993
|
+
zed += fmt.Sprint(bar)
|
|
994
|
+
} else {
|
|
995
|
+
zed += "0"
|
|
996
|
+
}
|
|
997
|
+
} else {
|
|
998
|
+
zed += "0"
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
return map[string]any{
|
|
1002
|
+
"zed": zed,
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// NewStructSDK creates a new StructSDK instance with the given options
|
|
1007
|
+
func NewStructSDK(opts map[string]any) *StructSDK {
|
|
1008
|
+
if opts == nil {
|
|
1009
|
+
opts = map[string]any{}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
sdk := &StructSDK{
|
|
1013
|
+
opts: opts,
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// Create the StructUtility
|
|
1017
|
+
structUtil := &StructUtility{
|
|
1018
|
+
IsNode: voxgigstruct.IsNode,
|
|
1019
|
+
Clone: voxgigstruct.Clone,
|
|
1020
|
+
CloneFlags: voxgigstruct.CloneFlags,
|
|
1021
|
+
GetPath: voxgigstruct.GetPath,
|
|
1022
|
+
Inject: voxgigstruct.Inject,
|
|
1023
|
+
Items: voxgigstruct.Items,
|
|
1024
|
+
Stringify: voxgigstruct.Stringify,
|
|
1025
|
+
Walk: voxgigstruct.Walk,
|
|
1026
|
+
|
|
1027
|
+
DelProp: voxgigstruct.DelProp,
|
|
1028
|
+
EscRe: voxgigstruct.EscRe,
|
|
1029
|
+
EscUrl: voxgigstruct.EscUrl,
|
|
1030
|
+
Filter: voxgigstruct.Filter,
|
|
1031
|
+
Flatten: voxgigstruct.Flatten,
|
|
1032
|
+
GetDef: voxgigstruct.GetDef,
|
|
1033
|
+
GetElem: voxgigstruct.GetElem,
|
|
1034
|
+
GetProp: voxgigstruct.GetProp,
|
|
1035
|
+
HasKey: voxgigstruct.HasKey,
|
|
1036
|
+
IsEmpty: voxgigstruct.IsEmpty,
|
|
1037
|
+
IsFunc: voxgigstruct.IsFunc,
|
|
1038
|
+
IsKey: voxgigstruct.IsKey,
|
|
1039
|
+
IsList: voxgigstruct.IsList,
|
|
1040
|
+
IsMap: voxgigstruct.IsMap,
|
|
1041
|
+
Join: voxgigstruct.Join,
|
|
1042
|
+
Jsonify: voxgigstruct.Jsonify,
|
|
1043
|
+
KeysOf: voxgigstruct.KeysOf,
|
|
1044
|
+
Merge: voxgigstruct.Merge,
|
|
1045
|
+
Pad: voxgigstruct.Pad,
|
|
1046
|
+
Pathify: voxgigstruct.Pathify,
|
|
1047
|
+
Select: voxgigstruct.Select,
|
|
1048
|
+
SetPath: voxgigstruct.SetPath,
|
|
1049
|
+
SetProp: voxgigstruct.SetProp,
|
|
1050
|
+
Size: voxgigstruct.Size,
|
|
1051
|
+
Slice: voxgigstruct.Slice,
|
|
1052
|
+
StrKey: voxgigstruct.StrKey,
|
|
1053
|
+
Transform: voxgigstruct.Transform,
|
|
1054
|
+
Typify: voxgigstruct.Typify,
|
|
1055
|
+
Typename: voxgigstruct.Typename,
|
|
1056
|
+
Validate: voxgigstruct.Validate,
|
|
1057
|
+
|
|
1058
|
+
SKIP: voxgigstruct.SKIP,
|
|
1059
|
+
DELETE: voxgigstruct.DELETE,
|
|
1060
|
+
|
|
1061
|
+
Jo: voxgigstruct.Jo,
|
|
1062
|
+
Ja: voxgigstruct.Ja,
|
|
1063
|
+
|
|
1064
|
+
CheckPlacement: voxgigstruct.CheckPlacement,
|
|
1065
|
+
InjectorArgs: voxgigstruct.InjectorArgs,
|
|
1066
|
+
InjectChild: voxgigstruct.InjectChild,
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// Create the utility
|
|
1070
|
+
sdk.utility = &StructSDKUtility{
|
|
1071
|
+
sdk: sdk,
|
|
1072
|
+
structu: structUtil,
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
return sdk
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// MakeTestStructSDK creates a new StructSDK instance for testing
|
|
1079
|
+
func MakeTestStructSDK(opts map[string]any) (*StructSDK, error) {
|
|
1080
|
+
return NewStructSDK(opts), nil
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// Tester creates a new StructSDK instance with options or default options
|
|
1084
|
+
func (s *StructSDK) Tester(opts map[string]any) (*StructSDK, error) {
|
|
1085
|
+
if opts == nil {
|
|
1086
|
+
opts = s.opts
|
|
1087
|
+
}
|
|
1088
|
+
return NewStructSDK(opts), nil
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// Utility returns the utility object
|
|
1092
|
+
func (s *StructSDK) Utility() StructUtilityIF {
|
|
1093
|
+
return s.utility
|
|
1094
|
+
}
|