@voxgig/create-sdkgen 0.19.0 → 0.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/create-sdkgen",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "main": "dist/create-sdkgen.js",
5
5
  "type": "commonjs",
6
6
  "types": "dist/create-sdkgen.d.ts",
@@ -0,0 +1,190 @@
1
+ # Cost feature behaviour, as language-neutral cases.
2
+ #
3
+ # Every case is DATA: features are activated by name through the generated
4
+ # config, the options are plain JSON, the transport is scripted by `res`, and
5
+ # the assertion is a subset of the client's cost record. Nothing here needs a
6
+ # function, which is what makes it corpus-expressible where featureAdd /
7
+ # featureHook / featureInit are not (see their PENDING notes).
8
+ #
9
+ # CASE SHAPE
10
+ # name label for the runner
11
+ # feature activation list, IN ORDER. The array form is the one that fixes
12
+ # nesting: each transport feature wraps whatever is already
13
+ # installed, so position IS order. Entries are flat -
14
+ # { name, active, ...options } - as makeOptions expects.
15
+ # res scripted transport responses, consumed in order; the last is
16
+ # reused once exhausted. `throw: true` makes that attempt reject
17
+ # at the transport rather than return a response.
18
+ # op operations to run, in order. `err` marks an op that must FAIL:
19
+ # `true` for any failure, a string to pin the error code.
20
+ # out expected SUBSET of client._cost - runners match partially, so a
21
+ # case asserts only what it is about
22
+ #
23
+ # OPERATION TOKENS
24
+ # The corpus is shared by every generated SDK, and each has its own
25
+ # entities and operations, so no case may name one. `#OP1` and `#OP2` stand
26
+ # for two DISTINCT operations this SDK can actually perform; the runner
27
+ # discovers them from the generated client and substitutes them throughout
28
+ # the case, keys included. They resolve to the '<entity>.<op>' key the cost
29
+ # record attributes spend under, which is why a rate table can be keyed on
30
+ # one. A SDK that offers fewer operations than a case needs skips that case
31
+ # rather than failing it.
32
+ #
33
+ # Determinism comes from data alone: netsim is counter-driven, and retry runs
34
+ # with `minDelay: 1`, which leaves no room for jitter (it is drawn from
35
+ # [0, minDelay)) and costs no real time.
36
+
37
+ basic: set: [
38
+
39
+ # --- pricing sources ----------------------------------------------------
40
+
41
+ {
42
+ name: 'rate table prices each call and attributes it to the op'
43
+ feature: [ { name: 'cost', active: true, rates: { '#OP1': 0.25, '*': 0.01 } } ]
44
+ op: [ { op: '#OP1' }, { op: '#OP1' }, { op: '#OP2' } ]
45
+ out: {
46
+ total: { calls: 3, amount: 0.51, estimated: 0.51 }
47
+ ops: { '#OP1': { calls: 2, amount: 0.5 }, '#OP2': { calls: 1, amount: 0.01 } }
48
+ }
49
+ }
50
+
51
+ {
52
+ name: 'flat unit is the fallback when no rate matches'
53
+ feature: [ { name: 'cost', active: true, unit: 0.002 } ]
54
+ op: [ { op: '#OP1' } ]
55
+ out: { total: { calls: 1, amount: 0.002 }, last: { source: 'unit' } }
56
+ }
57
+
58
+ {
59
+ name: 'a reported header counts as reported, not estimated, and beats the unit'
60
+ feature: [ { name: 'cost', active: true, header: 'x-request-cost', perUnit: 0.5, unit: 99 } ]
61
+ res: [ { status: 200, headers: { 'x-request-cost': '4' }, body: { ok: true } } ]
62
+ op: [ { op: '#OP1' } ]
63
+ out: { total: { amount: 2, reported: 2, estimated: 0 }, last: { source: 'header' } }
64
+ }
65
+
66
+ {
67
+ name: 'a plain header map is matched case-insensitively'
68
+ feature: [ { name: 'cost', active: true, header: 'x-request-cost', perUnit: 0.5, unit: 99 } ]
69
+ res: [ { status: 200, headers: { 'X-Request-Cost': '4' }, body: { ok: true } } ]
70
+ out: { total: { amount: 2, reported: 2 }, last: { source: 'header' } }
71
+ op: [ { op: '#OP1' } ]
72
+ }
73
+
74
+ {
75
+ name: 'a body usage figure prices the whole call, replacing the per-attempt estimate'
76
+ feature: [ { name: 'cost', active: true, path: 'usage.total_tokens', perUnit: 0.00001, unit: 5 } ]
77
+ res: [ { status: 200, body: { usage: { total_tokens: 1000 } } } ]
78
+ op: [ { op: '#OP1' } ]
79
+ out: { total: { amount: 0.01, reported: 0.01, estimated: 0 }, last: { source: 'body' } }
80
+ }
81
+
82
+ # --- attempts vs operations ---------------------------------------------
83
+
84
+ {
85
+ name: 'every retry attempt is charged, but attributed as one operation'
86
+ feature: [
87
+ { name: 'netsim', active: true, failTimes: 2, failStatus: 503 }
88
+ { name: 'cost', active: true, unit: 1 }
89
+ { name: 'retry', active: true, retries: 3, minDelay: 1 }
90
+ ]
91
+ op: [ { op: '#OP1' } ]
92
+ out: { total: { calls: 1, attempts: 3, amount: 3 }, last: { attempts: 3 } }
93
+ }
94
+
95
+ {
96
+ name: 'reported and estimated stay apart across attempts of one call'
97
+ feature: [
98
+ { name: 'netsim', active: true, failTimes: 1, failStatus: 503 }
99
+ { name: 'cost', active: true, header: 'x-cost', perUnit: 1, rates: { '*': 5 } }
100
+ { name: 'retry', active: true, retries: 2, minDelay: 1 }
101
+ ]
102
+ res: [ { status: 200, headers: { 'x-cost': '2' }, body: { ok: true } } ]
103
+ op: [ { op: '#OP1' } ]
104
+ out: { total: { amount: 7, estimated: 5, reported: 2 } }
105
+ }
106
+
107
+ {
108
+ name: 'a rejecting transport is still charged per attempt'
109
+ feature: [
110
+ { name: 'cost', active: true, unit: 1 }
111
+ { name: 'retry', active: true, retries: 2, minDelay: 1 }
112
+ ]
113
+ res: [ { throw: true } ]
114
+ op: [ { op: '#OP1', err: true } ]
115
+ out: { total: { calls: 1, attempts: 3, amount: 3 } }
116
+ }
117
+
118
+ {
119
+ name: 'a failed operation still commits its spend'
120
+ feature: [ { name: 'cost', active: true, unit: 2 } ]
121
+ res: [ { status: 500, body: { fail: true } } ]
122
+ op: [ { op: '#OP1', err: true } ]
123
+ out: { total: { calls: 1, amount: 2 } }
124
+ }
125
+
126
+ # --- ordering against the cache -----------------------------------------
127
+
128
+ {
129
+ name: 'ordered inside the cache, a hit served from cache costs nothing'
130
+ feature: [
131
+ { name: 'cost', active: true, unit: 2 }
132
+ { name: 'cache', active: true, ttl: 10000 }
133
+ ]
134
+ op: [ { op: '#OP1' }, { op: '#OP1' } ]
135
+ out: { total: { calls: 2, attempts: 1, amount: 2 } }
136
+ }
137
+
138
+ # --- attribution ---------------------------------------------------------
139
+
140
+ {
141
+ name: 'spend is attributed to the per-call actor'
142
+ feature: [ { name: 'cost', active: true, unit: 1 } ]
143
+ op: [
144
+ { op: '#OP1', ctrl: { actor: 'user:42' } }
145
+ { op: '#OP1', ctrl: { actor: 'user:42' } }
146
+ { op: '#OP1', ctrl: { actor: 'svc:sync' } }
147
+ ]
148
+ out: { actors: { 'user:42': { calls: 2, amount: 2 }, 'svc:sync': { calls: 1, amount: 1 } } }
149
+ }
150
+
151
+ # --- the budget ----------------------------------------------------------
152
+
153
+ {
154
+ name: 'warn records the overrun and lets the call through'
155
+ feature: [ { name: 'cost', active: true, unit: 1, budget: 2 } ]
156
+ op: [ { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1' } ]
157
+ out: { total: { calls: 3, amount: 3 }, budget: { exceeded: true, remaining: 0 } }
158
+ }
159
+
160
+ {
161
+ name: 'deny refuses the call once the budget is spent, before the network'
162
+ feature: [ { name: 'cost', active: true, unit: 1, budget: 2, onBudget: 'deny' } ]
163
+ op: [ { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1', err: 'cost_budget' } ]
164
+ out: { total: { calls: 2, attempts: 2, amount: 2 }, budget: { exceeded: true } }
165
+ }
166
+
167
+ {
168
+ name: 'an explicit budget of zero means no ceiling'
169
+ feature: [ { name: 'cost', active: true, unit: 1, budget: 0, onBudget: 'deny' } ]
170
+ op: [ { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1' } ]
171
+ out: { total: { calls: 5, amount: 5 } }
172
+ }
173
+
174
+ # The case above supplies `budget: 0`; this one omits it. They are separate
175
+ # cases on purpose. An implementation that reads the option into a
176
+ # nullable, or treats "supplied" as "set a ceiling", passes one and fails
177
+ # the other, and a single case cannot tell which path it took.
178
+ {
179
+ name: 'an omitted budget means no ceiling either'
180
+ feature: [ { name: 'cost', active: true, unit: 1, onBudget: 'deny' } ]
181
+ op: [ { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1' }, { op: '#OP1' } ]
182
+ out: { total: { calls: 5, amount: 5 } }
183
+ }
184
+ ]
185
+
186
+ # NOT EXPRESSIBLE HERE, and deliberately left to the per-language suites.
187
+ # `partial`, not `basic: pending`: the section RUNS, so the deferral marker
188
+ # that means "this whole section is a hole" would be a lie here (and the
189
+ # corpus guard promotes any deferred section that has gained cases).
190
+ partial: 'the sink callback takes a function, which a JSON fixture cannot carry; covered per-language.'
@@ -0,0 +1,2 @@
1
+ # Feature behaviour cases, one section per feature.
2
+ feature: cost: @"cost.aon"
@@ -1,3 +1,4 @@
1
1
 
2
2
  @"struct/test.aon"
3
3
  @"primary/primary-test-index.aon"
4
+ @"feature/feature-test-index.aon"