255 lines
7.0 KiB
TypeScript
255 lines
7.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { MetadataService } from '../../src/services/metadata/metadata.service.js';
|
|
import type { AppMetadataSchema } from '@insforge/shared-schemas';
|
|
|
|
const metadataService = MetadataService.getInstance();
|
|
|
|
const sampleMetadata: AppMetadataSchema = {
|
|
version: '1.2.3',
|
|
auth: {
|
|
oAuthProviders: ['google', 'github'],
|
|
customOAuthProviders: [],
|
|
smtpConfig: {
|
|
enabled: false,
|
|
host: '',
|
|
port: 587,
|
|
username: '',
|
|
hasPassword: false,
|
|
senderEmail: '',
|
|
senderName: '',
|
|
minIntervalSeconds: 60,
|
|
},
|
|
requireEmailVerification: true,
|
|
disableSignup: false,
|
|
passwordMinLength: 8,
|
|
requireNumber: false,
|
|
requireLowercase: false,
|
|
requireUppercase: false,
|
|
requireSpecialChar: false,
|
|
verifyEmailMethod: 'otp',
|
|
resetPasswordMethod: 'otp',
|
|
allowedRedirectUrls: [],
|
|
},
|
|
database: {
|
|
tables: [
|
|
{ tableName: 'users', recordCount: 150 },
|
|
{ tableName: 'posts', recordCount: 1200 },
|
|
],
|
|
totalSizeInGB: 0.3,
|
|
},
|
|
storage: {
|
|
buckets: [
|
|
{ name: 'avatars', public: true, createdAt: '2026-01-01T00:00:00Z', objectCount: 42 },
|
|
],
|
|
totalSizeInGB: 0.5,
|
|
},
|
|
functions: [
|
|
{ slug: 'hello-world', name: 'hello-world', status: 'active', description: 'Test function' },
|
|
],
|
|
realtime: {
|
|
channels: [
|
|
{
|
|
id: '00000000-0000-0000-0000-000000000001',
|
|
pattern: 'chat',
|
|
description: null,
|
|
webhookUrls: null,
|
|
enabled: true,
|
|
createdAt: '2026-01-01T00:00:00Z',
|
|
updatedAt: '2026-01-01T00:00:00Z',
|
|
},
|
|
],
|
|
permissions: { tables: {} },
|
|
},
|
|
};
|
|
|
|
describe('MetadataService.formatAsMarkdown', () => {
|
|
it('renders a complete markdown document from metadata', () => {
|
|
const md = metadataService.formatAsMarkdown(sampleMetadata);
|
|
|
|
// Header
|
|
expect(md).toContain('# Project Metadata');
|
|
expect(md).toContain('v1.2.3');
|
|
|
|
// Auth
|
|
expect(md).toContain('## Auth');
|
|
expect(md).toContain('google, github');
|
|
expect(md).toContain('required');
|
|
expect(md).toContain('enabled');
|
|
|
|
// Database tables
|
|
expect(md).toContain('| users | 150 |');
|
|
expect(md).toContain('| posts | 1200 |');
|
|
expect(md).toContain('0.3 GB');
|
|
|
|
// Storage
|
|
expect(md).toContain('`avatars`');
|
|
expect(md).toContain('public');
|
|
expect(md).toContain('42 objects');
|
|
expect(md).toContain('0.5 GB');
|
|
|
|
// Functions
|
|
expect(md).toContain('`hello-world`');
|
|
expect(md).toContain('active');
|
|
expect(md).toContain('Test function');
|
|
|
|
// Realtime
|
|
expect(md).toContain('`chat`');
|
|
});
|
|
|
|
it('handles missing optional sections gracefully', () => {
|
|
const minimal: AppMetadataSchema = {
|
|
auth: {
|
|
oAuthProviders: [],
|
|
customOAuthProviders: [],
|
|
smtpConfig: {
|
|
enabled: false,
|
|
host: '',
|
|
port: 587,
|
|
username: '',
|
|
hasPassword: false,
|
|
senderEmail: '',
|
|
senderName: '',
|
|
minIntervalSeconds: 60,
|
|
},
|
|
requireEmailVerification: false,
|
|
disableSignup: false,
|
|
passwordMinLength: 8,
|
|
requireNumber: false,
|
|
requireLowercase: false,
|
|
requireUppercase: false,
|
|
requireSpecialChar: false,
|
|
verifyEmailMethod: 'otp',
|
|
resetPasswordMethod: 'otp',
|
|
allowedRedirectUrls: [],
|
|
},
|
|
database: {
|
|
tables: [],
|
|
totalSizeInGB: 0,
|
|
},
|
|
storage: {
|
|
buckets: [],
|
|
totalSizeInGB: 0,
|
|
},
|
|
functions: [],
|
|
};
|
|
|
|
const md = metadataService.formatAsMarkdown(minimal);
|
|
|
|
expect(md).toContain('# Project Metadata');
|
|
expect(md).toContain('No storage buckets configured.');
|
|
expect(md).toContain('No edge functions deployed.');
|
|
// Realtime section omitted entirely when undefined
|
|
expect(md).not.toContain('## Realtime');
|
|
// Deployments section omitted for self-hosted
|
|
expect(md).not.toContain('## Deployments');
|
|
});
|
|
|
|
it('renders database hint when present', () => {
|
|
const withHint: AppMetadataSchema = {
|
|
...sampleMetadata,
|
|
database: {
|
|
tables: [{ tableName: 'users', recordCount: 10 }],
|
|
totalSizeInGB: 0.1,
|
|
hint: 'Consider adding indexes',
|
|
},
|
|
};
|
|
|
|
const md = metadataService.formatAsMarkdown(withHint);
|
|
|
|
expect(md).toContain('| users | 10 |');
|
|
expect(md).toContain('Consider adding indexes');
|
|
});
|
|
|
|
it('renders deployments section for cloud projects', () => {
|
|
const withDeployments: AppMetadataSchema = {
|
|
...sampleMetadata,
|
|
deployments: { customSlug: 'my-app' },
|
|
};
|
|
|
|
const md = metadataService.formatAsMarkdown(withDeployments);
|
|
|
|
expect(md).toContain('## Deployments');
|
|
expect(md).toContain('`my-app`');
|
|
});
|
|
|
|
it('renders deployments with null slug', () => {
|
|
const withNullSlug: AppMetadataSchema = {
|
|
...sampleMetadata,
|
|
deployments: { customSlug: null },
|
|
};
|
|
|
|
const md = metadataService.formatAsMarkdown(withNullSlug);
|
|
|
|
expect(md).toContain('## Deployments');
|
|
expect(md).toContain('not set');
|
|
});
|
|
|
|
it('preserves backticks in values using double-backtick code spans', () => {
|
|
const withBackticks: AppMetadataSchema = {
|
|
...sampleMetadata,
|
|
database: {
|
|
tables: [{ tableName: 'users', recordCount: 10 }],
|
|
totalSizeInGB: 0.1,
|
|
hint: 'Use `auth.uid()` for RLS policies',
|
|
},
|
|
functions: [
|
|
{
|
|
slug: 'check-`role`',
|
|
name: 'check-role',
|
|
status: 'active',
|
|
description: 'Validates `admin` access',
|
|
},
|
|
],
|
|
};
|
|
|
|
const md = metadataService.formatAsMarkdown(withBackticks);
|
|
|
|
// Hint is plain text — backticks preserved as-is
|
|
expect(md).toContain('Use `auth.uid()` for RLS policies');
|
|
// Slug with backticks uses double-backtick code span
|
|
expect(md).toContain('`` check-`role` ``');
|
|
// Description is plain text — backticks preserved
|
|
expect(md).toContain('Validates `admin` access');
|
|
});
|
|
|
|
it('handles consecutive backtick runs with wider delimiters', () => {
|
|
const withDoubleBackticks: AppMetadataSchema = {
|
|
...sampleMetadata,
|
|
functions: [
|
|
{
|
|
slug: 'a``b',
|
|
name: 'a-b',
|
|
status: 'active',
|
|
description: '',
|
|
},
|
|
],
|
|
};
|
|
|
|
const md = metadataService.formatAsMarkdown(withDoubleBackticks);
|
|
|
|
// Double-backtick run in value requires triple-backtick delimiter
|
|
expect(md).toContain('``` a``b ```');
|
|
});
|
|
|
|
it('escapes pipes in table cells without affecting code spans', () => {
|
|
const withPipes: AppMetadataSchema = {
|
|
...sampleMetadata,
|
|
database: {
|
|
tables: [{ tableName: 'public|users', recordCount: 5 }],
|
|
totalSizeInGB: 0.1,
|
|
},
|
|
storage: {
|
|
buckets: [{ name: 'a|b', public: true, createdAt: '2026-01-01T00:00:00Z', objectCount: 1 }],
|
|
totalSizeInGB: 0,
|
|
},
|
|
};
|
|
|
|
const md = metadataService.formatAsMarkdown(withPipes);
|
|
|
|
// Pipes escaped in table cells
|
|
expect(md).toContain('| public\\|users | 5 |');
|
|
// Bucket name in code span — pipes not escaped
|
|
expect(md).toContain('`a|b`');
|
|
});
|
|
});
|