a54fa8c28f
* feat(server): add server authz pipeline rework first sketch * feat(server authz): add new server authz middleware poc implementation * test(server authz): add unittests for the new server authz workflow * feat(wip rework of fileuploads vs blob storage): add basim impl of separate blob storage service * feat(fileimport service): refactored file import service to utilize the new asssetstorage service * refactor(server errors): refactor server errors to use the shared module definitions Now all the errors inherit from BaseError * refactor(fileimport service): cleanup after refactor * feat(frontend fileimports): use the new blob storage for downloading the original file * refactor(server fileimports): clean up the remnants of S3 storage from file imports * refactor(server authz): centralize generic authz pipeline configs * refactor(server blob storage): refactor / rename everything to use the `blob-storage` name * ci(circleci): add s3 objectstorage environment variables * ci(circleci): fix missing env variables * ci(circleci): add minio test container * ci(circleci): fix minio app startup * ci(circleci): enable circleci remote docker * ci(circleci): fix minio startup * ci(cirleci): detach and wait properly for minio to start * ci(circleci): revert to additional minio img config, it only fails when the container is stopped ?! * ci(circleci): disable file uploads * fix(fileimports): update with blob storage refactor leftovers * feat(server blob storage): add blob storage graphql api * refactor(server errors): merge new errors to shared module * fix(server comments rte): fix import for RTE error * chore(fileimports): remove node-fetch from dependency * chore(server): remove body parser dependency * fix(server blob storage): fix gql api * fix(frontend): fix fileupload item not loading the new upload status, cause of premature event fire * feat(server blob storage): fix file size limit and allow for public streams * Update packages/server/modules/blobstorage/graph/schemas/blobstorage.graphql Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com> * chore(blobstorage): fix PR review issues * fix(server): fix import bugs * test(server): blob storage first test * test(server blob services): add tests for blob storage services * test(server blob storage): add service and rest api tests * test(server blob storage): add server blob storage graphql api tests * feat(server blob storage): store and make available blob fileHash attribute * feat(server authz): add fatal failure option to server authz pipeline Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
133 lines
5.3 KiB
JavaScript
133 lines
5.3 KiB
JavaScript
const { Buffer } = require('node:buffer')
|
|
const request = require('supertest')
|
|
const expect = require('chai').expect
|
|
const { beforeEachContext } = require('@/test/hooks')
|
|
const { createStream } = require('@/modules/core/services/streams')
|
|
const { createToken } = require('@/modules/core/services/tokens')
|
|
const { createUser } = require('@/modules/core/services/users')
|
|
const { Scopes } = require('@/modules/core/helpers/mainConstants')
|
|
|
|
describe('Blobs integration @blobstorage', () => {
|
|
let app
|
|
let token
|
|
const user = {
|
|
name: 'Baron Von Blubba',
|
|
email: 'barron@bubble.bobble',
|
|
password: 'bubblesAreMyBlobs'
|
|
}
|
|
|
|
before(async () => {
|
|
;({ app } = await beforeEachContext())
|
|
user.id = await createUser(user)
|
|
;({ token } = await createToken({
|
|
userId: user.id,
|
|
name: 'test token',
|
|
scopes: [Scopes.Streams.Write, Scopes.Streams.Read]
|
|
}))
|
|
})
|
|
it('Uploads from multipart upload', async () => {
|
|
const streamId = await createStream({ ownerId: user.id })
|
|
const response = await request(app)
|
|
.post(`/api/stream/${streamId}/blob`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.attach('blob1', require.resolve('@/readme.md'))
|
|
.attach('blob2', require.resolve('@/package.json'))
|
|
expect(response.status).to.equal(201)
|
|
expect(response.body.uploadResults).to.exist
|
|
const uploadResults = response.body.uploadResults
|
|
expect(uploadResults).to.have.lengthOf(2)
|
|
expect(uploadResults.map((r) => r.uploadStatus)).to.have.members([1, 1])
|
|
})
|
|
|
|
it('Errors for too big files, file is deleted', async () => {
|
|
const streamId = await createStream({ ownerId: user.id })
|
|
const response = await request(app)
|
|
.post(`/api/stream/${streamId}/blob`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.attach('blob1', Buffer.alloc(114_857_601, 'asdf'), 'dummy.blob')
|
|
expect(response.body.uploadResults).to.have.lengthOf(1)
|
|
const [uploadResult] = response.body.uploadResults
|
|
expect(uploadResult.uploadStatus).to.equal(2)
|
|
expect(uploadResult.uploadError).to.equal('File size limit reached')
|
|
const blob = await request(app)
|
|
.get(`/api/stream/${streamId}/blob/${uploadResult.blobId}`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
expect(blob.status).to.equal(404)
|
|
})
|
|
|
|
it('Gets blob metadata', async () => {
|
|
const streamId = await createStream({ ownerId: user.id })
|
|
const response = await request(app)
|
|
.post(`/api/stream/${streamId}/blob`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.attach('blob1', Buffer.alloc(100, 'asdf'), 'dummy.blob')
|
|
expect(response.status).to.equal(201)
|
|
expect(response.body.uploadResults).to.have.lengthOf(1)
|
|
const [uploadResult] = response.body.uploadResults
|
|
|
|
const metadataResult = await request(app)
|
|
.get(`/api/stream/${streamId}/blobs`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
expect(metadataResult.status).to.equal(200)
|
|
expect(metadataResult.body.blobs).to.have.lengthOf(1)
|
|
expect(metadataResult.body.blobs[0].id).to.equal(uploadResult.blobId)
|
|
})
|
|
|
|
it('Deletes blob and object metadata', async () => {
|
|
const streamId = await createStream({ ownerId: user.id })
|
|
const response = await request(app)
|
|
.post(`/api/stream/${streamId}/blob`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.attach('blob1', Buffer.alloc(100, 'asdf'), 'dummy.blob')
|
|
expect(response.status).to.equal(201)
|
|
expect(response.body.uploadResults).to.have.lengthOf(1)
|
|
const [uploadResult] = response.body.uploadResults
|
|
|
|
const deleteResult = await request(app)
|
|
.delete(`/api/stream/${streamId}/blob/${uploadResult.blobId}`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
expect(deleteResult.status).to.equal(204)
|
|
const blob = await request(app)
|
|
.get(`/api/stream/${streamId}/blob/${uploadResult.blobId}`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
expect(blob.status).to.equal(404)
|
|
|
|
const metadataResult = await request(app)
|
|
.get(`/api/stream/${streamId}/blobs`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
expect(metadataResult.status).to.equal(200)
|
|
expect(metadataResult.body).to.deep.equal({ blobs: [], cursor: null })
|
|
})
|
|
|
|
it('Gets uploaded blob data', async () => {
|
|
const streamId = await createStream({ ownerId: user.id })
|
|
const response = await request(app)
|
|
.post(`/api/stream/${streamId}/blob`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.attach('blob1', Buffer.alloc(10, 'a'), 'dummy.blob')
|
|
expect(response.body.uploadResults).to.have.lengthOf(1)
|
|
const [uploadResult] = response.body.uploadResults
|
|
|
|
const blob = await request(app)
|
|
.get(`/api/stream/${streamId}/blob/${uploadResult.blobId}`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
expect(blob.status).to.equal(200)
|
|
expect(blob.headers['content-disposition']).to.equal(
|
|
'attachment; filename="dummy.blob"'
|
|
)
|
|
expect(blob.body.toString()).to.equal('a'.repeat(10))
|
|
})
|
|
|
|
it('Returns 400 for bad form data', async () => {
|
|
const streamId = await createStream({ ownerId: user.id })
|
|
const response = await request(app)
|
|
.post(`/api/stream/${streamId}/blob`)
|
|
.set('Authorization', `Bearer ${token}`)
|
|
.set('Content-type', 'multipart/form-data; boundary=XXX')
|
|
// sending an unfinished part
|
|
.send('--XXX\r\nCon')
|
|
|
|
expect(response.status).to.equal(400)
|
|
})
|
|
})
|