Skip to content

Commit

Permalink
Add more upload unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cesardeazevedo committed Oct 29, 2024
1 parent 0558204 commit 2f08030
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 57 deletions.
56 changes: 51 additions & 5 deletions src/__tests__/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { readFile } from 'fs/promises'
import { join } from 'node:path'
import type { NostrEvent } from 'nostr-tools'
import { Markdown as MarkdownExtension } from 'tiptap-markdown'
import type { Mock } from 'vitest'
import { test as base } from 'vitest'
import type { FileUploadExtension } from '../extensions/FileUploadExtension'
import { NostrExtension } from '../extensions/NostrExtension'

const extensions = [
Expand All @@ -23,20 +25,28 @@ const extensions = [
}),
]

const editor = new Editor({ extensions })

type Fixtures = {
editor: typeof editor
editor: Editor
editorMarkdown: Editor
editorUserAbout: Editor
getFile: (filaneme: string) => Promise<File>
fileUploadExtension: (editor: Editor) => typeof FileUploadExtension
fileUploadSpies: (editor: Editor) => {
spySign: Mock
spyHash: Mock
spyDrop: Mock
spyStart: Mock
spyUpload: Mock
spyUploadError: Mock
spyComplete: Mock
}
}

// We ideally want to have a single editor instance to parse markdown and user abouts,
// But currently no ideal way to dynamically load extensions
export const test = base.extend<Fixtures>({
editor: ({}, use) => {
return use(editor)
return use(new Editor({ extensions }))
},
editorMarkdown: ({}, use) => {
return use(
Expand All @@ -62,7 +72,43 @@ export const test = base.extend<Fixtures>({
getFile: ({}, use) => {
return use(async (filename: string) => {
const buffer = await readFile(join(__dirname, filename))
return new File([buffer], 'image.jpg', { type: 'image/jpeg' })
return new File([buffer], filename, { type: 'image/png' })
})
},
fileUploadExtension: ({}, use) => {
return use((editor: Editor) => {
return editor.extensionManager.extensions.find((x) => x.name === 'fileUpload') as typeof FileUploadExtension
})
},
fileUploadSpies: ({ fileUploadExtension }, use) => {
return use((editor: Editor) => {
const fileUpload = fileUploadExtension(editor)

const spySign = vitest.fn()
const spyHash = vitest.fn()
const spyDrop = vitest.fn()
const spyStart = vitest.fn()
const spyUpload = vitest.fn()
const spyUploadError = vitest.fn()
const spyComplete = vitest.fn()

fileUpload.options.sign = spySign
fileUpload.options.hash = spyHash
fileUpload.options.onDrop = spyDrop
fileUpload.options.onStart = spyStart
fileUpload.options.onUpload = spyUpload
fileUpload.options.onUploadError = spyUploadError
fileUpload.options.onComplete = spyComplete

return {
spySign,
spyHash,
spyDrop,
spyStart,
spyUpload,
spyUploadError,
spyComplete,
}
})
},
})
22 changes: 14 additions & 8 deletions src/__tests__/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ describe('parseNote()', () => {
"hash": null,
"sha256": null,
"src": "http://host.com/image",
"tags": null,
"uploadError": null,
"uploadType": "nip96",
"uploadUrl": "https://nostr.build",
"uploadType": "blossom",
"uploadUrl": "https://localhost:3000",
"uploading": false,
},
"type": "image",
Expand All @@ -89,6 +90,7 @@ describe('parseNote()', () => {
"file": null,
"sha256": null,
"src": "http://host.com/video",
"tags": null,
"uploadError": null,
"uploadType": "nip96",
"uploadUrl": "https://nostr.build",
Expand Down Expand Up @@ -230,9 +232,10 @@ describe('parseNote()', () => {
"hash": null,
"sha256": null,
"src": "https://nostr.com/img.jpg",
"tags": null,
"uploadError": null,
"uploadType": "nip96",
"uploadUrl": "https://nostr.build",
"uploadType": "blossom",
"uploadUrl": "https://localhost:3000",
"uploading": false,
},
"type": "image",
Expand All @@ -252,6 +255,7 @@ describe('parseNote()', () => {
"file": null,
"sha256": null,
"src": "https://v.nostr.build/g6BQ.mp4",
"tags": null,
"uploadError": null,
"uploadType": "nip96",
"uploadUrl": "https://nostr.build",
Expand Down Expand Up @@ -482,9 +486,10 @@ https://host.com/2.jpeg
"hash": null,
"sha256": null,
"src": "https://host.com/1.jpeg",
"tags": null,
"uploadError": null,
"uploadType": "nip96",
"uploadUrl": "https://nostr.build",
"uploadType": "blossom",
"uploadUrl": "https://localhost:3000",
"uploading": false,
},
"type": "image",
Expand All @@ -504,9 +509,10 @@ https://host.com/2.jpeg
"hash": null,
"sha256": null,
"src": "https://host.com/2.jpeg",
"tags": null,
"uploadError": null,
"uploadType": "nip96",
"uploadUrl": "https://nostr.build",
"uploadType": "blossom",
"uploadUrl": "https://localhost:3000",
"uploading": false,
},
"type": "image",
Expand Down
Binary file added src/__tests__/test_upload.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/__tests__/test_upload2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/__tests__/test_upload_error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 84 additions & 43 deletions src/__tests__/upload.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import crypto from 'crypto'
import { HttpResponse, http } from 'msw'
import { setupServer } from 'msw/node'
import type { FileUploadExtension } from '../extensions/FileUploadExtension'
import { bufferToHex } from '../extensions/FileUploadExtension'
import { test } from './fixtures'

const hash1 = '6c36995913e97b73d5365f93a7b524a9e45edc68e4f11b78060154987c53602c'
const hash2 = '008a2224c4d2a513ab2a4add09a2ac20c2d9cec1144b5111bc1317edb2366eac'
// error hash
const hash3 = '94f4e40be68952422f78f5bf5ff63cddd2490bfdb7fa92351c3a38317043426c'

const res1 = {
sha256: hash1,
url: `https://localhost:3000/${hash1}`,
type: 'image/png',
size: 21792,
}
const res2 = {
sha256: hash2,
url: `https://localhost:3000/${hash2}`,
type: 'image/png',
size: 16630,
}

const server = setupServer(
http.put('https://localhost:3000/upload', async (info) => {
const contentType = info.request.headers.get('content-type')
const buffer = (await info.request.body?.getReader().read())?.value
if (buffer) {
const sha256 = bufferToHex(await crypto.subtle.digest('SHA-256', buffer.buffer))
// Test error file
if (sha256 === hash3) {
return HttpResponse.json({ message: 'Invalid file' }, { status: 401 })
}
return HttpResponse.json({
sha256,
url: `https://localhost:3000/${sha256}`,
Expand All @@ -34,34 +55,17 @@ describe('FileUpload', () => {
server.resetHandlers()
})

test('assert 2 successfully file uploads', async ({ editor, getFile }) => {
const fileUpload = editor.extensionManager.extensions.find(
(x) => x.name === 'fileUpload',
) as typeof FileUploadExtension

const spySign = vitest.fn()
const spyHash = vitest.fn()
const spyStart = vitest.fn()
const spyUpload = vitest.fn()
const spyComplete = vitest.fn()
test('assert 2 successfully file uploads', async ({ editor, getFile, fileUploadSpies }) => {
const { spySign, spyHash, spyDrop, spyStart, spyUpload, spyUploadError, spyComplete } = fileUploadSpies(editor)

fileUpload.options.sign = spySign
fileUpload.options.hash = spyHash
fileUpload.options.onStart = spyStart
fileUpload.options.onUpload = spyUpload
fileUpload.options.onComplete = spyComplete

editor.setOptions()
const file = await getFile('test_upload.png')
const file2 = await getFile('test_upload2.png')

editor.commands.setContent('GM!')
editor.commands.addFile(file, editor.$doc.size - 2)
editor.commands.addFile(file2, editor.$doc.size - 2)
editor.commands.addFile(file, editor.$doc.size - 2)

// less than ideal
await new Promise<void>((resolve) => setTimeout(() => resolve()))

const schema = editor.getJSON()

expect(schema.content).toHaveLength(3)
Expand All @@ -73,42 +77,79 @@ describe('FileUpload', () => {
expect(schema.content?.[2].attrs?.sha256).toBe(null)
expect(schema.content?.[2].attrs?.src).toContain('blob:nodedata')

editor.commands.uploadFiles()
const files = await editor.storage.fileUpload.uploader.start()

await new Promise<void>((resolve) => setTimeout(() => resolve(), 100))
expect(files).toHaveLength(2)

const schema2 = editor.getJSON()
const hash1 = '008a2224c4d2a513ab2a4add09a2ac20c2d9cec1144b5111bc1317edb2366eac'
const hash2 = '6c36995913e97b73d5365f93a7b524a9e45edc68e4f11b78060154987c53602c'
expect(schema2.content?.[1].attrs?.sha256).toStrictEqual(hash1)
expect(schema2.content?.[1].attrs?.src).toStrictEqual(`https://localhost:3000/${hash1}`)
expect(schema2.content?.[2].attrs?.sha256).toStrictEqual(hash2)
expect(schema2.content?.[2].attrs?.src).toStrictEqual(`https://localhost:3000/${hash2}`)
const files = [
{
sha256: '008a2224c4d2a513ab2a4add09a2ac20c2d9cec1144b5111bc1317edb2366eac',
url: 'https://localhost:3000/008a2224c4d2a513ab2a4add09a2ac20c2d9cec1144b5111bc1317edb2366eac',
type: 'image/jpeg',
size: 16630,
},
{
sha256: '6c36995913e97b73d5365f93a7b524a9e45edc68e4f11b78060154987c53602c',
url: 'https://localhost:3000/6c36995913e97b73d5365f93a7b524a9e45edc68e4f11b78060154987c53602c',
type: 'image/jpeg',
size: 21792,
},
]
expect(editor.storage.fileUpload).toStrictEqual({ files })

expect(spySign).toHaveBeenCalledTimes(2)
expect(spyHash).toHaveBeenCalledTimes(2)
expect(spyDrop).toHaveBeenCalledTimes(2)
expect(spyStart).toHaveBeenCalledOnce()
expect(spyUpload).toHaveBeenNthCalledWith(1, editor, files[0])
expect(spyUpload).toHaveBeenNthCalledWith(2, editor, files[1])
expect(spyUpload).toHaveBeenNthCalledWith(1, editor, res1)
expect(spyUpload).toHaveBeenNthCalledWith(2, editor, res2)
expect(spyUploadError).not.toHaveBeenCalled()
expect(spyComplete).toHaveBeenNthCalledWith(1, editor, files)

expect(editor.getText({ blockSeparator: ' ' })).toStrictEqual(
`GM! https://localhost:3000/008a2224c4d2a513ab2a4add09a2ac20c2d9cec1144b5111bc1317edb2366eac https://localhost:3000/6c36995913e97b73d5365f93a7b524a9e45edc68e4f11b78060154987c53602c`,
`GM! https://localhost:3000/${hash1} https://localhost:3000/${hash2}`,
)
})

test('assert error upload', async ({ editor, getFile, fileUploadSpies }) => {
const { spyDrop, spyUpload, spyUploadError, spyComplete } = fileUploadSpies(editor)

const file = await getFile('test_upload.png')
const file2 = await getFile('test_upload_error.png')

editor.commands.setContent('GM!')
editor.commands.addFile(file2, editor.$doc.size - 2)
editor.commands.addFile(file, editor.$doc.size - 2)

await new Promise<void>((resolve) => setTimeout(() => resolve()))
await expect(editor.storage.fileUpload.uploader.start()).rejects.toStrictEqual(new Error('Error: Invalid file'))

const schema2 = editor.getJSON()
expect(schema2.content?.[2].attrs?.sha256).toBeNull()
expect(schema2.content?.[2].attrs?.uploadError).toStrictEqual('Error: Invalid file')
expect(schema2.content?.[1].attrs?.sha256).toStrictEqual(hash1)
expect(schema2.content?.[1].attrs?.uploadError).toBeNull()

expect(spyDrop).toHaveBeenCalledTimes(2)
expect(spyUpload).toHaveBeenCalledOnce()
expect(spyUpload).toHaveBeenCalledWith(editor, res1)
expect(spyUploadError).toHaveBeenCalledOnce()
expect(spyUploadError).toHaveBeenCalledWith(editor, { uploadError: 'Error: Invalid file' })
expect(spyComplete).not.toHaveBeenCalledOnce()
})

test('assert uploads with immediateUpload true', async ({
editor,
getFile,
fileUploadExtension,
fileUploadSpies,
}) => {
const fileUpload = fileUploadExtension(editor)
fileUpload.options.immediateUpload = true

const { spyDrop, spyStart, spyUpload, spyUploadError, spyComplete } = fileUploadSpies(editor)

const file = await getFile('test_upload.png')

editor.commands.setContent('GM!')
editor.commands.addFile(file, editor.$doc.size - 2)

await new Promise<void>((resolve) => setTimeout(() => resolve(), 100))

expect(spyDrop).toHaveBeenCalledOnce()
expect(spyStart).toHaveBeenCalledOnce()
expect(spyUpload).toHaveBeenCalledOnce()
expect(spyUploadError).not.toHaveBeenCalled()
expect(spyComplete).toHaveBeenCalledOnce()
})
})
2 changes: 1 addition & 1 deletion src/uploaders/nip96.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface NIP96Options {

export async function uploadNIP96(options: NIP96Options) {
if (!options.sign) {
return Promise.reject('No signer found')
throw new Error('No signer provided')
}
try {
const server = await readServerConfig(options.serverUrl)
Expand Down

0 comments on commit 2f08030

Please sign in to comment.