diff --git a/dist/schema.json b/dist/schema.json index 6ea7f8a..9726923 100644 --- a/dist/schema.json +++ b/dist/schema.json @@ -35204,6 +35204,28 @@ "x", "y" ] + }, + "small": { + "description": "Details about the small preview version of the attachment.", + "oneOf": [ + { + "$ref": "#/components/schemas/MetaDetails" + }, + { + "type": "null" + } + ] + }, + "original": { + "description": "Details about the original version of the attachment.", + "oneOf": [ + { + "$ref": "#/components/schemas/MetaDetails" + }, + { + "type": "null" + } + ] } } }, @@ -35241,6 +35263,58 @@ "description": "Official Mastodon API documentation" } }, + "MetaDetails": { + "type": "object", + "description": "Metadata details about a media attachment.", + "properties": { + "aspect": { + "description": "The media aspect ratio of the video or image attachment.", + "type": [ + "number", + "null" + ] + }, + "bitrate": { + "description": "The media bitrate of the video or audio attachment.", + "type": [ + "integer", + "null" + ] + }, + "duration": { + "description": "The duration of the video attachment.", + "type": [ + "number", + "null" + ] + }, + "frame_rate": { + "description": "The frame rate of the video attachment.", + "type": [ + "string", + "null" + ] + }, + "height": { + "description": "The height of the attachment in pixels.", + "type": [ + "integer", + "null" + ] + }, + "width": { + "description": "The width of the attachment in pixels.", + "type": [ + "integer", + "null" + ] + } + }, + "externalDocs": { + "url": "https://docs.joinmastodon.org/entities/MetaDetails/#attributes", + "description": "Official Mastodon API documentation" + } + }, "Notification": { "type": "object", "description": "Represents a notification of an event relevant to the user.", diff --git a/src/__tests__/parsers/EntityParser.draft.test.ts b/src/__tests__/parsers/EntityParser.draft.test.ts index e1bd554..e353ed3 100644 --- a/src/__tests__/parsers/EntityParser.draft.test.ts +++ b/src/__tests__/parsers/EntityParser.draft.test.ts @@ -40,6 +40,6 @@ describe('EntityParser - Draft File Handling', () => { // This test validates that only explicit draft: true is filtered // This is implicitly tested by checking that we still get a reasonable number of entities const entities = parser.parseAllEntities(); - expect(entities.length).toBe(91); // Exact count after removing EncryptedMessage and entities from blocked files (increased due to extracted nested entities + Admin::DimensionData + DiscoverOauthServerConfigurationResponse + OEmbedResponse) + expect(entities.length).toBe(92); // Exact count after removing EncryptedMessage and entities from blocked files (increased due to extracted nested entities + Admin::DimensionData + DiscoverOauthServerConfigurationResponse + OEmbedResponse + MetaDetails) }); }); diff --git a/src/__tests__/parsers/EntityParser.metadetails.test.ts b/src/__tests__/parsers/EntityParser.metadetails.test.ts new file mode 100644 index 0000000..3c1d40e --- /dev/null +++ b/src/__tests__/parsers/EntityParser.metadetails.test.ts @@ -0,0 +1,85 @@ +import { EntityParser } from '../../parsers/EntityParser'; + +describe('EntityParser - MetaDetails Entity', () => { + let entities: ReturnType; + + beforeAll(() => { + const parser = new EntityParser(); + entities = parser.parseAllEntities(); + }); + + test('should parse MetaDetails entity', () => { + const metaDetails = entities.find((e) => e.name === 'MetaDetails'); + + expect(metaDetails).toBeDefined(); + expect(metaDetails?.description).toBe( + 'Metadata details about a media attachment.' + ); + }); + + test('MetaDetails should have all required properties', () => { + const metaDetails = entities.find((e) => e.name === 'MetaDetails'); + + expect(metaDetails).toBeDefined(); + expect(metaDetails?.attributes).toBeDefined(); + + const attributeNames = metaDetails?.attributes.map((attr) => attr.name); + expect(attributeNames).toContain('width'); + expect(attributeNames).toContain('height'); + expect(attributeNames).toContain('frame_rate'); + expect(attributeNames).toContain('duration'); + expect(attributeNames).toContain('bitrate'); + expect(attributeNames).toContain('aspect'); + }); + + test('MetaDetails properties should be nullable', () => { + const metaDetails = entities.find((e) => e.name === 'MetaDetails'); + + expect(metaDetails).toBeDefined(); + expect(metaDetails?.attributes).toBeDefined(); + + // All properties should be nullable + metaDetails?.attributes.forEach((attr) => { + expect(attr.nullable).toBe(true); + }); + }); + + test('MediaAttachment should have meta[small] and meta[original] properties', () => { + const mediaAttachment = entities.find((e) => e.name === 'MediaAttachment'); + + expect(mediaAttachment).toBeDefined(); + expect(mediaAttachment?.attributes).toBeDefined(); + + const attributeNames = mediaAttachment?.attributes.map((attr) => attr.name); + expect(attributeNames).toContain('meta[small]'); + expect(attributeNames).toContain('meta[original]'); + }); + + test('MediaAttachment meta[small] should reference MetaDetails', () => { + const mediaAttachment = entities.find((e) => e.name === 'MediaAttachment'); + + expect(mediaAttachment).toBeDefined(); + + const metaSmall = mediaAttachment?.attributes.find( + (attr) => attr.name === 'meta[small]' + ); + expect(metaSmall).toBeDefined(); + expect(metaSmall?.type).toContain('MetaDetails'); + expect(metaSmall?.optional).toBe(true); + expect(metaSmall?.nullable).toBe(true); + }); + + test('MediaAttachment meta[original] should reference MetaDetails', () => { + const mediaAttachment = entities.find((e) => e.name === 'MediaAttachment'); + + expect(mediaAttachment).toBeDefined(); + + const metaOriginal = mediaAttachment?.attributes.find( + (attr) => attr.name === 'meta[original]' + ); + expect(metaOriginal).toBeDefined(); + expect(metaOriginal?.type).toContain('MetaDetails'); + expect(metaOriginal?.optional).toBe(true); + expect(metaOriginal?.nullable).toBe(true); + }); +});