Begin unit tests
This commit is contained in:
parent
bf1f650817
commit
d3ea897591
9 changed files with 252 additions and 42 deletions
|
@ -78,9 +78,9 @@ function addVideoCommentThread (req: express.Request, res: express.Response) {
|
|||
return sequelizeTypescript.transaction(async t => {
|
||||
return createVideoComment({
|
||||
text: videoCommentInfo.text,
|
||||
inReplyToComment: null,
|
||||
inReplyToCommentId: null,
|
||||
video: res.locals.video,
|
||||
actorId: res.locals.oauth.token.User.Account.Actor.id
|
||||
accountId: res.locals.oauth.token.User.Account.id
|
||||
}, t)
|
||||
})
|
||||
}
|
||||
|
@ -106,9 +106,9 @@ function addVideoCommentReply (req: express.Request, res: express.Response, next
|
|||
return sequelizeTypescript.transaction(async t => {
|
||||
return createVideoComment({
|
||||
text: videoCommentInfo.text,
|
||||
inReplyToComment: res.locals.videoComment.id,
|
||||
inReplyToCommentId: res.locals.videoComment.id,
|
||||
video: res.locals.video,
|
||||
actorId: res.locals.oauth.token.User.Account.Actor.id
|
||||
accountId: res.locals.oauth.token.User.Account.id
|
||||
}, t)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) {
|
|||
originCommentId: null,
|
||||
inReplyToComment: null,
|
||||
videoId: video.id,
|
||||
actorId: byActor.id
|
||||
accountId: byAccount.id
|
||||
}, { transaction: t })
|
||||
}
|
||||
|
||||
|
@ -281,7 +281,7 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) {
|
|||
originCommentId,
|
||||
inReplyToCommentId: inReplyToComment.id,
|
||||
videoId: inReplyToComment.videoId,
|
||||
actorId: byActor.id
|
||||
accountId: byAccount.id
|
||||
}, { transaction: t })
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
import { ResultList } from '../../shared/models'
|
||||
import { VideoCommentThread } from '../../shared/models/videos/video-comment.model'
|
||||
import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
|
||||
import { VideoModel } from '../models/video/video'
|
||||
import { VideoCommentModel } from '../models/video/video-comment'
|
||||
import { getVideoCommentActivityPubUrl } from './activitypub'
|
||||
|
||||
async function createVideoComment (obj: {
|
||||
text: string,
|
||||
inReplyToComment: number,
|
||||
inReplyToCommentId: number,
|
||||
video: VideoModel
|
||||
actorId: number
|
||||
accountId: number
|
||||
}, t: Sequelize.Transaction) {
|
||||
let originCommentId: number = null
|
||||
if (obj.inReplyToComment) {
|
||||
const repliedComment = await VideoCommentModel.loadById(obj.inReplyToComment)
|
||||
|
||||
if (obj.inReplyToCommentId) {
|
||||
const repliedComment = await VideoCommentModel.loadById(obj.inReplyToCommentId)
|
||||
if (!repliedComment) throw new Error('Unknown replied comment.')
|
||||
|
||||
originCommentId = repliedComment.originCommentId || repliedComment.id
|
||||
|
@ -22,22 +23,23 @@ async function createVideoComment (obj: {
|
|||
const comment = await VideoCommentModel.create({
|
||||
text: obj.text,
|
||||
originCommentId,
|
||||
inReplyToComment: obj.inReplyToComment,
|
||||
inReplyToCommentId: obj.inReplyToCommentId,
|
||||
videoId: obj.video.id,
|
||||
actorId: obj.actorId
|
||||
}, { transaction: t })
|
||||
accountId: obj.accountId,
|
||||
url: 'fake url'
|
||||
}, { transaction: t, validate: false })
|
||||
|
||||
comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment))
|
||||
|
||||
return comment.save({ transaction: t })
|
||||
}
|
||||
|
||||
function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>): VideoCommentThread {
|
||||
function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>): VideoCommentThreadTree {
|
||||
// Comments are sorted by id ASC
|
||||
const comments = resultList.data
|
||||
|
||||
const comment = comments.shift()
|
||||
const thread: VideoCommentThread = {
|
||||
const thread: VideoCommentThreadTree = {
|
||||
comment: comment.toFormattedJSON(),
|
||||
children: []
|
||||
}
|
||||
|
@ -48,7 +50,7 @@ function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>):
|
|||
while (comments.length !== 0) {
|
||||
const childComment = comments.shift()
|
||||
|
||||
const childCommentThread: VideoCommentThread = {
|
||||
const childCommentThread: VideoCommentThreadTree = {
|
||||
comment: childComment.toFormattedJSON(),
|
||||
children: []
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { logger } from '../../helpers'
|
|||
import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
|
||||
import { isValidVideoCommentText } from '../../helpers/custom-validators/video-comments'
|
||||
import { isVideoExist } from '../../helpers/custom-validators/videos'
|
||||
import { VideoModel } from '../../models/video/video'
|
||||
import { VideoCommentModel } from '../../models/video/video-comment'
|
||||
import { areValidationErrors } from './utils'
|
||||
|
||||
|
@ -11,7 +12,7 @@ const listVideoCommentThreadsValidator = [
|
|||
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
|
||||
logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isVideoExist(req.params.videoId, res)) return
|
||||
|
@ -25,11 +26,11 @@ const listVideoThreadCommentsValidator = [
|
|||
param('threadId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid threadId'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
|
||||
logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isVideoExist(req.params.videoId, res)) return
|
||||
if (!await isVideoCommentThreadExist(req.params.threadId, req.params.videoId, res)) return
|
||||
if (!await isVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ const addVideoCommentThreadValidator = [
|
|||
body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
|
||||
logger.debug('Checking addVideoCommentThread parameters.', { parameters: req.params })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isVideoExist(req.params.videoId, res)) return
|
||||
|
@ -55,11 +56,11 @@ const addVideoCommentReplyValidator = [
|
|||
body('text').custom(isValidVideoCommentText).not().isEmpty().withMessage('Should have a valid comment text'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking blacklistRemove parameters.', { parameters: req.params })
|
||||
logger.debug('Checking addVideoCommentReply parameters.', { parameters: req.params })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
if (!await isVideoExist(req.params.videoId, res)) return
|
||||
if (!await isVideoCommentExist(req.params.commentId, req.params.videoId, res)) return
|
||||
if (!await isVideoCommentExist(req.params.commentId, res.locals.video, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ export {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function isVideoCommentThreadExist (id: number, videoId: number, res: express.Response) {
|
||||
async function isVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) {
|
||||
const videoComment = await VideoCommentModel.loadById(id)
|
||||
|
||||
if (!videoComment) {
|
||||
|
@ -87,7 +88,7 @@ async function isVideoCommentThreadExist (id: number, videoId: number, res: expr
|
|||
return false
|
||||
}
|
||||
|
||||
if (videoComment.videoId !== videoId) {
|
||||
if (videoComment.videoId !== video.id) {
|
||||
res.status(400)
|
||||
.json({ error: 'Video comment is associated to this video.' })
|
||||
.end()
|
||||
|
@ -107,7 +108,7 @@ async function isVideoCommentThreadExist (id: number, videoId: number, res: expr
|
|||
return true
|
||||
}
|
||||
|
||||
async function isVideoCommentExist (id: number, videoId: number, res: express.Response) {
|
||||
async function isVideoCommentExist (id: number, video: VideoModel, res: express.Response) {
|
||||
const videoComment = await VideoCommentModel.loadById(id)
|
||||
|
||||
if (!videoComment) {
|
||||
|
@ -118,7 +119,7 @@ async function isVideoCommentExist (id: number, videoId: number, res: express.Re
|
|||
return false
|
||||
}
|
||||
|
||||
if (videoComment.videoId !== videoId) {
|
||||
if (videoComment.videoId !== video.id) {
|
||||
res.status(400)
|
||||
.json({ error: 'Video comment is associated to this video.' })
|
||||
.end()
|
||||
|
|
|
@ -6,18 +6,18 @@ import {
|
|||
import { VideoComment } from '../../../shared/models/videos/video-comment.model'
|
||||
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
import { ActorModel } from '../activitypub/actor'
|
||||
import { AccountModel } from '../account/account'
|
||||
import { getSort, throwIfNotValid } from '../utils'
|
||||
import { VideoModel } from './video'
|
||||
|
||||
enum ScopeNames {
|
||||
WITH_ACTOR = 'WITH_ACTOR'
|
||||
WITH_ACCOUNT = 'WITH_ACCOUNT'
|
||||
}
|
||||
|
||||
@Scopes({
|
||||
[ScopeNames.WITH_ACTOR]: {
|
||||
[ScopeNames.WITH_ACCOUNT]: {
|
||||
include: [
|
||||
() => ActorModel
|
||||
() => AccountModel
|
||||
]
|
||||
}
|
||||
})
|
||||
|
@ -84,17 +84,17 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
})
|
||||
Video: VideoModel
|
||||
|
||||
@ForeignKey(() => ActorModel)
|
||||
@ForeignKey(() => AccountModel)
|
||||
@Column
|
||||
actorId: number
|
||||
accountId: number
|
||||
|
||||
@BelongsTo(() => ActorModel, {
|
||||
@BelongsTo(() => AccountModel, {
|
||||
foreignKey: {
|
||||
allowNull: false
|
||||
},
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
Actor: ActorModel
|
||||
Account: AccountModel
|
||||
|
||||
@AfterDestroy
|
||||
static sendDeleteIfOwned (instance: VideoCommentModel) {
|
||||
|
@ -132,12 +132,13 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
limit: count,
|
||||
order: [ getSort(sort) ],
|
||||
where: {
|
||||
videoId
|
||||
videoId,
|
||||
inReplyToCommentId: null
|
||||
}
|
||||
}
|
||||
|
||||
return VideoCommentModel
|
||||
.scope([ ScopeNames.WITH_ACTOR ])
|
||||
.scope([ ScopeNames.WITH_ACCOUNT ])
|
||||
.findAndCountAll(query)
|
||||
.then(({ rows, count }) => {
|
||||
return { total: count, data: rows }
|
||||
|
@ -146,7 +147,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
|
||||
static listThreadCommentsForApi (videoId: number, threadId: number) {
|
||||
const query = {
|
||||
order: [ 'id', 'ASC' ],
|
||||
order: [ [ 'id', 'ASC' ] ],
|
||||
where: {
|
||||
videoId,
|
||||
[ Sequelize.Op.or ]: [
|
||||
|
@ -157,7 +158,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
}
|
||||
|
||||
return VideoCommentModel
|
||||
.scope([ ScopeNames.WITH_ACTOR ])
|
||||
.scope([ ScopeNames.WITH_ACCOUNT ])
|
||||
.findAndCountAll(query)
|
||||
.then(({ rows, count }) => {
|
||||
return { total: count, data: rows }
|
||||
|
@ -173,7 +174,10 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
|||
inReplyToCommentId: this.inReplyToCommentId,
|
||||
videoId: this.videoId,
|
||||
createdAt: this.createdAt,
|
||||
updatedAt: this.updatedAt
|
||||
updatedAt: this.updatedAt,
|
||||
account: {
|
||||
name: this.Account.name
|
||||
}
|
||||
} as VideoComment
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,3 +4,4 @@ import './video-transcoder'
|
|||
import './multiple-servers'
|
||||
import './follows'
|
||||
import './jobs'
|
||||
import './video-comments'
|
||||
|
|
135
server/tests/api/video-comments.ts
Normal file
135
server/tests/api/video-comments.ts
Normal file
|
@ -0,0 +1,135 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import { VideoComment, VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
|
||||
import { dateIsValid, flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, uploadVideo } from '../utils'
|
||||
import { addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads, getVideoThreadComments } from '../utils/video-comments'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
describe('Test a video comments', function () {
|
||||
let server: ServerInfo
|
||||
let videoId
|
||||
let videoUUID
|
||||
let threadId
|
||||
|
||||
before(async function () {
|
||||
this.timeout(10000)
|
||||
|
||||
await flushTests()
|
||||
|
||||
server = await runServer(1)
|
||||
|
||||
await setAccessTokensToServers([ server ])
|
||||
|
||||
const res = await uploadVideo(server.url, server.accessToken, {})
|
||||
videoUUID = res.body.video.uuid
|
||||
videoId = res.body.video.id
|
||||
})
|
||||
|
||||
it('Should not have threads on this video', async function () {
|
||||
const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
|
||||
|
||||
expect(res.body.total).to.equal(0)
|
||||
expect(res.body.data).to.be.an('array')
|
||||
expect(res.body.data).to.have.lengthOf(0)
|
||||
})
|
||||
|
||||
it('Should create a thread in this video', async function () {
|
||||
const text = 'my super first comment'
|
||||
|
||||
await addVideoCommentThread(server.url, server.accessToken, videoUUID, text)
|
||||
})
|
||||
|
||||
it('Should list threads of this video', async function () {
|
||||
const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
|
||||
|
||||
expect(res.body.total).to.equal(1)
|
||||
expect(res.body.data).to.be.an('array')
|
||||
expect(res.body.data).to.have.lengthOf(1)
|
||||
|
||||
const comment: VideoComment = res.body.data[0]
|
||||
expect(comment.inReplyToCommentId).to.be.null
|
||||
expect(comment.text).equal('my super first comment')
|
||||
expect(comment.videoId).to.equal(videoId)
|
||||
expect(comment.id).to.equal(comment.threadId)
|
||||
expect(comment.account.name).to.equal('root')
|
||||
expect(dateIsValid(comment.createdAt as string)).to.be.true
|
||||
expect(dateIsValid(comment.updatedAt as string)).to.be.true
|
||||
|
||||
threadId = comment.threadId
|
||||
})
|
||||
|
||||
it('Should get all the thread created', async function () {
|
||||
const res = await getVideoThreadComments(server.url, videoUUID, threadId)
|
||||
|
||||
const rootComment = res.body.comment
|
||||
expect(rootComment.inReplyToCommentId).to.be.null
|
||||
expect(rootComment.text).equal('my super first comment')
|
||||
expect(rootComment.videoId).to.equal(videoId)
|
||||
expect(dateIsValid(rootComment.createdAt as string)).to.be.true
|
||||
expect(dateIsValid(rootComment.updatedAt as string)).to.be.true
|
||||
})
|
||||
|
||||
it('Should create multiple replies in this thread', async function () {
|
||||
const text1 = 'my super answer to thread 1'
|
||||
const childCommentRes = await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text1)
|
||||
const childCommentId = childCommentRes.body.comment.id
|
||||
|
||||
const text2 = 'my super answer to answer of thread 1'
|
||||
await addVideoCommentReply(server.url, server.accessToken, videoId, childCommentId, text2)
|
||||
|
||||
const text3 = 'my second answer to thread 1'
|
||||
await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text3)
|
||||
})
|
||||
|
||||
it('Should get correctly the replies', async function () {
|
||||
const res = await getVideoThreadComments(server.url, videoUUID, threadId)
|
||||
|
||||
const tree: VideoCommentThreadTree = res.body
|
||||
expect(tree.comment.text).equal('my super first comment')
|
||||
expect(tree.children).to.have.lengthOf(2)
|
||||
|
||||
const firstChild = tree.children[0]
|
||||
expect(firstChild.comment.text).to.equal('my super answer to thread 1')
|
||||
expect(firstChild.children).to.have.lengthOf(1)
|
||||
|
||||
const childOfFirstChild = firstChild.children[0]
|
||||
expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
|
||||
expect(childOfFirstChild.children).to.have.lengthOf(0)
|
||||
|
||||
const secondChild = tree.children[1]
|
||||
expect(secondChild.comment.text).to.equal('my second answer to thread 1')
|
||||
expect(secondChild.children).to.have.lengthOf(0)
|
||||
})
|
||||
|
||||
it('Should create other threads', async function () {
|
||||
const text1 = 'super thread 2'
|
||||
await addVideoCommentThread(server.url, server.accessToken, videoUUID, text1)
|
||||
|
||||
const text2 = 'super thread 3'
|
||||
await addVideoCommentThread(server.url, server.accessToken, videoUUID, text2)
|
||||
})
|
||||
|
||||
it('Should list the threads', async function () {
|
||||
const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt')
|
||||
|
||||
expect(res.body.total).to.equal(3)
|
||||
expect(res.body.data).to.be.an('array')
|
||||
expect(res.body.data).to.have.lengthOf(3)
|
||||
|
||||
expect(res.body.data[0].text).to.equal('my super first comment')
|
||||
expect(res.body.data[1].text).to.equal('super thread 2')
|
||||
expect(res.body.data[2].text).to.equal('super thread 3')
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers([ server ])
|
||||
|
||||
// Keep the logs if the test failed
|
||||
if (this['ok']) {
|
||||
await flushTests()
|
||||
}
|
||||
})
|
||||
})
|
64
server/tests/utils/video-comments.ts
Normal file
64
server/tests/utils/video-comments.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import * as request from 'supertest'
|
||||
|
||||
function getVideoCommentThreads (url: string, videoId: number, start: number, count: number, sort?: string) {
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads'
|
||||
|
||||
const req = request(url)
|
||||
.get(path)
|
||||
.query({ start: start })
|
||||
.query({ count: count })
|
||||
|
||||
if (sort) req.query({ sort })
|
||||
|
||||
return req.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.expect('Content-Type', /json/)
|
||||
}
|
||||
|
||||
function getVideoThreadComments (url: string, videoId: number, threadId: number) {
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
|
||||
|
||||
return request(url)
|
||||
.get(path)
|
||||
.set('Accept', 'application/json')
|
||||
.expect(200)
|
||||
.expect('Content-Type', /json/)
|
||||
}
|
||||
|
||||
function addVideoCommentThread (url: string, token: string, videoId: number, text: string, expectedStatus = 200) {
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads'
|
||||
|
||||
return request(url)
|
||||
.post(path)
|
||||
.send({ text })
|
||||
.set('Accept', 'application/json')
|
||||
.set('Authorization', 'Bearer ' + token)
|
||||
.expect(expectedStatus)
|
||||
}
|
||||
|
||||
function addVideoCommentReply (
|
||||
url: string,
|
||||
token: string,
|
||||
videoId: number,
|
||||
inReplyToCommentId: number,
|
||||
text: string,
|
||||
expectedStatus = 200
|
||||
) {
|
||||
const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
|
||||
|
||||
return request(url)
|
||||
.post(path)
|
||||
.send({ text })
|
||||
.set('Accept', 'application/json')
|
||||
.set('Authorization', 'Bearer ' + token)
|
||||
.expect(expectedStatus)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
getVideoCommentThreads,
|
||||
getVideoThreadComments,
|
||||
addVideoCommentThread,
|
||||
addVideoCommentReply
|
||||
}
|
|
@ -7,11 +7,14 @@ export interface VideoComment {
|
|||
videoId: number
|
||||
createdAt: Date | string
|
||||
updatedAt: Date | string
|
||||
account: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface VideoCommentThread {
|
||||
export interface VideoCommentThreadTree {
|
||||
comment: VideoComment
|
||||
children: VideoCommentThread[]
|
||||
children: VideoCommentThreadTree[]
|
||||
}
|
||||
|
||||
export interface VideoCommentCreate {
|
||||
|
|
Loading…
Reference in a new issue