Skip to content

Commit

Permalink
feat: 100% test coverage #51
Browse files Browse the repository at this point in the history
  • Loading branch information
Gozala authored Mar 31, 2021
2 parents cb4ba43 + bff3fc7 commit eacf295
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 61 deletions.
14 changes: 11 additions & 3 deletions .github/workflows/client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ on:
branches:
- main
paths:
- "client/**"
- 'client/**'
pull_request:
branches:
- main
paths:
- "client/**"
- 'client/**'

jobs:
check:
Expand Down Expand Up @@ -59,6 +59,14 @@ jobs:
- name: Test (Web)
run: yarn --cwd client test:web

- name: Test (CJS)
run: |
yarn --cwd client build:cjs
yarn --cwd client test:cjs
- name: Coverage
run: yarn --cwd client coverage

publish:
name: Publish client
needs:
Expand Down Expand Up @@ -115,4 +123,4 @@ jobs:
with:
author_name: documentation-generator
add: docs/client
message: "chore: generate client docs"
message: 'chore: generate client docs'
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ node_modules

# testing
coverage
.nyc_output

# next.js
.next
Expand Down Expand Up @@ -32,4 +33,4 @@ node_modules
worker
yarn.lock
package-lock.json
site/public
site/public
3 changes: 3 additions & 0 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![styled with prettier][prettier.icon]][prettier.url]
[![size][size.icon]][size.url]
[![deps][deps.icon]][deps.url]
[![codecov][cov.icon]][cov.url]

A client library for the https://nft.storage/ service. It provides a convenient interface for working with the [Raw HTTP API][] from a web browser or [Node.js][] and comes bundled with TS for out-of-the box type inference and better IntelliSense.

Expand Down Expand Up @@ -46,3 +47,5 @@ For more examples please see the [API documentation][].
[size.url]: https://bundlephobia.com/result?p=nft.storage
[deps.icon]: https://status.david-dm.org/gh/ipfs-shipyard/nft.storage.svg?path=client
[deps.url]: https://david-dm.org/ipfs-shipyard/nft.storage?path=client
[cov.icon]: https://codecov.io/gh/ipfs-shipyard/nft.storage/branch/main/graph/badge.svg?token=dU5oWrlqHF
[cov.url]: https://codecov.io/gh/ipfs-shipyard/nft.storage
15 changes: 9 additions & 6 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
"./src/platform.js": "./src/platform.web.js"
},
"scripts": {
"test:web": "node test/test.js playwright-test test/*.spec.js",
"test:es": "node test/test.js mocha test/*.spec.js --exit",
"test:web": "node test/test.js playwright-test test/*.spec.js --cov && nyc report",
"test:es": "node test/test.js hundreds mocha test/*.spec.js --exit",
"test:cjs": "node test/test.js mocha dist/test/*.spec.cjs --exit",
"build": "npm run build:ts && npm run build:cjs",
"build:ts": "tsc --build",
"build:cjs": "rollup --config rollup.config.js",
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && npx codecov",
"typedoc": "typedoc --entryPoints src --out ../docs/client",
"prepare": "npm run build"
},
Expand All @@ -40,20 +41,22 @@
},
"devDependencies": {
"@ssttevee/multipart-parser": "0.1.8",
"uvu": "0.5.1",
"mocha": "8.3.2",
"@types/mocha": "8.2.1",
"hundreds": "0.0.9",
"ipfs-unixfs-importer": "6.0.1",
"ipld": "0.29.0",
"ipld-dag-pb": "0.22.0",
"ipld-in-memory": "8.0.0",
"mocha": "8.3.2",
"multicodec": "3.0.1",
"multiformats": "4.5.3",
"multihashing-async": "2.1.2",
"nyc": "15.1.0",
"playwright-test": "2.1.0",
"typedoc": "0.20.32",
"rollup": "2.22.1",
"rollup-plugin-multi-input": "1.1.1"
"rollup-plugin-multi-input": "1.1.1",
"typedoc": "0.20.32",
"uvu": "0.5.1"
},
"homepage": "https://github.com/ipfs-shipyard/nft.storage/tree/main/client",
"bugs": "https://github.com/ipfs-shipyard/nft.storage/issues"
Expand Down
35 changes: 35 additions & 0 deletions client/test/lib.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ describe('client', () => {
assert.equal(cid1, cid2, 'cids match')
assert.equal(status1.created, status2.created, 'dates match')
})

