@voxgig/sdkgen 4.6.0 → 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.
Files changed (115) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Feature.js +5 -0
  3. package/dist/cmp/Feature.js.map +1 -1
  4. package/dist/cmp/TestControl.d.ts +4 -0
  5. package/dist/cmp/TestControl.js +51 -0
  6. package/dist/cmp/TestControl.js.map +1 -0
  7. package/dist/helpers/featureSource.d.ts +3 -1
  8. package/dist/helpers/featureSource.js +105 -0
  9. package/dist/helpers/featureSource.js.map +1 -1
  10. package/dist/sdkgen.d.ts +3 -2
  11. package/dist/sdkgen.js +9 -4
  12. package/dist/sdkgen.js.map +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/dist/utility.d.ts +2 -1
  15. package/dist/utility.js +52 -0
  16. package/dist/utility.js.map +1 -1
  17. package/model/sdkgen.aon +58 -0
  18. package/package.json +1 -1
  19. package/project/.sdk/model/feature/secrets.aon +82 -0
  20. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
  21. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
  22. package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
  23. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -6
  24. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
  25. package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
  26. package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
  27. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +23 -6
  28. package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
  29. package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
  30. package/project/.sdk/src/cmp/go/Main_go.ts +2 -1
  31. package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
  32. package/project/.sdk/src/cmp/java/Main_java.ts +2 -1
  33. package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
  34. package/project/.sdk/src/cmp/js/Main_js.ts +2 -1
  35. package/project/.sdk/src/cmp/js/TestDirect_js.ts +30 -5
  36. package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
  37. package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
  38. package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
  39. package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
  40. package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
  41. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
  42. package/project/.sdk/src/cmp/lua/Main_lua.ts +2 -1
  43. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +28 -4
  44. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
  45. package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
  46. package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
  47. package/project/.sdk/src/cmp/perl/Main_perl.ts +2 -1
  48. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -4
  49. package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
  50. package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
  51. package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
  52. package/project/.sdk/src/cmp/php/Main_php.ts +2 -1
  53. package/project/.sdk/src/cmp/php/TestDirect_php.ts +24 -5
  54. package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
  55. package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
  56. package/project/.sdk/src/cmp/py/Main_py.ts +2 -1
  57. package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
  58. package/project/.sdk/src/cmp/rb/Main_rb.ts +2 -1
  59. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +24 -5
  60. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
  61. package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
  62. package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
  63. package/project/.sdk/src/cmp/rust/Main_rust.ts +2 -1
  64. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +31 -4
  65. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
  66. package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
  67. package/project/.sdk/src/cmp/scala/Main_scala.ts +2 -1
  68. package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
  69. package/project/.sdk/src/cmp/ts/Config_ts.ts +50 -8
  70. package/project/.sdk/src/cmp/ts/Main_ts.ts +10 -2
  71. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +10 -3
  72. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +11 -3
  73. package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
  74. package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
  75. package/project/.sdk/tm/dart/test/utility.dart +39 -0
  76. package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
  77. package/project/.sdk/tm/js/test/utility.js +91 -1
  78. package/project/.sdk/tm/lua/test/runner.lua +40 -0
  79. package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
  80. package/project/.sdk/tm/perl/t/runner.pm +30 -0
  81. package/project/.sdk/tm/php/test/Runner.php +36 -0
  82. package/project/.sdk/tm/rb/test/runner.rb +25 -0
  83. package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
  84. package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
  85. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +8 -2
  86. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +29 -5
  87. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +30 -21
  88. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
  89. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
  90. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
  91. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
  92. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
  93. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
  94. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
  95. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
  96. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
  97. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
  98. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
  99. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
  100. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
  101. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
  102. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
  103. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
  104. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +15 -13
  105. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +25 -19
  106. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +1 -1
  107. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
  108. package/project/.sdk/tm/zig/core/utility.zig +1 -1
  109. package/project/sdkgen-package.json +1 -1
  110. package/src/cmp/Feature.ts +5 -0
  111. package/src/cmp/TestControl.ts +64 -0
  112. package/src/helpers/featureSource.ts +130 -0
  113. package/src/sdkgen.ts +7 -0
  114. package/src/utility.ts +61 -0
  115. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +0 -1221
