Skip to main content
Glama
metadata-schema.test.ts13.3 kB
/** * Metadata Schema Tests * * Tests for the metadata schema system including parsing, validation, * and integration with note types. */ import { test, describe } from 'node:test'; import assert from 'node:assert'; import { MetadataSchemaParser, MetadataValidator, type MetadataSchema, type MetadataFieldType } from '../../src/core/metadata-schema.ts'; describe('MetadataSchemaParser', () => { describe('parseFromDescription', () => { test('should parse basic metadata schema from description', () => { const description = `# Book Reviews ## Purpose Track books I've read. ## Metadata Schema Expected frontmatter or metadata fields for this note type: - title: Book title (required, string) - author: Author name (required, string) - rating: Personal rating (required, number, min: 1, max: 5) - genre: Book genre (optional, string) - isbn: ISBN number (optional, string, pattern: "^[0-9-]{10,17}$") - tags: Topic tags (optional, array) `; const schema = MetadataSchemaParser.parseFromDescription(description); assert.strictEqual(schema.fields.length, 6); const titleField = schema.fields.find(f => f.name === 'title'); assert.ok(titleField); assert.strictEqual(titleField.name, 'title'); assert.strictEqual(titleField.type, 'string'); assert.strictEqual(titleField.required, true); assert.strictEqual(titleField.description, 'Book title'); const ratingField = schema.fields.find(f => f.name === 'rating'); assert.ok(ratingField); assert.strictEqual(ratingField.name, 'rating'); assert.strictEqual(ratingField.type, 'number'); assert.strictEqual(ratingField.required, true); assert.strictEqual(ratingField.description, 'Personal rating'); assert.ok(ratingField.constraints); assert.strictEqual(ratingField.constraints.min, 1); assert.strictEqual(ratingField.constraints.max, 5); const isbnField = schema.fields.find(f => f.name === 'isbn'); assert.ok(isbnField); assert.strictEqual(isbnField.name, 'isbn'); assert.strictEqual(isbnField.type, 'string'); assert.strictEqual(isbnField.required, false); assert.strictEqual(isbnField.description, 'ISBN number'); assert.ok(isbnField.constraints); assert.strictEqual(isbnField.constraints.pattern, '^[0-9-]{10,17}$'); const tagsField = schema.fields.find(f => f.name === 'tags'); assert.ok(tagsField); assert.strictEqual(tagsField.name, 'tags'); assert.strictEqual(tagsField.type, 'array'); assert.strictEqual(tagsField.required, false); assert.strictEqual(tagsField.description, 'Topic tags'); }); test('should handle select fields with options', () => { const description = `# Meeting Notes ## Metadata Schema Expected frontmatter or metadata fields for this note type: - status: Meeting status (required, select, options: ["scheduled", "completed", "cancelled"]) - priority: Priority level (optional, select, options: ["low", "medium", "high"]) `; const schema = MetadataSchemaParser.parseFromDescription(description); const statusField = schema.fields.find(f => f.name === 'status'); assert.ok(statusField); assert.strictEqual(statusField.name, 'status'); assert.strictEqual(statusField.type, 'select'); assert.strictEqual(statusField.required, true); assert.ok(statusField.constraints); assert.deepStrictEqual(statusField.constraints.options, [ 'scheduled', 'completed', 'cancelled' ]); const priorityField = schema.fields.find(f => f.name === 'priority'); assert.ok(priorityField); assert.strictEqual(priorityField.name, 'priority'); assert.strictEqual(priorityField.type, 'select'); assert.strictEqual(priorityField.required, false); assert.ok(priorityField.constraints); assert.deepStrictEqual(priorityField.constraints.options, [ 'low', 'medium', 'high' ]); }); test('should return empty schema when no metadata section exists', () => { const description = `# Simple Note Type ## Purpose Just a simple note type. ## Agent Instructions - Be helpful `; const schema = MetadataSchemaParser.parseFromDescription(description); assert.strictEqual(schema.fields.length, 0); }); }); describe('generateSchemaSection', () => { test('should generate metadata section from schema definition', () => { const schema: MetadataSchema = { fields: [ { name: 'title', type: 'string' as MetadataFieldType, required: true, description: 'Book title' }, { name: 'rating', type: 'number' as MetadataFieldType, required: true, description: 'Personal rating', constraints: { min: 1, max: 5 } }, { name: 'tags', type: 'array' as MetadataFieldType, required: false, description: 'Topic tags' } ] }; const section = MetadataSchemaParser.generateSchemaSection(schema); assert.ok(section.includes('## Metadata Schema')); assert.ok(section.includes('- title: Book title (required)')); assert.ok( section.includes('- rating: Personal rating (required, number, min: 1, max: 5)') ); assert.ok(section.includes('- tags: Topic tags (optional, array)')); }); test('should generate default schema when no fields provided', () => { const schema: MetadataSchema = { fields: [] }; const section = MetadataSchemaParser.generateSchemaSection(schema); assert.ok(section.includes('## Metadata Schema')); assert.ok(section.includes('- type: Note type (auto-set)')); assert.ok(section.includes('- created: Creation timestamp (auto-set)')); assert.ok( section.includes('- tags: Relevant tags for categorization (array, optional)') ); }); }); }); describe('MetadataValidator', () => { describe('validate', () => { const schema: MetadataSchema = { fields: [ { name: 'title', type: 'string' as MetadataFieldType, required: true, description: 'Title' }, { name: 'rating', type: 'number' as MetadataFieldType, required: true, constraints: { min: 1, max: 5 } }, { name: 'status', type: 'select' as MetadataFieldType, required: false, constraints: { options: ['draft', 'published', 'archived'] } }, { name: 'tags', type: 'array' as MetadataFieldType, required: false }, { name: 'published_date', type: 'date' as MetadataFieldType, required: false } ] }; test('should validate correct metadata', () => { const metadata = { title: 'Test Note', rating: 4, status: 'published', tags: ['test', 'example'], published_date: '2024-01-01T00:00:00Z' }; const result = MetadataValidator.validate(metadata, schema); assert.strictEqual(result.valid, true); assert.strictEqual(result.errors.length, 0); }); test('should detect missing required fields', () => { const metadata = { rating: 4 // missing required 'title' field }; const result = MetadataValidator.validate(metadata, schema); assert.strictEqual(result.valid, false); assert.strictEqual(result.errors.length, 1); assert.strictEqual(result.errors[0].field, 'title'); assert.ok(result.errors[0].message.includes("Required field 'title' is missing")); }); test('should validate field types', () => { const metadata = { title: 'Test Note', rating: 'invalid_number', // should be number tags: 'not_an_array' // should be array }; const result = MetadataValidator.validate(metadata, schema); assert.strictEqual(result.valid, false); assert.strictEqual(result.errors.length, 2); const ratingError = result.errors.find(e => e.field === 'rating'); assert.ok(ratingError); assert.ok(ratingError.message.includes('must be a number')); const tagsError = result.errors.find(e => e.field === 'tags'); assert.ok(tagsError); assert.ok(tagsError.message.includes('must be an array')); }); test('should validate number constraints', () => { const metadata = { title: 'Test Note', rating: 10 // exceeds max of 5 }; const result = MetadataValidator.validate(metadata, schema); assert.strictEqual(result.valid, false); assert.strictEqual(result.errors.length, 1); assert.strictEqual(result.errors[0].field, 'rating'); assert.ok(result.errors[0].message.includes('must be at most 5')); }); test('should validate select field options', () => { const metadata = { title: 'Test Note', rating: 4, status: 'invalid_status' // not in allowed options }; const result = MetadataValidator.validate(metadata, schema); assert.strictEqual(result.valid, false); assert.strictEqual(result.errors.length, 1); assert.ok( result.errors[0].message.includes('must be one of: draft, published, archived') ); }); test('should validate date fields', () => { const metadata = { title: 'Test Note', rating: 4, published_date: 'not-a-date' }; const result = MetadataValidator.validate(metadata, schema); assert.strictEqual(result.valid, false); assert.strictEqual(result.errors.length, 1); assert.ok(result.errors[0].message.includes('must be a valid date string')); }); test('should warn about unknown fields', () => { const metadata = { title: 'Test Note', rating: 4, unknown_field: 'some value' }; const result = MetadataValidator.validate(metadata, schema); assert.strictEqual(result.valid, true); assert.strictEqual(result.warnings.length, 1); assert.strictEqual(result.warnings[0].field, 'unknown_field'); assert.ok( result.warnings[0].message.includes( "Unknown field 'unknown_field' not defined in schema" ) ); }); test('should validate string patterns', () => { const patternSchema: MetadataSchema = { fields: [ { name: 'isbn', type: 'string' as MetadataFieldType, required: false, constraints: { pattern: '^[0-9-]{10,17}$' } } ] }; const metadata = { isbn: 'invalid-isbn-format' }; const result = MetadataValidator.validate(metadata, patternSchema); assert.strictEqual(result.valid, false); assert.ok(result.errors[0].message.includes('does not match required pattern')); }); }); describe('getDefaults', () => { test('should provide default values for required fields', () => { const schema: MetadataSchema = { fields: [ { name: 'title', type: 'string' as MetadataFieldType, required: true }, { name: 'rating', type: 'number' as MetadataFieldType, required: true, constraints: { min: 1, max: 5 } }, { name: 'published', type: 'boolean' as MetadataFieldType, required: true }, { name: 'tags', type: 'array' as MetadataFieldType, required: true }, { name: 'created', type: 'date' as MetadataFieldType, required: false }, { name: 'status', type: 'select' as MetadataFieldType, required: true, constraints: { options: ['draft', 'published'] } } ] }; const defaults = MetadataValidator.getDefaults(schema); assert.strictEqual(defaults.title, ''); assert.strictEqual(defaults.rating, 1); // min value assert.strictEqual(defaults.published, false); assert.deepStrictEqual(defaults.tags, []); assert.strictEqual(defaults.status, 'draft'); // first option assert.ok(!Object.prototype.hasOwnProperty.call(defaults, 'created')); }); test('should use explicit default values when provided', () => { const schema: MetadataSchema = { fields: [ { name: 'priority', type: 'select' as MetadataFieldType, required: false, default: 'medium', constraints: { options: ['low', 'medium', 'high'] } }, { name: 'score', type: 'number' as MetadataFieldType, required: false, default: 3 } ] }; const defaults = MetadataValidator.getDefaults(schema); assert.strictEqual(defaults.priority, 'medium'); assert.strictEqual(defaults.score, 3); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/disnet/flint-note'

If you have feedback or need assistance with the MCP directory API, please join our Discord server