@@ -12,6 +12,7 @@ import {
1212 type ProvisioningPagerDutyAlert ,
1313 type Tenant ,
1414 type TenantProvisioningDriver ,
15+ type TenantProvisioningRequest ,
1516} from "../dist/index.js" ;
1617
1718/** A driver where exactly one named step throws `error`; every other step is a no-op success. */
@@ -153,3 +154,122 @@ test("deprovisionTenant defaults to the real notifyProvisioningFailure + process
153154
154155 await assert . rejects ( deprovisionTenant ( tenant , "ams" , driver ) , / d b d r o p f a i l e d / ) ;
155156} ) ;
157+
158+ // #8202: injectSecrets moved ahead of createContainer, so secretRef can now be minted and THEN orphaned if
159+ // createContainer fails right after -- provisionTenant always rethrows rather than returning, so no caller ever
160+ // gets secretRef to persist and revoke later otherwise. These prove the fix: a best-effort self-revoke, safe
161+ // even when that revoke itself fails, and correctly scoped to only fire once a real secretRef actually exists.
162+
163+ test ( "#8202: provisionTenant best-effort revokes the just-injected secret when createContainer fails right after, before rethrowing" , async ( ) => {
164+ const revokeCalls : TenantProvisioningRequest [ ] = [ ] ;
165+ const driver : TenantProvisioningDriver = {
166+ ...createFakeTenantProvisioningDriver ( ) ,
167+ injectSecrets : async ( ) => ( { secretRef : "orbenr_abc" , bootstrapSecret : "orbsec_xyz" } ) ,
168+ createContainer : async ( ) => {
169+ throw new Error ( "container quota exceeded" ) ;
170+ } ,
171+ revokeSecrets : async ( request ) => {
172+ revokeCalls . push ( request ) ;
173+ } ,
174+ } ;
175+ const tenant : Tenant = { name : "acme" } ;
176+
177+ await assert . rejects ( provisionTenant ( tenant , "orb" , driver ) , / c o n t a i n e r q u o t a e x c e e d e d / ) ;
178+
179+ assert . equal ( revokeCalls . length , 1 ) ;
180+ assert . equal ( revokeCalls [ 0 ] ?. secretRef , "orbenr_abc" ) ;
181+ } ) ;
182+
183+ test ( "#8202: a failure in the best-effort revoke itself does not mask the real createContainer error" , async ( ) => {
184+ const driver : TenantProvisioningDriver = {
185+ ...createFakeTenantProvisioningDriver ( ) ,
186+ injectSecrets : async ( ) => ( { secretRef : "orbenr_abc" } ) ,
187+ createContainer : async ( ) => {
188+ throw new Error ( "container quota exceeded" ) ;
189+ } ,
190+ revokeSecrets : async ( ) => {
191+ throw new Error ( "broker unreachable" ) ;
192+ } ,
193+ } ;
194+ const tenant : Tenant = { name : "acme" } ;
195+
196+ await assert . rejects ( provisionTenant ( tenant , "orb" , driver ) , / c o n t a i n e r q u o t a e x c e e d e d / ) ;
197+ } ) ;
198+
199+ test ( "#8202: provisionTenant does NOT attempt a revoke when no secretRef was ever obtained (e.g. provisionDatabase itself failed)" , async ( ) => {
200+ const revokeCalls : TenantProvisioningRequest [ ] = [ ] ;
201+ const driver : TenantProvisioningDriver = {
202+ ...driverThatThrowsOn ( "provisionDatabase" , new Error ( "db provisioning failed" ) ) ,
203+ revokeSecrets : async ( request ) => {
204+ revokeCalls . push ( request ) ;
205+ } ,
206+ } ;
207+ const tenant : Tenant = { name : "acme" } ;
208+
209+ await assert . rejects ( provisionTenant ( tenant , "orb" , driver ) , / d b p r o v i s i o n i n g f a i l e d / ) ;
210+
211+ assert . equal ( revokeCalls . length , 0 ) ;
212+ } ) ;
213+
214+ test ( "#8202: provisionTenant does NOT attempt a revoke when injectSecrets itself is the step that failed" , async ( ) => {
215+ const revokeCalls : TenantProvisioningRequest [ ] = [ ] ;
216+ const driver : TenantProvisioningDriver = {
217+ ...driverThatThrowsOn ( "injectSecrets" , new Error ( "secret injection failed" ) ) ,
218+ revokeSecrets : async ( request ) => {
219+ revokeCalls . push ( request ) ;
220+ } ,
221+ } ;
222+ const tenant : Tenant = { name : "acme" } ;
223+
224+ await assert . rejects ( provisionTenant ( tenant , "orb" , driver ) , / s e c r e t i n j e c t i o n f a i l e d / ) ;
225+
226+ assert . equal ( revokeCalls . length , 0 ) ;
227+ } ) ;
228+
229+ test ( "#8202: the PagerDuty alert carries secretRef when injectSecrets had already succeeded before the failing step" , async ( ) => {
230+ const calls : ProvisioningPagerDutyAlert [ ] = [ ] ;
231+ const notify : NotifyProvisioningFailure = async ( alert ) => {
232+ calls . push ( alert ) ;
233+ } ;
234+ const driver : TenantProvisioningDriver = {
235+ ...createFakeTenantProvisioningDriver ( ) ,
236+ injectSecrets : async ( ) => ( { secretRef : "orbenr_abc" } ) ,
237+ createContainer : async ( ) => {
238+ throw new Error ( "container quota exceeded" ) ;
239+ } ,
240+ } ;
241+ const tenant : Tenant = { name : "acme" } ;
242+
243+ await assert . rejects ( provisionTenant ( tenant , "orb" , driver , { notify } ) , / c o n t a i n e r q u o t a e x c e e d e d / ) ;
244+ await Promise . resolve ( ) ;
245+
246+ assert . equal ( calls [ 0 ] ?. customDetails . secretRef , "orbenr_abc" ) ;
247+ } ) ;
248+
249+ test ( "#8202: the PagerDuty alert omits secretRef entirely when none was ever obtained" , async ( ) => {
250+ const calls : ProvisioningPagerDutyAlert [ ] = [ ] ;
251+ const notify : NotifyProvisioningFailure = async ( alert ) => {
252+ calls . push ( alert ) ;
253+ } ;
254+ const driver = driverThatThrowsOn ( "provisionDatabase" , new Error ( "db provisioning failed" ) ) ;
255+ const tenant : Tenant = { name : "acme" } ;
256+
257+ await assert . rejects ( provisionTenant ( tenant , "orb" , driver , { notify } ) , / d b p r o v i s i o n i n g f a i l e d / ) ;
258+ await Promise . resolve ( ) ;
259+
260+ assert . equal ( "secretRef" in ( calls [ 0 ] ?. customDetails ?? { } ) , false ) ;
261+ } ) ;
262+
263+ test ( "#8202: deprovisionTenant's PagerDuty alert carries the secretRef it was given, for operator convenience" , async ( ) => {
264+ const calls : ProvisioningPagerDutyAlert [ ] = [ ] ;
265+ const notify : NotifyProvisioningFailure = async ( alert ) => {
266+ calls . push ( alert ) ;
267+ } ;
268+ const driver = driverThatThrowsOn ( "dropDatabase" , new Error ( "db drop failed" ) ) ;
269+ const tenant : Tenant = { name : "acme" } ;
270+
271+ await assert . rejects ( deprovisionTenant ( tenant , "ams" , driver , { notify } , "orbenr_abc" ) , / d b d r o p f a i l e d / ) ;
272+ await Promise . resolve ( ) ;
273+
274+ assert . equal ( calls [ 0 ] ?. customDetails . secretRef , "orbenr_abc" ) ;
275+ } ) ;
0 commit comments