@@ -191,6 +191,70 @@ describe("push", () => {
191191 await fs . promises . rm ( stateDir , { recursive : true , force : true } ) ;
192192 } ) ;
193193
194+ it ( "fails a successful push when releasing the lock fails" , async ( ) => {
195+ const stateDir = await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , "b2-state-" ) ) ;
196+ const lockDir = path . join ( stateDir , ".b2-backup-push.lock" ) ;
197+ await fs . promises . writeFile ( path . join ( stateDir , "openclaw.json" ) , "{}" ) ;
198+ const config : ResolvedB2BackupConfig = {
199+ keyId : "test-key" ,
200+ applicationKey : "test-secret" ,
201+ bucket : "test-bucket" ,
202+ region : "test-region" ,
203+ encrypt : false ,
204+ } ;
205+ const b2 = mockB2 ( ) ;
206+ const logger = { info : vi . fn ( ) , warn : vi . fn ( ) , debug : vi . fn ( ) } ;
207+ const rm = fs . promises . rm . bind ( fs . promises ) ;
208+ const rmSpy = vi . spyOn ( fs . promises , "rm" ) . mockImplementation ( async ( target , options ) => {
209+ if ( path . resolve ( String ( target ) ) === path . resolve ( lockDir ) ) {
210+ throw new Error ( "release failed" ) ;
211+ }
212+ return rm ( target , options ) ;
213+ } ) ;
214+
215+ await expect ( push ( config , stateDir , b2 , logger ) ) . rejects . toThrow ( "release failed" ) ;
216+ expect ( logger . warn ) . toHaveBeenCalledWith (
217+ expect . stringContaining ( "failed to release push lock" ) ,
218+ ) ;
219+
220+ rmSpy . mockRestore ( ) ;
221+ await fs . promises . rm ( stateDir , { recursive : true , force : true } ) ;
222+ } ) ;
223+
224+ it ( "preserves the push error when lock release also fails" , async ( ) => {
225+ const stateDir = await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , "b2-state-" ) ) ;
226+ const lockDir = path . join ( stateDir , ".b2-backup-push.lock" ) ;
227+ await fs . promises . writeFile ( path . join ( stateDir , "openclaw.json" ) , "{}" ) ;
228+ const config : ResolvedB2BackupConfig = {
229+ keyId : "test-key" ,
230+ applicationKey : "test-secret" ,
231+ bucket : "test-bucket" ,
232+ region : "test-region" ,
233+ encrypt : false ,
234+ } ;
235+ const b2 = mockB2 ( {
236+ putObject : vi . fn ( async ( ) => {
237+ throw new Error ( "upload failed" ) ;
238+ } ) ,
239+ } ) ;
240+ const logger = { info : vi . fn ( ) , warn : vi . fn ( ) , debug : vi . fn ( ) } ;
241+ const rm = fs . promises . rm . bind ( fs . promises ) ;
242+ const rmSpy = vi . spyOn ( fs . promises , "rm" ) . mockImplementation ( async ( target , options ) => {
243+ if ( path . resolve ( String ( target ) ) === path . resolve ( lockDir ) ) {
244+ throw new Error ( "release failed" ) ;
245+ }
246+ return rm ( target , options ) ;
247+ } ) ;
248+
249+ await expect ( push ( config , stateDir , b2 , logger ) ) . rejects . toThrow ( "upload failed" ) ;
250+ expect ( logger . warn ) . toHaveBeenCalledWith (
251+ expect . stringContaining ( "failed to release push lock" ) ,
252+ ) ;
253+
254+ rmSpy . mockRestore ( ) ;
255+ await fs . promises . rm ( stateDir , { recursive : true , force : true } ) ;
256+ } ) ;
257+
194258 it ( "adds a random suffix to snapshot IDs" , ( ) => {
195259 const snapshotId = createSnapshotId ( "2026-02-19T12:00:00.000Z" ) ;
196260
0 commit comments