@@ -12,7 +12,7 @@ import {
1212 fetch as secretStreamFetch ,
1313 Agent as SecretStreamAgent ,
1414} from 'secret-stream-http'
15- import { describe , it , expect } from 'vitest'
15+ import { describe , it , expect , vi } from 'vitest'
1616
1717import type { MapShareState } from '../src/types.js'
1818import {
@@ -150,7 +150,9 @@ describe('Map Shares and Downloads', () => {
150150
151151 // Create a third device (another receiver)
152152 const receiver2KeyPair = SecretStreamAgent . keyPair ( Buffer . alloc ( 32 , 2 ) )
153- const receiver2DeviceId = Buffer . from ( receiver2KeyPair . publicKey ) . toString ( 'hex' )
153+ const receiver2DeviceId = Buffer . from (
154+ receiver2KeyPair . publicKey ,
155+ ) . toString ( 'hex' )
154156
155157 // Create share for first receiver
156158 const share1 = await createShare ( ) . json ( )
@@ -1104,7 +1106,9 @@ describe('Map Shares and Downloads', () => {
11041106
11051107 // Create a third device with different keys
11061108 const wrongKeyPair = SecretStreamAgent . keyPair ( )
1107- const wrongDeviceId = Buffer . from ( wrongKeyPair . publicKey ) . toString ( 'hex' )
1109+ const wrongDeviceId = Buffer . from ( wrongKeyPair . publicKey ) . toString (
1110+ 'hex' ,
1111+ )
11081112
11091113 // Create a share for a different device
11101114 const { shareId : shareId2 } = await sender
@@ -1134,7 +1138,9 @@ describe('Map Shares and Downloads', () => {
11341138
11351139 // Create a third device with different keys
11361140 const wrongKeyPair = SecretStreamAgent . keyPair ( )
1137- const wrongDeviceId = Buffer . from ( wrongKeyPair . publicKey ) . toString ( 'hex' )
1141+ const wrongDeviceId = Buffer . from ( wrongKeyPair . publicKey ) . toString (
1142+ 'hex' ,
1143+ )
11381144
11391145 // Create a share for wrongDeviceId
11401146 const { shareId } = await sender
@@ -1187,7 +1193,9 @@ describe('Map Shares and Downloads', () => {
11871193
11881194 // Create a third device
11891195 const wrongKeyPair = SecretStreamAgent . keyPair ( )
1190- const wrongDeviceId = Buffer . from ( wrongKeyPair . publicKey ) . toString ( 'hex' )
1196+ const wrongDeviceId = Buffer . from ( wrongKeyPair . publicKey ) . toString (
1197+ 'hex' ,
1198+ )
11911199
11921200 // Create a share for wrongDeviceId
11931201 const { shareId } = await sender
@@ -1638,6 +1646,51 @@ describe('Map Shares and Downloads', () => {
16381646 expect ( tempFiles ) . toHaveLength ( 0 )
16391647 } )
16401648
1649+ it ( 'should report MAP_WRITE_ERROR when write fails during download' , async ( t ) => {
1650+ const { createShare, createDownload, receiver } = await startServers ( t )
1651+ const receiverDir = path . dirname ( receiver . customMapPath )
1652+ const receiverBasename = path . basename ( receiver . customMapPath )
1653+
1654+ // Mock fs.createWriteStream on the receiver to simulate disk-full
1655+ const originalCreateWriteStream = fs . createWriteStream
1656+ const spy = vi
1657+ . spyOn ( fs , 'createWriteStream' )
1658+ . mockImplementation ( ( ...args : any [ ] ) => {
1659+ const stream = originalCreateWriteStream . apply ( fs , args as any )
1660+ stream . _write = (
1661+ _chunk : any ,
1662+ _encoding : BufferEncoding ,
1663+ callback : ( error ?: Error | null ) => void ,
1664+ ) => {
1665+ callback ( new Error ( 'ENOSPC: no space left on device' ) )
1666+ }
1667+ return stream
1668+ } )
1669+ t . onTestFinished ( ( ) => spy . mockRestore ( ) )
1670+
1671+ const share = await createShare ( ) . json ( )
1672+ const download = await createDownload ( share ) . json < any > ( )
1673+
1674+ // Wait for download to fail
1675+ await eventsUntil ( receiver , download . downloadId , 'error' )
1676+
1677+ // Check download state has MAP_WRITE_ERROR
1678+ const downloadStatus = await receiver
1679+ . get ( `downloads/${ download . downloadId } ` )
1680+ . json < any > ( )
1681+ expect ( downloadStatus . status ) . toBe ( 'error' )
1682+ expect ( downloadStatus ) . toHaveProperty ( 'error.code' , 'MAP_WRITE_ERROR' )
1683+
1684+ await delay ( 100 )
1685+
1686+ // Verify temp files are cleaned up
1687+ const files = fs . readdirSync ( receiverDir )
1688+ const tempFiles = files . filter (
1689+ ( f ) => f . startsWith ( receiverBasename ) && f . includes ( '.download-' ) ,
1690+ )
1691+ expect ( tempFiles ) . toHaveLength ( 0 )
1692+ } )
1693+
16411694 it ( 'should try next URL when first mapShareUrl fails during download' , async ( t ) => {
16421695 const { createShare, sender, receiver } = await startServers ( t )
16431696
0 commit comments