Cap articles.content at 10000 characters, with coverage

PocketBase silently enforced its own default max on unbounded text
fields, which broke saving a real production article longer than that.
Make the limit explicit and generous enough for the content this blog
actually publishes, and add a test asserting the exact boundary.
This commit is contained in:
2026-09-11 13:53:17 +02:00
parent 7ce073d2a9
commit 847edc7397
3 changed files with 41 additions and 1 deletions
@@ -18,7 +18,7 @@ migrate((app) => {
fields: [
{ type: 'text', name: 'title', required: true, max: 160 },
{ type: 'text', name: 'slug', required: true, max: 160, pattern: '^[a-z0-9]+(-[a-z0-9]+)*$' },
{ type: 'text', name: 'content', required: true },
{ type: 'text', name: 'content', required: true, max: 10000 },
{
type: 'file',
name: 'cover',
+37
View File
@@ -0,0 +1,37 @@
import { afterAll, describe, expect, it } from 'vitest'
import { startTestPocketbase } from '../support/pocketbase'
// Exercises the PocketBase collection schema directly (pb_migrations), not the
// Nitro /content/* endpoints — this is about what the CMS itself accepts.
const pb = await startTestPocketbase()
afterAll(async () => {
await pb.stop()
})
async function createArticle(content: string) {
return fetch(`${pb.url}/api/collections/articles/records`, {
method: 'POST',
headers: { Authorization: pb.token, 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'Vincolo lunghezza contenuto',
slug: 'vincolo-lunghezza-contenuto',
content,
publishedAt: '2025-01-01 00:00:00',
}),
})
}
describe('articles.content max length', () => {
it('accepts content up to 10000 characters', async () => {
const response = await createArticle('a'.repeat(10000))
expect(response.status).toBe(200)
})
it('rejects content over 10000 characters', async () => {
const response = await createArticle('a'.repeat(10001))
expect(response.status).toBe(400)
const body = await response.json()
expect(body.data.content.code).toBe('validation_max_text_constraint')
})
})
+3
View File
@@ -83,6 +83,8 @@ export interface TestCategory {
export interface TestPocketbase {
url: string
/** Superuser auth token, for tests that need to create/inspect records beyond the seeded fixtures. */
token: string
categories: TestCategory[]
articles: TestArticle[]
stop(): Promise<void>
@@ -155,6 +157,7 @@ export async function startTestPocketbase(options: StartOptions = {}): Promise<T
return {
url,
token,
categories,
articles,
async stop() {