@@ -7,7 +7,8 @@ import type {
7
7
  ModelEntity
8
8
  } from '@voxgig/apidef'
9
9
 
10
- import { cmp, each, snakify, Folder, File, Content, entityCollection } from '@voxgig/sdkgen'
10
+ import { cmp, each, snakify, Folder, File, Content, entityCollection,
11
+ TestControl } from '@voxgig/sdkgen'
11
12
 
12
13
 
13
14
  import { TestDirect } from './TestDirect_dart'
@@ -24,6 +25,9 @@ const Test = cmp(function Test(props: any) {
24
25
 
25
26
  Folder({ name: 'test' }, () => {
26
27
 
28
+ // Write-once: a project's edited control file survives regeneration.
29
+ TestControl({ target, dir: 'test' })
30
+
27
31
  // Suite entry: registers every static suite plus the generated
28
32
  // per-entity suites, then runs them (Makefile: dart run test/main.dart).
29
33
  File({ name: 'main.' + target.ext }, () => {
@@ -6,6 +6,7 @@ import {
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
7
  entityClassName, entityCollection, goModule, goPackageIdent,
8
8
  targetFeatures,
9
+ TEST_CONTROL_EXCLUDE
9
10
  } from '@voxgig/sdkgen'
10
11
 
11
12
 
@@ -59,7 +60,7 @@ const Main = cmp(async function Main(props: any) {
59
60
  // downstream consumers.
60
61
  Copy({
61
62
  from: 'tm/' + target.name,
62
- exclude: [/src\//, /utility\/struct\/go\.mod$/],
63
+ exclude: [/src\//, /utility\/struct\/go\.mod$/, TEST_CONTROL_EXCLUDE],
63
64
  replace: {
64
65
  ...props.ctx$.stdrep,
65
66
  GOMODULE: gomodule,
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, File, Content, goModule, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, File, Content, goModule, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_go'
@@ -21,6 +22,9 @@ const Test = cmp(function Test(props: any) {
21
22
 
22
23
  Folder({ name: 'test' }, () => {
23
24
 
25
+ // Write-once: a project's edited control file survives regeneration.
26
+ TestControl({ target, dir: 'test' })
27
+
24
28
  // Generate exists_test.go programmatically to avoid duplication
25
29
  // with any scaffolded test file (e.g. projectname_sdk_test.go).
26
30
  File({ name: 'exists_test.' + target.ext }, () => {
@@ -5,6 +5,7 @@ import {
5
5
  cmp, each,
6
6
  File, Content, Copy, Folder, Fragment,
7
7
  targetFeatures,
8
+ TEST_CONTROL_EXCLUDE
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -52,7 +53,7 @@ const Main = cmp(async function Main(props: any) {
52
53
  // token used throughout the templates (package/import statements).
53
54
  Copy({
54
55
  from: 'tm/' + target.name,
55
- exclude: [/src\//],
56
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
56
57
  replace: {
57
58
  ...props.ctx$.stdrep,
58
59
  JAVAPACKAGE: javapackage,
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_java'
@@ -20,6 +21,9 @@ const Test = cmp(function Test(props: any) {
20
21
 
21
22
  Folder({ name: 'test' }, () => {
22
23
 
24
+ // Write-once: a project's edited control file survives regeneration.
25
+ TestControl({ target, dir: 'test' })
26
+
23
27
  // The shared test infrastructure (RunnerSupport, FeatureHarness,
24
28
  // PipelineTest, FeatureTest, NetsimTest, PrimaryUtilityTest,
25
29
  // CustomUtilityTest, StructRunner/StructCorpusTest, ExistsTest and
@@ -6,6 +6,7 @@ import {
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
7
  entityClassName, entityCollection, srcFeatureExcludes, stationLibrary,
8
8
  targetFeatures,
9
+ TEST_CONTROL_EXCLUDE
9
10
  } from '@voxgig/sdkgen'
10
11
 
11
12
 
@@ -49,7 +50,7 @@ const Main = cmp(async function Main(props: any) {
49
50
  from: 'tm/' + target.name,
50
51
  // Root copies src/feature/<name>/ per ACTIVE feature; keep this blanket
51
52
  // copy from restoring one that was switched off after `target add`.
52
- exclude: srcFeatureExcludes(model),
53
+ exclude: [...srcFeatureExcludes(model), TEST_CONTROL_EXCLUDE],
53
54
  replace: {
54
55
  ...props.ctx$.stdrep,
55
56
  }
@@ -17,7 +17,10 @@ import {
17
17
  cmp,
18
18
  snakify,
19
19
  isAuthActive,
20
- jsProp, envName, envToken
20
+ serverVarEnv,
21
+ serverVariables,
22
+ jsProp, envName, envToken,
23
+ jsKey
21
24
  } from '@voxgig/sdkgen'
22
25
 
23
26
 
@@ -41,13 +44,32 @@ const TestDirect = cmp(function TestDirect(props: any) {
41
44
 
42
45
  const authActive = isAuthActive(model)
43
46
  const apikeyEnvEntry = authActive
44
- ? `\n '${PROJECTNAME}_APIKEY': 'NONE',`
47
+ ? `\n '${PROJECTNAME}_APIKEY': '',`
45
48
  : ''
46
49
  const apikeyLiveField = authActive
47
50
  ? `
48
51
  apikey: env.${PROJECTNAME}_APIKEY,`
49
52
  : ''
50
53
 
54
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
55
+ // impossible to construct without values: makeOptions raises rather than
56
+ // request a URL with a literal `{account_id}` in it. Taken from the
57
+ // environment, the same way the apikey is.
58
+ //
59
+ // Keys are quoted and the env read is bracketed via jsKey/jsProp: a server
60
+ // variable name is spec-derived and need not be a JS identifier — the URL
61
+ // grammar admits a leading digit ({2fa}), and a declared-but-unreferenced
62
+ // variable ({edge-zone}) is not constrained at all. Bare `name:` and
63
+ // `env.PROJ_SERVER_EDGE-ZONE` are both syntax errors.
64
+ const svars = serverVariables(model)
65
+ const serverEnvEntry = svars
66
+ .map((v: any) => `\n '${serverVarEnv(PROJECTNAME, v.name)}': ${JSON.stringify(v.dflt)},`).join('')
67
+ const serverLiveField = 0 === svars.length ? '' : `
68
+ server: {${svars
69
+ .map((v: any) => `
70
+ ${jsKey(v.name)}: ${jsProp('env', serverVarEnv(PROJECTNAME, v.name))},`).join('')}
71
+ },`
72
+
51
73
  const opnames = Object.keys(entity.op || {})
52
74
  const hasLoad = opnames.includes('load')
53
75
  const hasList = opnames.includes('list')
@@ -78,14 +100,17 @@ function directSetup(mockres) {
78
100
 
79
101
  const env = envOverride({
80
102
  '${entidEnvVar}': {},
81
- '${PROJECTNAME}_TEST_LIVE': 'FALSE',${apikeyEnvEntry}
103
+ '${PROJECTNAME}_TEST_LIVE': 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
82
104
  })
83
105
 
84
106
  const live = 'TRUE' === env.${PROJECTNAME}_TEST_LIVE
85
107
 
86
108
  if (live) {
87
- const client = new ${nom(model.const, 'Name')}SDK({${apikeyLiveField}
88
- })
109
+ // Merged so the generated fields win: sdk-test-control.json's
110
+ // test.client.options adds to the live client, it does not redirect it.
111
+ const client = new ${nom(model.const, 'Name')}SDK(
112
+ Object.assign({}, liveClientOptions(), {${apikeyLiveField}${serverLiveField}
113
+ }))
89
114
 
90
115
  let idmap = env['${entidEnvVar}']
91
116
  if ('string' === typeof idmap && idmap.startsWith('{')) {
@@ -30,7 +30,11 @@ import {
30
30
  cmp,
31
31
  each,
32
32
  isAuthActive,
33
- entityDataIdField, envName, envToken
33
+ serverVarEnv,
34
+ serverVariables,
35
+ entityDataIdField, envName, envToken,
36
+ jsKey,
37
+ jsProp
34
38
  } from '@voxgig/sdkgen'
35
39
 
36
40
 
@@ -63,13 +67,32 @@ const TestEntity = cmp(function TestEntity(props: any) {
63
67
  const ENTENVNAME = envToken(entity.name)
64
68
  const authActive = isAuthActive(model)
65
69
  const apikeyEnvEntry = authActive
66
- ? `\n '${PROJENVNAME}_APIKEY': 'NONE',`
70
+ ? `\n '${PROJENVNAME}_APIKEY': '',`
67
71
  : ''
68
72
  const apikeyLiveField = authActive
69
73
  ? `
70
74
  apikey: env.${PROJENVNAME}_APIKEY,`
71
75
  : ''
72
76
 
77
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
78
+ // impossible to construct without values: makeOptions raises rather than
79
+ // request a URL with a literal `{account_id}` in it. So the live suite
80
+ // takes them from the environment the same way it takes the apikey.
81
+ //
82
+ // Keys are quoted and the env read is bracketed via jsKey/jsProp: a server
83
+ // variable name is spec-derived and need not be a JS identifier — the URL
84
+ // grammar admits a leading digit ({2fa}), and a declared-but-unreferenced
85
+ // variable ({edge-zone}) is not constrained at all. Bare `name:` and
86
+ // `env.PROJ_SERVER_EDGE-ZONE` are both syntax errors.
87
+ const svars = serverVariables(model)
88
+ const serverEnvEntry = svars
89
+ .map((v: any) => `\n '${serverVarEnv(PROJENVNAME, v.name)}': ${JSON.stringify(v.dflt)},`).join('')
90
+ const serverLiveField = 0 === svars.length ? '' : `
91
+ server: {${svars
92
+ .map((v: any) => `
93
+ ${jsKey(v.name)}: ${jsProp('env', serverVarEnv(PROJENVNAME, v.name))},`).join('')}
94
+ },`
95
+
73
96
  // TODO: should be a utility function
74
97
  const ff = projectPath('src/cmp/js/fragment/')
75
98
 
@@ -143,16 +166,23 @@ function basicSetup(extra) {
143
166
  const env = envOverride({
144
167
  '${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID': idmap,
145
168
  '${PROJENVNAME}_TEST_LIVE': 'FALSE',
146
- '${PROJENVNAME}_TEST_EXPLAIN': 'FALSE',${apikeyEnvEntry}
169
+ '${PROJENVNAME}_TEST_EXPLAIN': 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
147
170
  })
148
171
 
149
172
  idmap = env['${PROJENVNAME}_TEST_${ENTENVNAME}_ENTID']
150
173
 
151
174
  if ('TRUE' === env.${PROJENVNAME}_TEST_LIVE) {
152
175
  client = new ${model.Name}SDK(merge([
153
- {${apikeyLiveField}
176
+ // FIRST, so the generated fields below win: sdk-test-control.json's
177
+ // test.client.options adds to the live client, it does not redirect it.
178
+ liveClientOptions(),
179
+ {${apikeyLiveField}${serverLiveField}
154
180
  },
155
- extra
181
+ // 'extra || {}', not a bare 'extra': struct.merge returns UNDEFINED when
182
+ // the last entry is undefined, and basicSetup is normally called with no
183
+ // argument at all - so a bare 'extra' silently discarded the apikey and
184
+ // server values above and handed the SDK undefined.
185
+ extra || {}
156
186
  ]))
157
187
  }
158
188
 
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestDirect } from './TestDirect_js'
@@ -16,6 +17,9 @@ const Test = cmp(function Test(props: any) {
16
17
 
17
18
  Folder({ name: 'test' }, () => {
18
19
 
20
+ // Write-once: a project's edited control file survives regeneration.
21
+ TestControl({ target, dir: 'test' })
22
+
19
23
  Folder({ name: 'entity' }, () => {
20
24
  const entity = each(entityCollection(model))
21
25
  .filter((e: any) => false !== e.active)
@@ -2,7 +2,7 @@
2
2
  const envlocal = __dirname + '/../../../.env.local'
3
3
  require('dotenv').config({ quiet: true, path: [envlocal] })
4
4
 
5
- const { test, describe } = require('node:test')
5
+ const { test, describe, afterEach } = require('node:test')
6
6
  const assert = require('node:assert')
7
7
 
8
8
 
@@ -10,11 +10,17 @@ const { ProjectNameSDK } = require('../../..')
10
10
 
11
11
  const {
12
12
  envOverride,
13
+ liveClientOptions,
14
+ liveDelay,
13
15
  } = require('../../utility')
14
16
 
15
17
 
16
18
  describe('EntityNameDirect', async () => {
17
19
 
20
+ // Per-test live pacing. Delay is read from sdk-test-control.json's
21
+ // `test.live.delayMs`; only sleeps when PROJECTENV_TEST_LIVE=TRUE.
22
+ afterEach(liveDelay('PROJECTENV_TEST_LIVE'))
23
+
18
24
  test('direct-exists', async () => {
19
25
  const sdk = new ProjectNameSDK({
20
26
  // Concrete base: a live construction must satisfy any server
@@ -5,7 +5,7 @@ require('dotenv').config({ quiet: true, path: [envlocal] })
5
5
  const Path = require('node:path')
6
6
  const Fs = require('node:fs')
7
7
 
8
- const { test, describe } = require('node:test')
8
+ const { test, describe, afterEach } = require('node:test')
9
9
  const assert = require('node:assert')
10
10
 
11
11
 
@@ -13,6 +13,8 @@ const { ProjectNameSDK, BaseFeature, stdutil, config } = require('../../..')
13
13
 
14
14
  const {
15
15
  envOverride,
16
+ liveClientOptions,
17
+ liveDelay,
16
18
  makeCtrl,
17
19
  makeMatch,
18
20
  makeReqdata,
@@ -23,6 +25,10 @@ const {
23
25
 
24
26
  describe('EntityNameEntity', async () => {
25
27
 
28
+ // Per-test live pacing. Delay is read from sdk-test-control.json's
29
+ // `test.live.delayMs`; only sleeps when PROJECTENV_TEST_LIVE=TRUE.
30
+ afterEach(liveDelay('PROJECTENV_TEST_LIVE'))
31
+
26
32
  test('instance', async () => {
27
33
  const testsdk = ProjectNameSDK.test()
28
34
  const ent = testsdk.EntityName()
@@ -4,6 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each,
6
6
  File, Copy, Folder, Fragment,
7
+ TEST_CONTROL_EXCLUDE
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -47,7 +48,7 @@ const Main = cmp(async function Main(props: any) {
47
48
  // token used throughout the templates (package/import statements).
48
49
  Copy({
49
50
  from: 'tm/' + target.name,
50
- exclude: [/src\//],
51
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
51
52
  replace: {
52
53
  ...props.ctx$.stdrep,
53
54
  KOTLINPACKAGE: kotlinpackage,
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_kotlin'
@@ -20,6 +21,9 @@ const Test = cmp(function Test(props: any) {
20
21
 
21
22
  Folder({ name: 'test' }, () => {
22
23
 
24
+ // Write-once: a project's edited control file survives regeneration.
25
+ TestControl({ target, dir: 'test' })
26
+
23
27
  // The shared test infrastructure (RunnerSupport, FeatureHarness,
24
28
  // PipelineTest, FeatureTest, NetsimTest, PrimaryUtilityTest,
25
29
  // CustomUtilityTest, StructRunner/StructCorpusTest, ExistsTest and
@@ -5,6 +5,7 @@ import {
5
5
  cmp, each, names, cmap,
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
7
  targetFeatures,
8
+ TEST_CONTROL_EXCLUDE
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -44,7 +45,7 @@ const Main = cmp(async function Main(props: any) {
44
45
  // Copy tm/lua files with replacements
45
46
  Copy({
46
47
  from: 'tm/' + target.name,
47
- exclude: [/src\//],
48
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
48
49
  replace: {
49
50
  ...props.ctx$.stdrep,
50
51
  }
@@ -11,9 +11,13 @@ import {
11
11
  File,
12
12
  cmp,
13
13
  snakify,
14
- isAuthActive, envName, envToken
14
+ isAuthActive, envName, envToken,
15
+ serverVarEnv,
16
+ serverVariables
15
17
  } from '@voxgig/sdkgen'
16
18
 
19
+ import { formatLuaValue } from './utility_lua'
20
+
17
21
 
18
22
  function normalizePathParams(
19
23
  parts: string[],
@@ -64,12 +68,25 @@ const TestDirect = cmp(function TestDirect(props: any) {
64
68
 
65
69
  const authActive = isAuthActive(model)
66
70
  const apikeyEnvEntry = authActive
67
- ? `\n ["${PROJECTNAME}_APIKEY"] = "NONE",`
71
+ ? `\n ["${PROJECTNAME}_APIKEY"] = "",`
68
72
  : ''
69
73
  const apikeyLiveField = authActive
70
74
  ? `\n apikey = env["${PROJECTNAME}_APIKEY"],`
71
75
  : ''
72
76
 
77
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
78
+ // impossible to construct without values: makeOptions raises rather than
79
+ // request a URL with a literal `{account_id}` in it. So the live suite
80
+ // takes them from the environment the same way it takes the apikey.
81
+ const svars = serverVariables(model)
82
+ const serverEnvEntry = svars
83
+ .map((v: any) => `\n ["${serverVarEnv(PROJECTNAME, v.name)}"] = ${formatLuaValue(v.dflt)},`).join('')
84
+ const serverLiveField = 0 === svars.length ? '' : `
85
+ server = {${svars
86
+ .map((v: any) => `
87
+ ["${v.name}"] = env["${serverVarEnv(PROJECTNAME, v.name)}"],`).join('')}
88
+ },`
89
+
73
90
  const opnames = Object.keys(entity.op || {})
74
91
  const hasLoad = opnames.includes('load')
75
92
  const hasList = opnames.includes('list')
@@ -350,14 +367,21 @@ function ${entity.name}_direct_setup(mockres)
350
367
 
351
368
  local env = runner.env_override({
352
369
  ["${entidEnvVar}"] = {},
353
- ["${PROJECTNAME}_TEST_LIVE"] = "FALSE",${apikeyEnvEntry}
370
+ ["${PROJECTNAME}_TEST_LIVE"] = "FALSE",${apikeyEnvEntry}${serverEnvEntry}
354
371
  })
355
372
 
356
373
  local live = env["${PROJECTNAME}_TEST_LIVE"] == "TRUE"
357
374
 
358
375
  if live then
359
- local merged_opts = {${apikeyLiveField}
376
+ local merged_opts = {${apikeyLiveField}${serverLiveField}
360
377
  }
378
+ -- sdk-test-control.json's test.client.options goes UNDER the generated
379
+ -- fields: it adds to the live client, it does not redirect it.
380
+ for _k, _v in pairs(runner.live_client_options()) do
381
+ if merged_opts[_k] == nil then
382
+ merged_opts[_k] = _v
383
+ end
384
+ end
361
385
  local client = sdk.new(merged_opts)
362
386
  return {
363
387
  client = client,
@@ -24,9 +24,13 @@ import {
24
24
  buildIdNames,
25
25
  getMatchEntries,
26
26
  isAuthActive,
27
- entityDataIdField, envName, envToken
27
+ entityDataIdField, envName, envToken,
28
+ serverVarEnv,
29
+ serverVariables
28
30
  } from '@voxgig/sdkgen'
29
31
 
32
+ import { formatLuaValue } from './utility_lua'
33
+
30
34
 
31
35
  // See TestEntity_ts.ts for the GenCtx/OpGen contract.
32
36
  type GenCtx = {
@@ -62,12 +66,25 @@ const TestEntity = cmp(function TestEntity(props: any) {
62
66
 
63
67
  const authActive = isAuthActive(model)
64
68
  const apikeyEnvEntry = authActive
65
- ? `\n ["${PROJUPPER}_APIKEY"] = "NONE",`
69
+ ? `\n ["${PROJUPPER}_APIKEY"] = "",`
66
70
  : ''
67
71
  const apikeyLiveField = authActive
68
72
  ? `\n apikey = env["${PROJUPPER}_APIKEY"],`
69
73
  : ''
70
74
 
75
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
76
+ // impossible to construct without values: makeOptions raises rather than
77
+ // request a URL with a literal `{account_id}` in it. So the live suite
78
+ // takes them from the environment the same way it takes the apikey.
79
+ const svars = serverVariables(model)
80
+ const serverEnvEntry = svars
81
+ .map((v: any) => `\n ["${serverVarEnv(PROJUPPER, v.name)}"] = ${formatLuaValue(v.dflt)},`).join('')
82
+ const serverLiveField = 0 === svars.length ? '' : `
83
+ server = {${svars
84
+ .map((v: any) => `
85
+ ["${v.name}"] = env["${serverVarEnv(PROJUPPER, v.name)}"],`).join('')}
86
+ },`
87
+
71
88
  const idnames = buildIdNames(entity, basicflow)
72
89
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
73
90
 
@@ -241,7 +258,7 @@ end)
241
258
  local env = runner.env_override({
242
259
  ["${PROJUPPER}_TEST_${envToken(entity.name)}_ENTID"] = idmap,
243
260
  ["${PROJUPPER}_TEST_LIVE"] = "FALSE",
244
- ["${PROJUPPER}_TEST_EXPLAIN"] = "FALSE",${apikeyEnvEntry}
261
+ ["${PROJUPPER}_TEST_EXPLAIN"] = "FALSE",${apikeyEnvEntry}${serverEnvEntry}
245
262
  })
246
263
 
247
264
  local idmap_resolved = helpers.to_map(
@@ -262,7 +279,10 @@ end)
262
279
  Content(`
263
280
  if env["${PROJUPPER}_TEST_LIVE"] == "TRUE" then
264
281
  local merged_opts = vs.merge({
265
- {${apikeyLiveField}
282
+ -- FIRST, so the generated fields below win: sdk-test-control.json's
283
+ -- test.client.options adds to the live client, it does not redirect it.
284
+ runner.live_client_options(),
285
+ {${apikeyLiveField}${serverLiveField}
266
286
  },
267
287
  extra or {},
268
288
  })
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, File, Content, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, File, Content, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_lua'
@@ -17,6 +18,9 @@ const Test = cmp(function Test(props: any) {
17
18
 
18
19
  Folder({ name: 'test' }, () => {
19
20
 
21
+ // Write-once: a project's edited control file survives regeneration.
22
+ TestControl({ target, dir: 'test' })
23
+
20
24
  // Generate exists test
21
25
  File({ name: 'exists_test.' + target.ext }, () => {
22
26
  Content(`-- ${model.const.Name} SDK exists test
@@ -19,6 +19,7 @@ const Gitignore = cmp(async function Gitignore(_props: any) {
19
19
  a.out
20
20
  run_sdk_test
21
21
  run_struct_corpus
22
+ run_primary_corpus
22
23
 
23
24
  # dune (unused; the build is stock ocamlc via the Makefile)
24
25
  _build/
@@ -5,6 +5,7 @@ import {
5
5
  cmp, each, names,
6
6
  File, Content, Copy, Folder, Fragment,
7
7
  targetFeatures,
8
+ TEST_CONTROL_EXCLUDE
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -49,7 +50,7 @@ const Main = cmp(async function Main(props: any) {
49
50
  // Copy tm/perl files with replacements
50
51
  Copy({
51
52
  from: 'tm/' + target.name,
52
- exclude: [/src\//],
53
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
53
54
  replace: {
54
55
  ...props.ctx$.stdrep,
55
56
  }
@@ -11,9 +11,13 @@ import {
11
11
  File,
12
12
  cmp,
13
13
  snakify,
14
- isAuthActive, envName, envToken
14
+ isAuthActive, envName, envToken,
15
+ serverVarEnv,
16
+ serverVariables
15
17
  } from '@voxgig/sdkgen'
16
18
 
19
+ import { perlStringLiteral } from './utility_perl'
20
+
17
21
 
18
22
  function normalizePathParams(
19
23
  parts: string[],
@@ -74,12 +78,25 @@ const TestDirect = cmp(function TestDirect(props: any) {
74
78
 
75
79
  const authActive = isAuthActive(model)
76
80
  const apikeyEnvEntry = authActive
77
- ? `\n '${PROJECTNAME}_APIKEY' => 'NONE',`
81
+ ? `\n '${PROJECTNAME}_APIKEY' => '',`
78
82
  : ''
79
83
  const apikeyLiveField = authActive
80
84
  ? `\n 'apikey' => $env->{'${PROJECTNAME}_APIKEY'},`
81
85
  : ''
82
86
 
87
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
88
+ // impossible to construct without values: makeOptions raises rather than
89
+ // request a URL with a literal `{account_id}` in it. So the live suite
90
+ // takes them from the environment the same way it takes the apikey.
91
+ const svars = serverVariables(model)
92
+ const serverEnvEntry = svars
93
+ .map((v: any) => `\n '${serverVarEnv(PROJECTNAME, v.name)}' => ${perlStringLiteral(v.dflt)},`).join('')
94
+ const serverLiveField = 0 === svars.length ? '' : `
95
+ 'server' => {${svars
96
+ .map((v: any) => `
97
+ '${v.name}' => $env->{'${serverVarEnv(PROJECTNAME, v.name)}'},`).join('')}
98
+ },`
99
+
83
100
  const opnames = Object.keys(entity.op || {})
84
101
  const hasLoad = opnames.includes('load')
85
102
  const hasList = opnames.includes('list')
@@ -377,13 +394,17 @@ sub ${entity.name}_direct_setup {
377
394
 
378
395
  my $env = ${N}TestRunner::env_override({
379
396
  '${entidEnvVar}' => {},
380
- '${PROJECTNAME}_TEST_LIVE' => 'FALSE',${apikeyEnvEntry}
397
+ '${PROJECTNAME}_TEST_LIVE' => 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
381
398
  });
382
399
 
383
400
  my $live = ((($env->{'${PROJECTNAME}_TEST_LIVE'}) || '') eq 'TRUE') ? 1 : 0;
384
401
 
385
402
  if ($live) {
386
- my $client = ${N}SDK->new({${apikeyLiveField}
403
+ # live_client_options() FIRST so the generated fields below win:
404
+ # sdk-test-control.json's test.client.options adds to the live client,
405
+ # it does not redirect it (a later key wins in a Perl hash literal).
406
+ my $client = ${N}SDK->new({
407
+ %{ ${N}TestRunner::live_client_options() },${apikeyLiveField}${serverLiveField}
387
408
  });
388
409
  return {
389
410
  'client' => $client,