Skip to content

Commit

Permalink
chore: add a test to cover failing to get a receipt
Browse files Browse the repository at this point in the history
  • Loading branch information
joaosa committed Jun 3, 2024
1 parent 5e7c5c0 commit d0dc1ea
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 22 deletions.
1 change: 1 addition & 0 deletions packages/upload-client/src/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ export async function add(
)

// @ts-ignore Property
/* c8 ignore next 5 */
if (!acceptReceipt?.out.ok?.site) {
throw new Error(`failed ${BlobCapabilities.add.can} invocation`, {
cause: 'failed to get blob/accept receipt',
Expand Down
30 changes: 24 additions & 6 deletions packages/upload-client/src/receipts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ import { CAR } from '@ucanto/transport'
import { receiptsEndpoint } from './service.js'
import { REQUEST_RETRIES } from './constants.js'

export class ReceiptNotFound extends Error {
/**
* @param {import('multiformats').UnknownLink} taskCid
*/
constructor(taskCid) {
super()
this.taskCid = taskCid
}

/* c8 ignore start */
get reason() {
return `receipt not found for task ${this.taskCid}`
}
/* c8 ignore end */

get name() {
return 'ReceiptNotFound'
}
}

export class Receipt {
/**
* @param {import('./types.js').RequestOptions} [options]
Expand Down Expand Up @@ -60,9 +80,7 @@ export class Receipt {
/* c8 ignore start */
if (!workflowResponse.ok) {
return {
error: new Error(
`no receipt available for requested task ${taskCid.toString()}`
),
error: new ReceiptNotFound(taskCid),
}
}
/* c8 ignore stop */
Expand All @@ -78,10 +96,10 @@ export class Receipt {
// Get receipt from the potential multiple receipts in the message
const receipt = agentMessage.receipts.get(taskCid.toString())
if (!receipt) {
const error = new Error()
error.name = 'ReceiptNotFound'
return {
error,
error: new Error(
`no receipt available for requested task ${taskCid.toString()}`
),
}
}
return {
Expand Down
65 changes: 63 additions & 2 deletions packages/upload-client/test/blob.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ describe('Blob.add', () => {
)
})

it('throws when it cannot get the blob/accept receipt', async () => {
it('throws when it cannot get blob/accept receipt', async () => {
const space = await Signer.generate()
const agent = await Signer.generate()
const bytes = await randomBytes(128)
Expand Down Expand Up @@ -232,7 +232,7 @@ describe('Blob.add', () => {
{
connection,
retries: 0,
receiptsEndpoint: 'http://localhost:9201/unavailable/',
receiptsEndpoint: 'http://localhost:9201/failed/',
}
),
{
Expand All @@ -241,6 +241,67 @@ describe('Blob.add', () => {
)
})

it('throws when the blob/accept receipt is not yet available', async () => {
const space = await Signer.generate()
const agent = await Signer.generate()
const bytes = await randomBytes(128)

const proofs = [
await BlobCapabilities.add.delegate({
issuer: space,
audience: agent,
with: space.did(),
expiration: Infinity,
}),
]

const service = mockService({
ucan: {
conclude: provide(UCAN.conclude, () => {
return { ok: { time: Date.now() } }
}),
},
space: {
blob: {
// @ts-ignore Argument of type
add: provide(BlobCapabilities.add, ({ invocation }) => {
return setupBlobAddSuccessResponse(
{ issuer: space, audience: agent, with: space, proofs },
invocation
)
}),
},
},
})

const server = Server.create({
id: serviceSigner,
service,
codec: CAR.inbound,
validateAuthorization,
})
const connection = Client.connect({
id: serviceSigner,
codec: CAR.outbound,
channel: server,
})

await assert.rejects(
Blob.add(
{ issuer: agent, with: space.did(), proofs, audience: serviceSigner },
bytes,
{
connection,
retries: 0,
receiptsEndpoint: 'http://localhost:9201/unavailable/',
}
),
{
message: 'blob/accept receipt not yet available',
}
)
})

it('throws for bucket URL client error 4xx', async () => {
const space = await Signer.generate()
const agent = await Signer.generate()
Expand Down
39 changes: 25 additions & 14 deletions packages/upload-client/test/helpers/receipts-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,10 @@ import { randomCAR } from './random.js'

const port = process.env.PORT ?? 9201

const server = createServer(async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', '*')
res.setHeader('Access-Control-Allow-Headers', '*')

const taskID = req.url?.split('/')[1] ?? ''
if (taskID === 'unavailable') {
res.writeHead(404)
return res.end()
}

/**
* @param {string} taskCid
*/
const generateReceipt = async (taskCid) => {
const issuer = await Signer.generate()
const content = (await randomCAR(128)).cid
const locationClaim = await Assert.location.delegate({
Expand All @@ -37,7 +30,7 @@ const server = createServer(async (req, res) => {
fx: {
fork: [locationClaim],
},
ran: parseLink(taskID),
ran: parseLink(taskCid),
result: {
ok: {
site: locationClaim.link(),
Expand All @@ -48,9 +41,27 @@ const server = createServer(async (req, res) => {
const message = await Message.build({
receipts: [receipt],
})
const request = CAR.request.encode(message)
return CAR.request.encode(message).body
}

const server = createServer(async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', '*')
res.setHeader('Access-Control-Allow-Headers', '*')

const taskCid = req.url?.split('/')[1] ?? ''
if (taskCid === 'unavailable') {
res.writeHead(404)
return res.end()
} else if (taskCid === 'failed') {
const body = await generateReceipt((await randomCAR(128)).cid.toString())
res.writeHead(200)
return res.end(body)
}

const body = await generateReceipt(taskCid)
res.writeHead(200)
res.end(request.body)
res.end(body)
})

server.listen(port, () => console.log(`Listening on :${port}`))
Expand Down
Binary file removed packages/w3up-client/test/fixtures/workflow.car
Binary file not shown.

0 comments on commit d0dc1ea

Please sign in to comment.