@voxgig/sdkgen 4.10.0 → 4.11.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/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +330 -0
- package/project/.sdk/model/target/c.aon +15 -2
- package/project/.sdk/model/target/cpp.aon +18 -0
- package/project/.sdk/model/target/lua.aon +17 -0
- package/project/.sdk/model/target/ocaml.aon +48 -5
- package/project/.sdk/model/target/zig.aon +30 -4
- package/project/.sdk/src/cmp/c/Config_c.ts +338 -1
- package/project/.sdk/src/cmp/c/Main_c.ts +57 -2
- package/project/.sdk/src/cmp/c/Package_c.ts +12 -2
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +324 -2
- package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +2 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +62 -4
- package/project/.sdk/src/cmp/cpp/Package_cpp.ts +10 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +88 -0
- package/project/.sdk/src/cmp/lua/Gitignore_lua.ts +4 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +93 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +60 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +10 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +183 -5
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +160 -3
- package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +25 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +130 -9
- package/project/.sdk/src/cmp/zig/Main_zig.ts +208 -4
- package/project/.sdk/{tm/zig/build.zig → src/cmp/zig/fragment/Build.fragment.zig} +7 -0
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +25 -0
- package/project/.sdk/{tm/zig/root.zig → src/cmp/zig/fragment/Root.fragment.zig} +9 -0
- package/project/.sdk/tm/c/Makefile +58 -9
- package/project/.sdk/tm/c/core/sdk.h +10 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +133 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +39 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +83 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +48 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.c +413 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.h +24 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +264 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +29 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.c +220 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.c +97 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +237 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +17 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.c +1494 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.c +288 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.h +80 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +170 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +52 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +98 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.c +150 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.h +104 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.c +649 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.c +151 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.h +34 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +281 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +207 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +164 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +75 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +144 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +177 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +239 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +599 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +165 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +194 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +233 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +270 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +382 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/support.h +153 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +420 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +86 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +721 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +813 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +944 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +490 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +442 -0
- package/project/.sdk/tm/c/feature/secrets.c +797 -0
- package/project/.sdk/tm/c/feature/secrets.h +46 -0
- package/project/.sdk/tm/c/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/c/tests/feature/secrets/secrets_test.c +802 -0
- package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +38 -11
- package/project/.sdk/tm/clojure/test/sdk/test/feature/secrets.clj +40 -6
- package/project/.sdk/tm/cpp/Makefile +48 -4
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +41 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +66 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +376 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +29 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +230 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +31 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +205 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +84 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +24 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +222 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +21 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +1200 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +22 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +110 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +79 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +136 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +82 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +90 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +65 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +491 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +120 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +143 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +36 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +248 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +30 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +149 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +117 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +180 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +52 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +87 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +153 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +666 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +160 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +123 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +125 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +223 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +104 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +242 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +58 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +260 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +476 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +89 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +312 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +263 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +354 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +111 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +613 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +227 -0
- package/project/.sdk/tm/cpp/feature/secrets.hpp +745 -0
- package/project/.sdk/tm/cpp/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/cpp/test/feature/secrets/secrets_test.cpp +808 -0
- package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +34 -8
- package/project/.sdk/tm/csharp/test/feature/secrets/SecretsFeatureTest.cs +85 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +17 -2
- package/project/.sdk/tm/elixir/test/feature/secrets/secrets_test.exs +41 -0
- package/project/.sdk/tm/go/feature/secrets_feature.go +32 -10
- package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +116 -0
- package/project/.sdk/tm/java/feature/SecretsFeature.java +33 -8
- package/project/.sdk/tm/java/test/feature/secrets/SecretsFeatureTest.java +64 -0
- package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +34 -11
- package/project/.sdk/tm/js/test/feature/secrets/Secrets.test.js +73 -0
- package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +32 -7
- package/project/.sdk/tm/kotlin/test/feature/secrets/SecretsTest.kt +69 -0
- package/project/.sdk/tm/lua/Makefile +15 -4
- package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +806 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +135 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +63 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +361 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +232 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +168 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +66 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +194 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +1190 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +169 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +207 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +106 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +115 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +78 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +275 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +131 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin.lua +76 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +47 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +315 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +228 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +137 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +212 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +89 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +129 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +154 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +248 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +113 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +449 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +198 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +126 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +93 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +269 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +146 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +346 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +479 -0
- package/project/.sdk/tm/lua/feature/secrets_feature.lua +690 -0
- package/project/.sdk/tm/lua/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/lua/test/extend_test.lua +105 -0
- package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +862 -0
- package/project/.sdk/tm/ocaml/Makefile +52 -8
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +106 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +43 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +349 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +143 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +217 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +197 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +89 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +227 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +1112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +202 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +138 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +126 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +62 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +387 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +139 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +171 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +116 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +95 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +149 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +61 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +93 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +120 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +421 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +66 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +85 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +68 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +259 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +44 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +358 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +376 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +647 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +299 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +347 -0
- package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +453 -0
- package/project/.sdk/tm/ocaml/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +670 -0
- package/project/.sdk/tm/perl/feature/secrets_feature.pm +33 -12
- package/project/.sdk/tm/perl/t/feature/secrets/secrets.t +47 -0
- package/project/.sdk/tm/php/feature/SecretsFeature.php +30 -6
- package/project/.sdk/tm/php/test/feature/secrets/SecretsTest.php +98 -0
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +28 -6
- package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +114 -0
- package/project/.sdk/tm/rb/feature/secrets_feature.rb +28 -6
- package/project/.sdk/tm/rb/test/feature/secrets/secrets_feature_test.rb +93 -0
- package/project/.sdk/tm/rust/feature/secrets.rs +33 -10
- package/project/.sdk/tm/rust/tests/feature/secrets/main.rs +96 -0
- package/project/.sdk/tm/scala/feature/SecretsFeature.scala +29 -7
- package/project/.sdk/tm/scala/sdktest/feature/secrets/SecretsTestMain.scala +71 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +35 -12
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +73 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +137 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +90 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +346 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +244 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +201 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +83 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +212 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +1224 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +126 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +228 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +34 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +166 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +147 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +74 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +140 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +662 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +139 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +294 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +222 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +188 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +135 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +170 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +242 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +535 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +167 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +202 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +160 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +336 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +148 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +259 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +440 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +944 -0
- package/project/.sdk/tm/zig/feature/secrets.zig +854 -0
- package/project/.sdk/tm/zig/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/zig/test/feature/secrets/secrets_test.zig +891 -0
- package/project/sdkgen-package.json +1 -1
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
(* Behavioural tests for the secrets feature (vendored @voxgig/sekreto).
|
|
2
|
+
*
|
|
3
|
+
* The contract under test: the `apikey` OPTION keeps its exact old meaning
|
|
4
|
+
* and always wins, because the feature seats it FIRST in the provider chain
|
|
5
|
+
* (a `memory` store named `options`) - explicit-beats-lookup falls out of
|
|
6
|
+
* sekreto's first-hit rule rather than from special-case logic. With the
|
|
7
|
+
* feature inactive nothing changes at all. With it active and the option
|
|
8
|
+
* unset, the chain supplies the credential instead.
|
|
9
|
+
*
|
|
10
|
+
* THE SHAPE OF EVERY FAIL-CLOSED CASE HERE, and why. A test that passes
|
|
11
|
+
* whether or not the thing it names is present is worthless, so:
|
|
12
|
+
* - the client is LIVE (Sdk_client.make), so `options.system.fetch` really
|
|
13
|
+
* is the transport and the feature's wrapper sits above it - never the
|
|
14
|
+
* test-mode mock, which replaces the fetcher and hides the seam;
|
|
15
|
+
* - every assertion reads the authorization header the RECORDER received,
|
|
16
|
+
* never the options map, which this feature never writes;
|
|
17
|
+
* - a refusal must carry the PROVIDER'S OWN message (sekreto's wording),
|
|
18
|
+
* not any error;
|
|
19
|
+
* - beside every refusal is a CONTROL leg: the same construction with a
|
|
20
|
+
* working provider reaches the same recorder exactly once - so a zero
|
|
21
|
+
* on the refused leg means REFUSED, not UNWIRED.
|
|
22
|
+
* Delete the gate in feature/secrets_feature.ml and these go RED.
|
|
23
|
+
*
|
|
24
|
+
* This file lives in the test/feature/ container on purpose: `target add`
|
|
25
|
+
* trims it, along with the feature source and the vendored trees, for a
|
|
26
|
+
* project whose model does not select `secrets`; feature/secrets/feature.mk lists it, so
|
|
27
|
+
* it is linked into run_sdk_test whenever the feature is. It prints
|
|
28
|
+
* `feature.secrets: ran N check(s)` from EXECUTIONS, which the generator's
|
|
29
|
+
* own lane requires - a suite trimmed to nothing cannot pass by exiting
|
|
30
|
+
* zero. *)
|
|
31
|
+
|
|
32
|
+
open Voxgig_struct
|
|
33
|
+
open Sdk_types
|
|
34
|
+
open Sdk_helpers
|
|
35
|
+
open Testutil
|
|
36
|
+
|
|
37
|
+
(* Executed-case count, for the line the lane reads. *)
|
|
38
|
+
let ran = ref 0
|
|
39
|
+
let test (name : string) (f : unit -> unit) : unit =
|
|
40
|
+
incr ran;
|
|
41
|
+
Testutil.test ("secrets." ^ name) f
|
|
42
|
+
|
|
43
|
+
let envprefix = "PROJECTENV_TEST_SECRETS_"
|
|
44
|
+
|
|
45
|
+
(* ---------------------------------------------------------------------- *)
|
|
46
|
+
(* the recorder: a system.fetch that remembers what reached it *)
|
|
47
|
+
(* ---------------------------------------------------------------------- *)
|
|
48
|
+
|
|
49
|
+
let ok200 (data : value) : value =
|
|
50
|
+
jo [("status", Num 200.); ("statusText", Str "OK"); ("headers", empty_map ());
|
|
51
|
+
("body", Str "not-used"); ("json", json_thunk data)]
|
|
52
|
+
|
|
53
|
+
let status_res (status : int) (data : value) : value =
|
|
54
|
+
jo [("status", vint_of status); ("statusText", Str (if status < 400 then "OK" else "ERR"));
|
|
55
|
+
("headers", empty_map ()); ("body", Str "not-used"); ("json", json_thunk data)]
|
|
56
|
+
|
|
57
|
+
type recorder = {
|
|
58
|
+
calls : value list ref;
|
|
59
|
+
(* (call number, url, fetchdef) -> the transport-shaped response *)
|
|
60
|
+
mutable reply : (int -> string -> value -> value) option;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let is_token (url : string) : bool =
|
|
64
|
+
let suffix = "/auth/token" in
|
|
65
|
+
let lu = String.length url and ls = String.length suffix in
|
|
66
|
+
lu >= ls && String.sub url (lu - ls) ls = suffix
|
|
67
|
+
|
|
68
|
+
let recorder () : recorder * value =
|
|
69
|
+
let r = { calls = ref []; reply = None } in
|
|
70
|
+
let fetch = Func (fun _ args _ _ ->
|
|
71
|
+
let (url, fd) = match args with
|
|
72
|
+
| List l -> (match !l with
|
|
73
|
+
| Str u :: fd :: _ -> (u, fd)
|
|
74
|
+
| Str u :: [] -> (u, Noval)
|
|
75
|
+
| _ -> ("", Noval))
|
|
76
|
+
| _ -> ("", Noval) in
|
|
77
|
+
r.calls := !(r.calls) @ [jo [("url", Str url); ("fetchdef", fd)]];
|
|
78
|
+
match r.reply with
|
|
79
|
+
| Some f -> f (List.length !(r.calls)) url fd
|
|
80
|
+
| None -> ok200 (jo [("id", Str "r01")])) in
|
|
81
|
+
(r, fetch)
|
|
82
|
+
|
|
83
|
+
(* The API calls (not the token endpoint) the recorder saw, in order. *)
|
|
84
|
+
let api_calls (r : recorder) : value list =
|
|
85
|
+
List.filter (fun c -> not (is_token (match getp c "url" with Str u -> u | _ -> ""))) !(r.calls)
|
|
86
|
+
|
|
87
|
+
let token_calls (r : recorder) : value list =
|
|
88
|
+
List.filter (fun c -> is_token (match getp c "url" with Str u -> u | _ -> "")) !(r.calls)
|
|
89
|
+
|
|
90
|
+
(* The authorization header the i-th API call carried, or Noval. *)
|
|
91
|
+
let auth_of (r : recorder) (i : int) : value =
|
|
92
|
+
match List.nth_opt (api_calls r) i with
|
|
93
|
+
| Some c -> (match getp (getp c "fetchdef") "headers" with
|
|
94
|
+
| Map _ as h -> Sdk_features.header_ci h "authorization"
|
|
95
|
+
| _ -> Noval)
|
|
96
|
+
| None -> Noval
|
|
97
|
+
|
|
98
|
+
(* The Authorization header carries the SPEC's credential prefix, which a
|
|
99
|
+
* TEMPLATE cannot know - so assert on the CREDENTIAL and let the prefix be
|
|
100
|
+
* whatever this SDK's API declares. *)
|
|
101
|
+
let credential_is (header : value) (token : string) : bool =
|
|
102
|
+
match header with
|
|
103
|
+
| Str s -> s = token || Sdk_features.ends_with s (" " ^ token)
|
|
104
|
+
| _ -> false
|
|
105
|
+
|
|
106
|
+
let absent (header : value) : bool = is_nullish header
|
|
107
|
+
|
|
108
|
+
(* ---------------------------------------------------------------------- *)
|
|
109
|
+
(* clients and paths *)
|
|
110
|
+
(* ---------------------------------------------------------------------- *)
|
|
111
|
+
|
|
112
|
+
(* A LIVE client with the secrets feature active and the recorder as its
|
|
113
|
+
* transport. `sdkopts` are extra top-level options (apikey, auth, name...). *)
|
|
114
|
+
let live ?(sdkopts : (string * value) list = []) ?(base = "http://secrets.test/api")
|
|
115
|
+
(fetch : value) (fopts : (string * value) list) : sdk_client =
|
|
116
|
+
Sdk_client.make (jo ([
|
|
117
|
+
("base", Str base);
|
|
118
|
+
("system", jo [("fetch", fetch)]);
|
|
119
|
+
("feature", jo [("secrets", jo (("active", Bool true) :: fopts))]);
|
|
120
|
+
] @ sdkopts))
|
|
121
|
+
|
|
122
|
+
(* The same construction with the feature declared INACTIVE. *)
|
|
123
|
+
let inactive ?(sdkopts : (string * value) list = []) (fetch : value) : sdk_client =
|
|
124
|
+
Sdk_client.make (jo ([
|
|
125
|
+
("base", Str "http://secrets.test/api");
|
|
126
|
+
("system", jo [("fetch", fetch)]);
|
|
127
|
+
("feature", jo [("secrets", jo [("active", Bool false)])]);
|
|
128
|
+
] @ sdkopts))
|
|
129
|
+
|
|
130
|
+
let direct (client : sdk_client) : value =
|
|
131
|
+
Sdk_client.direct client (jo [("path", Str "/thing"); ("method", Str "GET")])
|
|
132
|
+
|
|
133
|
+
let graphql (client : sdk_client) : value =
|
|
134
|
+
Sdk_client.graphql client "{ thing { id } }" Noval Noval
|
|
135
|
+
|
|
136
|
+
let res_ok (res : value) : bool = getp res "ok" = Bool true
|
|
137
|
+
|
|
138
|
+
let res_err (res : value) : string =
|
|
139
|
+
match getp res "err" with
|
|
140
|
+
| Str s -> s
|
|
141
|
+
| Map _ as m -> (match getp m "message" with Str s -> s | _ -> stringify m)
|
|
142
|
+
| _ -> ""
|
|
143
|
+
|
|
144
|
+
let contains (hay : string) (needle : string) : bool =
|
|
145
|
+
let lh = String.length hay and ln = String.length needle in
|
|
146
|
+
let rec go i = i + ln <= lh && (String.sub hay i ln = needle || go (i + 1)) in
|
|
147
|
+
ln = 0 || go 0
|
|
148
|
+
|
|
149
|
+
(* THE ENTITY PIPELINE, through the entities this SDK generated. This file
|
|
150
|
+
* is a template and knows no project's entity names, so they are read from
|
|
151
|
+
* the generated config and reached through Sdk_client.entity (the by-name
|
|
152
|
+
* accessor). Real ops are driven until one reaches the recorder or the
|
|
153
|
+
* chain refuses one; an op the API does not define fails BEFORE the
|
|
154
|
+
* transport, which is why several may need driving. *)
|
|
155
|
+
let entity_names () : string list =
|
|
156
|
+
match getp (Sdk_config.make_config ()) "entity" with
|
|
157
|
+
| Map _ as m -> keysof m
|
|
158
|
+
| _ -> []
|
|
159
|
+
|
|
160
|
+
(* Drive ops until one request reached the recorder. *)
|
|
161
|
+
let drive_entity (client : sdk_client) (r : recorder) : (unit, string) Stdlib.result =
|
|
162
|
+
let before = List.length (api_calls r) in
|
|
163
|
+
let out = ref (Error "") in
|
|
164
|
+
(* A refused op raises before the recorder sees anything; a working one
|
|
165
|
+
* reaches it. Either way the first op that consulted the chain settles
|
|
166
|
+
* it, so ops are driven until the recorder grew or an op reported. *)
|
|
167
|
+
let rec go names =
|
|
168
|
+
match names with
|
|
169
|
+
| [] -> ()
|
|
170
|
+
| name :: rest ->
|
|
171
|
+
(match Sdk_client.entity client name Noval with
|
|
172
|
+
| None -> go rest
|
|
173
|
+
| Some ent ->
|
|
174
|
+
let rec ops = function
|
|
175
|
+
| [] -> go rest
|
|
176
|
+
| opname :: more ->
|
|
177
|
+
(match (match opname with
|
|
178
|
+
| "list" -> ignore (ent.e_list (empty_map ()) Noval)
|
|
179
|
+
| _ -> ignore (ent.e_load (jo [("id", Str "id01")]) Noval)) with
|
|
180
|
+
| () ->
|
|
181
|
+
out := Ok ();
|
|
182
|
+
if before < List.length (api_calls r) then () else ops more
|
|
183
|
+
| exception Sdk_error_exc e ->
|
|
184
|
+
(* An op the entity does not define fails before the chain
|
|
185
|
+
* is consulted: keep going. Anything else is the answer. *)
|
|
186
|
+
if e.err_code = "unsupported_op" then ops more
|
|
187
|
+
else (out := Error e.err_msg)
|
|
188
|
+
| exception e -> out := Error (Printexc.to_string e))
|
|
189
|
+
in
|
|
190
|
+
ops ["list"; "load"])
|
|
191
|
+
in
|
|
192
|
+
go (entity_names ());
|
|
193
|
+
!out
|
|
194
|
+
|
|
195
|
+
(* Custom (Func) providers: asked the secret name, answer a string, a miss
|
|
196
|
+
* (Null) or raise. `asked` counts lookups. *)
|
|
197
|
+
let custom (answer : int -> value) : value * int ref =
|
|
198
|
+
let asked = ref 0 in
|
|
199
|
+
let fn = vfunc1 (fun _name -> incr asked; answer !asked) in
|
|
200
|
+
(fn, asked)
|
|
201
|
+
|
|
202
|
+
let working (v : string) : value = fst (custom (fun _ -> Str v))
|
|
203
|
+
|
|
204
|
+
let broken (message : string) : value =
|
|
205
|
+
vfunc1 (fun _ -> Secret.fail message)
|
|
206
|
+
|
|
207
|
+
let memory ?(name = "options2") (values : (string * value) list) : value =
|
|
208
|
+
jo [("kind", Str "memory"); ("name", Str name); ("values", jo values)]
|
|
209
|
+
|
|
210
|
+
(* ---------------------------------------------------------------------- *)
|
|
211
|
+
(* inactive *)
|
|
212
|
+
(* ---------------------------------------------------------------------- *)
|
|
213
|
+
|
|
214
|
+
let () =
|
|
215
|
+
test "inactive.apikey_option_behaves_as_before" (fun () ->
|
|
216
|
+
let (r, fetch) = recorder () in
|
|
217
|
+
let client = inactive ~sdkopts:[("apikey", Str "OPTKEY01")] fetch in
|
|
218
|
+
check "ok" (res_ok (direct client));
|
|
219
|
+
check_int "one call" (List.length (api_calls r)) 1;
|
|
220
|
+
check "carries OPTKEY01" (credential_is (auth_of r 0) "OPTKEY01"));
|
|
221
|
+
|
|
222
|
+
test "inactive.no_apikey_no_header" (fun () ->
|
|
223
|
+
let (r, fetch) = recorder () in
|
|
224
|
+
let client = inactive fetch in
|
|
225
|
+
check "ok" (res_ok (direct client));
|
|
226
|
+
check "no header" (absent (auth_of r 0)));
|
|
227
|
+
|
|
228
|
+
(* ---------------------------------------------------------------------- *)
|
|
229
|
+
(* the chain *)
|
|
230
|
+
(* ---------------------------------------------------------------------- *)
|
|
231
|
+
|
|
232
|
+
Unix.putenv (envprefix ^ "APIKEY") "ENVKEY01";
|
|
233
|
+
|
|
234
|
+
test "chain.apikey_option_still_wins" (fun () ->
|
|
235
|
+
let (r, fetch) = recorder () in
|
|
236
|
+
let client = live ~sdkopts:[("apikey", Str "OPTKEY01")] fetch
|
|
237
|
+
[("providers", ja [jo [("kind", Str "env"); ("prefix", Str envprefix)]])] in
|
|
238
|
+
check "ok" (res_ok (direct client));
|
|
239
|
+
check "carries OPTKEY01, not ENVKEY01" (credential_is (auth_of r 0) "OPTKEY01"));
|
|
240
|
+
|
|
241
|
+
test "chain.omitted_apikey_defers_to_the_chain" (fun () ->
|
|
242
|
+
let (r, fetch) = recorder () in
|
|
243
|
+
let client = live fetch
|
|
244
|
+
[("providers", ja [jo [("kind", Str "env"); ("prefix", Str envprefix)]])] in
|
|
245
|
+
check "ok" (res_ok (direct client));
|
|
246
|
+
check "carries ENVKEY01" (credential_is (auth_of r 0) "ENVKEY01"));
|
|
247
|
+
|
|
248
|
+
test "chain.explicitly_empty_apikey_defers_to_the_chain" (fun () ->
|
|
249
|
+
let (r, fetch) = recorder () in
|
|
250
|
+
let client = live ~sdkopts:[("apikey", Str "")] fetch
|
|
251
|
+
[("providers", ja [jo [("kind", Str "env"); ("prefix", Str envprefix)]])] in
|
|
252
|
+
check "ok" (res_ok (direct client));
|
|
253
|
+
check "carries ENVKEY01" (credential_is (auth_of r 0) "ENVKEY01"));
|
|
254
|
+
|
|
255
|
+
test "chain.custom_provider_accepted_verbatim" (fun () ->
|
|
256
|
+
let (r, fetch) = recorder () in
|
|
257
|
+
let names = ref [] in
|
|
258
|
+
let fn = vfunc1 (fun name -> names := !names @ [name]; Str "CUSTOM01") in
|
|
259
|
+
let client = live fetch [("providers", ja [fn])] in
|
|
260
|
+
check "ok" (res_ok (direct client));
|
|
261
|
+
check "carries CUSTOM01" (credential_is (auth_of r 0) "CUSTOM01");
|
|
262
|
+
check "asked for the configured secret name" (!names = [Str "apikey"]));
|
|
263
|
+
|
|
264
|
+
test "chain.custom_provider_asked_for_the_configured_name" (fun () ->
|
|
265
|
+
let (r, fetch) = recorder () in
|
|
266
|
+
let names = ref [] in
|
|
267
|
+
let fn = vfunc1 (fun name -> names := !names @ [name]; Str "CUSTOM02") in
|
|
268
|
+
let client = live fetch [("providers", ja [fn]); ("name", Str "api.token")] in
|
|
269
|
+
check "ok" (res_ok (direct client));
|
|
270
|
+
check "carries CUSTOM02" (credential_is (auth_of r 0) "CUSTOM02");
|
|
271
|
+
check "asked api.token" (!names = [Str "api.token"]));
|
|
272
|
+
|
|
273
|
+
test "chain.miss_everywhere_leaves_the_header_off" (fun () ->
|
|
274
|
+
let (r, fetch) = recorder () in
|
|
275
|
+
let (fn, asked) = custom (fun _ -> Null) in
|
|
276
|
+
let client = live fetch [("providers", ja [fn])] in
|
|
277
|
+
check "ok" (res_ok (direct client));
|
|
278
|
+
check_int "one call" (List.length (api_calls r)) 1;
|
|
279
|
+
check "no header" (absent (auth_of r 0));
|
|
280
|
+
check_int "the chain was asked" !asked 1);
|
|
281
|
+
|
|
282
|
+
test "chain.miss_falls_through_to_the_next_provider" (fun () ->
|
|
283
|
+
let (r, fetch) = recorder () in
|
|
284
|
+
let (fn, _) = custom (fun _ -> Null) in
|
|
285
|
+
let client = live fetch [("providers", ja [fn; memory [("APIKEY", Str "MEM01")]])] in
|
|
286
|
+
check "ok" (res_ok (direct client));
|
|
287
|
+
check "carries MEM01" (credential_is (auth_of r 0) "MEM01"));
|
|
288
|
+
|
|
289
|
+
(* ---------------------------------------------------------------------- *)
|
|
290
|
+
(* fail closed: a provider ERROR never degrades into an unauthenticated *)
|
|
291
|
+
(* request, on every wire path *)
|
|
292
|
+
(* ---------------------------------------------------------------------- *)
|
|
293
|
+
|
|
294
|
+
test "error.direct_is_refused_with_the_providers_message" (fun () ->
|
|
295
|
+
let (r, fetch) = recorder () in
|
|
296
|
+
let client = live fetch [("providers", ja [broken "vault unreachable"])] in
|
|
297
|
+
let res = direct client in
|
|
298
|
+
check "refused" (not (res_ok res));
|
|
299
|
+
check ("carries the provider's own message: " ^ res_err res)
|
|
300
|
+
(contains (res_err res) "vault unreachable");
|
|
301
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0;
|
|
302
|
+
(* CONTROL *)
|
|
303
|
+
let (r2, fetch2) = recorder () in
|
|
304
|
+
let ok = live fetch2 [("providers", ja [working "CUSTOM01"])] in
|
|
305
|
+
check "control: the working chain succeeds" (res_ok (direct ok));
|
|
306
|
+
check_int "control: reached the wire once" (List.length (api_calls r2)) 1;
|
|
307
|
+
check "control: with the credential" (credential_is (auth_of r2 0) "CUSTOM01"));
|
|
308
|
+
|
|
309
|
+
test "error.graphql_is_refused_with_the_providers_message" (fun () ->
|
|
310
|
+
let (r, fetch) = recorder () in
|
|
311
|
+
let client = live fetch [("providers", ja [broken "vault unreachable"])] in
|
|
312
|
+
let res = graphql client in
|
|
313
|
+
check "refused" (not (res_ok res));
|
|
314
|
+
check ("carries the provider's own message: " ^ res_err res)
|
|
315
|
+
(contains (res_err res) "vault unreachable");
|
|
316
|
+
check_int "graphql sent nothing" (List.length (api_calls r)) 0;
|
|
317
|
+
let (r2, fetch2) = recorder () in
|
|
318
|
+
let ok = live fetch2 [("providers", ja [working "CUSTOM01"])] in
|
|
319
|
+
check "control: graphql with a working chain succeeds" (res_ok (graphql ok));
|
|
320
|
+
check_int "control: graphql reached the wire once" (List.length (api_calls r2)) 1;
|
|
321
|
+
check "control: graphql carried the credential" (credential_is (auth_of r2 0) "CUSTOM01"));
|
|
322
|
+
|
|
323
|
+
test "error.entity_op_is_refused_with_the_providers_message" (fun () ->
|
|
324
|
+
let (r, fetch) = recorder () in
|
|
325
|
+
let client = live fetch [("providers", ja [broken "vault unreachable"])] in
|
|
326
|
+
(match drive_entity client r with
|
|
327
|
+
| Ok () -> failwith "entity: the op succeeded through a broken chain"
|
|
328
|
+
| Error msg ->
|
|
329
|
+
check ("entity: carries the provider's own message: " ^ msg)
|
|
330
|
+
(contains msg "vault unreachable"));
|
|
331
|
+
check_int "entity: nothing reached the transport" (List.length (api_calls r)) 0;
|
|
332
|
+
let (r2, fetch2) = recorder () in
|
|
333
|
+
let ok = live fetch2 [("providers", ja [working "CUSTOM01"])] in
|
|
334
|
+
(match drive_entity ok r2 with
|
|
335
|
+
| Ok () -> ()
|
|
336
|
+
| Error msg -> failwith ("control: the entity op failed: " ^ msg));
|
|
337
|
+
check_int "control: the entity op reached the transport once" (List.length (api_calls r2)) 1;
|
|
338
|
+
check "control: the entity op carried the credential" (credential_is (auth_of r2 0) "CUSTOM01"));
|
|
339
|
+
|
|
340
|
+
test "error.fails_even_though_a_later_provider_has_the_secret" (fun () ->
|
|
341
|
+
let (r, fetch) = recorder () in
|
|
342
|
+
let client = live fetch
|
|
343
|
+
[("providers", ja [broken "vault unreachable"; memory [("APIKEY", Str "MEM01")]])] in
|
|
344
|
+
let res = direct client in
|
|
345
|
+
check "refused" (not (res_ok res));
|
|
346
|
+
check "the provider's message" (contains (res_err res) "vault unreachable");
|
|
347
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
348
|
+
|
|
349
|
+
test "error.a_broken_store_does_not_poison_the_client" (fun () ->
|
|
350
|
+
let (r, fetch) = recorder () in
|
|
351
|
+
let (fn, _) = custom (fun n -> if n = 1 then Secret.fail "vault unreachable" else Str "LATE01") in
|
|
352
|
+
let client = live fetch [("providers", ja [fn])] in
|
|
353
|
+
check "first op refused" (not (res_ok (direct client)));
|
|
354
|
+
check_int "nothing sent" (List.length (api_calls r)) 0;
|
|
355
|
+
check "second op succeeds" (res_ok (direct client));
|
|
356
|
+
check_int "one call" (List.length (api_calls r)) 1;
|
|
357
|
+
check "carries LATE01" (credential_is (auth_of r 0) "LATE01"));
|
|
358
|
+
|
|
359
|
+
test "error.a_builtin_that_cannot_read_is_an_error_not_a_miss" (fun () ->
|
|
360
|
+
(* dotenv pointed at a DIRECTORY: sekreto's own wording, from the
|
|
361
|
+
* vendored built-in, and nothing on the wire. *)
|
|
362
|
+
let (r, fetch) = recorder () in
|
|
363
|
+
let dir = Filename.get_temp_dir_name () in
|
|
364
|
+
let client = live fetch
|
|
365
|
+
[("providers", ja [jo [("kind", Str "dotenv"); ("file", Str dir)];
|
|
366
|
+
memory [("APIKEY", Str "MEM01")]])] in
|
|
367
|
+
let res = direct client in
|
|
368
|
+
check "refused" (not (res_ok res));
|
|
369
|
+
check ("sekreto's message: " ^ res_err res)
|
|
370
|
+
(contains (res_err res) "sekreto: dotenv provider cannot read");
|
|
371
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
372
|
+
|
|
373
|
+
(* ---------------------------------------------------------------------- *)
|
|
374
|
+
(* the init-failure gate: a chain that cannot be built refuses every *)
|
|
375
|
+
(* request; a malformed entry is refused, never dropped *)
|
|
376
|
+
(* ---------------------------------------------------------------------- *)
|
|
377
|
+
|
|
378
|
+
test "gate.a_bare_kind_name_is_refused_not_dropped" (fun () ->
|
|
379
|
+
let (r, fetch) = recorder () in
|
|
380
|
+
let client = live fetch
|
|
381
|
+
[("providers", ja [Str "hashicorp"; memory [("APIKEY", Str "MEM01")]])] in
|
|
382
|
+
let res = direct client in
|
|
383
|
+
check "refused" (not (res_ok res));
|
|
384
|
+
check ("sekreto's wording: " ^ res_err res)
|
|
385
|
+
(contains (res_err res) "sekreto: not a provider or a provider spec"
|
|
386
|
+
&& contains (res_err res) "hashicorp");
|
|
387
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
388
|
+
|
|
389
|
+
test "gate.a_number_is_refused_not_dropped" (fun () ->
|
|
390
|
+
let (r, fetch) = recorder () in
|
|
391
|
+
let client = live fetch [("providers", ja [Num 42.; memory [("APIKEY", Str "MEM01")]])] in
|
|
392
|
+
let res = direct client in
|
|
393
|
+
check "refused" (not (res_ok res));
|
|
394
|
+
check "sekreto's wording" (contains (res_err res) "not a provider or a provider spec");
|
|
395
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
396
|
+
|
|
397
|
+
test "gate.a_null_hole_does_not_shorten_the_chain" (fun () ->
|
|
398
|
+
let (r, fetch) = recorder () in
|
|
399
|
+
let client = live fetch [("providers", ja [Null; memory [("APIKEY", Str "MEM01")]])] in
|
|
400
|
+
let res = direct client in
|
|
401
|
+
check "refused" (not (res_ok res));
|
|
402
|
+
check "sekreto's wording" (contains (res_err res) "not a provider or a provider spec");
|
|
403
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
404
|
+
|
|
405
|
+
test "gate.an_unknown_kind_is_refused_with_sekretos_message" (fun () ->
|
|
406
|
+
let (r, fetch) = recorder () in
|
|
407
|
+
let client = live fetch [("providers", ja [jo [("kind", Str "nosuchkind")]])] in
|
|
408
|
+
let res = direct client in
|
|
409
|
+
check "refused" (not (res_ok res));
|
|
410
|
+
check ("sekreto's message: " ^ res_err res)
|
|
411
|
+
(contains (res_err res) "sekreto: unknown provider kind: nosuchkind");
|
|
412
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
413
|
+
|
|
414
|
+
test "gate.an_invalid_secret_name_is_refused_not_skipped" (fun () ->
|
|
415
|
+
let (r, fetch) = recorder () in
|
|
416
|
+
let client = live ~sdkopts:[("apikey", Str "OPTKEY01")] fetch
|
|
417
|
+
[("providers", ja [working "CUSTOM01"]); ("name", Str "not a name!")] in
|
|
418
|
+
let res = direct client in
|
|
419
|
+
check "refused" (not (res_ok res));
|
|
420
|
+
check ("sekreto's message: " ^ res_err res) (contains (res_err res) "sekreto:");
|
|
421
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
422
|
+
|
|
423
|
+
test "gate.plugin_kinds_are_the_models_choice" (fun () ->
|
|
424
|
+
(* The vocabulary really is the model's: the definitions Config_ocaml
|
|
425
|
+
* selected, read back through the generated accessor. A plugin kind
|
|
426
|
+
* either resolves through its vendored module (here: refused by the
|
|
427
|
+
* store's own unreachable-address error, since nothing listens) or,
|
|
428
|
+
* when its group is off, is refused as "not passed in" - sekreto's
|
|
429
|
+
* own message in both cases, and nothing on the wire in either. *)
|
|
430
|
+
let defs = Sdk_config.feature_plugins "secrets" in
|
|
431
|
+
Printf.printf "secrets: %d plugin definition(s) selected by the model\n" (List.length defs);
|
|
432
|
+
let (r, fetch) = recorder () in
|
|
433
|
+
let client = live fetch
|
|
434
|
+
[("providers", ja [jo [("kind", Str "hashicorp"); ("addr", Str "http://127.0.0.1:1");
|
|
435
|
+
("token", Str "t")]])] in
|
|
436
|
+
let res = direct client in
|
|
437
|
+
check "refused" (not (res_ok res));
|
|
438
|
+
let msg = res_err res in
|
|
439
|
+
Printf.printf "secrets: hashicorp kind -> %s\n" msg;
|
|
440
|
+
check ("sekreto's message: " ^ msg)
|
|
441
|
+
(contains msg "is a sekreto plugin, not built in" || contains msg "sekreto: cannot reach");
|
|
442
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0);
|
|
443
|
+
|
|
444
|
+
(* ---------------------------------------------------------------------- *)
|
|
445
|
+
(* auth suppression *)
|
|
446
|
+
(* ---------------------------------------------------------------------- *)
|
|
447
|
+
|
|
448
|
+
test "auth.null_suppresses_the_credential_chain_or_no_chain" (fun () ->
|
|
449
|
+
let (r, fetch) = recorder () in
|
|
450
|
+
let client = live ~sdkopts:[("auth", Null)] fetch
|
|
451
|
+
[("providers", ja [jo [("kind", Str "env"); ("prefix", Str envprefix)]])] in
|
|
452
|
+
check "ok" (res_ok (direct client));
|
|
453
|
+
check_int "one call" (List.length (api_calls r)) 1;
|
|
454
|
+
check "no header, though the chain resolved" (absent (auth_of r 0));
|
|
455
|
+
(* The suppression survives option validation as a present null. *)
|
|
456
|
+
check "options.auth survives as a present null"
|
|
457
|
+
(getprop_raw client.cl_options "auth" = Null));
|
|
458
|
+
|
|
459
|
+
test "auth.null_suppresses_an_explicit_apikey_too" (fun () ->
|
|
460
|
+
let (r, fetch) = recorder () in
|
|
461
|
+
let client = live ~sdkopts:[("auth", Null); ("apikey", Str "OPTKEY01")] fetch
|
|
462
|
+
[("providers", ja [working "CUSTOM01"])] in
|
|
463
|
+
check "ok" (res_ok (direct client));
|
|
464
|
+
check "no header" (absent (auth_of r 0)));
|
|
465
|
+
|
|
466
|
+
test "auth.null_suppresses_on_the_entity_path" (fun () ->
|
|
467
|
+
let (r, fetch) = recorder () in
|
|
468
|
+
let client = live ~sdkopts:[("auth", Null)] fetch [("providers", ja [working "CUSTOM01"])] in
|
|
469
|
+
(match drive_entity client r with
|
|
470
|
+
| Ok () -> ()
|
|
471
|
+
| Error msg -> failwith ("entity op failed: " ^ msg));
|
|
472
|
+
check_int "reached the transport once" (List.length (api_calls r)) 1;
|
|
473
|
+
check "no header" (absent (auth_of r 0)));
|
|
474
|
+
|
|
475
|
+
test "entity.carries_the_chain_resolved_credential" (fun () ->
|
|
476
|
+
let (r, fetch) = recorder () in
|
|
477
|
+
let client = live fetch [("providers", ja [jo [("kind", Str "env"); ("prefix", Str envprefix)]])] in
|
|
478
|
+
(match drive_entity client r with
|
|
479
|
+
| Ok () -> ()
|
|
480
|
+
| Error msg -> failwith ("entity op failed: " ^ msg));
|
|
481
|
+
check_int "reached the transport once" (List.length (api_calls r)) 1;
|
|
482
|
+
check "carries ENVKEY01" (credential_is (auth_of r 0) "ENVKEY01"));
|
|
483
|
+
|
|
484
|
+
(* ---------------------------------------------------------------------- *)
|
|
485
|
+
(* cache *)
|
|
486
|
+
(* ---------------------------------------------------------------------- *)
|
|
487
|
+
|
|
488
|
+
test "cache.caches_the_resolved_credential_by_default" (fun () ->
|
|
489
|
+
let (r, fetch) = recorder () in
|
|
490
|
+
let (fn, asked) = custom (fun _ -> Str "C01") in
|
|
491
|
+
let client = live fetch [("providers", ja [fn])] in
|
|
492
|
+
check "ok 1" (res_ok (direct client));
|
|
493
|
+
check "ok 2" (res_ok (direct client));
|
|
494
|
+
check_int "two calls" (List.length (api_calls r)) 2;
|
|
495
|
+
check_int "asked once" !asked 1;
|
|
496
|
+
check "both carry C01" (credential_is (auth_of r 0) "C01" && credential_is (auth_of r 1) "C01"));
|
|
497
|
+
|
|
498
|
+
test "cache.false_asks_the_chain_once_per_request" (fun () ->
|
|
499
|
+
let (r, fetch) = recorder () in
|
|
500
|
+
let (fn, asked) = custom (fun _ -> Str "C01") in
|
|
501
|
+
let client = live fetch [("providers", ja [fn]); ("cache", Bool false)] in
|
|
502
|
+
check "ok 1" (res_ok (direct client));
|
|
503
|
+
check "ok 2" (res_ok (direct client));
|
|
504
|
+
check_int "two calls" (List.length (api_calls r)) 2;
|
|
505
|
+
check_int "asked twice" !asked 2);
|
|
506
|
+
|
|
507
|
+
test "cache.an_uncached_miss_after_a_hit_retracts_the_credential" (fun () ->
|
|
508
|
+
let (r, fetch) = recorder () in
|
|
509
|
+
let (fn, _) = custom (fun n -> if n = 1 then Str "C01" else Null) in
|
|
510
|
+
let client = live fetch [("providers", ja [fn]); ("cache", Bool false)] in
|
|
511
|
+
check "ok 1" (res_ok (direct client));
|
|
512
|
+
check "first carries C01" (credential_is (auth_of r 0) "C01");
|
|
513
|
+
check "ok 2" (res_ok (direct client));
|
|
514
|
+
check "second carries nothing: the revoked value must not keep going out"
|
|
515
|
+
(absent (auth_of r 1)));
|
|
516
|
+
|
|
517
|
+
(* ---------------------------------------------------------------------- *)
|
|
518
|
+
(* the access-token exchange *)
|
|
519
|
+
(* ---------------------------------------------------------------------- *)
|
|
520
|
+
|
|
521
|
+
let xchg (extra : (string * value) list) : (string * value) =
|
|
522
|
+
("exchange", jo (("active", Bool true) :: extra)) in
|
|
523
|
+
let refresh_chain (v : string) : (string * value) =
|
|
524
|
+
("providers", ja [memory ~name:"refresh" [("REFRESH_TOKEN", Str v)]]) in
|
|
525
|
+
|
|
526
|
+
(* A token endpoint answering ACCESS<k> to the k-th purchase; the API
|
|
527
|
+
* answers `api` (status by the token it was handed). *)
|
|
528
|
+
let token_server (r : recorder) (api : string -> int) : unit =
|
|
529
|
+
r.reply <- Some (fun _n url fd ->
|
|
530
|
+
if is_token url then
|
|
531
|
+
ok200 (jo [("access_token", Str ("ACCESS" ^ string_of_int (List.length (token_calls r))))])
|
|
532
|
+
else
|
|
533
|
+
let token = match getp fd "headers" with
|
|
534
|
+
| Map _ as h -> (match Sdk_features.header_ci h "authorization" with Str s -> s | _ -> "")
|
|
535
|
+
| _ -> "" in
|
|
536
|
+
status_res (api token) (jo [("id", Str "r01")])) in
|
|
537
|
+
|
|
538
|
+
let body_of (call : value) : value =
|
|
539
|
+
match getp (getp call "fetchdef") "body" with
|
|
540
|
+
| Str s -> (try Sdk_json.json_read s with _ -> Noval)
|
|
541
|
+
| b -> b in
|
|
542
|
+
|
|
543
|
+
test "exchange.buys_an_access_token_with_the_resolved_refresh_token" (fun () ->
|
|
544
|
+
let (r, fetch) = recorder () in
|
|
545
|
+
token_server r (fun _ -> 200);
|
|
546
|
+
let client = live fetch [refresh_chain "R1"; ("name", Str "refresh_token"); xchg []] in
|
|
547
|
+
check "ok" (res_ok (direct client));
|
|
548
|
+
check_int "one purchase" (List.length (token_calls r)) 1;
|
|
549
|
+
check_int "one API call" (List.length (api_calls r)) 1;
|
|
550
|
+
check "the API call carries the bought token" (credential_is (auth_of r 0) "ACCESS1");
|
|
551
|
+
let tc = List.hd (token_calls r) in
|
|
552
|
+
check "POSTed to base + exchange.path"
|
|
553
|
+
((match getp tc "url" with Str u -> u | _ -> "") = "http://secrets.test/api/auth/token");
|
|
554
|
+
check "the body is JSON-marshalled with the refresh token"
|
|
555
|
+
(getp (body_of tc) "refresh_token" = Str "R1"));
|
|
556
|
+
|
|
557
|
+
test "exchange.body_is_marshalled_not_concatenated" (fun () ->
|
|
558
|
+
let (r, fetch) = recorder () in
|
|
559
|
+
token_server r (fun _ -> 200);
|
|
560
|
+
let quoted = "R\"1\\x" in
|
|
561
|
+
let client = live fetch [refresh_chain quoted; ("name", Str "refresh_token"); xchg []] in
|
|
562
|
+
check "ok" (res_ok (direct client));
|
|
563
|
+
check "a refresh token carrying a quote and a backslash survives"
|
|
564
|
+
(getp (body_of (List.hd (token_calls r))) "refresh_token" = Str quoted));
|
|
565
|
+
|
|
566
|
+
test "exchange.a_spent_token_is_repurchased_and_retried_once" (fun () ->
|
|
567
|
+
let (r, fetch) = recorder () in
|
|
568
|
+
token_server r (fun token -> if credential_is (Str token) "ACCESS1" then 401 else 200);
|
|
569
|
+
let client = live fetch [refresh_chain "R1"; ("name", Str "refresh_token"); xchg []] in
|
|
570
|
+
check "ok" (res_ok (direct client));
|
|
571
|
+
check_int "two purchases" (List.length (token_calls r)) 2;
|
|
572
|
+
check_int "two API calls: the refusal and the retry" (List.length (api_calls r)) 2;
|
|
573
|
+
check "the retry carries the fresh token" (credential_is (auth_of r 1) "ACCESS2"));
|
|
574
|
+
|
|
575
|
+
test "exchange.a_second_refusal_surfaces_rather_than_spinning" (fun () ->
|
|
576
|
+
let (r, fetch) = recorder () in
|
|
577
|
+
token_server r (fun _ -> 401);
|
|
578
|
+
let client = live fetch [refresh_chain "R1"; ("name", Str "refresh_token"); xchg []] in
|
|
579
|
+
let res = direct client in
|
|
580
|
+
check "the refusal surfaces" (not (res_ok res));
|
|
581
|
+
check_vnum "with the API's status" (getp res "status") 401.;
|
|
582
|
+
check_int "exactly two API calls" (List.length (api_calls r)) 2;
|
|
583
|
+
check_int "exactly two purchases" (List.length (token_calls r)) 2);
|
|
584
|
+
|
|
585
|
+
test "exchange.retries_zero_never_repurchases" (fun () ->
|
|
586
|
+
let (r, fetch) = recorder () in
|
|
587
|
+
token_server r (fun _ -> 401);
|
|
588
|
+
let client = live fetch [refresh_chain "R1"; ("name", Str "refresh_token"); xchg [("retries", Num 0.)]] in
|
|
589
|
+
check "refused" (not (res_ok (direct client)));
|
|
590
|
+
check_int "one API call" (List.length (api_calls r)) 1;
|
|
591
|
+
check_int "one purchase" (List.length (token_calls r)) 1);
|
|
592
|
+
|
|
593
|
+
test "exchange.refresh_option_seats_first_as_the_explicit_credential" (fun () ->
|
|
594
|
+
let (r, fetch) = recorder () in
|
|
595
|
+
token_server r (fun _ -> 200);
|
|
596
|
+
let client = live fetch [refresh_chain "R2"; ("name", Str "refresh_token"); xchg [("refresh", Str "R1")]] in
|
|
597
|
+
check "ok" (res_ok (direct client));
|
|
598
|
+
check "the explicit refresh token was spent, not the chain's"
|
|
599
|
+
(getp (body_of (List.hd (token_calls r))) "refresh_token" = Str "R1"));
|
|
600
|
+
|
|
601
|
+
test "exchange.a_held_apikey_is_spent_before_anything_is_bought" (fun () ->
|
|
602
|
+
let (r, fetch) = recorder () in
|
|
603
|
+
token_server r (fun _ -> 200);
|
|
604
|
+
let client = live ~sdkopts:[("apikey", Str "HELD01")] fetch
|
|
605
|
+
[refresh_chain "R1"; ("name", Str "refresh_token"); xchg []] in
|
|
606
|
+
check "ok" (res_ok (direct client));
|
|
607
|
+
check_int "nothing bought" (List.length (token_calls r)) 0;
|
|
608
|
+
check "the held access token went out" (credential_is (auth_of r 0) "HELD01"));
|
|
609
|
+
|
|
610
|
+
test "exchange.no_refresh_token_anywhere_is_a_refusal_with_the_features_message" (fun () ->
|
|
611
|
+
let (r, fetch) = recorder () in
|
|
612
|
+
token_server r (fun _ -> 200);
|
|
613
|
+
let client = live fetch [("providers", ja []); ("name", Str "refresh_token"); xchg []] in
|
|
614
|
+
let res = direct client in
|
|
615
|
+
check "refused" (not (res_ok res));
|
|
616
|
+
check ("the feature's message: " ^ res_err res) (contains (res_err res) "secrets: no refresh token");
|
|
617
|
+
check_int "nothing reached the wire" (List.length (api_calls r)) 0;
|
|
618
|
+
check_int "nothing bought" (List.length (token_calls r)) 0);
|
|
619
|
+
|
|
620
|
+
test "exchange.without_a_transport_is_a_named_refusal" (fun () ->
|
|
621
|
+
(* This SDK's core bundles no HTTP client. With no options.system.fetch
|
|
622
|
+
* the purchase either has the vendored sekreto client (a plugin
|
|
623
|
+
* group needing one is active: it dials the loopback port nothing
|
|
624
|
+
* listens on and says so) or nothing at all (and says so). Never a
|
|
625
|
+
* silent unauthenticated send. *)
|
|
626
|
+
let client = Sdk_client.make (jo [
|
|
627
|
+
("base", Str "http://127.0.0.1:1");
|
|
628
|
+
("feature", jo [("secrets", jo [("active", Bool true); refresh_chain "R1";
|
|
629
|
+
("name", Str "refresh_token"); xchg []])])]) in
|
|
630
|
+
let res = direct client in
|
|
631
|
+
check "refused" (not (res_ok res));
|
|
632
|
+
let msg = res_err res in
|
|
633
|
+
Printf.printf "secrets: exchange without system.fetch -> %s\n" msg;
|
|
634
|
+
check ("a named refusal: " ^ msg)
|
|
635
|
+
(contains msg "secrets: the token exchange has no HTTP transport"
|
|
636
|
+
|| contains msg "sekreto: cannot reach"));
|
|
637
|
+
|
|
638
|
+
test "exchange.test_mode_buys_nothing" (fun () ->
|
|
639
|
+
(* A non-live client: the credential becomes the deterministic
|
|
640
|
+
* `test-<response>` and no purchase is attempted. Observed through
|
|
641
|
+
* ctrl.explain, which records the live fetchdef whose headers map
|
|
642
|
+
* the wrapper rewrites in place. *)
|
|
643
|
+
let client = Sdk_client.test_with Noval (jo [
|
|
644
|
+
("feature", jo [("secrets", jo [("active", Bool true); refresh_chain "R1";
|
|
645
|
+
("name", Str "refresh_token"); xchg []])])]) in
|
|
646
|
+
let explain = empty_map () in
|
|
647
|
+
let seen = ref false in
|
|
648
|
+
List.iter (fun name ->
|
|
649
|
+
if not !seen then
|
|
650
|
+
match Sdk_client.entity client name Noval with
|
|
651
|
+
| None -> ()
|
|
652
|
+
| Some ent ->
|
|
653
|
+
(try ignore (ent.e_list (empty_map ()) (jo [("explain", explain)])) with _ -> ());
|
|
654
|
+
(match getpath_s explain "fetchdef.headers" with
|
|
655
|
+
| Map _ -> seen := true
|
|
656
|
+
| _ -> (try ignore (ent.e_load (jo [("id", Str "id01")]) (jo [("explain", explain)])) with _ -> ());
|
|
657
|
+
(match getpath_s explain "fetchdef.headers" with Map _ -> seen := true | _ -> ())))
|
|
658
|
+
(entity_names ());
|
|
659
|
+
check "an entity op recorded its fetchdef" !seen;
|
|
660
|
+
let header = match getpath_s explain "fetchdef.headers" with
|
|
661
|
+
| Map _ as h -> Sdk_features.header_ci h "authorization"
|
|
662
|
+
| _ -> Noval in
|
|
663
|
+
check ("carries the deterministic test token: " ^ stringify header)
|
|
664
|
+
(credential_is header "test-access_token");
|
|
665
|
+
check_vnum "one (fake) purchase, tracked" (getp (track_get client "secrets") "buys") 1.);
|
|
666
|
+
|
|
667
|
+
()
|
|
668
|
+
|
|
669
|
+
(* Printed from EXECUTIONS, for the generator's lane. *)
|
|
670
|
+
let () = Printf.printf "feature.secrets: ran %d check(s)\n" !ran
|