@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.
Files changed (157) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Feature.js +11 -9
  3. package/dist/cmp/Feature.js.map +1 -1
  4. package/dist/cmp/Main.js +6 -2
  5. package/dist/cmp/Main.js.map +1 -1
  6. package/model/sdkgen.jsonic +1 -1
  7. package/package.json +3 -3
  8. package/project/.sdk/model/feature/log.jsonic +1 -1
  9. package/project/.sdk/model/feature/test.jsonic +1 -1
  10. package/project/.sdk/model/target/go.jsonic +19 -4
  11. package/project/.sdk/model/target/ts.jsonic +3 -3
  12. package/project/.sdk/src/cmp/go/Config_go.ts +119 -0
  13. package/project/.sdk/src/cmp/go/EntityOperation_go.ts +48 -0
  14. package/project/.sdk/src/cmp/go/Entity_go.ts +67 -0
  15. package/project/.sdk/src/cmp/go/MainEntity_go.ts +22 -0
  16. package/project/.sdk/src/cmp/go/Main_go.ts +209 -0
  17. package/project/.sdk/src/cmp/go/Package_go.ts +89 -0
  18. package/project/.sdk/src/cmp/go/TestDirect_go.ts +373 -0
  19. package/project/.sdk/src/cmp/go/TestEntity_go.ts +341 -0
  20. package/project/.sdk/src/cmp/go/Test_go.ts +37 -0
  21. package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +146 -0
  22. package/project/.sdk/src/cmp/go/fragment/EntityCreateOp.fragment.go +35 -0
  23. package/project/.sdk/src/cmp/go/fragment/EntityListOp.fragment.go +26 -0
  24. package/project/.sdk/src/cmp/go/fragment/EntityLoadOp.fragment.go +38 -0
  25. package/project/.sdk/src/cmp/go/fragment/EntityRemoveOp.fragment.go +38 -0
  26. package/project/.sdk/src/cmp/go/fragment/EntityUpdateOp.fragment.go +38 -0
  27. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +250 -0
  28. package/project/.sdk/src/cmp/go/fragment/SdkError.fragment.go +22 -0
  29. package/project/.sdk/src/cmp/go/tsconfig.json +15 -0
  30. package/project/.sdk/src/cmp/go/utility_go.ts +88 -0
  31. package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +6 -6
  32. package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +6 -6
  33. package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +6 -6
  34. package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +6 -6
  35. package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +6 -6
  36. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +85 -1
  37. package/project/.sdk/src/cmp/ts/EntityOperation_ts.ts +1 -1
  38. package/project/.sdk/src/cmp/ts/Main_ts.ts +4 -4
  39. package/project/.sdk/src/cmp/ts/SdkError_ts.ts +42 -0
  40. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +288 -0
  41. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +14 -6
  42. package/project/.sdk/src/cmp/ts/Test_ts.ts +2 -0
  43. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +30 -0
  44. package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +12 -12
  45. package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +12 -12
  46. package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +12 -12
  47. package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +13 -13
  48. package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +12 -12
  49. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +102 -6
  50. package/project/.sdk/src/cmp/ts/fragment/SdkError.fragment.ts +25 -0
  51. package/project/.sdk/tm/go/Makefile +10 -0
  52. package/project/.sdk/tm/go/core/context.go +267 -0
  53. package/project/.sdk/tm/go/core/control.go +7 -0
  54. package/project/.sdk/tm/go/core/error.go +25 -0
  55. package/project/.sdk/tm/go/core/helpers.go +33 -0
  56. package/project/.sdk/tm/go/core/operation.go +61 -0
  57. package/project/.sdk/tm/go/core/response.go +55 -0
  58. package/project/.sdk/tm/go/core/result.go +73 -0
  59. package/project/.sdk/tm/go/core/spec.go +97 -0
  60. package/project/.sdk/tm/go/core/target.go +102 -0
  61. package/project/.sdk/tm/go/core/types.go +44 -0
  62. package/project/.sdk/tm/go/core/utility_type.go +54 -0
  63. package/project/.sdk/tm/go/feature/base_feature.go +40 -0
  64. package/project/.sdk/tm/go/feature/test_feature.go +196 -0
  65. package/project/.sdk/tm/go/src/feature/README.md +1 -0
  66. package/project/.sdk/tm/go/src/feature/base/.gitkeep +0 -0
  67. package/project/.sdk/tm/go/src/feature/test/.gitkeep +0 -0
  68. package/project/.sdk/tm/go/test/custom_utility_test.go +80 -0
  69. package/project/.sdk/tm/go/test/exists_test.go +16 -0
  70. package/project/.sdk/tm/go/test/primary_utility_test.go +899 -0
  71. package/project/.sdk/tm/go/test/runner_test.go +428 -0
  72. package/project/.sdk/tm/go/test/struct_runner_test.go +1094 -0
  73. package/project/.sdk/tm/go/test/struct_utility_test.go +1423 -0
  74. package/project/.sdk/tm/go/utility/clean.go +7 -0
  75. package/project/.sdk/tm/go/utility/done.go +20 -0
  76. package/project/.sdk/tm/go/utility/feature_add.go +10 -0
  77. package/project/.sdk/tm/go/utility/feature_hook.go +30 -0
  78. package/project/.sdk/tm/go/utility/feature_init.go +30 -0
  79. package/project/.sdk/tm/go/utility/fetcher.go +102 -0
  80. package/project/.sdk/tm/go/utility/make_context.go +7 -0
  81. package/project/.sdk/tm/go/utility/make_error.go +69 -0
  82. package/project/.sdk/tm/go/utility/make_fetch_def.go +44 -0
  83. package/project/.sdk/tm/go/utility/make_options.go +130 -0
  84. package/project/.sdk/tm/go/utility/make_request.go +59 -0
  85. package/project/.sdk/tm/go/utility/make_response.go +46 -0
  86. package/project/.sdk/tm/go/utility/make_result.go +55 -0
  87. package/project/.sdk/tm/go/utility/make_spec.go +68 -0
  88. package/project/.sdk/tm/go/utility/make_target.go +95 -0
  89. package/project/.sdk/tm/go/utility/make_url.go +41 -0
  90. package/project/.sdk/tm/go/utility/param.go +66 -0
  91. package/project/.sdk/tm/go/utility/prepare_auth.go +40 -0
  92. package/project/.sdk/tm/go/utility/prepare_body.go +14 -0
  93. package/project/.sdk/tm/go/utility/prepare_headers.go +22 -0
  94. package/project/.sdk/tm/go/utility/prepare_method.go +21 -0
  95. package/project/.sdk/tm/go/utility/prepare_params.go +41 -0
  96. package/project/.sdk/tm/go/utility/prepare_path.go +21 -0
  97. package/project/.sdk/tm/go/utility/prepare_query.go +47 -0
  98. package/project/.sdk/tm/go/utility/register.go +39 -0
  99. package/project/.sdk/tm/go/utility/result_basic.go +31 -0
  100. package/project/.sdk/tm/go/utility/result_body.go +17 -0
  101. package/project/.sdk/tm/go/utility/result_headers.go +22 -0
  102. package/project/.sdk/tm/go/utility/struct/go.mod +3 -0
  103. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +4891 -0
  104. package/project/.sdk/tm/go/utility/transform_request.go +32 -0
  105. package/project/.sdk/tm/go/utility/transform_response.go +45 -0
  106. package/project/.sdk/tm/js/src/feature/log/LogFeature.js +2 -2
  107. package/project/.sdk/tm/ts/src/Context.ts +144 -0
  108. package/project/.sdk/tm/ts/src/Control.ts +20 -0
  109. package/project/.sdk/tm/ts/src/Operation.ts +24 -0
  110. package/project/.sdk/tm/ts/src/Response.ts +26 -0
  111. package/project/.sdk/tm/ts/src/Result.ts +30 -0
  112. package/project/.sdk/tm/ts/src/Spec.ts +40 -0
  113. package/project/.sdk/tm/ts/src/Target.ts +36 -0
  114. package/project/.sdk/tm/ts/src/feature/base/BaseFeature.ts +1 -1
  115. package/project/.sdk/tm/ts/src/feature/log/LogFeature.ts +2 -2
  116. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +7 -7
  117. package/project/.sdk/tm/ts/src/types.ts +15 -205
  118. package/project/.sdk/tm/ts/src/utility/DoneUtility.ts +2 -3
  119. package/project/.sdk/tm/ts/src/utility/{AddfeatureUtility.ts → FeatureAddUtility.ts} +2 -2
  120. package/project/.sdk/tm/ts/src/utility/{InitfeatureUtility.ts → FeatureInitUtility.ts} +2 -2
  121. package/project/.sdk/tm/ts/src/utility/FetcherUtility.ts +5 -3
  122. package/project/.sdk/tm/ts/src/utility/{ErrorUtility.ts → MakeErrorUtility.ts} +4 -4
  123. package/project/.sdk/tm/ts/src/utility/MakeFetchDefUtility.ts +46 -0
  124. package/project/.sdk/tm/ts/src/utility/{OptionsUtility.ts → MakeOptionsUtility.ts} +10 -7
  125. package/project/.sdk/tm/ts/src/utility/MakeRequestUtility.ts +66 -0
  126. package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +61 -0
  127. package/project/.sdk/tm/ts/src/utility/{ResultUtility.ts → MakeResultUtility.ts} +6 -6
  128. package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +61 -0
  129. package/project/.sdk/tm/ts/src/utility/{SelectionUtility.ts → MakeTargetUtility.ts} +22 -24
  130. package/project/.sdk/tm/ts/src/utility/{FullurlUtility.ts → MakeUrlUtility.ts} +7 -8
  131. package/project/.sdk/tm/ts/src/utility/{FindparamUtility.ts → ParamUtility.ts} +21 -8
  132. package/project/.sdk/tm/ts/src/utility/{AuthUtility.ts → PrepareAuthUtility.ts} +4 -4
  133. package/project/.sdk/tm/ts/src/utility/{BodyUtility.ts → PrepareBodyUtility.ts} +7 -7
  134. package/project/.sdk/tm/ts/src/utility/{HeadersUtility.ts → PrepareHeadersUtility.ts} +5 -7
  135. package/project/.sdk/tm/ts/src/utility/{MethodUtility.ts → PrepareMethodUtility.ts} +6 -6
  136. package/project/.sdk/tm/ts/src/utility/PrepareParamsUtility.ts +37 -0
  137. package/project/.sdk/tm/ts/src/utility/PreparePathUtility.ts +3 -4
  138. package/project/.sdk/tm/ts/src/utility/PrepareQueryUtility.ts +30 -0
  139. package/project/.sdk/tm/ts/src/utility/{ResbasicUtility.ts → ResultBasicUtility.ts} +11 -6
  140. package/project/.sdk/tm/ts/src/utility/{ResbodyUtility.ts → ResultBodyUtility.ts} +4 -3
  141. package/project/.sdk/tm/ts/src/utility/{ResheadersUtility.ts → ResultHeadersUtility.ts} +4 -3
  142. package/project/.sdk/tm/ts/src/utility/{ReqformUtility.ts → TransformRequestUtility.ts} +9 -7
  143. package/project/.sdk/tm/ts/src/utility/{ResformUtility.ts → TransformResponseUtility.ts} +10 -7
  144. package/project/.sdk/tm/ts/src/utility/Utility.ts +47 -62
  145. package/project/.sdk/tm/ts/test/runner.ts +25 -4
  146. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +386 -175
  147. package/src/cmp/Feature.ts +11 -9
  148. package/src/cmp/Main.ts +8 -2
  149. package/project/.sdk/tm/ts/src/utility/JoinurlUtility.ts +0 -15
  150. package/project/.sdk/tm/ts/src/utility/OperationUtility.ts +0 -23
  151. package/project/.sdk/tm/ts/src/utility/ParamsUtility.ts +0 -37
  152. package/project/.sdk/tm/ts/src/utility/QueryUtility.ts +0 -27
  153. package/project/.sdk/tm/ts/src/utility/RequestUtility.ts +0 -83
  154. package/project/.sdk/tm/ts/src/utility/ResponseUtility.ts +0 -61
  155. package/project/.sdk/tm/ts/src/utility/SpecUtility.ts +0 -68
  156. /package/project/.sdk/tm/ts/src/utility/{FeaturehookUtility.ts → FeatureHookUtility.ts} +0 -0
  157. /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
+ }