@voxgig/sdkgen 4.3.0 → 4.4.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 (33) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/package.json +1 -1
  3. package/project/.sdk/src/cmp/zig/Config_zig.ts +1 -1
  4. package/project/.sdk/tm/c/tests/primary_corpus_test.c +565 -0
  5. package/project/.sdk/tm/c/utility/prepare_method.c +3 -1
  6. package/project/.sdk/tm/c/utility/transform_request.c +4 -3
  7. package/project/.sdk/tm/clojure/src/sdk/core.clj +55 -5
  8. package/project/.sdk/tm/clojure/test/sdk/test/primary.clj +171 -1
  9. package/project/.sdk/tm/clojure/test/sdk/test/struct_corpus.clj +13 -2
  10. package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +2 -0
  11. package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +104 -0
  12. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +82 -2
  13. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +50 -2
  14. package/project/.sdk/tm/elixir/test/primary_utility_test.exs +18 -200
  15. package/project/.sdk/tm/elixir/test/support/struct_corpus.ex +413 -2
  16. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +56 -2
  17. package/project/.sdk/tm/lua/utility/make_options.lua +22 -2
  18. package/project/.sdk/tm/ocaml/Makefile +15 -4
  19. package/project/.sdk/tm/ocaml/sdk_runtime.ml +8 -1
  20. package/project/.sdk/tm/ocaml/test/corpus_runner.ml +185 -0
  21. package/project/.sdk/tm/ocaml/test/primary_utility_test.ml +238 -0
  22. package/project/.sdk/tm/ocaml/test/struct_corpus.ml +5 -160
  23. package/project/.sdk/tm/scala/Makefile +1 -0
  24. package/project/.sdk/tm/scala/sdktest/PrimaryCorpus.scala +294 -0
  25. package/project/.sdk/tm/scala/sdktest/StructCorpus.scala +0 -0
  26. package/project/.sdk/tm/scala/utility/Make.scala +49 -2
  27. package/project/.sdk/tm/scala/utility/Prepare.scala +3 -1
  28. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -3
  29. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +13 -2
  30. package/project/.sdk/tm/zig/core/utility.zig +8 -1
  31. package/project/.sdk/tm/zig/test/primary_utility_test.zig +445 -0
  32. package/project/.sdk/tm/zig/test/struct_runner.zig +238 -0
  33. package/project/sdkgen-package.json +1 -1
package/bin/voxgig-sdkgen CHANGED
@@ -8,7 +8,7 @@ const { Shape, One, Skip } = require('shape')
8
8
 
9
9
  const { SdkGen } = require('../dist/sdkgen.js')
10
10
 
11
- const VERSION = '4.3.0'
11
+ const VERSION = '4.4.0'
12
12
  const KONSOLE = console
13
13
 
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/sdkgen",
3
- "version": "4.3.0",
3
+ "version": "4.4.0",
4
4
  "main": "dist/sdkgen.js",
5
5
  "type": "commonjs",
