import assert from 'node:assert/strict'; import test from 'node:test'; import { createDbPool, initSchema, isDatabaseConfigured } from './db.mjs'; import { createPlazaPostService, formatPostRow, mapPlazaError } from './plaza-posts.mjs'; import { createPlazaOpsService } from './plaza-ops.mjs'; import { createIntegrationUser, createOnlinePublication, getPlazaCategoryId, setOpsRole, } from './plaza-test-fixtures.mjs'; const integrationEnabled = isDatabaseConfigured(); function buildPlazaServices(pool) { const posts = createPlazaPostService(pool, { autoApprove: false, formatPostRow, }); const ops = createPlazaOpsService(pool, { formatPostRow, reviewPost: (...args) => posts.reviewPost(...args), }); return { posts, ops }; } test( 'integration: publish → pending → approve → visible in feed', { skip: !integrationEnabled && 'MySQL not configured (set MYSQL_* or DATABASE_URL)' }, async () => { const pool = createDbPool(); await initSchema(pool); const { posts, ops } = buildPlazaServices(pool); await posts.ensureCategories(); const author = await createIntegrationUser(pool, 'author'); const reviewer = await createIntegrationUser(pool, 'reviewer'); await setOpsRole(pool, reviewer.id, 'reviewer'); const { publicationId } = await createOnlinePublication(pool, author.id); const categoryId = await getPlazaCategoryId(pool); assert.ok(categoryId, 'plaza categories should be seeded'); const created = await posts.createPost(author.id, { publication_id: publicationId, category_id: categoryId, tags: ['integration'], }); assert.equal(created.status, 'pending_review'); const pendingFeed = await posts.listFeed({ sort: 'hot' }); assert.ok(!pendingFeed.posts.some((post) => post.id === created.id)); const reviewed = await ops.reviewPostAsOps(reviewer.id, created.id, 'approve'); assert.equal(reviewed.status, 'published'); const [auditRows] = await pool.query( `SELECT action FROM ops_audit_log WHERE target_type = 'post' AND target_id = ?`, [created.id], ); assert.equal(auditRows.length, 1); assert.equal(auditRows[0].action, 'approve_post'); const publishedFeed = await posts.listFeed({ sort: 'hot' }); assert.ok(publishedFeed.posts.some((post) => post.id === created.id)); await pool.end(); }, ); test( 'integration: non-author cannot hide another users post', { skip: !integrationEnabled && 'MySQL not configured (set MYSQL_* or DATABASE_URL)' }, async () => { const pool = createDbPool(); await initSchema(pool); const { posts, ops } = buildPlazaServices(pool); await posts.ensureCategories(); const author = await createIntegrationUser(pool, 'owner'); const other = await createIntegrationUser(pool, 'other'); const reviewer = await createIntegrationUser(pool, 'mod'); await setOpsRole(pool, reviewer.id, 'reviewer'); const { publicationId } = await createOnlinePublication(pool, author.id); const categoryId = await getPlazaCategoryId(pool); const created = await posts.createPost(author.id, { publication_id: publicationId, category_id: categoryId, }); await ops.reviewPostAsOps(reviewer.id, created.id, 'approve'); await assert.rejects(() => posts.hidePost(other.id, created.id), (error) => { assert.equal(error.code, 'POST_NOT_FOUND'); return true; }); assert.equal(mapPlazaError({ code: 'POST_NOT_FOUND' }), 404); await pool.end(); }, ); test( 'integration: duplicate publication publish returns ALREADY_PUBLISHED', { skip: !integrationEnabled && 'MySQL not configured (set MYSQL_* or DATABASE_URL)' }, async () => { const pool = createDbPool(); await initSchema(pool); const { posts } = buildPlazaServices(pool); await posts.ensureCategories(); const author = await createIntegrationUser(pool, 'dup'); const { publicationId } = await createOnlinePublication(pool, author.id); const categoryId = await getPlazaCategoryId(pool); await posts.createPost(author.id, { publication_id: publicationId, category_id: categoryId, }); await assert.rejects( () => posts.createPost(author.id, { publication_id: publicationId, category_id: categoryId, }), (error) => { assert.equal(error.code, 'ALREADY_PUBLISHED'); return true; }, ); assert.equal(mapPlazaError({ code: 'ALREADY_PUBLISHED' }), 409); await pool.end(); }, );