Files
speckle-server/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js
T
2024-10-17 12:37:43 +03:00

272 lines
10 KiB
JavaScript

const { Buffer } = require('node:buffer')
const request = require('supertest')
const expect = require('chai').expect
const { beforeEachContext } = require('@/test/hooks')
const { Scopes } = require('@/modules/core/helpers/mainConstants')
const {
getStreamFactory,
createStreamFactory
} = require('@/modules/core/repositories/streams')
const { db } = require('@/db/knex')
const {
legacyCreateStreamFactory,
createStreamReturnRecordFactory
} = require('@/modules/core/services/streams/management')
const {
inviteUsersToProjectFactory
} = require('@/modules/serverinvites/services/projectInviteManagement')
const {
createAndSendInviteFactory
} = require('@/modules/serverinvites/services/creation')
const {
findUserByTargetFactory,
insertInviteAndDeleteOldFactory,
deleteServerOnlyInvitesFactory,
updateAllInviteTargetsFactory
} = require('@/modules/serverinvites/repositories/serverInvites')
const {
collectAndValidateCoreTargetsFactory
} = require('@/modules/serverinvites/services/coreResourceCollection')
const {
buildCoreInviteEmailContentsFactory
} = require('@/modules/serverinvites/services/coreEmailContents')
const { getEventBus } = require('@/modules/shared/services/eventBus')
const { createBranchFactory } = require('@/modules/core/repositories/branches')
const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter')
const {
addStreamCreatedActivityFactory
} = require('@/modules/activitystream/services/streamActivity')
const { saveActivityFactory } = require('@/modules/activitystream/repositories')
const { publish } = require('@/modules/shared/utils/subscriptions')
const {
getUsersFactory,
getUserFactory,
storeUserFactory,
countAdminUsersFactory,
storeUserAclFactory
} = require('@/modules/core/repositories/users')
const {
findEmailFactory,
createUserEmailFactory,
ensureNoPrimaryEmailForUserFactory
} = require('@/modules/core/repositories/userEmails')
const {
requestNewEmailVerificationFactory
} = require('@/modules/emails/services/verification/request')
const {
deleteOldAndInsertNewVerificationFactory
} = require('@/modules/emails/repositories')
const { renderEmail } = require('@/modules/emails/services/emailRendering')
const { sendEmail } = require('@/modules/emails/services/sending')
const { createUserFactory } = require('@/modules/core/services/users/management')
const {
validateAndCreateUserEmailFactory
} = require('@/modules/core/services/userEmails')
const {
finalizeInvitedServerRegistrationFactory
} = require('@/modules/serverinvites/services/processing')
const { UsersEmitter } = require('@/modules/core/events/usersEmitter')
const { createTokenFactory } = require('@/modules/core/services/tokens')
const {
storeApiTokenFactory,
storeTokenScopesFactory,
storeTokenResourceAccessDefinitionsFactory
} = require('@/modules/core/repositories/tokens')
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
const getServerInfo = getServerInfoFactory({ db })
const getUser = getUserFactory({ db })
const getUsers = getUsersFactory({ db })
const addStreamCreatedActivity = addStreamCreatedActivityFactory({
saveActivity: saveActivityFactory({ db }),
publish
})
const getStream = getStreamFactory({ db })
const createStream = legacyCreateStreamFactory({
createStreamReturnRecord: createStreamReturnRecordFactory({
inviteUsersToProject: inviteUsersToProjectFactory({
createAndSendInvite: createAndSendInviteFactory({
findUserByTarget: findUserByTargetFactory({ db }),
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
getStream
}),
buildInviteEmailContents: buildCoreInviteEmailContentsFactory({
getStream
}),
emitEvent: ({ eventName, payload }) =>
getEventBus().emit({
eventName,
payload
}),
getUser,
getServerInfo
}),
getUsers
}),
createStream: createStreamFactory({ db }),
createBranch: createBranchFactory({ db }),
addStreamCreatedActivity,
projectsEventsEmitter: ProjectsEmitter.emit
})
})
const findEmail = findEmailFactory({ db })
const requestNewEmailVerification = requestNewEmailVerificationFactory({
findEmail,
getUser: getUserFactory({ db }),
getServerInfo,
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }),
renderEmail,
sendEmail
})
const createUser = createUserFactory({
getServerInfo,
findEmail,
storeUser: storeUserFactory({ db }),
countAdminUsers: countAdminUsersFactory({ db }),
storeUserAcl: storeUserAclFactory({ db }),
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
createUserEmail: createUserEmailFactory({ db }),
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
findEmail,
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
}),
requestNewEmailVerification
}),
usersEventsEmitter: UsersEmitter.emit
})
const createToken = createTokenFactory({
storeApiToken: storeApiTokenFactory({ db }),
storeTokenScopes: storeTokenScopesFactory({ db }),
storeTokenResourceAccessDefinitions: storeTokenResourceAccessDefinitionsFactory({
db
})
})
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)
})
})