@voxgig/sdkgen 4.5.3 → 4.7.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.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/action/check.js +15 -0
- package/dist/action/check.js.map +1 -1
- package/dist/cmp/AgentGuide.js +1 -1
- package/dist/cmp/AgentGuide.js.map +1 -1
- package/dist/cmp/AgentGuideContent.d.ts +1 -1
- package/dist/cmp/AgentGuideContent.js +8 -2
- package/dist/cmp/AgentGuideContent.js.map +1 -1
- package/dist/cmp/Feature.js +22 -0
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/cmp/FeatureDocs.d.ts +1 -1
- package/dist/cmp/FeatureDocs.js +6 -1
- package/dist/cmp/FeatureDocs.js.map +1 -1
- package/dist/cmp/ReadmeExplanation.js +3 -1
- package/dist/cmp/ReadmeExplanation.js.map +1 -1
- package/dist/cmp/ReadmeFeatures.js +1 -1
- package/dist/cmp/ReadmeFeatures.js.map +1 -1
- package/dist/cmp/ReadmeRefFeatures.js +1 -1
- package/dist/cmp/ReadmeRefFeatures.js.map +1 -1
- package/dist/cmp/TestControl.d.ts +4 -0
- package/dist/cmp/TestControl.js +51 -0
- package/dist/cmp/TestControl.js.map +1 -0
- package/dist/helpers/applicability.d.ts +6 -0
- package/dist/helpers/applicability.js +103 -0
- package/dist/helpers/applicability.js.map +1 -0
- package/dist/helpers/collectDeps.js +4 -2
- package/dist/helpers/collectDeps.js.map +1 -1
- package/dist/helpers/featureSource.d.ts +3 -1
- package/dist/helpers/featureSource.js +105 -0
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/serverVars.d.ts +2 -1
- package/dist/helpers/serverVars.js +17 -0
- package/dist/helpers/serverVars.js.map +1 -1
- package/dist/sdkgen.d.ts +5 -3
- package/dist/sdkgen.js +16 -4
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +59 -1
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aon +97 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/feature-index.aon +1 -0
- package/project/.sdk/model/feature/secrets.aon +200 -0
- package/project/.sdk/model/target/ts.aon +8 -1
- package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
- package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -6
- package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
- package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
- package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
- package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +23 -6
- package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
- package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
- package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
- package/project/.sdk/src/cmp/go/Main_go.ts +8 -3
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
- package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
- package/project/.sdk/src/cmp/java/Main_java.ts +7 -2
- package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
- package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
- package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
- package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
- package/project/.sdk/src/cmp/js/Main_js.ts +7 -2
- package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +30 -5
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
- package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
- package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
- package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
- package/project/.sdk/src/cmp/lua/Main_lua.ts +7 -2
- package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +28 -4
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
- package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
- package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
- package/project/.sdk/src/cmp/perl/Main_perl.ts +7 -2
- package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -4
- package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
- package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
- package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
- package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
- package/project/.sdk/src/cmp/php/Main_php.ts +7 -2
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +24 -5
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
- package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
- package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +7 -2
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
- package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
- package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/Main_rb.ts +7 -2
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +24 -5
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
- package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
- package/project/.sdk/src/cmp/rust/Main_rust.ts +7 -2
- package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +31 -4
- package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
- package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
- package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
- package/project/.sdk/src/cmp/scala/Main_scala.ts +7 -2
- package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
- package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
- package/project/.sdk/src/cmp/ts/Config_ts.ts +55 -9
- package/project/.sdk/src/cmp/ts/Main_ts.ts +67 -3
- package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +75 -8
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +36 -5
- package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
- package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
- package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
- package/project/.sdk/tm/c/utility/make_options.c +22 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
- package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
- package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
- package/project/.sdk/tm/dart/test/utility.dart +39 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
- package/project/.sdk/tm/go/core/context.go +1 -1
- package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
- package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
- package/project/.sdk/tm/go/feature/test_feature.go +12 -5
- package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
- package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
- package/project/.sdk/tm/go/test/runner_test.go +46 -0
- package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
- package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
- package/project/.sdk/tm/go/utility/fetcher.go +2 -2
- package/project/.sdk/tm/go/utility/make_options.go +24 -5
- package/project/.sdk/tm/go/utility/make_point.go +1 -1
- package/project/.sdk/tm/go/utility/make_spec.go +17 -1
- package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
- package/project/.sdk/tm/go/utility/transform_request.go +29 -1
- package/project/.sdk/tm/go/utility/transform_response.go +20 -1
- package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
- package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
- package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
- package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
- package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/js/test/utility.js +91 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
- package/project/.sdk/tm/lua/test/runner.lua +40 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
- package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
- package/project/.sdk/tm/perl/t/runner.pm +30 -0
- package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
- package/project/.sdk/tm/php/test/Runner.php +36 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
- package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
- package/project/.sdk/tm/py/test/runner.py +32 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
- package/project/.sdk/tm/rb/test/runner.rb +25 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
- package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +26 -1
- package/project/.sdk/tm/scala/utility/Make.scala +17 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +387 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +175 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +51 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
- package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
- package/project/.sdk/tm/ts/test/omni.ts +86 -0
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
- package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
- package/project/.sdk/tm/ts/test/utility.ts +122 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +694 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +244 -0
- package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
- package/project/.sdk/tm/zig/core/utility.zig +24 -1
- package/project/sdkgen-package.json +2 -1
- package/src/action/check.ts +18 -0
- package/src/cmp/AgentGuide.ts +1 -1
- package/src/cmp/AgentGuideContent.ts +9 -2
- package/src/cmp/Feature.ts +24 -0
- package/src/cmp/FeatureDocs.ts +7 -1
- package/src/cmp/ReadmeExplanation.ts +4 -1
- package/src/cmp/ReadmeFeatures.ts +1 -1
- package/src/cmp/ReadmeRefFeatures.ts +1 -1
- package/src/cmp/TestControl.ts +64 -0
- package/src/helpers/applicability.ts +123 -0
- package/src/helpers/collectDeps.ts +5 -1
- package/src/helpers/featureSource.ts +130 -0
- package/src/helpers/serverVars.ts +19 -0
- package/src/sdkgen.ts +16 -1
- package/src/utility.ts +71 -1
- package/project/.sdk/tm/ts/test/runner.ts +0 -425
|
@@ -84,7 +84,16 @@ function scriptedFetcher(res) {
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
function makeClient(kase) {
|
|
87
|
+
// `test: { active: true }` is the OPTION, not the `test` FEATURE.
|
|
88
|
+
//
|
|
89
|
+
// It says "this client is not live", which is what makes a REQUIRED
|
|
90
|
+
// OpenAPI server variable resolve to a deterministic `test-<name>`
|
|
91
|
+
// rather than refuse to construct (see makeOptions). It installs no
|
|
92
|
+
// transport, so the scripted fetcher below still stands — the FEATURE
|
|
93
|
+
// is transport: 'base' and would shadow it, which is why this cannot
|
|
94
|
+
// just turn the feature on.
|
|
87
95
|
return new SDK({
|
|
96
|
+
test: { active: true },
|
|
88
97
|
feature: kase.feature,
|
|
89
98
|
utility: { fetcher: scriptedFetcher(kase.res || [{ status: 200, body: {} }]) },
|
|
90
99
|
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"_doc": "Per-SDK test control. Lists tests/operations to skip in unit and live modes; tunes per-test pacing. Edit by hand. Loaded by js/test/utility.js.",
|
|
4
|
+
"test": {
|
|
5
|
+
"skip": {
|
|
6
|
+
"live": {
|
|
7
|
+
"direct": [],
|
|
8
|
+
"entityOp": []
|
|
9
|
+
},
|
|
10
|
+
"unit": {
|
|
11
|
+
"direct": [],
|
|
12
|
+
"entityOp": []
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"live": {
|
|
16
|
+
"delayMs": 500
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
* for creating test data, transformations, validations, and environment overrides.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
const Fs = require('node:fs')
|
|
9
|
+
const Path = require('node:path')
|
|
10
|
+
|
|
11
|
+
|
|
8
12
|
// Creates a new step data structure within the data model
|
|
9
13
|
function makeStepData(dm, stepname) {
|
|
10
14
|
dm.s[stepname] = {
|
|
@@ -62,11 +66,97 @@ function envOverride(m) {
|
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
|
|
69
|
+
// Loads sdk-test-control.json (cached). Returns an empty-skip object if
|
|
70
|
+
// the file is missing or unparsable so tests never crash on a bad config.
|
|
71
|
+
let _testControlCache = null
|
|
72
|
+
|
|
73
|
+
function loadTestControl() {
|
|
74
|
+
if (_testControlCache) return _testControlCache
|
|
75
|
+
const ctrlPath = Path.resolve(__dirname, '../test/sdk-test-control.json')
|
|
76
|
+
try {
|
|
77
|
+
_testControlCache = JSON.parse(Fs.readFileSync(ctrlPath, 'utf8'))
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
_testControlCache = {
|
|
81
|
+
version: 1,
|
|
82
|
+
test: { skip: { live: { direct: [], entityOp: [] }, unit: { direct: [], entityOp: [] } } }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return _testControlCache
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
// Extra SDK options every LIVE client is constructed with, read from
|
|
90
|
+
// sdk-test-control.json `test.client.options`.
|
|
91
|
+
//
|
|
92
|
+
// The generated live client knows two things: the base URL (from the spec)
|
|
93
|
+
// and the credential (from the environment). Everything else about how a
|
|
94
|
+
// particular API wants to be talked to - which features to switch on, and
|
|
95
|
+
// with what settings - is a property of THAT API, known to the project and
|
|
96
|
+
// to nothing in the toolchain. So it lives in the file the project owns and
|
|
97
|
+
// describes, where it can be read and reviewed. Secrets still come from the
|
|
98
|
+
// environment (the providers this block names read them).
|
|
99
|
+
//
|
|
100
|
+
// Merged UNDER the generated fields, so the suite's own base/apikey/server
|
|
101
|
+
// values win - this adds to the live client, it does not redirect it.
|
|
102
|
+
//
|
|
103
|
+
// That contract is enforced HERE rather than left to each merge site: the
|
|
104
|
+
// generated object only names a field when the model calls for one, so a
|
|
105
|
+
// `base` in this block would face no competing value and would silently
|
|
106
|
+
// redirect the whole suite - credential included - to another host. The
|
|
107
|
+
// reserved fields are stripped once, where the block is read, so every
|
|
108
|
+
// caller gets the same guarantee whether or not it happens to emit them.
|
|
109
|
+
const LIVE_RESERVED = ['base', 'prefix', 'suffix', 'server', 'apikey', 'secret']
|
|
110
|
+
|
|
111
|
+
function liveClientOptions() {
|
|
112
|
+
const ctrl = loadTestControl()
|
|
113
|
+
const opts = ctrl && ctrl.test && ctrl.test.client && ctrl.test.client.options
|
|
114
|
+
|
|
115
|
+
if (null == opts || 'object' !== typeof opts) {
|
|
116
|
+
return {}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const out = {}
|
|
120
|
+
for (const key of Object.keys(opts)) {
|
|
121
|
+
if (!LIVE_RESERVED.includes(key)) {
|
|
122
|
+
out[key] = opts[key]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return out
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
// Per-test live pacing delay (ms). Read from sdk-test-control.json
|
|
131
|
+
// `test.live.delayMs`; defaults to 500ms if absent or invalid.
|
|
132
|
+
function liveDelayMs() {
|
|
133
|
+
const ctrl = loadTestControl()
|
|
134
|
+
const v = ctrl && ctrl.test && ctrl.test.live && ctrl.test.live.delayMs
|
|
135
|
+
return ('number' === typeof v && v >= 0) ? v : 500
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
// afterEach hook helper for live pacing. Generated tests register this via
|
|
141
|
+
// `afterEach(liveDelay(<envVar>))`; it sleeps `liveDelayMs()` only when the
|
|
142
|
+
// SDK's *_TEST_LIVE env var is set.
|
|
143
|
+
function liveDelay(liveEnvVar) {
|
|
144
|
+
return async () => {
|
|
145
|
+
if ('TRUE' === process.env[liveEnvVar]) {
|
|
146
|
+
await new Promise(r => setTimeout(r, liveDelayMs()))
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
65
151
|
module.exports = {
|
|
66
152
|
makeStepData,
|
|
67
153
|
makeMatch,
|
|
68
154
|
makeReqdata,
|
|
69
155
|
makeValid,
|
|
70
156
|
makeCtrl,
|
|
71
|
-
envOverride
|
|
157
|
+
envOverride,
|
|
158
|
+
loadTestControl,
|
|
159
|
+
liveClientOptions,
|
|
160
|
+
liveDelayMs,
|
|
161
|
+
liveDelay
|
|
72
162
|
}
|
|
@@ -28,8 +28,24 @@ fun makeOptions(ctx: Context): MutableMap<String, Any?> {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// `auth: null` is the documented way to disable auth outright, and
|
|
32
|
+
// PrepareAuth honours it before it ever reads the apikey. It cannot survive
|
|
33
|
+
// validate: depending on the struct port a stored null is either REPLACED
|
|
34
|
+
// by the optspec default - transmitting the credential the caller withheld
|
|
35
|
+
// - or REJECTED outright. Withhold the key for validate, then put the null
|
|
36
|
+
// back. Same fix as ts/js/go makeOptions.
|
|
37
|
+
//
|
|
38
|
+
// Suppliedness cannot be recovered after validate, hence here, and it must
|
|
39
|
+
// tell an ABSENT auth from a present null: containsKey rather than a null
|
|
40
|
+
// check on the value, which cannot distinguish them.
|
|
41
|
+
val authSuppressed = options.containsKey("auth") && null == options["auth"]
|
|
42
|
+
|
|
31
43
|
var opts = Struct.clone(options) as MutableMap<String, Any?>
|
|
32
44
|
|
|
45
|
+
if (authSuppressed) {
|
|
46
|
+
opts.remove("auth")
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
// Feature add-order. options.feature may be given as an ordered LIST of
|
|
34
50
|
// { name, active, ...opts } entries (the list position IS the order in which
|
|
35
51
|
// features are added), or as a { name: {opts} } map. Normalize a list to a
|
|
@@ -107,6 +123,11 @@ fun makeOptions(ctx: Context): MutableMap<String, Any?> {
|
|
|
107
123
|
val validated = Struct.validate(merged, optspec, vopts)
|
|
108
124
|
opts = validated as MutableMap<String, Any?>
|
|
109
125
|
|
|
126
|
+
// Restore the suppression the optspec default would otherwise erase.
|
|
127
|
+
if (authSuppressed) {
|
|
128
|
+
opts["auth"] = null
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
// Restore system.fetch.
|
|
111
132
|
if (sysFetch != null) {
|
|
112
133
|
val sys = Helpers.toMapAny(opts["system"])
|
|
@@ -260,8 +260,19 @@ def prepareAuth (ctx : Value) : SIO (Value × Option Value) := do
|
|
|
260
260
|
| .map _ => do
|
|
261
261
|
let headers ← gpMap specV "headers"
|
|
262
262
|
let options ← gp ctx "options"
|
|
263
|
+
-- `auth: null` is the documented way to suppress auth outright: NO
|
|
264
|
+
-- authorization header, whatever the apikey says. Without this the
|
|
265
|
+
-- withheld credential goes on the wire.
|
|
266
|
+
--
|
|
267
|
+
-- The ports that run options through validate against an optspec carrying
|
|
268
|
+
-- an `auth` default can use absence as the signal (validate guarantees the
|
|
269
|
+
-- key is present otherwise). lean's makeOptions has no optspec, so `auth`
|
|
270
|
+
-- is ABSENT in the ordinary case and absence must stay the ordinary case.
|
|
271
|
+
-- getpropRaw is the only reader that tells a STORED null from an absent
|
|
272
|
+
-- key - gp collapses both to .noval - so the stored null alone suppresses.
|
|
273
|
+
let authRaw ← getpropRaw options "auth"
|
|
263
274
|
let apikey ← gpS options "apikey"
|
|
264
|
-
if apikey == "" then do
|
|
275
|
+
if authRaw == .null || apikey == "" then do
|
|
265
276
|
dp headers "authorization"
|
|
266
277
|
pure (specV, none)
|
|
267
278
|
else do
|
|
@@ -146,6 +146,46 @@ function runner.is_control_skipped(kind, name, mode)
|
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
|
|
149
|
+
-- Extra SDK options every LIVE client is constructed with, read from
|
|
150
|
+
-- sdk-test-control.json `test.client.options`.
|
|
151
|
+
--
|
|
152
|
+
-- The generated live client knows two things: the base URL (from the spec)
|
|
153
|
+
-- and the credential (from the environment). Everything else about how a
|
|
154
|
+
-- particular API wants to be talked to - which features to switch on, and
|
|
155
|
+
-- with what settings - is a property of THAT API, known to the project and
|
|
156
|
+
-- to nothing in the toolchain.
|
|
157
|
+
--
|
|
158
|
+
-- Merged UNDER the generated fields, so the suite's own base/apikey/server
|
|
159
|
+
-- values win: this ADDS to the live client, it does not redirect it.
|
|
160
|
+
--
|
|
161
|
+
-- Reserved fields are stripped HERE rather than at each merge site: the
|
|
162
|
+
-- generated table only names a field when the model calls for one, so a
|
|
163
|
+
-- "base" in this block would face no competing value and would silently
|
|
164
|
+
-- redirect the whole suite - credential included - to another host.
|
|
165
|
+
local LIVE_RESERVED = {
|
|
166
|
+
base = true, prefix = true, suffix = true,
|
|
167
|
+
server = true, apikey = true, secret = true,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function runner.live_client_options()
|
|
171
|
+
local ctrl = runner.load_test_control()
|
|
172
|
+
local test = ctrl.test
|
|
173
|
+
if type(test) ~= "table" then return {} end
|
|
174
|
+
local client = test.client
|
|
175
|
+
if type(client) ~= "table" then return {} end
|
|
176
|
+
local opts = client.options
|
|
177
|
+
if type(opts) ~= "table" then return {} end
|
|
178
|
+
|
|
179
|
+
local out = {}
|
|
180
|
+
for k, v in pairs(opts) do
|
|
181
|
+
if not LIVE_RESERVED[k] then
|
|
182
|
+
out[k] = v
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
return out
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
|
|
149
189
|
-- Per-test live pacing delay (ms); default 500.
|
|
150
190
|
function runner.live_delay_ms()
|
|
151
191
|
local ctrl = runner.load_test_control()
|
|
@@ -968,7 +968,16 @@ let make_options_util (ctx : ctx) : value =
|
|
|
968
968
|
| Some u -> List.iter (fun k -> setp u.u_custom k (getp custom_utils k)) (keysof custom_utils)
|
|
969
969
|
| None -> ())
|
|
970
970
|
| _ -> ());
|
|
971
|
+
(* `auth: null` is the documented way to suppress auth outright. validate()
|
|
972
|
+
treats a STORED null as "no value" (getprop returns the alt), so the
|
|
973
|
+
optspec `auth` default would fire and turn the suppression into "use the
|
|
974
|
+
default auth" - transmitting a credential the caller withheld. getprop_raw
|
|
975
|
+
is the only reader that tells a stored Null from an absent key. Withhold
|
|
976
|
+
the key across merge/validate, then restore the null below. *)
|
|
977
|
+
let auth_suppressed =
|
|
978
|
+
(match getprop_raw options "auth" with Null -> true | _ -> false) in
|
|
971
979
|
let opts = match clone options with Map _ as m -> m | _ -> empty_map () in
|
|
980
|
+
let () = if auth_suppressed then ignore (delprop opts (Str "auth")) in
|
|
972
981
|
(* Feature add-order. options.feature may be given as an ordered LIST of
|
|
973
982
|
{name; active; ...opts} entries (list position = add order) or a
|
|
974
983
|
{name => {opts}} map. Normalize a list to a map (so merge/validate/init
|
|
@@ -999,6 +1008,8 @@ let make_options_util (ctx : ctx) : value =
|
|
|
999
1008
|
let merged = merge (ja [empty_map (); cfgopts; opts]) in
|
|
1000
1009
|
let validated = validate merged optspec in
|
|
1001
1010
|
let opts = match validated with Map _ as m -> m | _ -> empty_map () in
|
|
1011
|
+
(* Restore the suppression the optspec default would otherwise erase. *)
|
|
1012
|
+
let () = if auth_suppressed then setp opts "auth" Null in
|
|
1002
1013
|
(* Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
1003
1014
|
Every placeholder must resolve to a non-empty value: from options.server
|
|
1004
1015
|
(user), else the Config default. A placeholder that resolves to "" is a
|
|
@@ -93,14 +93,24 @@ let make_client ?(server : server option) ?(mode = "test") ?(base = "http://api.
|
|
|
93
93
|
rootctx.c_op <- new_operation (jo [("name", Str "root"); ("entity", Str "_")]);
|
|
94
94
|
client.cl_rootctx <- Some rootctx;
|
|
95
95
|
List.iter (fun (name, opts) ->
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
(* `netsim` is a test-only network simulator implemented in
|
|
97
|
+
Sdk_features, not a model feature, so it never appears in the
|
|
98
|
+
generated config and has_feature rejects it. Construct it directly:
|
|
99
|
+
without this every netsim-driven test (retry backoff, timeout,
|
|
100
|
+
and the metrics/telemetry/audit error paths) silently runs against
|
|
101
|
+
an unsimulated transport and sees a plain 200. *)
|
|
102
|
+
let ftr =
|
|
103
|
+
if name = "netsim" then Some (Sdk_features.netsim_feature ())
|
|
104
|
+
else if has_feature name then Some (Sdk_config.make_feature name)
|
|
105
|
+
else None in
|
|
106
|
+
match ftr with
|
|
107
|
+
| None -> ()
|
|
108
|
+
| Some ftr ->
|
|
98
109
|
let fopts = jo [("active", Bool true)] in
|
|
99
110
|
(match opts with Map _ -> List.iter (fun k -> setp fopts k (getp opts k)) (keysof opts) | _ -> ());
|
|
100
111
|
setp (getp client.cl_options "feature") name fopts;
|
|
101
112
|
ftr.f_init rootctx fopts;
|
|
102
|
-
client.cl_features <- client.cl_features @ [ftr]
|
|
103
|
-
end) features;
|
|
113
|
+
client.cl_features <- client.cl_features @ [ftr]) features;
|
|
104
114
|
feature_hook_util rootctx "PostConstruct";
|
|
105
115
|
{ h_base = base; h_headers = hheaders; h_client = client; h_utility = utility; h_rootctx = rootctx }
|
|
106
116
|
|
|
@@ -129,6 +129,36 @@ sub is_control_skipped {
|
|
|
129
129
|
return (0, undef);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
# Extra SDK options every LIVE client is constructed with, read from
|
|
133
|
+
# sdk-test-control.json `test.client.options`.
|
|
134
|
+
#
|
|
135
|
+
# The generated live client knows two things: the base URL (from the spec)
|
|
136
|
+
# and the credential (from the environment). Everything else about how a
|
|
137
|
+
# particular API wants to be talked to - which features to switch on, and
|
|
138
|
+
# with what settings - is a property of THAT API, known to the project and
|
|
139
|
+
# to nothing in the toolchain.
|
|
140
|
+
#
|
|
141
|
+
# Merged UNDER the generated fields, so the suite's own base/apikey/server
|
|
142
|
+
# values win: this ADDS to the live client, it does not redirect it.
|
|
143
|
+
#
|
|
144
|
+
# Reserved fields are stripped HERE rather than at each merge site: the
|
|
145
|
+
# generated hash only names a field when the model calls for one, so a
|
|
146
|
+
# "base" in this block would face no competing value and would silently
|
|
147
|
+
# redirect the whole suite - credential included - to another host.
|
|
148
|
+
my %LIVE_RESERVED = map { $_ => 1 }
|
|
149
|
+
qw(base prefix suffix server apikey secret);
|
|
150
|
+
|
|
151
|
+
sub live_client_options {
|
|
152
|
+
my $ctrl = load_test_control();
|
|
153
|
+
my $opts = ProjectNameHelpers::gpath($ctrl, 'test.client.options');
|
|
154
|
+
return {} unless Voxgig::Struct::ismap($opts);
|
|
155
|
+
my %out;
|
|
156
|
+
for my $k (keys %$opts) {
|
|
157
|
+
$out{$k} = $opts->{$k} unless $LIVE_RESERVED{$k};
|
|
158
|
+
}
|
|
159
|
+
return \%out;
|
|
160
|
+
}
|
|
161
|
+
|
|
132
162
|
# Per-test live pacing delay (ms); default 500.
|
|
133
163
|
sub live_delay_ms {
|
|
134
164
|
my $ctrl = load_test_control();
|
|
@@ -69,7 +69,22 @@ $REGISTRY{make_options} = sub {
|
|
|
69
69
|
# reads options.extend, but clone/validate dropped the instances.
|
|
70
70
|
my $extend_raw = ProjectNameHelpers::gp($options, 'extend');
|
|
71
71
|
|
|
72
|
+
# `auth => undef` is the documented way to disable auth outright, and
|
|
73
|
+
# prepare_auth honours it before it ever reads the apikey. It cannot survive
|
|
74
|
+
# validate: depending on the struct port a stored null is either REPLACED by
|
|
75
|
+
# the optspec default - transmitting the credential the caller withheld - or
|
|
76
|
+
# REJECTED outright. Withhold the key for validate, then put the undef back.
|
|
77
|
+
# Same fix as ts/js/go make_options.
|
|
78
|
+
#
|
|
79
|
+
# Suppliedness cannot be recovered after validate, hence here, and it must
|
|
80
|
+
# tell an ABSENT auth from a present undef: exists rather than defined,
|
|
81
|
+
# which is false for both.
|
|
82
|
+
my $authsuppressed =
|
|
83
|
+
(ref($options) eq 'HASH' && exists $options->{auth} && !defined $options->{auth}) ? 1 : 0;
|
|
84
|
+
|
|
72
85
|
my $opts = Voxgig::Struct::clone($options);
|
|
86
|
+
|
|
87
|
+
delete $opts->{auth} if $authsuppressed && ref($opts) eq 'HASH';
|
|
73
88
|
$opts = {} unless Voxgig::Struct::ismap($opts);
|
|
74
89
|
delete $opts->{extend};
|
|
75
90
|
|
|
@@ -143,6 +158,9 @@ $REGISTRY{make_options} = sub {
|
|
|
143
158
|
my $validated = Voxgig::Struct::validate($merged, $optspec);
|
|
144
159
|
$opts = Voxgig::Struct::ismap($validated) ? $validated : {};
|
|
145
160
|
|
|
161
|
+
# Restore the suppression the optspec default would otherwise erase.
|
|
162
|
+
$opts->{auth} = undef if $authsuppressed;
|
|
163
|
+
|
|
146
164
|
if ($sys_fetch) {
|
|
147
165
|
$opts->{system} = {} unless Voxgig::Struct::ismap($opts->{system});
|
|
148
166
|
$opts->{system}{fetch} = $sys_fetch;
|
|
@@ -91,7 +91,16 @@ class FeatureCorpusTest extends TestCase
|
|
|
91
91
|
*/
|
|
92
92
|
private static function buildClient(array $kase)
|
|
93
93
|
{
|
|
94
|
-
|
|
94
|
+
// 'test' here is the OPTION, not the `test` FEATURE. It says "this
|
|
95
|
+
// client is not live", which is what makes a REQUIRED OpenAPI server
|
|
96
|
+
// variable resolve to a deterministic test-<name> rather than throw
|
|
97
|
+
// at construction (see makeOptions). It installs no transport, so the
|
|
98
|
+
// scripted fetcher still stands - the FEATURE is transport: 'base'
|
|
99
|
+
// and would shadow it.
|
|
100
|
+
$opts = [
|
|
101
|
+
'test' => ['active' => true],
|
|
102
|
+
'utility' => ['fetcher' => self::scriptedFetcher($kase['res'] ?? null)],
|
|
103
|
+
];
|
|
95
104
|
if (isset($kase['feature'])) {
|
|
96
105
|
$opts['feature'] = $kase['feature'];
|
|
97
106
|
}
|
|
@@ -138,6 +138,42 @@ class ProjectNameTestRunner
|
|
|
138
138
|
return [false, null];
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Extra SDK options every LIVE client is constructed with, read from
|
|
143
|
+
* sdk-test-control.json `test.client.options`.
|
|
144
|
+
*
|
|
145
|
+
* The generated live client knows two things: the base URL (from the
|
|
146
|
+
* spec) and the credential (from the environment). Everything else
|
|
147
|
+
* about how a particular API wants to be talked to - which features to
|
|
148
|
+
* switch on, and with what settings - is a property of THAT API, known
|
|
149
|
+
* to the project and to nothing in the toolchain.
|
|
150
|
+
*
|
|
151
|
+
* Merged UNDER the generated fields, so the suite's own
|
|
152
|
+
* base/apikey/server values win: this ADDS to the live client, it does
|
|
153
|
+
* not redirect it.
|
|
154
|
+
*
|
|
155
|
+
* Reserved fields are stripped HERE rather than at each merge site: the
|
|
156
|
+
* generated array only names a field when the model calls for one, so a
|
|
157
|
+
* "base" in this block would face no competing value and would silently
|
|
158
|
+
* redirect the whole suite - credential included - to another host.
|
|
159
|
+
*/
|
|
160
|
+
public static function live_client_options(): array
|
|
161
|
+
{
|
|
162
|
+
$ctrl = self::load_test_control();
|
|
163
|
+
$opts = $ctrl['test']['client']['options'] ?? null;
|
|
164
|
+
if (!is_array($opts)) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
$reserved = ['base', 'prefix', 'suffix', 'server', 'apikey', 'secret'];
|
|
168
|
+
$out = [];
|
|
169
|
+
foreach ($opts as $k => $v) {
|
|
170
|
+
if (!in_array($k, $reserved, true)) {
|
|
171
|
+
$out[$k] = $v;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return $out;
|
|
175
|
+
}
|
|
176
|
+
|
|
141
177
|
/** Per-test live pacing delay (ms); default 500. */
|
|
142
178
|
public static function live_delay_ms(): int
|
|
143
179
|
{
|
|
@@ -94,12 +94,31 @@ class ProjectNameMakeOptions
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// `auth: null` is the documented way to disable auth outright, and
|
|
98
|
+
// prepareAuth honours it before it ever reads the apikey. It cannot
|
|
99
|
+
// survive validate: depending on the struct port a stored null is
|
|
100
|
+
// either REPLACED by the optspec default - transmitting the
|
|
101
|
+
// credential the caller withheld - or REJECTED outright. Withhold the
|
|
102
|
+
// key for validate, then put the null back. Same fix as ts/js/go
|
|
103
|
+
// makeOptions.
|
|
104
|
+
//
|
|
105
|
+
// Suppliedness cannot be recovered after validate, hence here, and it
|
|
106
|
+
// must tell an ABSENT auth from a present null: array_key_exists
|
|
107
|
+
// rather than isset, which is false for both.
|
|
108
|
+
$authsuppressed = is_array($options)
|
|
109
|
+
&& array_key_exists('auth', $options)
|
|
110
|
+
&& null === $options['auth'];
|
|
111
|
+
|
|
97
112
|
$opts = \Voxgig\Struct\Struct::clone($options);
|
|
98
113
|
$opts = self::to_array_deep($opts);
|
|
99
114
|
if (!is_array($opts)) {
|
|
100
115
|
$opts = [];
|
|
101
116
|
}
|
|
102
117
|
|
|
118
|
+
if ($authsuppressed) {
|
|
119
|
+
unset($opts['auth']);
|
|
120
|
+
}
|
|
121
|
+
|
|
103
122
|
// Feature add-order. options['feature'] may be given as an ordered LIST
|
|
104
123
|
// of ['name'=>..., 'active'=>..., ...] entries (the list position IS the
|
|
105
124
|
// order in which features are added), or as a ['name'=>[opts]] map.
|
|
@@ -164,6 +183,11 @@ class ProjectNameMakeOptions
|
|
|
164
183
|
$opts = [];
|
|
165
184
|
}
|
|
166
185
|
|
|
186
|
+
// Restore the suppression the optspec default would otherwise erase.
|
|
187
|
+
if ($authsuppressed) {
|
|
188
|
+
$opts['auth'] = null;
|
|
189
|
+
}
|
|
190
|
+
|
|
167
191
|
// Reattach the station binding handle held aside above (the feature
|
|
168
192
|
// optspec is `$OPEN`, so the extra keys are legal; they were only
|
|
169
193
|
// kept out of the clone/validate round-trip).
|
|
@@ -65,10 +65,26 @@ def make_options_util(ctx):
|
|
|
65
65
|
if isinstance(options, dict) and options.get("extend") is not None:
|
|
66
66
|
options = {k: v for k, v in options.items() if k != "extend"}
|
|
67
67
|
|
|
68
|
+
# `auth: None` is the documented way to disable auth outright, and
|
|
69
|
+
# prepare_auth honours it before it ever reads the apikey. It cannot
|
|
70
|
+
# survive validate: depending on the struct port a stored null is either
|
|
71
|
+
# REPLACED by the optspec default — transmitting the credential the
|
|
72
|
+
# caller withheld — or REJECTED outright. Withhold the key for validate,
|
|
73
|
+
# then put the null back. Same fix as ts/js/go make_options.
|
|
74
|
+
#
|
|
75
|
+
# Suppliedness cannot be recovered after validate, hence here, and it
|
|
76
|
+
# must tell an ABSENT auth from a present None: only the latter is a
|
|
77
|
+
# suppression.
|
|
78
|
+
authsuppressed = (
|
|
79
|
+
isinstance(options, dict) and "auth" in options and options["auth"] is None)
|
|
80
|
+
|
|
68
81
|
opts = vs.clone(options)
|
|
69
82
|
if not isinstance(opts, dict):
|
|
70
83
|
opts = {}
|
|
71
84
|
|
|
85
|
+
if authsuppressed:
|
|
86
|
+
opts.pop("auth", None)
|
|
87
|
+
|
|
72
88
|
# Feature add-order. options["feature"] may be given as an ordered LIST of
|
|
73
89
|
# {name, active, ...opts} entries (the list position IS the order in which
|
|
74
90
|
# features are added), or as a {name: {opts}} map. Normalize a list to a
|
|
@@ -162,6 +178,10 @@ def make_options_util(ctx):
|
|
|
162
178
|
validated = {}
|
|
163
179
|
opts = validated
|
|
164
180
|
|
|
181
|
+
# Restore the suppression the optspec default would otherwise erase.
|
|
182
|
+
if authsuppressed:
|
|
183
|
+
opts["auth"] = None
|
|
184
|
+
|
|
165
185
|
# Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
166
186
|
# Every placeholder must resolve to a non-empty value: from
|
|
167
187
|
# options["server"] (user), else the config default. A placeholder that
|
|
@@ -101,6 +101,34 @@ class ProjectNameTestRunner:
|
|
|
101
101
|
return True, item.get("reason")
|
|
102
102
|
return False, None
|
|
103
103
|
|
|
104
|
+
@staticmethod
|
|
105
|
+
def live_client_options():
|
|
106
|
+
"""Extra SDK options every LIVE client is constructed with, from
|
|
107
|
+
sdk-test-control.json `test.client.options`.
|
|
108
|
+
|
|
109
|
+
The generated live client knows two things: the base URL (from the
|
|
110
|
+
spec) and the credential (from the environment). Everything else
|
|
111
|
+
about how a particular API wants to be talked to - which features to
|
|
112
|
+
switch on, and with what settings - is a property of THAT API, known
|
|
113
|
+
to the project and to nothing in the toolchain.
|
|
114
|
+
|
|
115
|
+
Merged UNDER the generated fields, so the suite's own
|
|
116
|
+
base/apikey/server values win: this ADDS to the live client, it does
|
|
117
|
+
not redirect it.
|
|
118
|
+
|
|
119
|
+
Reserved fields are stripped HERE rather than at each merge site:
|
|
120
|
+
the generated dict only names a field when the model calls for one,
|
|
121
|
+
so a "base" in this block would face no competing value and would
|
|
122
|
+
silently redirect the whole suite - credential included - to another
|
|
123
|
+
host.
|
|
124
|
+
"""
|
|
125
|
+
ctrl = ProjectNameTestRunner.load_test_control()
|
|
126
|
+
opts = ctrl.get("test", {}).get("client", {}).get("options")
|
|
127
|
+
if not isinstance(opts, dict):
|
|
128
|
+
return {}
|
|
129
|
+
reserved = ("base", "prefix", "suffix", "server", "apikey", "secret")
|
|
130
|
+
return {k: v for k, v in opts.items() if k not in reserved}
|
|
131
|
+
|
|
104
132
|
@staticmethod
|
|
105
133
|
def live_delay_ms():
|
|
106
134
|
"""Per-test live pacing delay (ms); default 500."""
|
|
@@ -161,5 +189,9 @@ def load_test_control():
|
|
|
161
189
|
return ProjectNameTestRunner.load_test_control()
|
|
162
190
|
|
|
163
191
|
|
|
192
|
+
def live_client_options():
|
|
193
|
+
return ProjectNameTestRunner.live_client_options()
|
|
194
|
+
|
|
195
|
+
|
|
164
196
|
def live_delay_ms():
|
|
165
197
|
return ProjectNameTestRunner.live_delay_ms()
|
|
@@ -95,7 +95,16 @@ def _client(kase):
|
|
|
95
95
|
transport: 'base' and REPLACES the transport, so a client in test mode
|
|
96
96
|
would shadow the script.
|
|
97
97
|
"""
|
|
98
|
-
|
|
98
|
+
# "test" here is the OPTION, not the `test` FEATURE. It says "this
|
|
99
|
+
# client is not live", which is what makes a REQUIRED OpenAPI server
|
|
100
|
+
# variable resolve to a deterministic test-<name> rather than raise at
|
|
101
|
+
# construction (see make_options). It installs no transport, so the
|
|
102
|
+
# scripted fetcher still stands - the FEATURE is transport: 'base' and
|
|
103
|
+
# would shadow it, which is why this cannot just turn the feature on.
|
|
104
|
+
opts = {
|
|
105
|
+
"test": {"active": True},
|
|
106
|
+
"utility": {"fetcher": _scripted_fetcher(kase.get("res"))},
|
|
107
|
+
}
|
|
99
108
|
if kase.get("feature") is not None:
|
|
100
109
|
opts["feature"] = kase["feature"]
|
|
101
110
|
return ProjectNameSDK(opts)
|
|
@@ -71,7 +71,15 @@ class FeatureCorpusTest < Minitest::Test
|
|
|
71
71
|
# transport: 'base' and REPLACES the transport, so a client in test mode
|
|
72
72
|
# would shadow the script.
|
|
73
73
|
def build_client(kase)
|
|
74
|
-
|
|
74
|
+
# "test" here is the OPTION, not the `test` FEATURE. It says "this
|
|
75
|
+
# client is not live", which is what makes a REQUIRED OpenAPI server
|
|
76
|
+
# variable resolve to a deterministic test-<name> rather than raise at
|
|
77
|
+
# construction (see make_options). It installs no transport, so the
|
|
78
|
+
# scripted fetcher still stands.
|
|
79
|
+
opts = {
|
|
80
|
+
"test" => { "active" => true },
|
|
81
|
+
"utility" => { "fetcher" => scripted_fetcher(kase["res"]) },
|
|
82
|
+
}
|
|
75
83
|
opts["feature"] = kase["feature"] unless kase["feature"].nil?
|
|
76
84
|
ProjectNameSDK.new(opts)
|
|
77
85
|
end
|
|
@@ -101,6 +101,31 @@ module ProjectNameTestRunner
|
|
|
101
101
|
[false, nil]
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
# Extra SDK options every LIVE client is constructed with, read from
|
|
105
|
+
# sdk-test-control.json `test.client.options`.
|
|
106
|
+
#
|
|
107
|
+
# The generated live client knows two things: the base URL (from the spec)
|
|
108
|
+
# and the credential (from the environment). Everything else about how a
|
|
109
|
+
# particular API wants to be talked to - which features to switch on, and
|
|
110
|
+
# with what settings - is a property of THAT API, known to the project and
|
|
111
|
+
# to nothing in the toolchain.
|
|
112
|
+
#
|
|
113
|
+
# Merged UNDER the generated fields, so the suite's own base/apikey/server
|
|
114
|
+
# values win: this ADDS to the live client, it does not redirect it.
|
|
115
|
+
#
|
|
116
|
+
# Reserved fields are stripped HERE rather than at each merge site: the
|
|
117
|
+
# generated hash only names a field when the model calls for one, so a
|
|
118
|
+
# "base" in this block would face no competing value and would silently
|
|
119
|
+
# redirect the whole suite - credential included - to another host.
|
|
120
|
+
LIVE_RESERVED = %w[base prefix suffix server apikey secret].freeze
|
|
121
|
+
|
|
122
|
+
def self.live_client_options
|
|
123
|
+
ctrl = load_test_control
|
|
124
|
+
opts = ctrl.dig('test', 'client', 'options')
|
|
125
|
+
return {} unless opts.is_a?(Hash)
|
|
126
|
+
opts.reject { |k, _v| LIVE_RESERVED.include?(k) }
|
|
127
|
+
end
|
|
128
|
+
|
|
104
129
|
# Per-test live pacing delay (ms); default 500.
|
|
105
130
|
def self.live_delay_ms
|
|
106
131
|
ctrl = load_test_control
|