()
| 121 | } |
| 122 | |
| 123 | async function main() { |
| 124 | process.stderr.write("booting computerd container ...\n"); |
| 125 | const container = await bootContainer(); |
| 126 | process.stderr.write(` ${container.url} (${container.cid.slice(0, 12)})\n`); |
| 127 | |
| 128 | let failed = false; |
| 129 | try { |
| 130 | // 1. Confirm FUSE actually mounted; the rest of the test is |
| 131 | // meaningless against a fallback to the container's root fs. |
| 132 | const { stdout: mount } = await dockerExec(container.cid, "mount"); |
| 133 | if (!/fuse on \/workspace/.test(mount)) { |
| 134 | throw new Error(`FUSE not mounted in container:\n${mount}`); |
| 135 | } |
| 136 | process.stderr.write(" FUSE mounted on /workspace\n"); |
| 137 | |
| 138 | // 2. Write through FUSE. echo > triggers FUSE create, write, |
| 139 | // flush (on close), release \u2014 every spill point landed in |
| 140 | // the 68407fc fix. |
| 141 | const payload = `from-fuse ${Date.now()}\n`; |
| 142 | await dockerExec(container.cid, "bash", "-c", `printf '%s' '${payload}' > /workspace/x.txt`); |
| 143 | process.stderr.write(` wrote ${payload.trim()} to /workspace/x.txt via FUSE\n`); |
| 144 | |
| 145 | // 3. Pull from the container's WebSocket on the host. Mirrors |
| 146 | // what the host DO does after exec returns. |
| 147 | const { createWorkspaceClient } = await import( |
| 148 | `${REPO_ROOT}/node_modules/@cloudflare/computer-rpc/dist/client.js` |
| 149 | ); |
| 150 | const { Database, SQLiteWorkspaceProvider, initializeSchema } = await import( |
| 151 | `${REPO_ROOT}/node_modules/@cloudflare/dofs/dist/index.js` |
| 152 | ); |
| 153 | const { SQLiteTestStorage } = await import( |
| 154 | `${REPO_ROOT}/node_modules/@cloudflare/dofs/dist/testing.js` |
| 155 | ); |
| 156 | const { pullOnce } = await import( |
| 157 | `${REPO_ROOT}/node_modules/@cloudflare/computer-rpc/dist/sync-driver.js` |
| 158 | ); |
| 159 | |
| 160 | const wsUrl = `${container.url.replace("http://", "ws://")}/ws`; |
| 161 | const client = createWorkspaceClient({ url: wsUrl }); |
| 162 | |
| 163 | const recvStorage = new SQLiteTestStorage(); |
| 164 | const recvDb = new Database(recvStorage); |
| 165 | initializeSchema(recvDb, () => Date.now()); |
| 166 | |
| 167 | try { |
| 168 | const applied = await pullOnce(recvDb, client.sync); |
| 169 | process.stderr.write(` pullOnce applied ${applied} entries\n`); |
| 170 | if (applied === 0) { |
| 171 | throw new Error("pullOnce applied 0 entries; expected at least 1 for /x.txt"); |
| 172 | } |
| 173 | |
| 174 | // 4. Read the file back through the receiver-side provider. |
| 175 | // The bytes have to match what we wrote through FUSE. |
| 176 | const provider = new SQLiteWorkspaceProvider(recvDb, { now: () => Date.now() }); |
| 177 | const back = provider.readFileSync("/x.txt", "utf8"); |
| 178 | if (back !== payload) { |
| 179 | throw new Error( |
| 180 | `byte mismatch:\n wrote: ${JSON.stringify(payload)}\n read: ${JSON.stringify(back)}`, |
no test coverage detected