@voxgig/sdkgen 4.24.0 → 4.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/Deploy.js +9 -6
- package/dist/cmp/Deploy.js.map +1 -1
- package/dist/helpers/semver.js +3 -1
- package/dist/helpers/semver.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/project/.sdk/tm/go/test/pipeline_test.go +163 -57
- package/project/.sdk/tm/go/test/primary_utility_test.go +53 -0
- package/project/.sdk/tm/java/test/PipelineTest.java +141 -48
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +67 -6
- package/project/.sdk/tm/js/test/pipeline.test.js +88 -22
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +47 -1
- package/project/.sdk/tm/lua/test/omni.lua +8 -1
- package/project/.sdk/tm/lua/test/pipeline_test.lua +107 -28
- package/project/.sdk/tm/lua/test/primary_utility_test.lua +83 -3
- package/project/.sdk/tm/perl/t/omni.pm +7 -1
- package/project/.sdk/tm/perl/t/pipeline.t +124 -27
- package/project/.sdk/tm/perl/t/primary_utility.t +73 -0
- package/project/.sdk/tm/php/test/PipelineTest.php +143 -26
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +80 -2
- package/project/.sdk/tm/py/test/test_pipeline.py +108 -31
- package/project/.sdk/tm/py/test/test_primary_utility.py +76 -1
- package/project/.sdk/tm/rb/test/pipeline_test.rb +100 -24
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +59 -1
- package/project/.sdk/tm/ts/test/pipeline.test.ts +52 -11
- package/project/sdkgen-package.json +1 -1
- package/src/cmp/Deploy.ts +11 -6
- package/src/helpers/semver.ts +3 -1
|
@@ -323,45 +323,111 @@ describe('pipeline:feature order', () => {
|
|
|
323
323
|
})
|
|
324
324
|
|
|
325
325
|
|
|
326
|
+
// A cookie credential as prepareAuth writes it: `<scheme>=K` for the probe
|
|
327
|
+
// key, with no scheme prefix and nothing else in the bag.
|
|
328
|
+
const COOKIE_PAIR = /^[^=;]+=K$/
|
|
329
|
+
|
|
330
|
+
|
|
326
331
|
describe('pipeline:prepareAuth', () => {
|
|
327
332
|
|
|
328
333
|
// Fake client so the exact options.auth / apikey shape is controlled.
|
|
329
|
-
function authCtx(options,
|
|
330
|
-
return base({ client: { options: () => options }, spec
|
|
334
|
+
function authCtx(options, spec) {
|
|
335
|
+
return base({ client: { options: () => options }, spec })
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function bags() {
|
|
339
|
+
return { headers: {}, query: {} }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// `basic: false` is explicit: an HTTP Basic API's generated config carries
|
|
343
|
+
// `auth.basic: true`, and a client that merges it in takes a branch that
|
|
344
|
+
// needs a secret as well. With none supplied that branch deliberately
|
|
345
|
+
// writes nothing, which the probe below would read as a public API.
|
|
346
|
+
function auth(prefix) {
|
|
347
|
+
return { prefix, basic: false }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Run prepareAuth with both containers present and see which one the
|
|
351
|
+
// generated utility writes to, and under what name. Null means this SDK
|
|
352
|
+
// places no credential at all — a public API — which is a legitimate
|
|
353
|
+
// shape, and the tests below assert exactly that instead.
|
|
354
|
+
//
|
|
355
|
+
// `pair` is the `<scheme>=` lead-in of a COOKIE credential, which rides the
|
|
356
|
+
// header bag under the key `cookie` instead of taking a header of its own.
|
|
357
|
+
function probe(options) {
|
|
358
|
+
const ctx = authCtx(options, bags())
|
|
359
|
+
stdutil.prepareAuth(ctx)
|
|
360
|
+
for (const where of ['headers', 'query']) {
|
|
361
|
+
const name = Object.keys(ctx.spec[where])[0]
|
|
362
|
+
if (null == name) continue
|
|
363
|
+
const value = ctx.spec[where][name]
|
|
364
|
+
const pair = 'headers' === where && 'cookie' === name
|
|
365
|
+
&& COOKIE_PAIR.test(String(value))
|
|
366
|
+
? String(value).slice(0, -1)
|
|
367
|
+
: ''
|
|
368
|
+
return { where, name, value, pair }
|
|
369
|
+
}
|
|
370
|
+
return null
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const CRED = probe({ apikey: 'K', auth: auth('Bearer') })
|
|
374
|
+
|
|
375
|
+
// Every credential this SDK could possibly place: both credentials and
|
|
376
|
+
// Basic switched on, so whichever branch the API has, something lands
|
|
377
|
+
// unless the API is public.
|
|
378
|
+
const ANY = probe({ apikey: 'K', secret: 'S', auth: { prefix: 'Bearer', basic: true } })
|
|
379
|
+
|
|
380
|
+
function placed(options, seed) {
|
|
381
|
+
const spec = bags()
|
|
382
|
+
if (null != CRED && null != seed) spec[CRED.where][CRED.name] = seed
|
|
383
|
+
const ctx = authCtx(options, spec)
|
|
384
|
+
stdutil.prepareAuth(ctx)
|
|
385
|
+
return null == CRED ? undefined : ctx.spec[CRED.where][CRED.name]
|
|
331
386
|
}
|
|
332
387
|
|
|
333
388
|
test('guards a missing spec', () => {
|
|
334
|
-
strictEqual(stdutil.prepareAuth(authCtx({ auth:
|
|
389
|
+
strictEqual(stdutil.prepareAuth(authCtx({ auth: auth(''), apikey: 'K' }, null)).code, 'auth_no_spec')
|
|
335
390
|
})
|
|
336
391
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
strictEqual(
|
|
392
|
+
// Without this the cases below cannot fail for an SDK whose credential the
|
|
393
|
+
// probe misses: every one of them takes the public-API path instead.
|
|
394
|
+
test('the probe finds the credential this SDK places', () => {
|
|
395
|
+
strictEqual(null != CRED, null != ANY)
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
test('the apikey is placed where this API puts it', () => {
|
|
399
|
+
if (null == CRED) {
|
|
400
|
+
// A public API places nothing, and that is the whole assertion.
|
|
401
|
+
strictEqual(placed({ apikey: 'K', auth: auth('Bearer') }), undefined)
|
|
402
|
+
return
|
|
403
|
+
}
|
|
404
|
+
ok('headers' === CRED.where || 'query' === CRED.where)
|
|
405
|
+
if ('' !== CRED.pair) {
|
|
406
|
+
// A cookie credential is a `<scheme>=<key>` pair, and the scheme name
|
|
407
|
+
// leaves no room for the option's prefix.
|
|
408
|
+
ok(COOKIE_PAIR.test(String(CRED.value)), String(CRED.value))
|
|
409
|
+
return
|
|
410
|
+
}
|
|
411
|
+
// A header credential is prefix-joined; a query credential is the raw
|
|
412
|
+
// key, because a query parameter has nowhere to put a scheme name.
|
|
413
|
+
strictEqual(CRED.value, 'headers' === CRED.where ? 'Bearer K' : 'K')
|
|
341
414
|
})
|
|
342
415
|
|
|
343
416
|
test('a raw apikey (empty prefix) goes in as-is', () => {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
strictEqual(ctx.spec.headers.authorization, 'K')
|
|
417
|
+
const raw = null == CRED ? undefined : CRED.pair + 'K'
|
|
418
|
+
strictEqual(placed({ apikey: 'K', auth: auth('') }), raw)
|
|
347
419
|
})
|
|
348
420
|
|
|
349
|
-
test('an empty apikey drops the
|
|
350
|
-
|
|
351
|
-
stdutil.prepareAuth(ctx)
|
|
352
|
-
strictEqual(ctx.spec.headers.authorization, undefined)
|
|
421
|
+
test('an empty apikey drops the credential', () => {
|
|
422
|
+
strictEqual(placed({ apikey: '', auth: auth('Bearer') }, 'stale'), undefined)
|
|
353
423
|
})
|
|
354
424
|
|
|
355
|
-
test('a public API (no auth block) drops the
|
|
356
|
-
|
|
357
|
-
stdutil.prepareAuth(ctx)
|
|
358
|
-
strictEqual(ctx.spec.headers.authorization, undefined)
|
|
425
|
+
test('a public API (no auth block) drops the credential', () => {
|
|
426
|
+
strictEqual(placed({ apikey: 'K' }, 'stale'), undefined)
|
|
359
427
|
})
|
|
360
428
|
|
|
361
|
-
test('a missing apikey option drops the
|
|
362
|
-
|
|
363
|
-
stdutil.prepareAuth(ctx)
|
|
364
|
-
strictEqual(ctx.spec.headers.authorization, undefined)
|
|
429
|
+
test('a missing apikey option drops the credential', () => {
|
|
430
|
+
strictEqual(placed({ auth: auth('Bearer') }, 'stale'), undefined)
|
|
365
431
|
})
|
|
366
432
|
})
|
|
367
433
|
|
|
@@ -79,10 +79,56 @@ describe('PrimaryUtility', async () => {
|
|
|
79
79
|
})
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
function credential() {
|
|
83
|
+
const ctx = {
|
|
84
|
+
utility,
|
|
85
|
+
// `basic: false`: a Basic API's config defaults it true, and the
|
|
86
|
+
// branch it selects needs a secret, so the probe would find nothing.
|
|
87
|
+
client: { options: () => ({ apikey: 'PROBE', auth: { prefix: '', basic: false } }) },
|
|
88
|
+
spec: { headers: {}, query: {} },
|
|
89
|
+
error: (code, msg) => Object.assign(new Error(msg), { code }),
|
|
90
|
+
}
|
|
91
|
+
try { utility.prepareAuth(ctx) } catch (e) { return null }
|
|
92
|
+
for (const where of ['headers', 'query']) {
|
|
93
|
+
const name = Object.keys(ctx.spec[where] || {})[0]
|
|
94
|
+
if (null != name) return { where, name }
|
|
95
|
+
}
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Rename the corpus's `headers` bag to the real container, and the
|
|
100
|
+
// `authorization` key inside it to the real credential name. Applied only
|
|
101
|
+
// to the prepareAuth section, so real header assertions elsewhere are
|
|
102
|
+
// untouched.
|
|
103
|
+
function retarget(node, cred) {
|
|
104
|
+
if (null == node || 'object' !== typeof node) return node
|
|
105
|
+
if (Array.isArray(node)) return node.map((n) => retarget(n, cred))
|
|
106
|
+
const out = {}
|
|
107
|
+
for (const key of Object.keys(node)) {
|
|
108
|
+
if ('headers' === key) {
|
|
109
|
+
const bag = {}
|
|
110
|
+
for (const bk of Object.keys(node[key] || {})) {
|
|
111
|
+
bag['authorization' === bk ? cred.name : bk] = retarget(node[key][bk], cred)
|
|
112
|
+
}
|
|
113
|
+
out[cred.where] = bag
|
|
114
|
+
}
|
|
115
|
+
else out[key] = retarget(node[key], cred)
|
|
116
|
+
}
|
|
117
|
+
return out
|
|
118
|
+
}
|
|
119
|
+
|
|
82
120
|
test('auth-basic', async () => {
|
|
83
121
|
const sdkopts = spec.prepareAuth?.DEF?.setup?.a || {}
|
|
84
122
|
const authClient = SDK.test({}, sdkopts)
|
|
85
|
-
|
|
123
|
+
|
|
124
|
+
const cred = credential()
|
|
125
|
+
ok(null != cred, 'prepareAuth placed no credential in headers or query')
|
|
126
|
+
|
|
127
|
+
const cases = 'headers' === cred.where && 'authorization' === cred.name
|
|
128
|
+
? spec.prepareAuth.basic
|
|
129
|
+
: retarget(spec.prepareAuth.basic, cred)
|
|
130
|
+
|
|
131
|
+
await runset(cases, (ctx) => {
|
|
86
132
|
ctx.client = authClient
|
|
87
133
|
fixctx(ctx)
|
|
88
134
|
return utility.prepareAuth(ctx)
|
|
@@ -403,7 +403,14 @@ end
|
|
|
403
403
|
--- omni's value model), which keeps smoke tests free of fixture files.
|
|
404
404
|
function M.makeRunner(testfile, client)
|
|
405
405
|
local specref = testfile
|
|
406
|
-
|
|
406
|
+
-- A drive letter and a UNC prefix are absolute too, and neither starts
|
|
407
|
+
-- with "/": treating one as relative joins it onto _test_dir and reads
|
|
408
|
+
-- nothing. Lua has no path library, so the shapes are spelled out.
|
|
409
|
+
local function isabs(p)
|
|
410
|
+
return nil ~= p:match("^/") or nil ~= p:match("^%a:[/\\]")
|
|
411
|
+
or nil ~= p:match("^\\\\")
|
|
412
|
+
end
|
|
413
|
+
if "string" == type(testfile) and not isabs(testfile) then
|
|
407
414
|
specref = _test_dir .. testfile
|
|
408
415
|
end
|
|
409
416
|
|
|
@@ -536,58 +536,137 @@ describe("pipeline", function()
|
|
|
536
536
|
|
|
537
537
|
describe("prepareAuth", function()
|
|
538
538
|
|
|
539
|
+
-- A cookie credential as prepare_auth writes it: `<scheme>=K` for the
|
|
540
|
+
-- probe key, with no scheme prefix and nothing else in the bag.
|
|
541
|
+
local function is_cookie_pair(value)
|
|
542
|
+
return "string" == type(value) and nil ~= value:match("^[^=;]+=K$")
|
|
543
|
+
end
|
|
544
|
+
|
|
539
545
|
-- Fake client so the exact options.auth / apikey shape is controlled.
|
|
540
|
-
local function auth_ctx(options,
|
|
546
|
+
local function auth_ctx(options, spec)
|
|
541
547
|
local ctx = base_ctx({})
|
|
542
548
|
ctx.client = {
|
|
543
549
|
options_map = function(_self)
|
|
544
550
|
return options
|
|
545
551
|
end,
|
|
546
552
|
}
|
|
547
|
-
|
|
548
|
-
ctx.spec = nil
|
|
549
|
-
else
|
|
550
|
-
ctx.spec = Spec.new({ headers = headers })
|
|
551
|
-
end
|
|
553
|
+
ctx.spec = spec
|
|
552
554
|
return ctx
|
|
553
555
|
end
|
|
554
556
|
|
|
557
|
+
local function auth_bags()
|
|
558
|
+
return Spec.new({ headers = {}, query = {} })
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
-- A cookie credential rides the header bag, because a cookie IS a header.
|
|
562
|
+
local function auth_bag(spec, where)
|
|
563
|
+
if "query" == where then return spec.query end
|
|
564
|
+
return spec.headers
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
-- `basic = false` is explicit: an HTTP Basic API's generated config
|
|
568
|
+
-- carries `auth.basic: true`, and a client that merges it in takes a
|
|
569
|
+
-- branch needing a secret as well. With none supplied that branch writes
|
|
570
|
+
-- nothing, which the probe would read as a public API.
|
|
571
|
+
local function auth_block(prefix)
|
|
572
|
+
return { prefix = prefix, basic = false }
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
-- Run prepare_auth with both containers present and see which one the
|
|
576
|
+
-- generated utility writes to, and under what name. nil means this SDK
|
|
577
|
+
-- places no credential at all - a public API - which is a legitimate
|
|
578
|
+
-- shape, and the tests below assert exactly that instead. `pair` is the
|
|
579
|
+
-- `<scheme>=` lead-in of a COOKIE credential, which rides the header bag
|
|
580
|
+
-- under the key "cookie" instead of taking a header of its own.
|
|
581
|
+
local function auth_probe(options)
|
|
582
|
+
local ctx = auth_ctx(options, auth_bags())
|
|
583
|
+
ctx.utility.prepare_auth(ctx)
|
|
584
|
+
for _, where in ipairs({ "headers", "query" }) do
|
|
585
|
+
for name, value in pairs(auth_bag(ctx.spec, where)) do
|
|
586
|
+
local pair = ""
|
|
587
|
+
if "headers" == where and "cookie" == name and is_cookie_pair(value) then
|
|
588
|
+
pair = value:sub(1, -2)
|
|
589
|
+
end
|
|
590
|
+
return { where = where, name = name, value = value, pair = pair }
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
return nil
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
local function auth_credential()
|
|
597
|
+
return auth_probe({ apikey = "K", auth = auth_block("Bearer") })
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
-- Every credential this SDK could possibly place: both credentials and
|
|
601
|
+
-- Basic switched on, so whichever branch the API has, something lands
|
|
602
|
+
-- unless the API is public.
|
|
603
|
+
local function auth_any_credential()
|
|
604
|
+
return auth_probe({ apikey = "K", secret = "S",
|
|
605
|
+
auth = { prefix = "Bearer", basic = true } })
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
local function auth_placed(options, seed)
|
|
609
|
+
local cred = auth_credential()
|
|
610
|
+
local spec = auth_bags()
|
|
611
|
+
if nil ~= cred and nil ~= seed then
|
|
612
|
+
auth_bag(spec, cred.where)[cred.name] = seed
|
|
613
|
+
end
|
|
614
|
+
local ctx = auth_ctx(options, spec)
|
|
615
|
+
ctx.utility.prepare_auth(ctx)
|
|
616
|
+
if nil == cred then return nil end
|
|
617
|
+
return auth_bag(ctx.spec, cred.where)[cred.name]
|
|
618
|
+
end
|
|
619
|
+
|
|
555
620
|
it("guards a missing spec", function()
|
|
556
|
-
local ctx = auth_ctx({ auth =
|
|
621
|
+
local ctx = auth_ctx({ auth = auth_block(""), apikey = "K" }, nil)
|
|
557
622
|
local _, err = ctx.utility.prepare_auth(ctx)
|
|
558
623
|
assert.are.equal("auth_no_spec", err.code)
|
|
559
624
|
end)
|
|
560
625
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
assert.are.equal(
|
|
626
|
+
-- Without this the cases below cannot fail for an SDK whose credential the
|
|
627
|
+
-- probe misses: every one of them takes the public-API path instead.
|
|
628
|
+
it("the probe finds the credential this SDK places", function()
|
|
629
|
+
assert.are.equal(nil == auth_credential(), nil == auth_any_credential())
|
|
630
|
+
end)
|
|
631
|
+
|
|
632
|
+
it("the apikey is placed where this API puts it", function()
|
|
633
|
+
local cred = auth_credential()
|
|
634
|
+
if nil == cred then
|
|
635
|
+
-- A public API places nothing, and that is the whole assertion.
|
|
636
|
+
assert.is_nil(auth_placed({ apikey = "K", auth = auth_block("Bearer") }))
|
|
637
|
+
return
|
|
638
|
+
end
|
|
639
|
+
assert.is_true("headers" == cred.where or "query" == cred.where)
|
|
640
|
+
if "" ~= cred.pair then
|
|
641
|
+
-- A cookie credential is a `<scheme>=<key>` pair, and the scheme name
|
|
642
|
+
-- leaves no room for the option's prefix.
|
|
643
|
+
assert.is_true(is_cookie_pair(cred.value), tostring(cred.value))
|
|
644
|
+
return
|
|
645
|
+
end
|
|
646
|
+
-- A header credential is prefix-joined; a query credential is the raw
|
|
647
|
+
-- key, because a query parameter has nowhere to put a scheme name.
|
|
648
|
+
local want = "Bearer K"
|
|
649
|
+
if "query" == cred.where then want = "K" end
|
|
650
|
+
assert.are.equal(want, cred.value)
|
|
565
651
|
end)
|
|
566
652
|
|
|
567
653
|
it("a raw apikey (empty prefix) goes in as-is", function()
|
|
568
|
-
local
|
|
569
|
-
|
|
570
|
-
|
|
654
|
+
local cred = auth_credential()
|
|
655
|
+
local want = nil
|
|
656
|
+
if nil ~= cred then want = cred.pair .. "K" end
|
|
657
|
+
assert.are.equal(want, auth_placed({ apikey = "K", auth = auth_block("") }))
|
|
571
658
|
end)
|
|
572
659
|
|
|
573
|
-
it("an empty apikey drops the
|
|
574
|
-
|
|
575
|
-
{ authorization = "stale" })
|
|
576
|
-
ctx.utility.prepare_auth(ctx)
|
|
577
|
-
assert.is_nil(ctx.spec.headers["authorization"])
|
|
660
|
+
it("an empty apikey drops the credential", function()
|
|
661
|
+
assert.is_nil(auth_placed({ apikey = "", auth = auth_block("Bearer") }, "stale"))
|
|
578
662
|
end)
|
|
579
663
|
|
|
580
|
-
it("a public API (no auth block) drops the
|
|
581
|
-
|
|
582
|
-
ctx.utility.prepare_auth(ctx)
|
|
583
|
-
assert.is_nil(ctx.spec.headers["authorization"])
|
|
664
|
+
it("a public API (no auth block) drops the credential", function()
|
|
665
|
+
assert.is_nil(auth_placed({ apikey = "K" }, "stale"))
|
|
584
666
|
end)
|
|
585
667
|
|
|
586
|
-
it("a missing apikey option drops the
|
|
587
|
-
|
|
588
|
-
{ authorization = "stale" })
|
|
589
|
-
ctx.utility.prepare_auth(ctx)
|
|
590
|
-
assert.is_nil(ctx.spec.headers["authorization"])
|
|
668
|
+
it("a missing apikey option drops the credential", function()
|
|
669
|
+
assert.is_nil(auth_placed({ auth = auth_block("Bearer") }, "stale"))
|
|
591
670
|
end)
|
|
592
671
|
end)
|
|
593
672
|
|
|
@@ -70,6 +70,64 @@ local function get_spec(base, ...)
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
-- Where this SDK's prepare_auth actually puts the credential: the name is
|
|
74
|
+
-- the API's OWN scheme name, not always `authorization`, so it is discovered
|
|
75
|
+
-- by running prepare_auth once.
|
|
76
|
+
local function auth_credential(auth_client, auth_utility)
|
|
77
|
+
local ctx = auth_utility.make_context({
|
|
78
|
+
opname = "load",
|
|
79
|
+
client = auth_client,
|
|
80
|
+
utility = auth_utility,
|
|
81
|
+
}, auth_client:get_root_ctx())
|
|
82
|
+
ctx.spec = Spec.new({ headers = {}, query = {} })
|
|
83
|
+
local _, err = auth_utility.prepare_auth(ctx)
|
|
84
|
+
if err ~= nil then
|
|
85
|
+
return nil
|
|
86
|
+
end
|
|
87
|
+
for _, where in ipairs({ "headers", "query" }) do
|
|
88
|
+
-- A cookie credential rides the header bag, because a cookie IS a header.
|
|
89
|
+
local bag = ctx.spec.headers
|
|
90
|
+
if "query" == where then bag = ctx.spec.query end
|
|
91
|
+
for name, _value in pairs(bag) do
|
|
92
|
+
return { where = where, name = name }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
return nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
-- Rename the corpus's `headers` bag to the real container, and the
|
|
100
|
+
-- `authorization` key inside it to the real credential name. Applied only to
|
|
101
|
+
-- the prepareAuth section, so real header assertions elsewhere are untouched.
|
|
102
|
+
local function retarget_auth(node, cred)
|
|
103
|
+
if type(node) ~= "table" then
|
|
104
|
+
return node
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
-- The metatable carries the corpus's `__jsontype` marker, which the struct
|
|
108
|
+
-- library's islist/ismap read to tell a one-element list from a map. A
|
|
109
|
+
-- plain-table copy leaves the rebuilt `set` unrecognisable.
|
|
110
|
+
local out = setmetatable({}, getmetatable(node))
|
|
111
|
+
for key, value in pairs(node) do
|
|
112
|
+
if "headers" == key then
|
|
113
|
+
local bag = {}
|
|
114
|
+
if type(value) == "table" then
|
|
115
|
+
setmetatable(bag, getmetatable(value))
|
|
116
|
+
for bagkey, bagvalue in pairs(value) do
|
|
117
|
+
local name = bagkey
|
|
118
|
+
if "authorization" == bagkey then name = cred.name end
|
|
119
|
+
bag[name] = retarget_auth(bagvalue, cred)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
out[cred.where] = bag
|
|
123
|
+
else
|
|
124
|
+
out[key] = retarget_auth(value, cred)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
return out
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
73
131
|
-- Sections deliberately left empty in the shared corpus
|
|
74
132
|
-- (.sdk/test/primary/<name>.aon carries a PENDING header). Everything else
|
|
75
133
|
-- MUST contribute cases.
|
|
@@ -749,7 +807,23 @@ describe("PrimaryUtility", function()
|
|
|
749
807
|
local auth_client = sdk.test(nil, setup_opts)
|
|
750
808
|
local auth_utility = auth_client:get_utility()
|
|
751
809
|
|
|
752
|
-
|
|
810
|
+
-- The corpus writes the credential as `headers.authorization`: a
|
|
811
|
+
-- PLACEHOLDER each runner points at the container and name this API
|
|
812
|
+
-- actually uses.
|
|
813
|
+
local cred = auth_credential(auth_client, auth_utility)
|
|
814
|
+
assert(cred ~= nil, "prepare_auth placed no credential in headers or query")
|
|
815
|
+
|
|
816
|
+
-- An absent section is runsection's report to make.
|
|
817
|
+
local section = get_spec(spec, "prepareAuth")
|
|
818
|
+
local original = nil
|
|
819
|
+
if section ~= nil
|
|
820
|
+
and not ("headers" == cred.where and "authorization" == cred.name)
|
|
821
|
+
then
|
|
822
|
+
original = section.basic
|
|
823
|
+
section.basic = retarget_auth(original, cred)
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
local subject = function(cin)
|
|
753
827
|
if type(cin) ~= "table" then
|
|
754
828
|
cin = {}
|
|
755
829
|
end
|
|
@@ -758,15 +832,21 @@ describe("PrimaryUtility", function()
|
|
|
758
832
|
|
|
759
833
|
unwrap(utility.prepare_auth(ctx))
|
|
760
834
|
|
|
761
|
-
-- Write live-context spec
|
|
835
|
+
-- Write the live-context spec bags back for match checking. QUERY as
|
|
836
|
+
-- well as headers: this API may place its credential there.
|
|
762
837
|
if ctx.spec ~= nil then
|
|
763
838
|
cin["spec"] = {
|
|
764
839
|
headers = ctx.spec.headers,
|
|
840
|
+
query = ctx.spec.query,
|
|
765
841
|
}
|
|
766
842
|
end
|
|
767
843
|
|
|
768
844
|
return nil
|
|
769
|
-
end
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
local ok, failure = pcall(runsection, "prepareAuth", subject)
|
|
848
|
+
if original ~= nil then section.basic = original end
|
|
849
|
+
if not ok then error(failure) end
|
|
770
850
|
end)
|
|
771
851
|
|
|
772
852
|
|
|
@@ -587,7 +587,13 @@ sub make_runner {
|
|
|
587
587
|
my ($testfile, $client) = @_;
|
|
588
588
|
|
|
589
589
|
my $specref = $testfile;
|
|
590
|
-
|
|
590
|
+
# A leading slash alone is not the whole of absolute: a Windows path
|
|
591
|
+
# starts `C:/` or `\\host\share`, and joining one onto $DIR reads
|
|
592
|
+
# nothing. Spelled out rather than left to File::Spec, which an MSYS perl
|
|
593
|
+
# resolves to the Unix flavour and which answers no for a drive letter.
|
|
594
|
+
if (defined $testfile && !ref $testfile
|
|
595
|
+
&& $testfile !~ m{^/} && $testfile !~ m{^[A-Za-z]:[\\/]}
|
|
596
|
+
&& $testfile !~ m{^\\\\}) {
|
|
591
597
|
$specref = Cwd::abs_path("$DIR/$testfile") || "$DIR/$testfile";
|
|
592
598
|
}
|
|
593
599
|
|