Files
speckle-server/packages/server/modules/core/tests/commits.spec.ts
T
2025-02-17 14:23:33 +02:00

577 lines
17 KiB
TypeScript

/* istanbul ignore file */
import { expect } from 'chai'
import { beforeEachContext } from '@/test/hooks'
import { createBranchAndNotifyFactory } from '@/modules/core/services/branch/management'
import cryptoRandomString from 'crypto-random-string'
import {
createBranchFactory,
getStreamBranchByNameFactory,
markCommitBranchUpdatedFactory,
getBranchByIdFactory
} from '@/modules/core/repositories/branches'
import { db } from '@/db/knex'
import {
getCommitFactory,
deleteCommitFactory,
createCommitFactory,
insertStreamCommitsFactory,
insertBranchCommitsFactory,
getCommitBranchFactory,
switchCommitBranchFactory,
updateCommitFactory,
getStreamCommitCountFactory,
legacyGetPaginatedUserCommitsPage,
legacyGetPaginatedStreamCommitsPageFactory,
getBranchCommitsTotalCountFactory,
getPaginatedBranchCommitsItemsFactory
} from '@/modules/core/repositories/commits'
import {
deleteCommitAndNotifyFactory,
createCommitByBranchIdFactory,
createCommitByBranchNameFactory,
updateCommitAndNotifyFactory
} from '@/modules/core/services/commit/management'
import {
getStreamFactory,
getCommitStreamFactory,
createStreamFactory,
markCommitStreamUpdatedFactory
} from '@/modules/core/repositories/streams'
import {
getObjectFactory,
storeSingleObjectIfNotFoundFactory
} from '@/modules/core/repositories/objects'
import {
legacyCreateStreamFactory,
createStreamReturnRecordFactory
} from '@/modules/core/services/streams/management'
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
import {
findUserByTargetFactory,
insertInviteAndDeleteOldFactory,
deleteServerOnlyInvitesFactory,
updateAllInviteTargetsFactory
} from '@/modules/serverinvites/repositories/serverInvites'
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
import { getEventBus } from '@/modules/shared/services/eventBus'
import {
getUsersFactory,
getUserFactory,
storeUserFactory,
countAdminUsersFactory,
storeUserAclFactory
} from '@/modules/core/repositories/users'
import {
findEmailFactory,
createUserEmailFactory,
ensureNoPrimaryEmailForUserFactory
} from '@/modules/core/repositories/userEmails'
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
import { renderEmail } from '@/modules/emails/services/emailRendering'
import { sendEmail } from '@/modules/emails/services/sending'
import { createUserFactory } from '@/modules/core/services/users/management'
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
import { getServerInfoFactory } from '@/modules/core/repositories/server'
import {
getBranchCommitsTotalCountByNameFactory,
getPaginatedBranchCommitsItemsByNameFactory
} from '@/modules/core/services/commit/retrieval'
import { createObjectFactory } from '@/modules/core/services/objects/management'
import { ensureError } from '@speckle/shared'
import { VersionEvents } from '@/modules/core/domain/commits/events'
const getServerInfo = getServerInfoFactory({ db })
const getUser = getUserFactory({ db })
const getUsers = getUsersFactory({ db })
const markCommitStreamUpdated = markCommitStreamUpdatedFactory({ db })
const getCommitStream = getCommitStreamFactory({ db })
const getStream = getStreamFactory({ db })
const createBranch = createBranchFactory({ db })
const createBranchAndNotify = createBranchAndNotifyFactory({
createBranch,
getStreamBranchByName: getStreamBranchByNameFactory({ db }),
eventEmit: getEventBus().emit
})
const getCommit = getCommitFactory({ db })
const deleteCommitAndNotify = deleteCommitAndNotifyFactory({
getCommit,
markCommitStreamUpdated,
markCommitBranchUpdated: markCommitBranchUpdatedFactory({ db }),
deleteCommit: deleteCommitFactory({ db }),
emitEvent: getEventBus().emit
})
const getObject = getObjectFactory({ db })
const createCommitByBranchId = createCommitByBranchIdFactory({
createCommit: createCommitFactory({ db }),
getObject,
getBranchById: getBranchByIdFactory({ db }),
insertStreamCommits: insertStreamCommitsFactory({ db }),
insertBranchCommits: insertBranchCommitsFactory({ db }),
markCommitStreamUpdated,
markCommitBranchUpdated: markCommitBranchUpdatedFactory({ db }),
emitEvent: getEventBus().emit
})
const createCommitByBranchName = createCommitByBranchNameFactory({
createCommitByBranchId,
getStreamBranchByName: getStreamBranchByNameFactory({ db }),
getBranchById: getBranchByIdFactory({ db })
})
const updateCommitAndNotify = updateCommitAndNotifyFactory({
getCommit: getCommitFactory({ db }),
getStream,
getCommitStream,
getStreamBranchByName: getStreamBranchByNameFactory({ db }),
getCommitBranch: getCommitBranchFactory({ db }),
switchCommitBranch: switchCommitBranchFactory({ db }),
updateCommit: updateCommitFactory({ db }),
emitEvent: getEventBus().emit,
markCommitStreamUpdated,
markCommitBranchUpdated: markCommitBranchUpdatedFactory({ db })
})
const getStreamCommitCount = getStreamCommitCountFactory({ 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 }),
emitEvent: getEventBus().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
}),
emitEvent: getEventBus().emit
})
const getCommitsByUserId = legacyGetPaginatedUserCommitsPage({ db })
const getCommitsByStreamId = legacyGetPaginatedStreamCommitsPageFactory({ db })
const getCommitsTotalCountByBranchName = getBranchCommitsTotalCountByNameFactory({
getStreamBranchByName: getStreamBranchByNameFactory({ db }),
getBranchCommitsTotalCount: getBranchCommitsTotalCountFactory({ db })
})
const getCommitsByBranchName = getPaginatedBranchCommitsItemsByNameFactory({
getStreamBranchByName: getStreamBranchByNameFactory({ db }),
getPaginatedBranchCommitsItems: getPaginatedBranchCommitsItemsFactory({ db })
})
const createObject = createObjectFactory({
storeSingleObjectIfNotFoundFactory: storeSingleObjectIfNotFoundFactory({ db })
})
describe('Commits @core-commits', () => {
const user = {
name: 'Dimitrie Stefanescu',
email: 'didimitrie4342@gmail.com',
password: 'sn3aky-1337-b1m',
id: ''
}
const stream = {
name: 'Test Stream References',
description: 'Whatever goes in here usually...',
id: ''
}
const testObject = {
foo: 'bar',
baz: 'qux'
}
const testObject2 = {
foo: 'bar3',
baz: 'qux3'
}
const testObject3 = {
foo: 'bar4',
baz: 'qux5'
}
let quitters: (() => void)[] = []
const generateObject = async (streamId = stream.id, object = testObject) =>
await createObject({ streamId, object })
const generateStream = async (streamBase = stream, ownerId = user.id) =>
await createStream({ ...streamBase, ownerId })
let commitId1: string, commitId2: string, commitId3: string
before(async () => {
await beforeEachContext()
user.id = await createUser(user)
stream.id = await createStream({ ...stream, ownerId: user.id })
const testObjectId = await createObject({ streamId: stream.id, object: testObject })
const testObject2Id = await createObject({
streamId: stream.id,
object: testObject2
})
const testObject3Id = await createObject({
streamId: stream.id,
object: testObject3
})
commitId1 = (
await createCommitByBranchName({
streamId: stream.id,
branchName: 'main',
message: 'first commit',
sourceApplication: 'tests',
objectId: testObjectId,
authorId: user.id
})
).id
commitId2 = (
await createCommitByBranchName({
streamId: stream.id,
branchName: 'main',
message: 'second commit',
sourceApplication: 'tests',
objectId: testObject2Id,
authorId: user.id,
parents: [commitId1]
})
).id
commitId3 = (
await createCommitByBranchName({
streamId: stream.id,
branchName: 'main',
message: 'third commit',
sourceApplication: 'tests',
objectId: testObject3Id,
authorId: user.id,
parents: [commitId1, commitId2]
})
).id
})
afterEach(() => {
quitters.forEach((quit) => quit())
quitters = []
})
it('Should create a commit by branch name', async () => {
const msg = 'first commit ive ever done, yes sir'
let eventFired = false
quitters.push(
getEventBus().listen(VersionEvents.Created, ({ payload }) => {
expect(payload.version.message).to.equal(msg)
eventFired = true
})
)
const objectId = await generateObject()
const id = (
await createCommitByBranchName({
streamId: stream.id,
branchName: 'main',
message: msg,
sourceApplication: 'tests',
objectId,
authorId: user.id
})
).id
expect(id).to.be.a.string
expect(eventFired).to.be.true
})
// support SKDs not being able to handle the fe1 - fe2 link transition
it('Should create a commit by branch id', async () => {
const objectId = await generateObject()
const branch = await createBranchAndNotify(
{ name: 'foobar', projectId: stream.id },
user.id
)
const id = (
await createCommitByBranchName({
streamId: stream.id,
branchName: branch.id,
message: 'first commit',
sourceApplication: 'tests',
objectId,
authorId: user.id
})
).id
expect(id).to.be.a.string
})
it('Should fail to create a commit if the branch is not a valid name or id', async () => {
const objectId = await generateObject()
try {
const id = (
await createCommitByBranchName({
streamId: stream.id,
branchName: cryptoRandomString({ length: 10 }),
message: 'first commit',
sourceApplication: 'tests',
objectId,
authorId: user.id
})
).id
expect(id).null
} catch (error) {
expect(ensureError(error).message).contains(
'Failed to find branch with name or id'
)
}
})
it('Should create a commit with a previous commit id', async () => {
const objectId = await generateObject()
const objectId2 = await generateObject()
const id = (
await createCommitByBranchName({
streamId: stream.id,
branchName: 'main',
message: 'second commit',
sourceApplication: 'tests',
objectId,
authorId: user.id,
parents: [commitId1]
})
).id
expect(id).to.be.a.string
const id2 = (
await createCommitByBranchName({
streamId: stream.id,
branchName: 'main',
message: 'third commit',
sourceApplication: 'tests',
objectId: objectId2,
authorId: user.id,
parents: [commitId1, commitId2]
})
).id
expect(id2).to.be.a.string
})
it('Should update a commit', async () => {
const res = await updateCommitAndNotify(
{
id: commitId1,
message: 'FIRST COMMIT YOOOOOO',
streamId: stream.id
},
user.id
)
expect(res).to.be.ok
})
it('Should delete a commit', async () => {
const objectId = await generateObject()
const tempCommitId = (
await createCommitByBranchName({
streamId: stream.id,
branchName: 'main',
message: 'temp commit',
sourceApplication: 'tests',
objectId,
authorId: user.id
})
).id
const res = await deleteCommitAndNotify(tempCommitId, stream.id, user.id)
expect(res).to.be.ok
})
it('Should get a commit by id', async () => {
const cm = await getCommit(commitId1, { streamId: stream.id })
expect(cm!.message).to.equal('FIRST COMMIT YOOOOOO')
expect(cm!.author).to.equal(user.id)
})
it('Should get the commits and their total count from a branch', async () => {
const streamId = await generateStream()
for (let i = 0; i < 10; i++) {
const t = { qux: i, id: '' }
t.id = await createObject({ streamId, object: t })
await createCommitByBranchName({
streamId,
branchName: 'main',
message: `commit # ${i + 3}`,
sourceApplication: 'tests',
objectId: t.id,
authorId: user.id
})
}
const { commits, cursor } = await getCommitsByBranchName({
streamId,
branchName: 'main',
limit: 2
})
expect(commits).to.be.an('array')
expect(commits.length).to.equal(2)
const { commits: commits2 } = await getCommitsByBranchName({
streamId,
branchName: 'main',
limit: 5,
cursor
})
expect(commits2.length).to.equal(5)
const c = await getCommitsTotalCountByBranchName({
streamId,
branchName: 'main'
})
expect(c).to.equal(10)
})
it('Should get the commits and their total count from a stream', async () => {
const streamId = await generateStream()
await createBranch({
name: 'dim/dev',
streamId,
authorId: user.id,
description: null
})
for (let i = 0; i < 15; i++) {
const t = { thud: i, id: '' }
t.id = await createObject({ streamId, object: t })
await createCommitByBranchName({
streamId,
branchName: 'dim/dev',
message: `pushed something # ${i + 3}`,
sourceApplication: 'tests',
objectId: t.id,
authorId: user.id
})
}
const { commits, cursor } = await getCommitsByStreamId({
streamId,
limit: 10
})
const { commits: commits2 } = await getCommitsByStreamId({
streamId,
limit: 20,
cursor
})
expect(commits.length).to.equal(10)
expect(commits2.length).to.equal(5)
const c = await getStreamCommitCount(streamId)
expect(c).to.equal(15)
})
it('Commits should have source, total count, branch name and parents fields', async () => {
const { commits: userCommits } = await getCommitsByUserId({
userId: user.id,
limit: 1000
})
const userCommit = userCommits[0]
const { commits: streamCommits } = await getCommitsByStreamId({
streamId: stream.id,
limit: 10
})
const serverCommit = streamCommits[0]
const { commits: branchCommits } = await getCommitsByBranchName({
streamId: stream.id,
branchName: 'main',
limit: 2
})
const branchCommit = branchCommits[0]
const idCommit = await getCommit(commitId3, { streamId: stream.id })
expect(userCommit).to.have.property('sourceApplication')
expect(userCommit.sourceApplication).to.be.a('string')
expect(userCommit).to.have.property('totalChildrenCount')
expect(userCommit.totalChildrenCount).to.be.a('number')
expect(userCommit).to.have.property('parents')
for (const commit of [serverCommit, branchCommit, idCommit]) {
expect(commit).to.have.property('sourceApplication')
expect(commit!.sourceApplication).to.be.a('string')
expect(commit).to.have.property('totalChildrenCount')
expect(commit!.totalChildrenCount).to.be.a('number')
expect(commit).to.have.property('parents')
expect(commit!.streamId).to.equal(stream.id)
}
expect(idCommit!.parents).to.be.a('array')
expect(idCommit!.parents!.length).to.equal(2)
expect(idCommit!.streamId).to.equal(stream.id)
})
it('Should have an array of parents', async () => {
const commits = [
await getCommit(commitId3, { streamId: stream.id }),
await getCommit(commitId2, { streamId: stream.id })
]
for (const commit of commits) {
expect(commit).to.have.property('parents')
expect(commit!.parents).to.be.a('array')
expect(commit!.parents!.length).to.greaterThan(0)
}
})
})