it('errors without token', async () => {
const client = new NFTStorage({ token: 'wrong', endpoint })
const blob = new Blob(['upload twice'])

try {
await client.storeBlob(blob)
assert.unreachable('sholud have failed')
} catch (error) {
assert.ok(error instanceof Error)
assert.match(error, /Unauthorized/)
}
})
})

describe('upload dir', () => {
Expand All @@ -63,6 +76,17 @@ describe('client', () => {

assert.equal(cid, 'QmQAE2tjfwYYmEFFEEnfr12CWikMqgwwtq5gqfyb62bJpw')
})

it('upload nothing', async () => {
const client = new NFTStorage({ token, endpoint })
try {
await client.storeDirectory([])
assert.unreachable('should fail if no content is provided')
} catch (error) {
assert.ok(error instanceof Error)
assert.match(error, /no files/i)
}
})
})

describe('status', () => {
Expand Down Expand Up @@ -120,5 +144,16 @@ describe('client', () => {
assert.ok(error.message.includes('not found'))
}
})

it('invalid cid errors', async () => {
const client = new NFTStorage({ token, endpoint })
try {
await client.delete('foo')
assert.unreachable('invalid cid')
} catch (error) {
assert.ok(error instanceof Error)
assert.match(error, /parse non base32/)
}
})
})
})
117 changes: 66 additions & 51 deletions client/test/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const importUpload = async (request) => {
if (contentType.includes('multipart/form-data')) {
const data = await request.formData()
const files = /** @type {File[]} */ (data.getAll('file'))
if (files.length === 0) {
throw Error('No files were provided')
}
return await importDirectory(files)
} else {
const content = await request.arrayBuffer()
Expand Down Expand Up @@ -67,65 +70,77 @@ export const handle = async (request, { store, AUTH_TOKEN }) => {
}
)
}

switch (`${request.method} /${api}/${param}`) {
case 'POST /api/upload': {
const { cid } = await importUpload(request)
const key = `${token}:${cid}`
if (!store.get(key)) {
const created = new Date()
store.set(key, {
cid: cid.toString(),
deals: { status: 'ongoing', deals: [] },
pin: {
try {
switch (`${request.method} /${api}/${param}`) {
case 'POST /api/upload': {
const { cid } = await importUpload(request)
const key = `${token}:${cid}`
if (!store.get(key)) {
const created = new Date()
store.set(key, {
cid: cid.toString(),
status: 'pinned',
deals: { status: 'ongoing', deals: [] },
pin: {
cid: cid.toString(),
status: 'pinned',
created,
},
created,
},
created,
})
}
const result = { ok: true, value: { cid: cid.toString() } }

return new Response(JSON.stringify(result), {
headers: headers(request),
})
}
const result = { ok: true, value: { cid: cid.toString() } }
case `GET /api/${param}`: {
const cid = CID.parse(param || '')
const value = store.get(`${token}:${cid}`)
const [status, result] = value
? [200, { ok: true, value }]
: [
404,
{
ok: false,
error: { message: `NFT with a CID ${cid} not found` },
},
]

return new Response(JSON.stringify(result), {
headers: headers(request),
})
}
case `GET /api/${param}`: {
const cid = CID.parse(param || '')
const value = store.get(`${token}:${cid}`)
const [status, result] = value
? [200, { ok: true, value }]
: [
404,
{
ok: false,
error: { message: `NFT with a CID ${cid} not found` },
},
]
return new Response(JSON.stringify(result), {
status,
headers: headers(request),
})
}
case `DELETE /api/${param}`: {
const cid = CID.parse(param || '')
store.delete(`${token}:${cid}`)
return new Response(JSON.stringify({ ok: true }), {
headers: headers(request),
})
}
default: {
const result = {
ok: false,
error: { message: `No such API endpoint ${url.pathname}` },
}

return new Response(JSON.stringify(result), {
status,
headers: headers(request),
})
}
case `DELETE /api/${param}`: {
const cid = CID.parse(param || '')
store.delete(`${token}:${cid}`)
return new Response(JSON.stringify({ ok: true }), {
headers: headers(request),
})
return new Response(JSON.stringify(result), {
status: 404,
headers: headers(request),
})
}
}
default: {
const result = {
} catch (error) {
return new Response(
JSON.stringify({
ok: false,
error: { message: `No such API endpoint ${url.pathname}` },
}

return new Response(JSON.stringify(result), {
status: 404,
error: { message: error.message },
}),
{
status: 500,
headers: headers(request),
})
}
}
)
}
}

0 comments on commit eacf295

Please sign in to comment.