Files
modrinth/packages/moderation/src/validators/project-validation/tests.ts
T

172 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict'
import test from 'node:test'
import type { Labrinth } from '@modrinth/api-client'
import { hasProjectFieldValidationFailures, validateProjectFields } from './index.ts'
function createProject(
overrides: Partial<Labrinth.Projects.v3.Project> = {},
): Labrinth.Projects.v3.Project {
return {
name: 'Ordinary Tools',
summary: 'A collection of ordinary tools.',
description: 'This project adds a collection of ordinary tools. '.repeat(5),
gallery: [],
...overrides,
} as Labrinth.Projects.v3.Project
}
test('validates project fields and gallery text', () => {
const project = createProject({
name: 'Ordinary Tools',
summary: 'Ordinary Tools',
description: '𝐀',
gallery: [
{
url: 'https://cdn.modrinth.com/gallery.png',
raw_url: 'https://cdn.modrinth.com/gallery.png',
featured: false,
name: 'This is $h!t',
description: '𝐁',
created: '2026-01-01T00:00:00Z',
ordering: 0,
},
],
})
const result = validateProjectFields(project)
assert.equal(result.valid, false)
assert.deepEqual(
result.failures.map(({ field, galleryIndex, galleryUrl, message }) => ({
field,
galleryIndex,
galleryUrl,
message: message.id,
})),
[
{
field: 'summary',
galleryIndex: undefined,
galleryUrl: undefined,
message: 'project.text-validation.summary-matches-title',
},
{
field: 'description',
galleryIndex: undefined,
galleryUrl: undefined,
message: 'project.text-validation.non-standard-text',
},
{
field: 'gallery-name',
galleryIndex: 0,
galleryUrl: 'https://cdn.modrinth.com/gallery.png',
message: 'project.text-validation.profanity',
},
{
field: 'gallery-description',
galleryIndex: 0,
galleryUrl: 'https://cdn.modrinth.com/gallery.png',
message: 'project.text-validation.non-standard-text',
},
],
)
assert.deepEqual(result.failures.find(({ field }) => field === 'gallery-name')?.values, {
value: '$h!t',
})
})
test('reports whether a project has field validation failures', () => {
const validProject = createProject()
const invalidProject = createProject({ summary: 'This project is shit' })
assert.deepEqual(validateProjectFields(validProject), {
valid: true,
failures: [],
})
assert.equal(hasProjectFieldValidationFailures(validProject), false)
assert.equal(hasProjectFieldValidationFailures(invalidProject), true)
assert.equal(
hasProjectFieldValidationFailures(
createProject({ name: 'Ordinary Tools', summary: 'Ordinary Tools' }),
),
true,
)
})
test('treats version numbers and explicit summary links as errors', () => {
const project = createProject({
name: 'Tools 1.2.3',
summary: 'Visit https://example.dev for more information',
})
const result = validateProjectFields(project)
assert.equal(result.valid, false)
assert.deepEqual(
result.failures.map(({ code, severity }) => ({ code, severity })),
[
{ code: 'title-version-number', severity: 'error' },
{ code: 'summary-link', severity: 'error' },
],
)
assert.equal(hasProjectFieldValidationFailures(project), true)
assert.equal(
hasProjectFieldValidationFailures(createProject({ name: 'Tools 1.2 Fabric Port' })),
false,
)
})
test('rejects blocklisted links in summaries and descriptions', () => {
const summaryResult = validateProjectFields(
createProject({ summary: 'Visit bit.ly/project for more information' }),
)
assert.equal(summaryResult.valid, false)
assert.equal(summaryResult.failures[0]?.field, 'summary')
assert.equal(summaryResult.failures[0]?.code, 'text-banned-link')
const descriptionResult = validateProjectFields(
createProject({ description: `Visit https://bit.ly/project. ${'More details. '.repeat(20)}` }),
)
assert.equal(descriptionResult.valid, false)
assert.equal(descriptionResult.failures[0]?.field, 'description')
assert.equal(descriptionResult.failures[0]?.code, 'text-banned-link')
})
test('reports summary recommendations without invalidating the project', () => {
const project = createProject({ summary: 'Short summary' })
assert.deepEqual(validateProjectFields(project), {
valid: true,
failures: [
{
code: 'summary-too-short',
field: 'summary',
severity: 'warn',
message: {
id: 'project.text-validation.summary-too-short',
defaultMessage:
'Your summary is {length, plural, one {# character} other {# characters}}. At least {minChars, plural, one {# character} other {# characters}} is recommended to create an informative and enticing summary.',
},
values: { length: 13, minChars: 30 },
},
],
})
assert.equal(hasProjectFieldValidationFailures(project), false)
})
test('treats summary formatting as a field validation failure', () => {
const project = createProject({ summary: '# A formatted project summary' })
const result = validateProjectFields(project)
assert.equal(result.valid, false)
assert.deepEqual(
result.failures.map(({ code, severity }) => ({ code, severity })),
[
{ code: 'summary-too-short', severity: 'warn' },
{ code: 'summary-special-formatting', severity: 'error' },
],
)
assert.equal(hasProjectFieldValidationFailures(project), true)
})