@voxgig/sdkgen 4.17.2 → 4.17.3
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
CHANGED
package/package.json
CHANGED
|
@@ -227,9 +227,7 @@ func driveEntityOpUntil(t *testing.T, client *sdk.ProjectNameSDK, what string, s
|
|
|
227
227
|
clientval := reflect.ValueOf(client)
|
|
228
228
|
|
|
229
229
|
for name := range entities {
|
|
230
|
-
|
|
231
|
-
runes[0] = unicode.ToUpper(runes[0])
|
|
232
|
-
accessor := clientval.MethodByName(string(runes))
|
|
230
|
+
accessor := clientval.MethodByName(exported(name))
|
|
233
231
|
if !accessor.IsValid() || 1 != accessor.Type().NumIn() {
|
|
234
232
|
continue
|
|
235
233
|
}
|
|
@@ -258,6 +256,96 @@ func driveEntityOpUntil(t *testing.T, client *sdk.ProjectNameSDK, what string, s
|
|
|
258
256
|
t.Fatalf("no entity operation %s - nothing to assert on", what)
|
|
259
257
|
}
|
|
260
258
|
|
|
259
|
+
// exported is the accessor name for an entity: the model spells entities in
|
|
260
|
+
// lower case, the generated client exports them capitalised.
|
|
261
|
+
func exported(name string) string {
|
|
262
|
+
runes := []rune(name)
|
|
263
|
+
if 0 < len(runes) {
|
|
264
|
+
runes[0] = unicode.ToUpper(runes[0])
|
|
265
|
+
}
|
|
266
|
+
return string(runes)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// routableOp names an entity operation that actually REACHES THE TRANSPORT.
|
|
270
|
+
//
|
|
271
|
+
// A generated entity exposes List, Load and Create whether or not the model
|
|
272
|
+
// declares them, so "drive the first entity the map yields" drives a dead
|
|
273
|
+
// end whenever the model does not declare that one. Go randomises map
|
|
274
|
+
// iteration, so which entity that is changes per run, and a test asserting
|
|
275
|
+
// on what reached the wire then passes or fails by lottery.
|
|
276
|
+
//
|
|
277
|
+
// Discover a real route ONCE, against a throwaway client whose provider
|
|
278
|
+
// always succeeds so nothing gates it, and drive that everywhere a test
|
|
279
|
+
// needs a request to come out.
|
|
280
|
+
func routableOp(t *testing.T) (string, string) {
|
|
281
|
+
t.Helper()
|
|
282
|
+
|
|
283
|
+
w := makewire()
|
|
284
|
+
client := secretsClient(w, map[string]any{
|
|
285
|
+
"feature": map[string]any{"secrets": map[string]any{
|
|
286
|
+
"active": true,
|
|
287
|
+
"providers": []any{
|
|
288
|
+
&customProvider{
|
|
289
|
+
lookup: func(name string) (string, bool, error) { return "PROBE01", true, nil },
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
}},
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
entities, _ := client.OptionsMap()["entity"].(map[string]any)
|
|
296
|
+
clientval := reflect.ValueOf(client)
|
|
297
|
+
|
|
298
|
+
for name := range entities {
|
|
299
|
+
accessor := clientval.MethodByName(exported(name))
|
|
300
|
+
if !accessor.IsValid() || 1 != accessor.Type().NumIn() {
|
|
301
|
+
continue
|
|
302
|
+
}
|
|
303
|
+
ent := accessor.Call([]reflect.Value{reflect.Zero(accessor.Type().In(0))})[0]
|
|
304
|
+
|
|
305
|
+
for _, opname := range []string{"List", "Load"} {
|
|
306
|
+
op := ent.MethodByName(opname)
|
|
307
|
+
if !op.IsValid() {
|
|
308
|
+
continue
|
|
309
|
+
}
|
|
310
|
+
before := len(w.api())
|
|
311
|
+
in := make([]reflect.Value, op.Type().NumIn())
|
|
312
|
+
for i := range in {
|
|
313
|
+
in[i] = reflect.Zero(op.Type().In(i))
|
|
314
|
+
}
|
|
315
|
+
op.Call(in)
|
|
316
|
+
if before < len(w.api()) {
|
|
317
|
+
return name, opname
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
t.Fatal("no entity operation reaches the transport - nothing to assert on")
|
|
323
|
+
return "", ""
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// driveNamedOp drives ONE operation already known to be routable.
|
|
327
|
+
//
|
|
328
|
+
// Safe to call from a spawned goroutine, unlike driveEntityOpUntil: it never
|
|
329
|
+
// calls t.Fatal, which Go permits only from the test goroutine.
|
|
330
|
+
func driveNamedOp(t *testing.T, client *sdk.ProjectNameSDK, entity string, opname string) {
|
|
331
|
+
t.Helper()
|
|
332
|
+
|
|
333
|
+
accessor := reflect.ValueOf(client).MethodByName(exported(entity))
|
|
334
|
+
if !accessor.IsValid() || 1 != accessor.Type().NumIn() {
|
|
335
|
+
return
|
|
336
|
+
}
|
|
337
|
+
ent := accessor.Call([]reflect.Value{reflect.Zero(accessor.Type().In(0))})[0]
|
|
338
|
+
op := ent.MethodByName(opname)
|
|
339
|
+
if !op.IsValid() {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
in := make([]reflect.Value, op.Type().NumIn())
|
|
343
|
+
for i := range in {
|
|
344
|
+
in[i] = reflect.Zero(op.Type().In(i))
|
|
345
|
+
}
|
|
346
|
+
op.Call(in)
|
|
347
|
+
}
|
|
348
|
+
|
|
261
349
|
// driveEntityOp drives ops until one request reached the recorder.
|
|
262
350
|
func driveEntityOp(t *testing.T, client *sdk.ProjectNameSDK, w *wire) {
|
|
263
351
|
t.Helper()
|
|
@@ -975,6 +1063,8 @@ func TestSecretsMalformedProviderEntry(t *testing.T) {
|
|
|
975
1063
|
// SUCCEEDS reopens it and every waiting operation goes out with the FRESH
|
|
976
1064
|
// credential - never the stale pre-failure header, and never nothing.
|
|
977
1065
|
func TestSecretsGateRecovery(t *testing.T) {
|
|
1066
|
+
entity, opname := routableOp(t)
|
|
1067
|
+
|
|
978
1068
|
var mu sync.Mutex
|
|
979
1069
|
mode := "fail"
|
|
980
1070
|
release := make(chan struct{})
|
|
@@ -1004,7 +1094,12 @@ func TestSecretsGateRecovery(t *testing.T) {
|
|
|
1004
1094
|
})
|
|
1005
1095
|
|
|
1006
1096
|
// 1. The failure closes the gate: nothing reaches the wire.
|
|
1007
|
-
|
|
1097
|
+
//
|
|
1098
|
+
// The operation is known to be routable, so an empty wire PROVES the gate
|
|
1099
|
+
// refused it. Driving an arbitrary entity would pass here for the wrong
|
|
1100
|
+
// reason: an operation the model never declared reaches no transport
|
|
1101
|
+
// whether the gate is open or shut.
|
|
1102
|
+
driveNamedOp(t, client, entity, opname)
|
|
1008
1103
|
if 0 != len(w.api()) {
|
|
1009
1104
|
t.Fatalf("a failed resolution must keep the wire silent, saw %d calls", len(w.api()))
|
|
1010
1105
|
}
|
|
@@ -1020,7 +1115,7 @@ func TestSecretsGateRecovery(t *testing.T) {
|
|
|
1020
1115
|
wg.Add(1)
|
|
1021
1116
|
go func() {
|
|
1022
1117
|
defer wg.Done()
|
|
1023
|
-
|
|
1118
|
+
driveNamedOp(t, client, entity, opname)
|
|
1024
1119
|
}()
|
|
1025
1120
|
}
|
|
1026
1121
|
go func() {
|
|
@@ -1032,7 +1127,10 @@ func TestSecretsGateRecovery(t *testing.T) {
|
|
|
1032
1127
|
for _, call := range w.api() {
|
|
1033
1128
|
credentialIs(t, call.auth, "FRESH01")
|
|
1034
1129
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1130
|
+
// BOTH, not "at least one": the claim is that a concurrent retry serves
|
|
1131
|
+
// every caller waiting on it, so one call reaching the wire while the
|
|
1132
|
+
// other is dropped is exactly the failure this guards.
|
|
1133
|
+
if 2 != len(w.api()) {
|
|
1134
|
+
t.Fatalf("recovery must let both operations out, saw %d", len(w.api()))
|
|
1037
1135
|
}
|
|
1038
1136
|
}
|