Barazo AppView backend
barazo.forum
1import { describe, expect, it } from 'vitest'
2
3import { pluginManifestSchema } from '../../../src/validation/plugin-manifest.js'
4
5const VALID_MANIFEST = {
6 name: '@barazo/plugin-signatures',
7 displayName: 'User Signatures',
8 version: '1.0.0',
9 description: 'Portable user signatures with per-community overrides',
10 barazoVersion: '^1.0.0',
11 source: 'core',
12 category: 'social',
13 author: { name: 'Barazo', url: 'https://barazo.forum' },
14 license: 'MIT',
15 permissions: {
16 backend: ['db:write:plugin_signatures', 'pds:read', 'pds:write'],
17 frontend: ['ui:inject:settings-community', 'ui:inject:post-content'],
18 },
19}
20
21describe('pluginManifestSchema', () => {
22 it('validates a complete manifest with all fields', () => {
23 const complete = {
24 ...VALID_MANIFEST,
25 lexicons: ['forum.barazo.plugin.signatures'],
26 dependencies: ['@barazo/plugin-profiles'],
27 settings: {
28 maxLength: {
29 type: 'number',
30 label: 'Max signature length',
31 description: 'Maximum character count for signatures',
32 default: 200,
33 min: 50,
34 max: 1000,
35 },
36 },
37 hooks: {
38 onInstall: './hooks/install.js',
39 onUninstall: './hooks/uninstall.js',
40 onEnable: './hooks/enable.js',
41 onDisable: './hooks/disable.js',
42 onProfileSync: './hooks/profile-sync.js',
43 },
44 backend: {
45 routes: './routes/index.js',
46 migrations: './migrations/',
47 },
48 frontend: {
49 register: './frontend/register.js',
50 },
51 }
52
53 const result = pluginManifestSchema.safeParse(complete)
54 expect(result.success).toBe(true)
55 })
56
57 it('validates a minimal manifest with only required fields', () => {
58 const result = pluginManifestSchema.safeParse(VALID_MANIFEST)
59 expect(result.success).toBe(true)
60 })
61
62 it('rejects an empty object', () => {
63 const result = pluginManifestSchema.safeParse({})
64 expect(result.success).toBe(false)
65 })
66
67 it('rejects an invalid source value', () => {
68 const result = pluginManifestSchema.safeParse({
69 ...VALID_MANIFEST,
70 source: 'unknown',
71 })
72 expect(result.success).toBe(false)
73 })
74
75 it('rejects an invalid version (not semver)', () => {
76 const result = pluginManifestSchema.safeParse({
77 ...VALID_MANIFEST,
78 version: 'v1.0',
79 })
80 expect(result.success).toBe(false)
81 })
82
83 it('accepts a manifest with all 4 settings types', () => {
84 const result = pluginManifestSchema.safeParse({
85 ...VALID_MANIFEST,
86 settings: {
87 enabled: {
88 type: 'boolean',
89 label: 'Enabled',
90 default: true,
91 },
92 prefix: {
93 type: 'string',
94 label: 'Prefix',
95 description: 'Text shown before the signature',
96 default: '--',
97 placeholder: 'Enter prefix...',
98 },
99 maxLength: {
100 type: 'number',
101 label: 'Max length',
102 default: 200,
103 min: 1,
104 max: 5000,
105 },
106 position: {
107 type: 'select',
108 label: 'Display position',
109 default: 'bottom',
110 options: ['top', 'bottom'],
111 },
112 },
113 })
114 expect(result.success).toBe(true)
115 })
116
117 it('accepts a manifest with hooks', () => {
118 const result = pluginManifestSchema.safeParse({
119 ...VALID_MANIFEST,
120 hooks: {
121 onInstall: './hooks/install.js',
122 onEnable: './hooks/enable.js',
123 },
124 })
125 expect(result.success).toBe(true)
126 })
127
128 it('accepts a manifest with backend and frontend entry points', () => {
129 const result = pluginManifestSchema.safeParse({
130 ...VALID_MANIFEST,
131 backend: { routes: './routes/index.js', migrations: './migrations/' },
132 frontend: { register: './frontend/register.js' },
133 })
134 expect(result.success).toBe(true)
135 })
136
137 it('accepts a manifest with dependencies and lexicons', () => {
138 const result = pluginManifestSchema.safeParse({
139 ...VALID_MANIFEST,
140 dependencies: ['@barazo/plugin-profiles'],
141 lexicons: ['forum.barazo.plugin.signatures'],
142 })
143 expect(result.success).toBe(true)
144 })
145
146 it('rejects a name that does not match plugin naming convention', () => {
147 const result = pluginManifestSchema.safeParse({
148 ...VALID_MANIFEST,
149 name: 'random-package',
150 })
151 expect(result.success).toBe(false)
152 })
153})