@voxgig/sdkgen 4.3.0 → 4.4.1
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/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +13 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +1 -1
- package/project/.sdk/tm/c/tests/primary_corpus_test.c +565 -0
- package/project/.sdk/tm/c/utility/prepare_method.c +3 -1
- package/project/.sdk/tm/c/utility/transform_request.c +4 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +55 -5
- package/project/.sdk/tm/clojure/test/sdk/test/primary.clj +171 -1
- package/project/.sdk/tm/clojure/test/sdk/test/struct_corpus.clj +13 -2
- package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +2 -0
- package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +104 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +82 -2
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +50 -2
- package/project/.sdk/tm/elixir/test/primary_utility_test.exs +18 -200
- package/project/.sdk/tm/elixir/test/support/struct_corpus.ex +413 -2
- package/project/.sdk/tm/go/test/feature_corpus_test.go +18 -0
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +8 -0
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +6 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +56 -2
- package/project/.sdk/tm/lua/utility/make_options.lua +22 -2
- package/project/.sdk/tm/ocaml/Makefile +15 -4
- package/project/.sdk/tm/ocaml/sdk_features.ml +224 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +8 -1
- package/project/.sdk/tm/ocaml/test/corpus_runner.ml +185 -0
- package/project/.sdk/tm/ocaml/test/primary_utility_test.ml +238 -0
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +5 -160
- package/project/.sdk/tm/perl/t/feature_corpus.t +9 -1
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +11 -0
- package/project/.sdk/tm/py/test/feature_harness.py +6 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +6 -0
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +6 -1
- package/project/.sdk/tm/rust/feature/cost.rs +25 -1
- package/project/.sdk/tm/scala/Makefile +1 -0
- package/project/.sdk/tm/scala/sdktest/PrimaryCorpus.scala +294 -0
- package/project/.sdk/tm/scala/sdktest/StructCorpus.scala +0 -0
- package/project/.sdk/tm/scala/utility/Make.scala +49 -2
- package/project/.sdk/tm/scala/utility/Prepare.scala +3 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -3
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +13 -2
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +12 -1
- package/project/.sdk/tm/zig/core/utility.zig +8 -1
- package/project/.sdk/tm/zig/test/primary_utility_test.zig +445 -0
- package/project/.sdk/tm/zig/test/struct_runner.zig +238 -0
- package/project/sdkgen-package.json +1 -1
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
(* ProjectName SDK primary-utility corpus.
|
|
2
|
+
*
|
|
3
|
+
* Drives the SHARED language-neutral corpus (.sdk/test/test.json -> "primary")
|
|
4
|
+
* through this SDK's request-shaping utilities, so the cases cannot drift from
|
|
5
|
+
* the reference implementation. Each section is looked up with getSpec and
|
|
6
|
+
* executed with runset, as the ts/js reference harness does.
|
|
7
|
+
*
|
|
8
|
+
* ocaml had NO primary-utility suite at all before this: its request-shaping
|
|
9
|
+
* utilities were unverified in every language-neutral sense. The harness is
|
|
10
|
+
* Corpus_runner, shared with the struct corpus.
|
|
11
|
+
*)
|
|
12
|
+
|
|
13
|
+
open Voxgig_struct
|
|
14
|
+
open Sdk_types
|
|
15
|
+
open Sdk_helpers
|
|
16
|
+
open Sdk_runtime
|
|
17
|
+
open Corpus_runner
|
|
18
|
+
|
|
19
|
+
let client () : sdk_client = Sdk_client.test ()
|
|
20
|
+
|
|
21
|
+
(* A client built from a section's DEF.setup.a block. prepare_auth reads the
|
|
22
|
+
* CLIENT's options, as the ts reference does via client.options(), so a
|
|
23
|
+
* section's setup cannot reach it through ctx.options. *)
|
|
24
|
+
let client_for primary name =
|
|
25
|
+
let setup =
|
|
26
|
+
List.fold_left (fun acc k -> getprop_raw_pub acc k) primary [name; "DEF"; "setup"; "a"]
|
|
27
|
+
in
|
|
28
|
+
match setup with
|
|
29
|
+
| Map _ -> Sdk_client.test_with Noval setup
|
|
30
|
+
| _ -> Sdk_client.test ()
|
|
31
|
+
|
|
32
|
+
let getspec root path =
|
|
33
|
+
List.fold_left (fun acc k -> getprop_raw_pub acc k) root path
|
|
34
|
+
|
|
35
|
+
(* A LIVE context from a corpus map. The utilities read and MUTATE spec,
|
|
36
|
+
* result and response through their record types, so a bare value map leaves
|
|
37
|
+
* them nothing to work on and every match reads null. *)
|
|
38
|
+
let ctx_from (cl : sdk_client) (ctxmap : value) : ctx =
|
|
39
|
+
let u = cl.cl_utility in
|
|
40
|
+
(* The corpus names the op — {"ctx": {"opname": "create"}} — and
|
|
41
|
+
* prepare_method reads it. Hardcoding "load" made every method GET. *)
|
|
42
|
+
(* Only when the corpus names one. Defaulting to "load" made the SDK report
|
|
43
|
+
* the wrong operation in error messages the corpus matches on — it expects
|
|
44
|
+
* "unknown operation" where no op is named. *)
|
|
45
|
+
let cs =
|
|
46
|
+
match getprop_raw_pub ctxmap "opname" with
|
|
47
|
+
| Str s -> { (default_ctxspec ()) with cs_opname = Some s }
|
|
48
|
+
| _ -> default_ctxspec ()
|
|
49
|
+
in
|
|
50
|
+
let c = u.u_make_context cs cl.cl_rootctx in
|
|
51
|
+
(match getprop_raw_pub ctxmap "spec" with
|
|
52
|
+
| Map _ as m -> c.c_spec <- Some (new_spec m)
|
|
53
|
+
| _ -> ());
|
|
54
|
+
(match getprop_raw_pub ctxmap "result" with
|
|
55
|
+
| Map _ as m ->
|
|
56
|
+
let rt = new_result m in
|
|
57
|
+
(* new_result hardcodes rt_err = None, so a corpus result carrying an err
|
|
58
|
+
* arrives empty and result_basic has no previous message to prepend —
|
|
59
|
+
* it produced "request: 400: BAD" where the contract says
|
|
60
|
+
* "Foo: request: 400: BAD". The lua and elixir drivers build it too. *)
|
|
61
|
+
(match getprop_raw_pub m "err" with
|
|
62
|
+
| Map _ as em ->
|
|
63
|
+
(match getprop_raw_pub em "message" with
|
|
64
|
+
| Str msg when msg <> "" ->
|
|
65
|
+
rt.rt_err <- Some { err_code = ""; err_msg = msg; err_result = Noval; err_spec = Noval }
|
|
66
|
+
| _ -> ())
|
|
67
|
+
| _ -> ());
|
|
68
|
+
c.c_result <- Some rt
|
|
69
|
+
| _ -> ());
|
|
70
|
+
(match getprop_raw_pub ctxmap "response" with
|
|
71
|
+
| Map _ as m ->
|
|
72
|
+
let r = new_response m in
|
|
73
|
+
(* result_body_util reads response.json and requires it to be CALLABLE;
|
|
74
|
+
* the corpus supplies a plain `body`, so wrap it, as the lua and elixir
|
|
75
|
+
* drivers do. Without this every ctx.result.body match reads empty. *)
|
|
76
|
+
(match getprop_raw_pub m "body" with
|
|
77
|
+
| Noval -> ()
|
|
78
|
+
| b -> r.rs_json <- Func (fun _ _ _ _ -> b));
|
|
79
|
+
(* Header names arrive from the wire in any case and the contract is
|
|
80
|
+
* lowercase; the lua and elixir drivers normalise here rather than in
|
|
81
|
+
* result_headers_util, which copies them verbatim. *)
|
|
82
|
+
(match getprop_raw_pub m "headers" with
|
|
83
|
+
| Map hm ->
|
|
84
|
+
let low = empty_map () in
|
|
85
|
+
List.iter (fun (k, v) -> ignore (setprop low (Str (String.lowercase_ascii k)) v))
|
|
86
|
+
hm.entries;
|
|
87
|
+
r.rs_headers <- low
|
|
88
|
+
| _ -> ());
|
|
89
|
+
c.c_response <- Some r
|
|
90
|
+
| _ -> ());
|
|
91
|
+
(match getprop_raw_pub ctxmap "point" with
|
|
92
|
+
| Map _ as m -> c.c_point <- m
|
|
93
|
+
| _ -> ());
|
|
94
|
+
(match getprop_raw_pub ctxmap "reqdata" with Noval -> () | v -> c.c_reqdata <- v);
|
|
95
|
+
(match getprop_raw_pub ctxmap "reqmatch" with Noval -> () | v -> c.c_reqmatch <- v);
|
|
96
|
+
(match getprop_raw_pub ctxmap "data" with Noval -> () | v -> c.c_data <- v);
|
|
97
|
+
(match getprop_raw_pub ctxmap "match" with Noval -> () | v -> c.c_match <- v);
|
|
98
|
+
c
|
|
99
|
+
|
|
100
|
+
(* The corpus speaks camelCase; this port stores rs_status_text / rt_ok. A
|
|
101
|
+
* neutral-named view is what the match assertions read. *)
|
|
102
|
+
let result_value (r : result option) : value =
|
|
103
|
+
match r with
|
|
104
|
+
| None -> Noval
|
|
105
|
+
| Some rt -> result_to_value rt
|
|
106
|
+
|
|
107
|
+
let ctx_arg args = match args with x :: _ -> x | [] -> Noval
|
|
108
|
+
|
|
109
|
+
(* The harness reports a non-Struct_error exception via Printexc, which renders
|
|
110
|
+
* a branded SDK error as "Sdk_types.Sdk_error_exc(_)" and loses the message the
|
|
111
|
+
* corpus matches on. Convert here rather than teaching the shared harness about
|
|
112
|
+
* SDK types — corpus_runner is struct's, not this SDK's. *)
|
|
113
|
+
let run_guarded f c =
|
|
114
|
+
try f c with
|
|
115
|
+
| Sdk_error_exc e -> raise (Struct_error e.err_msg)
|
|
116
|
+
|
|
117
|
+
(* Publish the MUTATED ctx back onto the corpus map the match reads.
|
|
118
|
+
* check_result matches against entry.ctx, which is the raw corpus map; the
|
|
119
|
+
* utilities mutate the live records beside it, so without this every
|
|
120
|
+
* `match: ctx.spec.*` / `ctx.result.*` assertion reads empty. Neutral names,
|
|
121
|
+
* because the corpus is camelCase and this port stores rs_status_text/rt_ok. *)
|
|
122
|
+
let publish (ctxmap : value) (c : ctx) =
|
|
123
|
+
(match c.c_spec with
|
|
124
|
+
| Some sp -> ignore (setprop ctxmap (Str "spec") (spec_to_value sp))
|
|
125
|
+
| None -> ());
|
|
126
|
+
(match c.c_result with
|
|
127
|
+
| Some _ -> ignore (setprop ctxmap (Str "result") (result_value c.c_result))
|
|
128
|
+
| None -> ());
|
|
129
|
+
(match c.c_response with
|
|
130
|
+
| Some _ -> ignore (setprop ctxmap (Str "response") (Str "__EXISTS__"))
|
|
131
|
+
| None -> ());
|
|
132
|
+
()
|
|
133
|
+
|
|
134
|
+
let () =
|
|
135
|
+
let testfile = if Array.length Sys.argv > 1 then Sys.argv.(1) else "../.sdk/test/test.json" in
|
|
136
|
+
let ic = open_in_bin testfile in
|
|
137
|
+
let len = in_channel_length ic in
|
|
138
|
+
let raw = really_input_string ic len in
|
|
139
|
+
close_in ic;
|
|
140
|
+
let alltests = json_read raw in
|
|
141
|
+
let primary = getprop_raw_pub alltests "primary" in
|
|
142
|
+
|
|
143
|
+
let cl = client () in
|
|
144
|
+
let u = cl.cl_utility in
|
|
145
|
+
|
|
146
|
+
(* Sections configured by their own DEF.setup block get their own client. *)
|
|
147
|
+
let ctx_section_with cl2 primary name f =
|
|
148
|
+
run_set name (getspec primary [name; "basic"])
|
|
149
|
+
(fun args ->
|
|
150
|
+
let ctxmap = ctx_arg args in
|
|
151
|
+
let c = ctx_from cl2 ctxmap in
|
|
152
|
+
let out = run_guarded f c in
|
|
153
|
+
publish ctxmap c;
|
|
154
|
+
out)
|
|
155
|
+
in
|
|
156
|
+
|
|
157
|
+
let ctx_section primary name f =
|
|
158
|
+
run_set name (getspec primary [name; "basic"])
|
|
159
|
+
(fun args ->
|
|
160
|
+
let ctxmap = ctx_arg args in
|
|
161
|
+
let c = ctx_from cl ctxmap in
|
|
162
|
+
let out = run_guarded f c in
|
|
163
|
+
publish ctxmap c;
|
|
164
|
+
out)
|
|
165
|
+
in
|
|
166
|
+
|
|
167
|
+
ctx_section primary "done" (fun c -> u.u_done c);
|
|
168
|
+
ctx_section primary "makeUrl" (fun c -> match make_url_util c with (s, _) -> Str s);
|
|
169
|
+
ctx_section primary "makeRequest"
|
|
170
|
+
(fun c -> ignore (make_request_util c); result_value c.c_result);
|
|
171
|
+
ctx_section primary "makeResponse"
|
|
172
|
+
(fun c -> ignore (make_response_util c); result_value c.c_result);
|
|
173
|
+
ctx_section_with (client_for primary "makeSpec") primary "makeSpec"
|
|
174
|
+
(fun c -> match make_spec_util c with
|
|
175
|
+
| (Some s, _) -> spec_to_value s | _ -> Noval);
|
|
176
|
+
ctx_section_with (client_for primary "prepareAuth") primary "prepareAuth"
|
|
177
|
+
(fun c -> ignore (prepare_auth_util c);
|
|
178
|
+
match c.c_spec with Some s -> spec_to_value s | None -> Noval);
|
|
179
|
+
ctx_section primary "prepareBody" (fun c -> prepare_body_util c);
|
|
180
|
+
ctx_section primary "prepareHeaders" (fun c -> prepare_headers_util c);
|
|
181
|
+
ctx_section primary "prepareMethod"
|
|
182
|
+
(fun c -> match prepare_method_util c with "" -> Noval | m -> Str m);
|
|
183
|
+
ctx_section primary "prepareParams" (fun c -> prepare_params_util c);
|
|
184
|
+
ctx_section primary "preparePath" (fun c -> Str (prepare_path_util c));
|
|
185
|
+
ctx_section primary "prepareQuery" (fun c -> prepare_query_util c);
|
|
186
|
+
ctx_section primary "resultBasic" (fun c -> result_basic_util c; result_value c.c_result);
|
|
187
|
+
ctx_section primary "resultBody" (fun c -> result_body_util c; result_value c.c_result);
|
|
188
|
+
ctx_section primary "resultHeaders" (fun c -> result_headers_util c; result_value c.c_result);
|
|
189
|
+
ctx_section primary "transformRequest" (fun c -> transform_request_util c);
|
|
190
|
+
ctx_section primary "transformResponse" (fun c -> transform_response_util c);
|
|
191
|
+
|
|
192
|
+
(* Sections that take a bare map or explicit args rather than a ctx. *)
|
|
193
|
+
let arg_section primary name f =
|
|
194
|
+
run_set name (getspec primary [name; "basic"]) (fun args -> run_guarded f args)
|
|
195
|
+
in
|
|
196
|
+
|
|
197
|
+
arg_section primary "makeContext" (fun args ->
|
|
198
|
+
let inv = ctx_arg args in
|
|
199
|
+
let c = ctx_from cl inv in
|
|
200
|
+
jo [("op", jo [("entity", Str c.c_op.op_entity); ("name", Str c.c_op.op_name);
|
|
201
|
+
("input", Str c.c_op.op_input); ("points", c.c_op.op_points)])]);
|
|
202
|
+
|
|
203
|
+
arg_section primary "makeOptions" (fun args ->
|
|
204
|
+
let inv = ctx_arg args in
|
|
205
|
+
let c = ctx_from cl (jo []) in
|
|
206
|
+
c.c_config <- getprop_raw_pub inv "config";
|
|
207
|
+
c.c_options <- getprop_raw_pub inv "options";
|
|
208
|
+
make_options_util c);
|
|
209
|
+
|
|
210
|
+
arg_section primary "makeError" (fun args ->
|
|
211
|
+
let a0 = ctx_arg args in
|
|
212
|
+
let a1 = (match args with _ :: y :: _ -> y | _ -> Noval) in
|
|
213
|
+
let c = ctx_from cl a0 in
|
|
214
|
+
let msg = (match getprop_raw_pub a1 "message" with Str m -> m | _ -> "") in
|
|
215
|
+
let e = { err_code = ""; err_msg = msg; err_result = Noval; err_spec = Noval } in
|
|
216
|
+
let out = make_error_util c (if msg = "" then None else Some e) in
|
|
217
|
+
publish a0 c;
|
|
218
|
+
out);
|
|
219
|
+
|
|
220
|
+
arg_section primary "operator" (fun args ->
|
|
221
|
+
let inv = ctx_arg args in
|
|
222
|
+
let op = new_operation inv in
|
|
223
|
+
jo [("entity", Str op.op_entity); ("input", Str op.op_input);
|
|
224
|
+
("name", Str op.op_name); ("points", op.op_points)]);
|
|
225
|
+
|
|
226
|
+
arg_section primary "param" (fun args ->
|
|
227
|
+
let a0 = ctx_arg args in
|
|
228
|
+
let a1 = (match args with _ :: y :: _ -> y | _ -> Noval) in
|
|
229
|
+
let c = ctx_from cl a0 in
|
|
230
|
+
let out = param_util c a1 in
|
|
231
|
+
publish a0 c;
|
|
232
|
+
out);
|
|
233
|
+
|
|
234
|
+
List.iter print_endline (List.rev !failures);
|
|
235
|
+
Printf.printf "\nPRIMARY CORPUS: PASS %d FAIL %d\n" !npass !nfail;
|
|
236
|
+
(* A run that executes nothing is not a pass. *)
|
|
237
|
+
if !npass = 0 then (print_endline "the primary corpus executed no cases"; exit 1);
|
|
238
|
+
if !nfail > 0 then exit 1
|
|
@@ -1,165 +1,10 @@
|
|
|
1
|
-
(*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
(* Struct corpus: drives test.json -> "struct" through the vendored
|
|
2
|
+
* Voxgig_struct implementation. The harness itself lives in Corpus_runner so
|
|
3
|
+
* the primary-utility suite can share it.
|
|
4
|
+
*)
|
|
4
5
|
|
|
5
6
|
open Voxgig_struct
|
|
6
|
-
|
|
7
|
-
let nullmark = "__NULL__"
|
|
8
|
-
let undefmark = "__UNDEF__"
|
|
9
|
-
let existsmark = "__EXISTS__"
|
|
10
|
-
|
|
11
|
-
(* The JSON reader now lives in the runtime (sdk_json.ml) so the SDK's
|
|
12
|
-
* generated config can use it; the corpus runs against that same function
|
|
13
|
-
* rather than a second copy that could drift from it. *)
|
|
14
|
-
let json_read = Sdk_json.json_read
|
|
15
|
-
|
|
16
|
-
(* ---------------- fixJSON / equality ---------------- *)
|
|
17
|
-
|
|
18
|
-
let rec fix_json v flag_null =
|
|
19
|
-
match v with
|
|
20
|
-
| Noval | Null -> if flag_null then Str nullmark else v
|
|
21
|
-
| Map m -> let o = empty_map () in
|
|
22
|
-
List.iter (fun (k, x) -> ignore (setprop o (Str k) (fix_json x flag_null))) m.entries; o
|
|
23
|
-
| List r -> lst (List.map (fun x -> fix_json x flag_null) !r)
|
|
24
|
-
| _ -> v
|
|
25
|
-
|
|
26
|
-
(* Order-independent deep equality for maps; sequence equality for lists. *)
|
|
27
|
-
let rec eqv a b =
|
|
28
|
-
match a, b with
|
|
29
|
-
| (Noval | Null), (Noval | Null) -> true
|
|
30
|
-
| Bool x, Bool y -> x = y
|
|
31
|
-
| Num x, Num y -> x = y
|
|
32
|
-
| Str x, Str y -> x = y
|
|
33
|
-
| List x, List y -> List.length !x = List.length !y && List.for_all2 eqv !x !y
|
|
34
|
-
| Map x, Map y ->
|
|
35
|
-
omap_len x = omap_len y &&
|
|
36
|
-
List.for_all (fun (k, v) -> match omap_get y k with Some w -> eqv v w | None -> false) x.entries
|
|
37
|
-
| _ -> a == b
|
|
38
|
-
|
|
39
|
-
(* ---------------- match support ---------------- *)
|
|
40
|
-
|
|
41
|
-
let matchval check base =
|
|
42
|
-
let check = if check = Str undefmark || check = Str nullmark then Noval else check in
|
|
43
|
-
if eqv check base then true
|
|
44
|
-
else match check with
|
|
45
|
-
| Str cs ->
|
|
46
|
-
let basestr = stringify base in
|
|
47
|
-
if String.length cs >= 2 && cs.[0] = '/' && cs.[String.length cs - 1] = '/' then
|
|
48
|
-
Vregex.test_str (String.sub cs 1 (String.length cs - 2)) basestr
|
|
49
|
-
else
|
|
50
|
-
let low s = String.lowercase_ascii s in
|
|
51
|
-
let contains hay needle =
|
|
52
|
-
let hl = String.length hay and nl = String.length needle in
|
|
53
|
-
let rec go i = if i + nl > hl then false
|
|
54
|
-
else if String.sub hay i nl = needle then true else go (i + 1) in
|
|
55
|
-
nl = 0 || go 0 in
|
|
56
|
-
contains (low basestr) (low (stringify check))
|
|
57
|
-
| Func _ -> true
|
|
58
|
-
| _ -> false
|
|
59
|
-
|
|
60
|
-
let do_match check base =
|
|
61
|
-
let base = clone base in
|
|
62
|
-
ignore (walk ~before:(fun _k v _p path ->
|
|
63
|
-
(if not (isnode v) then begin
|
|
64
|
-
let baseval = getpath base path in
|
|
65
|
-
if eqv baseval v then ()
|
|
66
|
-
else if v = Str undefmark && is_nullish baseval then ()
|
|
67
|
-
else if v = Str existsmark && not (is_nullish baseval) then ()
|
|
68
|
-
else if not (matchval v baseval) then
|
|
69
|
-
raise (Struct_error (Printf.sprintf "MATCH: %s: [%s] <=> [%s]"
|
|
70
|
-
(String.concat "." (List.map js_string (match path with List r -> !r | _ -> [])))
|
|
71
|
-
(stringify v) (stringify baseval)))
|
|
72
|
-
end);
|
|
73
|
-
v) check)
|
|
74
|
-
|
|
75
|
-
(* ---------------- result tracking ---------------- *)
|
|
76
|
-
|
|
77
|
-
let npass = ref 0
|
|
78
|
-
let nfail = ref 0
|
|
79
|
-
let failures = ref []
|
|
80
|
-
|
|
81
|
-
let record group name ok msg =
|
|
82
|
-
if ok then incr npass
|
|
83
|
-
else (incr nfail; failures := Printf.sprintf "FAIL %s %s - %s" group name msg :: !failures)
|
|
84
|
-
|
|
85
|
-
(* ---------------- per-entry runner ---------------- *)
|
|
86
|
-
|
|
87
|
-
let omap_v kvs =
|
|
88
|
-
let m = empty_map () in
|
|
89
|
-
List.iter (fun (k, v) -> ignore (setprop m (Str k) v)) kvs; m
|
|
90
|
-
|
|
91
|
-
let getprop_raw_pub e k = (match e with Map m -> (match omap_get m k with Some x -> x | None -> Noval) | _ -> Noval)
|
|
92
|
-
let entry_get e k = getprop_raw_pub e k
|
|
93
|
-
let entry_has e k = match e with Map m -> omap_has m k | _ -> false
|
|
94
|
-
let default_injdef_pub () =
|
|
95
|
-
{ d_meta = Noval; d_extra = Noval; d_errs = Noval; d_modify = None; d_handler = None;
|
|
96
|
-
d_base = Noval; d_dparent = Noval; d_dpath = Noval; d_key = Noval }
|
|
97
|
-
|
|
98
|
-
let resolve_args entry =
|
|
99
|
-
if entry_has entry "ctx" then [entry_get entry "ctx"]
|
|
100
|
-
else if entry_has entry "args" then (match entry_get entry "args" with List r -> !r | _ -> [])
|
|
101
|
-
else if entry_has entry "in" then [clone (entry_get entry "in")]
|
|
102
|
-
else [Noval]
|
|
103
|
-
|
|
104
|
-
let check_result entry args res =
|
|
105
|
-
let matched = ref false in
|
|
106
|
-
(if entry_has entry "match" then begin
|
|
107
|
-
do_match (entry_get entry "match")
|
|
108
|
-
(omap_v ["in", entry_get entry "in"; "args", lst args;
|
|
109
|
-
"out", entry_get entry "res"; "ctx", entry_get entry "ctx"]);
|
|
110
|
-
matched := true
|
|
111
|
-
end);
|
|
112
|
-
let out = entry_get entry "out" in
|
|
113
|
-
if eqv out res then ()
|
|
114
|
-
else if !matched && (out = Str nullmark || is_nullish out) then ()
|
|
115
|
-
else raise (Struct_error (Printf.sprintf "Expected: %s, got: %s" (stringify out) (stringify res)))
|
|
116
|
-
|
|
117
|
-
let handle_error entry err =
|
|
118
|
-
let msg = (match err with Struct_error m -> m | e -> Printexc.to_string e) in
|
|
119
|
-
if entry_has entry "err" then begin
|
|
120
|
-
let entry_err = entry_get entry "err" in
|
|
121
|
-
if entry_err = Bool true || matchval entry_err (Str msg) then begin
|
|
122
|
-
if entry_has entry "match" then
|
|
123
|
-
do_match (entry_get entry "match")
|
|
124
|
-
(omap_v ["in", entry_get entry "in"; "out", entry_get entry "res";
|
|
125
|
-
"ctx", entry_get entry "ctx"; "err", Str msg])
|
|
126
|
-
end else
|
|
127
|
-
raise (Struct_error (Printf.sprintf "ERROR MATCH: [%s] <=> [%s]" (stringify entry_err) msg))
|
|
128
|
-
end else raise err
|
|
129
|
-
|
|
130
|
-
let run_set ?(flags = []) group node subject =
|
|
131
|
-
let flag_null = (match List.assoc_opt "null" flags with Some b -> b | None -> true) in
|
|
132
|
-
let fixed = fix_json node flag_null in
|
|
133
|
-
let testset = (match getprop fixed (Str "set") with List r -> !r | _ -> []) in
|
|
134
|
-
List.iter (fun entry ->
|
|
135
|
-
let name = js_string (entry_get entry "name") in
|
|
136
|
-
try
|
|
137
|
-
(if not (entry_has entry "out") && flag_null then ignore (setprop entry (Str "out") (Str nullmark)));
|
|
138
|
-
let args = resolve_args entry in
|
|
139
|
-
let res = fix_json (subject args) flag_null in
|
|
140
|
-
ignore (setprop entry (Str "res") res);
|
|
141
|
-
check_result entry args res;
|
|
142
|
-
record group name true ""
|
|
143
|
-
with
|
|
144
|
-
| e ->
|
|
145
|
-
(try handle_error entry e; record group name true ""
|
|
146
|
-
with e2 -> record group name false
|
|
147
|
-
(match e2 with Struct_error m -> m | _ -> Printexc.to_string e2)))
|
|
148
|
-
testset
|
|
149
|
-
|
|
150
|
-
let run_single group node actual_fn =
|
|
151
|
-
try
|
|
152
|
-
let expected = getprop_raw_pub node "out" in
|
|
153
|
-
let actual = actual_fn (getprop_raw_pub node "in") in
|
|
154
|
-
if eqv expected actual then record group "single" true ""
|
|
155
|
-
else record group "single" false (Printf.sprintf "Expected: %s, got: %s" (stringify expected) (stringify actual))
|
|
156
|
-
with e -> record group "single" false (match e with Struct_error m -> m | _ -> Printexc.to_string e)
|
|
157
|
-
|
|
158
|
-
(* ---------------- arg helpers ---------------- *)
|
|
159
|
-
|
|
160
|
-
let arg1 f = fun args -> f (match args with x :: _ -> x | [] -> Noval)
|
|
161
|
-
let vget vin k = match vin with Map m -> (match omap_get m k with Some x -> x | None -> Noval) | _ -> Noval
|
|
162
|
-
let vhas vin k = match vin with Map m -> omap_has m k | _ -> false
|
|
7
|
+
open Corpus_runner
|
|
163
8
|
|
|
164
9
|
(* ---------------- test groups ---------------- *)
|
|
165
10
|
|
|
@@ -144,7 +144,15 @@ sub candidates {
|
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
|
|
148
|
+
# SAFE OPS FIRST - see the ts harness for the reasoning: the cache stores
|
|
149
|
+
# only successful GETs, so an SDK whose first usable op is a `create`
|
|
150
|
+
# (POST) can never satisfy "a hit served from cache costs nothing".
|
|
151
|
+
my %safe = (list => 0, load => 1);
|
|
152
|
+
return sort {
|
|
153
|
+
($safe{ $a->{op} } // 2) <=> ($safe{ $b->{op} } // 2)
|
|
154
|
+
|| $a->{key} cmp $b->{key}
|
|
155
|
+
} @out;
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
|
|
@@ -145,6 +145,17 @@ class FeatureCorpusTest extends TestCase
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
// SAFE OPS FIRST — see the ts harness for the reasoning: the cache
|
|
150
|
+
// stores only successful GETs, so an SDK whose first usable op is a
|
|
151
|
+
// `create` (POST) can never satisfy "a hit served from cache costs
|
|
152
|
+
// nothing".
|
|
153
|
+
$safe = ['list' => 0, 'load' => 1];
|
|
154
|
+
usort($out, function ($a, $b) use ($safe) {
|
|
155
|
+
$ra = $safe[$a['op']] ?? 2;
|
|
156
|
+
$rb = $safe[$b['op']] ?? 2;
|
|
157
|
+
return $ra === $rb ? strcmp($a['key'], $b['key']) : $ra - $rb;
|
|
158
|
+
});
|
|
148
159
|
return $out;
|
|
149
160
|
}
|
|
150
161
|
|
|
@@ -165,6 +165,12 @@ class _Ctx:
|
|
|
165
165
|
self.meta = {}
|
|
166
166
|
self.op = op
|
|
167
167
|
self.entity = entity
|
|
168
|
+
# The pipeline always resolves a point before a feature sees the ctx,
|
|
169
|
+
# so features read ctx.point freely — paging checks `point.kind` for
|
|
170
|
+
# graphql. This stub had no such attribute, so every paging test died
|
|
171
|
+
# with AttributeError before reaching the transport. Those tests were
|
|
172
|
+
# SKIPPED until an SDK activated paging, which is why it went unseen.
|
|
173
|
+
self.point = {}
|
|
168
174
|
self.spec = None
|
|
169
175
|
self.response = None
|
|
170
176
|
self.result = None
|
|
@@ -129,6 +129,12 @@ def _candidates(client):
|
|
|
129
129
|
"accessor": accessor,
|
|
130
130
|
"op": opname,
|
|
131
131
|
})
|
|
132
|
+
|
|
133
|
+
# SAFE OPS FIRST - see the ts harness for the reasoning: the cache stores
|
|
134
|
+
# only successful GETs, so an SDK whose first usable op is a `create`
|
|
135
|
+
# (POST) can never satisfy "a hit served from cache costs nothing".
|
|
136
|
+
safe = {"list": 0, "load": 1}
|
|
137
|
+
out.sort(key=lambda o: (safe.get(o["op"], 2), o["key"]))
|
|
132
138
|
return out
|
|
133
139
|
|
|
134
140
|
|
|
@@ -108,7 +108,12 @@ class FeatureCorpusTest < Minitest::Test
|
|
|
108
108
|
out << { "key" => "#{entname}.#{opname}", "accessor" => accessor, "op" => opname }
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
|
-
|
|
111
|
+
|
|
112
|
+
# SAFE OPS FIRST - see the ts harness for the reasoning: the cache stores
|
|
113
|
+
# only successful GETs, so an SDK whose first usable op is a `create`
|
|
114
|
+
# (POST) can never satisfy "a hit served from cache costs nothing".
|
|
115
|
+
safe = { "list" => 0, "load" => 1 }
|
|
116
|
+
out.sort_by { |o| [safe.fetch(o["op"], 2), o["key"]] }
|
|
112
117
|
end
|
|
113
118
|
|
|
114
119
|
def invoke(client, op, ctrl)
|
|
@@ -41,7 +41,10 @@ pub struct CostBucket {
|
|
|
41
41
|
pub amount: f64,
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// NOT `#[derive(Default)]`: the struct holds a `Value`, and `Value` has no
|
|
45
|
+
// `Default` impl — deriving it does not compile. This file only ships when the
|
|
46
|
+
// cost feature is active, so no SDK had ever built it. `Value::Noval` is the
|
|
47
|
+
// absent value, which is what a fresh track should carry.
|
|
45
48
|
pub struct CostTrack {
|
|
46
49
|
// Aggregates (mirrors the ts client._cost record).
|
|
47
50
|
pub currency: String,
|
|
@@ -60,6 +63,27 @@ pub struct CostTrack {
|
|
|
60
63
|
pub seq: i64,
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
impl Default for CostTrack {
|
|
67
|
+
fn default() -> Self {
|
|
68
|
+
CostTrack {
|
|
69
|
+
currency: String::new(),
|
|
70
|
+
calls: 0,
|
|
71
|
+
attempts: 0,
|
|
72
|
+
amount: 0.0,
|
|
73
|
+
reported: 0.0,
|
|
74
|
+
estimated: 0.0,
|
|
75
|
+
ops: HashMap::new(),
|
|
76
|
+
actors: HashMap::new(),
|
|
77
|
+
limit: 0.0,
|
|
78
|
+
spent: 0.0,
|
|
79
|
+
remaining: 0.0,
|
|
80
|
+
exceeded: false,
|
|
81
|
+
last: Value::Noval,
|
|
82
|
+
seq: 0,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
63
87
|
pub struct CostFeature {
|
|
64
88
|
pub name: String,
|
|
65
89
|
pub active: bool,
|
|
@@ -12,6 +12,7 @@ test:
|
|
|
12
12
|
scala-cli run . --main-class SdkTestMain
|
|
13
13
|
scala-cli run . --main-class SdkEntityTestMain
|
|
14
14
|
scala-cli run . --main-class Runner -- ../.sdk/test/test.json
|
|
15
|
+
scala-cli run . --main-class PrimaryCorpusMain -- ../.sdk/test/test.json
|
|
15
16
|
|
|
16
17
|
corpus:
|
|
17
18
|
scala-cli run . --main-class Runner -- ../.sdk/test/test.json
|