6
6
  "engines": {
@@ -154,7 +154,7 @@ pub fn make_feature(name: []const u8) Feature {
154
154
  // `support`, a helper module). Any model feature not already built in is
155
155
  // appended so bespoke features still resolve.
156
156
  const builtinFeatures = [
157
- 'audit', 'cache', 'clienttrack', 'debug', 'idempotency', 'log',
157
+ 'audit', 'cache', 'clienttrack', 'cost', 'debug', 'idempotency', 'log',
158
158
  'metrics', 'netsim', 'paging', 'proxy', 'ratelimit', 'rbac',
159
159
  'retry', 'streaming', 'telemetry', 'test', 'timeout',
160
160
  ]
@@ -0,0 +1,565 @@
1
+ /* Primary-utility corpus driver — C port.
2
+ *
3
+ * Drives every `primary` section of the shared corpus (.sdk/test/test.json)
4
+ * through this SDK's utilities, the way the ts reference harness does. The
5
+ * hand-written primary_utility_test.c beside this file covers the same
6
+ * utilities with directly-constructed contexts; that suite can drift from the
7
+ * contract, this one cannot — the cases ARE the contract.
8
+ *
9
+ * The corpus entry shapes are ctx+out, ctx+match, ctx+mark+out, args+mark+out,
10
+ * args+err and in+match. `mark` is a case label, not an assertion.
11
+ */
12
+
13
+ #include "feature_harness.h" /* test_sdk + Fetcher helpers + ctest.h */
14
+ #include "runner.h" /* normalize + deep_equal, shared with the struct corpus */
15
+ #include "voxgig_struct.h"
16
+
17
+ #include <ctype.h>
18
+ #include <stdio.h>
19
+ #include <stdlib.h>
20
+ #include <string.h>
21
+
22
+ static voxgig_value* CORPUS = NULL;
23
+ static int NPASS = 0;
24
+ static int NFAIL = 0;
25
+
26
+ /* ---- corpus access ------------------------------------------------------ */
27
+
28
+ static voxgig_value* mget(voxgig_value* m, const char* k) {
29
+ if (!voxgig_is_map(m)) return NULL;
30
+ return voxgig_map_get(voxgig_as_map(m), k);
31
+ }
32
+
33
+ static voxgig_value* primary_root(void) {
34
+ if (!CORPUS) CORPUS = voxgig_parse_json_file("../.sdk/test/test.json");
35
+ voxgig_value* p = mget(CORPUS, "primary");
36
+ return p ? p : v_map();
37
+ }
38
+
39
+ /* section(name) -> the `basic` node, which carries `set`. */
40
+ static voxgig_value* section_basic(const char* name) {
41
+ voxgig_value* sec = mget(primary_root(), name);
42
+ voxgig_value* basic = mget(sec, "basic");
43
+ return basic ? basic : v_map();
44
+ }
45
+
46
+ /* A section may carry its own client setup at DEF.setup.a — makeSpec and
47
+ * prepareAuth read defaults off the CLIENT, not off ctx.options, so those two
48
+ * cannot be driven with the shared client. */
49
+ static voxgig_value* section_setup(const char* name) {
50
+ voxgig_value* sec = mget(primary_root(), name);
51
+ voxgig_value* def = mget(sec, "DEF");
52
+ voxgig_value* setup = mget(def, "setup");
53
+ voxgig_value* a = mget(setup, "a");
54
+ return a;
55
+ }
56
+
57
+ /* ---- match machinery ---------------------------------------------------- */
58
+
59
+ static char* vstr(voxgig_value* v) {
60
+ if (!v) return strdup("");
61
+ char* s = voxgig_stringify(v, -1);
62
+ return s ? s : strdup("");
63
+ }
64
+
65
+ static char* lower_dup(const char* s) {
66
+ char* o = strdup(s ? s : "");
67
+ for (char* p = o; *p; p++) *p = (char)tolower((unsigned char)*p);
68
+ return o;
69
+ }
70
+
71
+ static bool is_nullish(voxgig_value* v) {
72
+ return v == NULL || voxgig_is_undef(v) || voxgig_is_null(v);
73
+ }
74
+
75
+ static bool matchval(voxgig_value* check, voxgig_value* base) {
76
+ voxgig_value* nc = normalize(check);
77
+ voxgig_value* nb = normalize(base);
78
+ bool eq = deep_equal(nc, nb);
79
+ voxgig_release(nc);
80
+ voxgig_release(nb);
81
+ if (eq) return true;
82
+
83
+ if (!check || !v_is_str(check)) return false;
84
+ const char* c = voxgig_as_string(check);
85
+ if (strcmp(c, "__UNDEF__") == 0) return is_nullish(base);
86
+ if (strcmp(c, "__EXISTS__") == 0) return !is_nullish(base);
87
+
88
+ char* bs = vstr(base);
89
+ size_t cl = strlen(c);
90
+
91
+ /* A /pattern/ is a regex over the stringified base. */
92
+ if (2 <= cl && c[0] == '/' && c[cl - 1] == '/') {
93
+ char* pat = (char*)malloc(cl - 1);
94
+ memcpy(pat, c + 1, cl - 2);
95
+ pat[cl - 2] = '\0';
96
+ bool ok = voxgig_re_test(pat, bs);
97
+ free(pat);
98
+ free(bs);
99
+ return ok;
100
+ }
101
+
102
+ /* Otherwise a case-insensitive substring, as the reference does. */
103
+ char* lb = lower_dup(bs);
104
+ char* lc = lower_dup(c);
105
+ bool ok = strstr(lb, lc) != NULL;
106
+ free(lb);
107
+ free(lc);
108
+ free(bs);
109
+ return ok;
110
+ }
111
+
112
+ /* Walk the CHECK structure; every leaf must matchval at the same path in
113
+ * base. A missing path in base reads as null, which only __UNDEF__ matches. */
114
+ static bool do_match(voxgig_value* check, voxgig_value* base,
115
+ const char* path, char** fail) {
116
+ if (voxgig_is_map(check)) {
117
+ voxgig_map* m = voxgig_as_map(check);
118
+ for (size_t i = 0; i < m->len; i++) {
119
+ const char* k = m->entries[i].key;
120
+ char sub[512];
121
+ snprintf(sub, sizeof(sub), "%s%s%s", path, path[0] ? "." : "", k);
122
+ if (!do_match(m->entries[i].value, mget(base, k), sub, fail)) return false;
123
+ }
124
+ return true;
125
+ }
126
+ if (voxgig_is_list(check)) {
127
+ voxgig_list* l = voxgig_as_list(check);
128
+ for (size_t i = 0; i < l->len; i++) {
129
+ char sub[512];
130
+ snprintf(sub, sizeof(sub), "%s.%zu", path, i);
131
+ voxgig_value* be = NULL;
132
+ if (voxgig_is_list(base) && i < voxgig_as_list(base)->len) {
133
+ be = voxgig_as_list(base)->items[i];
134
+ }
135
+ if (!do_match(l->items[i], be, sub, fail)) return false;
136
+ }
137
+ return true;
138
+ }
139
+ if (matchval(check, base)) return true;
140
+
141
+ char* cs = vstr(check);
142
+ char* bs = vstr(base);
143
+ char buf[1024];
144
+ snprintf(buf, sizeof(buf), "MATCH: %s: [%s] <=> [%s]", path, cs, bs);
145
+ free(cs);
146
+ free(bs);
147
+ *fail = strdup(buf);
148
+ return false;
149
+ }
150
+
151
+ /* ---- live context from a corpus map ------------------------------------- */
152
+
153
+ static voxgig_value* corpus_json_thunk(void* ud, voxgig_value* args) {
154
+ (void)args;
155
+ return ud ? (voxgig_value*)ud : v_undef();
156
+ }
157
+
158
+ static Context* corpus_ctx(ProjectNameSDK* cl, voxgig_value* ctxmap) {
159
+ CtxSpec cs;
160
+ memset(&cs, 0, sizeof(cs));
161
+ /* Only when the corpus names one: defaulting to "load" made the SDK report
162
+ * the wrong operation in the error messages the corpus matches on. */
163
+ voxgig_value* opn = mget(ctxmap, "opname");
164
+ if (opn && v_is_str(opn)) cs.opname = voxgig_as_string(opn);
165
+ cs.client = cl;
166
+ cs.utility = sdk_get_utility(cl);
167
+ Context* ctx = make_context_util(cs, sdk_get_root_ctx(cl));
168
+
169
+ voxgig_value* sp = mget(ctxmap, "spec");
170
+ if (voxgig_is_map(sp)) ctx->spec = spec_new(voxgig_clone(sp));
171
+
172
+ voxgig_value* rs = mget(ctxmap, "result");
173
+ if (voxgig_is_map(rs)) {
174
+ ctx->result = result_new(voxgig_clone(rs));
175
+ /* result_new does not carry an err, so a corpus result holding one
176
+ * arrived empty and result_basic had no previous message to prepend. */
177
+ voxgig_value* re = mget(rs, "err");
178
+ voxgig_value* rm = mget(re, "message");
179
+ if (rm && v_is_str(rm) && voxgig_as_string(rm)[0]) {
180
+ ctx->result->err = pn_error_new("", voxgig_as_string(rm));
181
+ }
182
+ }
183
+
184
+ voxgig_value* rp = mget(ctxmap, "response");
185
+ if (voxgig_is_map(rp)) {
186
+ ctx->response = response_new(voxgig_clone(rp));
187
+ /* result_body reads response.json and requires it to be CALLABLE; the
188
+ * corpus supplies a plain `body`. */
189
+ voxgig_value* body = mget(rp, "body");
190
+ if (body && !v_is_noval(body)) {
191
+ ctx->response->body = voxgig_retain(body);
192
+ ctx->response->json = vfn(corpus_json_thunk, voxgig_retain(body));
193
+ }
194
+ /* Header names arrive from the wire in any case; the contract is
195
+ * lowercase and result_headers copies them verbatim. */
196
+ voxgig_value* hs = mget(rp, "headers");
197
+ if (voxgig_is_map(hs)) {
198
+ voxgig_map* hm = voxgig_as_map(hs);
199
+ voxgig_value* low = v_map();
200
+ for (size_t i = 0; i < hm->len; i++) {
201
+ char* lk = lower_dup(hm->entries[i].key);
202
+ setp(low, lk, voxgig_retain(hm->entries[i].value));
203
+ free(lk);
204
+ }
205
+ ctx->response->headers = low;
206
+ }
207
+ }
208
+
209
+ voxgig_value* pt = mget(ctxmap, "point");
210
+ if (voxgig_is_map(pt)) ctx->point = voxgig_clone(pt);
211
+ voxgig_value* rd = mget(ctxmap, "reqdata");
212
+ if (rd && !v_is_noval(rd)) ctx->reqdata = voxgig_retain(rd);
213
+ voxgig_value* rmt = mget(ctxmap, "reqmatch");
214
+ if (rmt && !v_is_noval(rmt)) ctx->reqmatch = voxgig_retain(rmt);
215
+ voxgig_value* dt = mget(ctxmap, "data");
216
+ if (dt && !v_is_noval(dt)) ctx->data = voxgig_retain(dt);
217
+ voxgig_value* mt = mget(ctxmap, "match");
218
+ if (mt && !v_is_noval(mt)) ctx->mtch = voxgig_retain(mt);
219
+ voxgig_value* op = mget(ctxmap, "options");
220
+ if (voxgig_is_map(op)) ctx->options = voxgig_clone(op);
221
+ voxgig_value* cf = mget(ctxmap, "config");
222
+ if (voxgig_is_map(cf)) ctx->config = voxgig_clone(cf);
223
+ return ctx;
224
+ }
225
+
226
+ /* The match reads the corpus map while the utilities mutate the live objects
227
+ * hanging off the context, so without this every ctx.* assertion reads null. */
228
+ static void publish_ctx(voxgig_value* ctxmap, Context* ctx) {
229
+ if (!voxgig_is_map(ctxmap)) return;
230
+ if (ctx->spec) setp(ctxmap, "spec", spec_to_value(ctx->spec));
231
+ if (ctx->result) setp(ctxmap, "result", result_to_value(ctx->result));
232
+ if (ctx->response) setp(ctxmap, "response", v_str("__EXISTS__"));
233
+ }
234
+
235
+ /* ---- the section runner ------------------------------------------------- */
236
+
237
+ typedef voxgig_value* (*ctxfn)(Context* ctx, voxgig_value* args, char** err);
238
+ typedef voxgig_value* (*argfn)(voxgig_value* args, char** err);
239
+
240
+ static void record(const char* section, bool ok, const char* msg) {
241
+ if (ok) {
242
+ NPASS++;
243
+ } else {
244
+ NFAIL++;
245
+ printf("PRIMARY-FAIL %s - %s\n", section, msg ? msg : "?");
246
+ }
247
+ }
248
+
249
+ static voxgig_value* arg_at(voxgig_value* args, size_t i) {
250
+ if (!voxgig_is_list(args)) return NULL;
251
+ voxgig_list* l = voxgig_as_list(args);
252
+ return i < l->len ? l->items[i] : NULL;
253
+ }
254
+
255
+ static void runset(const char* name, ProjectNameSDK* cl,
256
+ ctxfn cf, argfn af) {
257
+ voxgig_value* basic = section_basic(name);
258
+ voxgig_value* set = mget(basic, "set");
259
+ if (!voxgig_is_list(set)) return;
260
+ voxgig_list* sl = voxgig_as_list(set);
261
+
262
+ for (size_t i = 0; i < sl->len; i++) {
263
+ voxgig_value* entry = sl->items[i];
264
+ if (!voxgig_is_map(entry)) continue;
265
+
266
+ /* resolve_args, mirroring the ts runner. */
267
+ voxgig_value* args = v_list();
268
+ voxgig_value* ectx = mget(entry, "ctx");
269
+ voxgig_value* eargs = mget(entry, "args");
270
+ voxgig_value* ein = mget(entry, "in");
271
+ if (ectx) {
272
+ voxgig_list_push(voxgig_as_list(args), voxgig_clone(ectx));
273
+ } else if (voxgig_is_list(eargs)) {
274
+ voxgig_list* al = voxgig_as_list(eargs);
275
+ for (size_t k = 0; k < al->len; k++) {
276
+ voxgig_list_push(voxgig_as_list(args), voxgig_retain(al->items[k]));
277
+ }
278
+ } else if (ein) {
279
+ voxgig_list_push(voxgig_as_list(args), voxgig_clone(ein));
280
+ }
281
+
282
+ /* ts's resolveArgs writes the live first arg back as entry.ctx so a
283
+ * `match: {ctx: ...}` resolves for args-style entries too. */
284
+ voxgig_value* first = arg_at(args, 0);
285
+ if (voxgig_is_map(first)) setp(entry, "ctx", voxgig_retain(first));
286
+
287
+ char* err = NULL;
288
+ voxgig_value* got = NULL;
289
+ if (cf) {
290
+ Context* ctx = corpus_ctx(cl, first);
291
+ got = cf(ctx, args, &err);
292
+ publish_ctx(first, ctx);
293
+ } else {
294
+ got = af(args, &err);
295
+ }
296
+
297
+ voxgig_value* eerr = mget(entry, "err");
298
+ if (eerr && !v_is_noval(eerr)) {
299
+ /* The case expects a failure. */
300
+ if (!err) {
301
+ record(name, false, "expected an error, got none");
302
+ continue;
303
+ }
304
+ voxgig_value* errv = v_str(err);
305
+ bool ok = (voxgig_is_bool(eerr) && voxgig_as_bool(eerr)) || matchval(eerr, errv);
306
+ if (!ok) {
307
+ char* es = vstr(eerr);
308
+ char buf[1024];
309
+ snprintf(buf, sizeof(buf), "ERROR MATCH: [%s] <=> [%s]", es, err);
310
+ free(es);
311
+ record(name, false, buf);
312
+ continue;
313
+ }
314
+ voxgig_value* emat = mget(entry, "match");
315
+ if (voxgig_is_map(emat)) {
316
+ /* ts hands do_match the ERROR OBJECT, so `match: {err: {message}}`
317
+ * resolves; a bare string leaves err.message reading null. */
318
+ voxgig_value* base = cmap(4, "in", ein ? voxgig_retain(ein) : v_undef(),
319
+ "out", got ? voxgig_retain(got) : v_undef(),
320
+ "ctx", voxgig_retain(mget(entry, "ctx")),
321
+ "err", cmap(1, "message", v_str(err)));
322
+ char* fail = NULL;
323
+ if (!do_match(emat, base, "", &fail)) {
324
+ record(name, false, fail);
325
+ continue;
326
+ }
327
+ }
328
+ record(name, true, NULL);
329
+ continue;
330
+ }
331
+
332
+ if (err) {
333
+ record(name, false, err);
334
+ continue;
335
+ }
336
+
337
+ /* check_result: `match` first, then `out`. */
338
+ bool matched = false;
339
+ voxgig_value* emat = mget(entry, "match");
340
+ if (voxgig_is_map(emat)) {
341
+ voxgig_value* base = cmap(4, "in", ein ? voxgig_retain(ein) : v_undef(),
342
+ "args", voxgig_retain(args),
343
+ "out", got ? voxgig_retain(got) : v_undef(),
344
+ "ctx", voxgig_retain(mget(entry, "ctx")));
345
+ char* fail = NULL;
346
+ if (!do_match(emat, base, "", &fail)) {
347
+ record(name, false, fail);
348
+ continue;
349
+ }
350
+ matched = true;
351
+ }
352
+
353
+ voxgig_value* eout = mget(entry, "out");
354
+ voxgig_value* expected = eout ? eout : v_null();
355
+ voxgig_value* ne = normalize(expected);
356
+ voxgig_value* ng = normalize(got);
357
+ bool eq = deep_equal(ne, ng);
358
+ voxgig_release(ne);
359
+ voxgig_release(ng);
360
+ if (!eq && !(matched && !eout)) {
361
+ char* es = vstr(expected);
362
+ char* gs = vstr(got);
363
+ char buf[1024];
364
+ snprintf(buf, sizeof(buf), "Expected: %s, got: %s", es, gs);
365
+ free(es);
366
+ free(gs);
367
+ record(name, false, buf);
368
+ continue;
369
+ }
370
+ record(name, true, NULL);
371
+ }
372
+ }
373
+
374
+ /* ---- per-section subjects ----------------------------------------------- */
375
+
376
+ #define ERRSET(e, p) do { if (p) { *(e) = strdup((p)->msg ? (p)->msg : "error"); } } while (0)
377
+
378
+ static voxgig_value* s_done(Context* c, voxgig_value* a, char** e) {
379
+ (void)a;
380
+ PNError* pe = NULL;
381
+ voxgig_value* r = done_util(c, &pe);
382
+ ERRSET(e, pe);
383
+ return r;
384
+ }
385
+ static voxgig_value* s_make_url(Context* c, voxgig_value* a, char** e) {
386
+ (void)a;
387
+ PNError* pe = NULL;
388
+ char* u = make_url_util(c, &pe);
389
+ ERRSET(e, pe);
390
+ return u ? v_str(u) : v_undef();
391
+ }
392
+ static voxgig_value* s_make_request(Context* c, voxgig_value* a, char** e) {
393
+ (void)a;
394
+ PNError* pe = NULL;
395
+ make_request_util(c, &pe);
396
+ ERRSET(e, pe);
397
+ return c->result ? result_to_value(c->result) : v_undef();
398
+ }
399
+ static voxgig_value* s_make_response(Context* c, voxgig_value* a, char** e) {
400
+ (void)a;
401
+ PNError* pe = NULL;
402
+ make_response_util(c, &pe);
403
+ ERRSET(e, pe);
404
+ return c->result ? result_to_value(c->result) : v_undef();
405
+ }
406
+ static voxgig_value* s_make_spec(Context* c, voxgig_value* a, char** e) {
407
+ (void)a;
408
+ PNError* pe = NULL;
409
+ Spec* s = make_spec_util(c, &pe);
410
+ ERRSET(e, pe);
411
+ if (s) c->spec = s;
412
+ return s ? spec_to_value(s) : v_undef();
413
+ }
414
+ static voxgig_value* s_prepare_auth(Context* c, voxgig_value* a, char** e) {
415
+ (void)a;
416
+ PNError* pe = NULL;
417
+ prepare_auth_util(c, &pe);
418
+ ERRSET(e, pe);
419
+ return c->spec ? spec_to_value(c->spec) : v_undef();
420
+ }
421
+ static voxgig_value* s_prepare_body(Context* c, voxgig_value* a, char** e) {
422
+ (void)a; (void)e;
423
+ return prepare_body_util(c);
424
+ }
425
+ static voxgig_value* s_prepare_headers(Context* c, voxgig_value* a, char** e) {
426
+ (void)a; (void)e;
427
+ return prepare_headers_util(c);
428
+ }
429
+ static voxgig_value* s_prepare_method(Context* c, voxgig_value* a, char** e) {
430
+ (void)a; (void)e;
431
+ const char* m = prepare_method_util(c);
432
+ return (m && m[0]) ? v_str(m) : v_undef();
433
+ }
434
+ static voxgig_value* s_prepare_params(Context* c, voxgig_value* a, char** e) {
435
+ (void)a; (void)e;
436
+ return prepare_params_util(c);
437
+ }
438
+ static voxgig_value* s_prepare_path(Context* c, voxgig_value* a, char** e) {
439
+ (void)a; (void)e;
440
+ char* p = prepare_path_util(c);
441
+ return p ? v_str(p) : v_undef();
442
+ }
443
+ static voxgig_value* s_prepare_query(Context* c, voxgig_value* a, char** e) {
444
+ (void)a; (void)e;
445
+ return prepare_query_util(c);
446
+ }
447
+ static voxgig_value* s_result_basic(Context* c, voxgig_value* a, char** e) {
448
+ (void)a; (void)e;
449
+ SdkResult* r = result_basic_util(c);
450
+ if (r) c->result = r;
451
+ return c->result ? result_to_value(c->result) : v_undef();
452
+ }
453
+ static voxgig_value* s_result_body(Context* c, voxgig_value* a, char** e) {
454
+ (void)a; (void)e;
455
+ SdkResult* r = result_body_util(c);
456
+ if (r) c->result = r;
457
+ return c->result ? result_to_value(c->result) : v_undef();
458
+ }
459
+ static voxgig_value* s_result_headers(Context* c, voxgig_value* a, char** e) {
460
+ (void)a; (void)e;
461
+ SdkResult* r = result_headers_util(c);
462
+ if (r) c->result = r;
463
+ return c->result ? result_to_value(c->result) : v_undef();
464
+ }
465
+ static voxgig_value* s_transform_request(Context* c, voxgig_value* a, char** e) {
466
+ (void)a; (void)e;
467
+ return transform_request_util(c);
468
+ }
469
+ static voxgig_value* s_transform_response(Context* c, voxgig_value* a, char** e) {
470
+ (void)a; (void)e;
471
+ return transform_response_util(c);
472
+ }
473
+ static voxgig_value* s_param(Context* c, voxgig_value* a, char** e) {
474
+ (void)e;
475
+ return param_util(c, arg_at(a, 1));
476
+ }
477
+ static voxgig_value* s_make_error(Context* c, voxgig_value* a, char** e) {
478
+ voxgig_value* a1 = arg_at(a, 1);
479
+ voxgig_value* msgv = mget(a1, "message");
480
+ PNError* in = NULL;
481
+ if (msgv && v_is_str(msgv) && voxgig_as_string(msgv)[0]) {
482
+ in = pn_error_new("", voxgig_as_string(msgv));
483
+ }
484
+ PNError* out = NULL;
485
+ voxgig_value* r = make_error_util(c, in, &out);
486
+ ERRSET(e, out);
487
+ return r;
488
+ }
489
+
490
+ /* Sections that take a bare map rather than a ctx. */
491
+ static ProjectNameSDK* SHARED = NULL;
492
+
493
+ static voxgig_value* s_make_context(voxgig_value* a, char** e) {
494
+ (void)e;
495
+ Context* c = corpus_ctx(SHARED, arg_at(a, 0));
496
+ voxgig_value* op = v_map();
497
+ if (c->op) {
498
+ setp(op, "entity", v_str(c->op->entity ? c->op->entity : ""));
499
+ setp(op, "name", v_str(c->op->name ? c->op->name : ""));
500
+ setp(op, "input", v_str(c->op->input ? c->op->input : ""));
501
+ setp(op, "points", c->op->points ? voxgig_retain(c->op->points) : v_list());
502
+ }
503
+ return cmap(1, "op", op);
504
+ }
505
+ static voxgig_value* s_make_options(voxgig_value* a, char** e) {
506
+ (void)e;
507
+ voxgig_value* in = arg_at(a, 0);
508
+ Context* c = corpus_ctx(SHARED, v_map());
509
+ voxgig_value* cf = mget(in, "config");
510
+ if (cf) c->config = voxgig_retain(cf);
511
+ voxgig_value* op = mget(in, "options");
512
+ if (op) c->options = voxgig_retain(op);
513
+ return make_options_util(c);
514
+ }
515
+ static voxgig_value* s_operator(voxgig_value* a, char** e) {
516
+ (void)e;
517
+ voxgig_value* in = arg_at(a, 0);
518
+ voxgig_value* en = mget(in, "entity");
519
+ voxgig_value* nm = mget(in, "name");
520
+ voxgig_value* ip = mget(in, "input");
521
+ voxgig_value* pts = mget(in, "points");
522
+ return cmap(4,
523
+ "entity", en ? voxgig_retain(en) : v_str("_"),
524
+ "input", ip ? voxgig_retain(ip) : v_str("_"),
525
+ "name", nm ? voxgig_retain(nm) : v_str("_"),
526
+ "points", voxgig_is_list(pts) ? voxgig_retain(pts) : v_list());
527
+ }
528
+
529
+ /* ---- main --------------------------------------------------------------- */
530
+
531
+ static ProjectNameSDK* client_for(const char* section) {
532
+ voxgig_value* setup = section_setup(section);
533
+ if (voxgig_is_map(setup)) return test_sdk(v_undef(), voxgig_clone(setup));
534
+ return SHARED;
535
+ }
536
+
537
+ int main(void) {
538
+ SHARED = test_sdk(v_undef(), v_undef());
539
+
540
+ runset("done", SHARED, s_done, NULL);
541
+ runset("makeUrl", SHARED, s_make_url, NULL);
542
+ runset("makeRequest", SHARED, s_make_request, NULL);
543
+ runset("makeResponse", SHARED, s_make_response, NULL);
544
+ runset("makeSpec", client_for("makeSpec"), s_make_spec, NULL);
545
+ runset("prepareAuth", client_for("prepareAuth"), s_prepare_auth, NULL);
546
+ runset("prepareBody", SHARED, s_prepare_body, NULL);
547
+ runset("prepareHeaders", SHARED, s_prepare_headers, NULL);
548
+ runset("prepareMethod", SHARED, s_prepare_method, NULL);
549
+ runset("prepareParams", SHARED, s_prepare_params, NULL);
550
+ runset("preparePath", SHARED, s_prepare_path, NULL);
551
+ runset("prepareQuery", SHARED, s_prepare_query, NULL);
552
+ runset("resultBasic", SHARED, s_result_basic, NULL);
553
+ runset("resultBody", SHARED, s_result_body, NULL);
554
+ runset("resultHeaders", SHARED, s_result_headers, NULL);
555
+ runset("transformRequest", SHARED, s_transform_request, NULL);
556
+ runset("transformResponse", SHARED, s_transform_response, NULL);
557
+ runset("param", SHARED, s_param, NULL);
558
+ runset("makeError", SHARED, s_make_error, NULL);
559
+ runset("makeContext", SHARED, NULL, s_make_context);
560
+ runset("makeOptions", SHARED, NULL, s_make_options);
561
+ runset("operator", SHARED, NULL, s_operator);
562
+
563
+ printf("\nPRIMARY CORPUS: PASS %d FAIL %d\n", NPASS, NFAIL);
564
+ return NFAIL == 0 ? 0 : 1;
565
+ }
@@ -30,5 +30,7 @@ const char* prepare_method_util(Context* ctx) {
30
30
  if (strcmp(op, "list") == 0) return "GET";
31
31
  if (strcmp(op, "remove") == 0) return "DELETE";
32
32
  if (strcmp(op, "patch") == 0) return "PATCH";
33
- return "GET";
33
+ /* No GET catch-all: an unrecognised op must fall through to the
34
+ allow.method gate, not be silently issued as a GET. */
35
+ return "";
34
36
  }
@@ -15,7 +15,8 @@ voxgig_value* transform_request_util(Context* ctx) {
15
15
  if (v_is_noval(reqform) || v_is_null(reqform)) return ctx->reqdata;
16
16
 
17
17
  voxgig_value* store = cmap(1, "reqdata", v_share(ctx->reqdata));
18
- voxgig_value* out = voxgig_transform(store, reqform, NULL);
19
- if (out && !v_is_noval(out)) return out;
20
- return ctx->reqdata;
18
+ /* Return the transform result verbatim, as the ts reference does. Falling
19
+ back to ctx->reqdata meant a reqform naming a path that does not resolve
20
+ sent the WHOLE request data instead of nothing. */
21
+ return voxgig_transform(store, reqform, NULL);
21
22
  }