Skip to content

Commit

Permalink
fix: ucan conclude scheduler invocation type and improve test (#1379)
Browse files Browse the repository at this point in the history
also runs linter
  • Loading branch information
vasco-santos authored Apr 17, 2024
1 parent 149f592 commit 11e0864
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 126 deletions.
4 changes: 2 additions & 2 deletions packages/upload-api/src/types/service.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import type {
UnknownLink,
Receipt,
Invocation,
Result,
Unit,
Failure,
ServiceInvocation,
} from '@ucanto/interface'
import { Storage } from './storage.js'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ReceiptsStorage = Storage<UnknownLink, Receipt<any, any>>
export interface TasksScheduler {
schedule: (invocation: Invocation) => Promise<Result<Unit, Failure>>
schedule: (invocation: ServiceInvocation) => Promise<Result<Unit, Failure>>
}
2 changes: 1 addition & 1 deletion packages/upload-api/src/ucan/conclude.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const ucanConcludeProvider = ({
.invoke({
issuer: id,
audience: id,
with: id.toDIDKey(),
with: id.did(),
nb: {
blob: cap.nb.body,
space: allocateCapability.nb.space,
Expand Down
53 changes: 30 additions & 23 deletions packages/upload-api/test/handlers/ucan.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,12 +430,33 @@ export const test = {
channel: createServer({
...context,
tasksScheduler: {
schedule: (invocation) => {
taskScheduled.resolve(invocation)

return Promise.resolve({
schedule: async (invocation) => {
assert.equal(invocation.capabilities.length, 1)
assert.equal(
invocation.capabilities[0].can,
W3sBlobCapabilities.accept.can
)
assert.equal(
invocation.capabilities[0].nb._put['ucan/await'][0],
'.out.ok'
)
assert.ok(
invocation.capabilities[0].nb._put['ucan/await'][1].equals(
httpPutDelegation.cid
)
)
assert.ok(invocation.capabilities[0].nb.blob)
assert.equal(invocation.capabilities[0].nb.space, spaceDid)
const [blobAcceptRes] = await connection.execute(invocation)

taskScheduled.resolve(blobAcceptRes)
if (blobAcceptRes.out.error) {
return blobAcceptRes.out
}

return {
ok: {},
})
}
},
},
}),
Expand Down Expand Up @@ -527,23 +548,9 @@ export const test = {
}

// verify accept was scheduled
/** @type {import('@ucanto/interface').Invocation<import('@web3-storage/capabilities/types').BlobAccept>} */
const blobAcceptInvocation = await taskScheduled.promise
assert.equal(blobAcceptInvocation.capabilities.length, 1)
assert.equal(
blobAcceptInvocation.capabilities[0].can,
W3sBlobCapabilities.accept.can
)
assert.equal(
blobAcceptInvocation.capabilities[0].nb._put['ucan/await'][0],
'.out.ok'
)
assert.ok(
blobAcceptInvocation.capabilities[0].nb._put['ucan/await'][1].equals(
httpPutDelegation.cid
)
)
assert.ok(blobAcceptInvocation.capabilities[0].nb.blob)
assert.equal(blobAcceptInvocation.capabilities[0].nb.space, spaceDid)
/** @type {import('@ucanto/interface').Receipt<import('@web3-storage/capabilities/types').BlobAcceptSuccess>} */
const blobAcceptReceipt = await taskScheduled.promise
assert.ok(blobAcceptReceipt.out.ok)
assert.ok(blobAcceptReceipt.out.ok?.site)
},
}
191 changes: 91 additions & 100 deletions packages/upload-api/test/handlers/web3.storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -520,104 +520,98 @@ export const test = {
const retryBlobAllocate = await serviceBlobAllocate.execute(connection)
assert.equal(retryBlobAllocate.out.error, undefined)
},
'web3.storage/blob/accept returns site delegation': async (
assert,
context
) => {
const { proof, spaceDid } = await registerSpace(alice, context)

// prepare data
const data = new Uint8Array([11, 22, 34, 44, 55])
const multihash = await sha256.digest(data)
const digest = multihash.bytes
const size = data.byteLength
const content = createLink(
rawCode,
new Digest(sha256.code, 32, digest, digest)
)

// create service connection
const connection = connect({
id: context.id,
channel: createServer(context),
})

// create `blob/add` invocation
const blobAddInvocation = BlobCapabilities.add.invoke({
issuer: alice,
audience: context.id,
with: spaceDid,
nb: {
blob: {
digest,
size,
'web3.storage/blob/accept returns site delegation':
async (assert, context) => {
const { proof, spaceDid } = await registerSpace(alice, context)

// prepare data
const data = new Uint8Array([11, 22, 34, 44, 55])
const multihash = await sha256.digest(data)
const digest = multihash.bytes
const size = data.byteLength
const content = createLink(
rawCode,
new Digest(sha256.code, 32, digest, digest)
)

// create service connection
const connection = connect({
id: context.id,
channel: createServer(context),
})

// create `blob/add` invocation
const blobAddInvocation = BlobCapabilities.add.invoke({
issuer: alice,
audience: context.id,
with: spaceDid,
nb: {
blob: {
digest,
size,
},
},
},
proofs: [proof],
})
const blobAdd = await blobAddInvocation.execute(connection)
if (!blobAdd.out.ok) {
throw new Error('invocation failed', { cause: blobAdd })
}

// parse receipt next
const next = parseBlobAddReceiptNext(blobAdd)

/** @type {import('@web3-storage/capabilities/types').BlobAddress} */
// @ts-expect-error receipt type is unknown
const address = next.allocate.receipt.out.ok.address

// Store the blob to the address
const goodPut = await fetch(address.url, {
method: 'PUT',
mode: 'cors',
body: data,
headers: address.headers,
})
assert.equal(goodPut.status, 200, await goodPut.text())

// invoke `web3.storage/blob/accept`
const serviceBlobAccept = W3sBlobCapabilities.accept.invoke({
issuer: context.id,
audience: context.id,
with: context.id.did(),
nb: {
blob: {
digest,
size,
proofs: [proof],
})
const blobAdd = await blobAddInvocation.execute(connection)
if (!blobAdd.out.ok) {
throw new Error('invocation failed', { cause: blobAdd })
}

// parse receipt next
const next = parseBlobAddReceiptNext(blobAdd)

/** @type {import('@web3-storage/capabilities/types').BlobAddress} */
// @ts-expect-error receipt type is unknown
const address = next.allocate.receipt.out.ok.address

// Store the blob to the address
const goodPut = await fetch(address.url, {
method: 'PUT',
mode: 'cors',
body: data,
headers: address.headers,
})
assert.equal(goodPut.status, 200, await goodPut.text())

// invoke `web3.storage/blob/accept`
const serviceBlobAccept = W3sBlobCapabilities.accept.invoke({
issuer: context.id,
audience: context.id,
with: context.id.did(),
nb: {
blob: {
digest,
size,
},
space: spaceDid,
_put: { 'ucan/await': ['.out.ok', next.put.task.link()] },
},
space: spaceDid,
_put: { 'ucan/await': ['.out.ok', next.put.task.link()] },
},
proofs: [proof],
})
const blobAccept = await serviceBlobAccept.execute(connection)
if (!blobAccept.out.ok) {
throw new Error('invocation failed', { cause: blobAccept })
}
// Validate out
assert.ok(blobAccept.out.ok)
assert.ok(blobAccept.out.ok.site)

// Validate effect
assert.equal(blobAccept.fx.fork.length, 1)
/** @type {import('@ucanto/interface').Delegation} */
// @ts-expect-error delegation not assignable to Effect per TS understanding
const delegation = blobAccept.fx.fork[0]
assert.equal(delegation.capabilities.length, 1)
assert.ok(delegation.capabilities[0].can, Assert.location.can)
// @ts-expect-error nb unknown
assert.ok(delegation.capabilities[0].nb.content.equals(content))
// @ts-expect-error nb unknown
const locations = delegation.capabilities[0].nb.location
assert.equal(locations.length, 1)
assert.ok(
locations[0].includes(
`https://w3s.link/ipfs/${content.toString()}?origin`
)
)
},
'web3.storage/blob/accept fails to provide site delegation when blob was not stored':
proofs: [proof],
})
const blobAccept = await serviceBlobAccept.execute(connection)
if (!blobAccept.out.ok) {
throw new Error('invocation failed', { cause: blobAccept })
}
// Validate out
assert.ok(blobAccept.out.ok)
assert.ok(blobAccept.out.ok.site)

// Validate effect
assert.equal(blobAccept.fx.fork.length, 1)
/** @type {import('@ucanto/interface').Delegation} */
// @ts-expect-error delegation not assignable to Effect per TS understanding
const delegation = blobAccept.fx.fork[0]
assert.equal(delegation.capabilities.length, 1)
assert.ok(delegation.capabilities[0].can, Assert.location.can)
// @ts-expect-error nb unknown
assert.ok(delegation.capabilities[0].nb.content.equals(content))
// @ts-expect-error nb unknown
const locations = delegation.capabilities[0].nb.location
assert.equal(locations.length, 1)
assert.ok(locations[0].includes(`https://w3s.link/ipfs/${content.toString()}?origin`))
},
'web3.storage/blob/accept fails to provide site delegation when blob was not stored':
async (assert, context) => {
const { proof, spaceDid } = await registerSpace(alice, context)

Expand Down Expand Up @@ -672,9 +666,6 @@ export const test = {
const blobAccept = await serviceBlobAccept.execute(connection)
// Validate out error
assert.ok(blobAccept.out.error)
assert.equal(
blobAccept.out.error?.name,
AllocatedMemoryHadNotBeenWrittenToName
)
assert.equal(blobAccept.out.error?.name, AllocatedMemoryHadNotBeenWrittenToName)
},
}

0 comments on commit 11e0864

Please sign in to comment.