Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: "weekly"
interval: 'weekly'
5 changes: 4 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Check code formatting
run: npm run format:check

- name: Build project
run: npm run build

- name: Run tests
run: npm test
run: npm test
17 changes: 17 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Dependencies
node_modules/

# Build output
dist/

# Coverage reports
coverage/

# Logs
*.log

# Documentation clone
mastodon-documentation/

# Lock files
package-lock.json
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# mastodon-openapi
# mastodon-openapi
7 changes: 2 additions & 5 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,5 @@ module.exports = {
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
],
};
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'],
};
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"test": "jest",
"test:watch": "jest --watch",
"clean": "rm -rf dist",
"format": "prettier --write .",
"format:check": "prettier --check .",
"postinstall": "git clone https://github.com/mastodon/documentation mastodon-documentation || true"
},
"keywords": [
Expand All @@ -26,6 +28,7 @@
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.15.30",
"jest": "^29.5.0",
"prettier": "^3.5.3",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.0",
"typescript": "^5.0.0"
Expand Down
40 changes: 21 additions & 19 deletions src/__tests__/generators/OpenAPIGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ describe('OpenAPIGenerator', () => {
{
name: 'id',
type: 'Integer',
description: 'The ID of the entity'
description: 'The ID of the entity',
},
{
name: 'name',
type: 'String',
description: 'The name of the entity',
optional: true
optional: true,
},
{
name: 'active',
type: 'Boolean',
description: 'Whether the entity is active',
deprecated: true
}
]
}
deprecated: true,
},
],
},
];

const methodFiles: ApiMethodsFile[] = [
Expand All @@ -48,7 +48,7 @@ describe('OpenAPIGenerator', () => {
endpoint: '/api/v1/test/:id',
description: 'Retrieve a test entity',
returns: 'TestEntity',
oauth: 'User token + read'
oauth: 'User token + read',
},
{
name: 'Create test entity',
Expand All @@ -59,17 +59,17 @@ describe('OpenAPIGenerator', () => {
{
name: 'name',
description: 'Name of the entity',
required: true
required: true,
},
{
name: 'active',
description: 'Whether entity is active'
}
description: 'Whether entity is active',
},
],
oauth: 'User token + write'
}
]
}
oauth: 'User token + write',
},
],
},
];

const spec = generator.generateSchema(entities, methodFiles);
Expand Down Expand Up @@ -118,12 +118,14 @@ describe('OpenAPIGenerator', () => {
const postOp = spec.paths['/api/v1/test'].post!;
expect(postOp.summary).toBe('Create test entity');
expect(postOp.requestBody).toBeDefined();
expect(postOp.requestBody?.content['application/x-www-form-urlencoded']).toBeDefined();
expect(
postOp.requestBody?.content['application/x-www-form-urlencoded']
).toBeDefined();
});

it('should handle empty inputs', () => {
const spec = generator.generateSchema([], []);

expect(spec.openapi).toBe('3.0.3');
expect(spec.paths).toEqual({});
expect(spec.components?.schemas).toEqual({});
Expand All @@ -134,13 +136,13 @@ describe('OpenAPIGenerator', () => {
it('should return valid JSON string', () => {
const entities: EntityClass[] = [];
const methodFiles: ApiMethodsFile[] = [];

generator.generateSchema(entities, methodFiles);
const json = generator.toJSON();

expect(() => JSON.parse(json)).not.toThrow();
const parsed = JSON.parse(json);
expect(parsed.openapi).toBe('3.0.3');
});
});
});
});
44 changes: 26 additions & 18 deletions src/__tests__/parsers/EntityParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,64 +19,72 @@ describe('EntityParser', () => {

test('should parse entities and extract basic structure', () => {
const entities = parser.parseAllEntities();

// Verify we found entities
expect(entities.length).toBeGreaterThan(50); // Should be around 64 entities

// Find a specific entity to test
const accountEntity = entities.find(e => e.name === 'Account');
const accountEntity = entities.find((e) => e.name === 'Account');
expect(accountEntity).toBeDefined();

if (accountEntity) {
expect(accountEntity.name).toBe('Account');
expect(accountEntity.description).toContain('user of Mastodon');
expect(accountEntity.attributes.length).toBeGreaterThan(20); // Account has many attributes

// Check some specific attributes exist
const idAttribute = accountEntity.attributes.find(attr => attr.name === 'id');
const idAttribute = accountEntity.attributes.find(
(attr) => attr.name === 'id'
);
expect(idAttribute).toBeDefined();
expect(idAttribute?.type).toContain('String');

const usernameAttribute = accountEntity.attributes.find(attr => attr.name === 'username');

const usernameAttribute = accountEntity.attributes.find(
(attr) => attr.name === 'username'
);
expect(usernameAttribute).toBeDefined();
expect(usernameAttribute?.type).toBe('String');
}
});

test('should correctly identify optional and deprecated attributes', () => {
const entities = parser.parseAllEntities();

// Find entities with optional/deprecated attributes
let foundOptional = false;
let foundDeprecated = false;

for (const entity of entities) {
for (const attr of entity.attributes) {
if (attr.optional) foundOptional = true;
if (attr.deprecated) foundDeprecated = true;
}
}

expect(foundOptional).toBe(true);
expect(foundDeprecated).toBe(true);
});

test('should parse entity with simple structure', () => {
const entities = parser.parseAllEntities();

// Find Application entity which has a simpler structure
const applicationEntity = entities.find(e => e.name === 'Application');
const applicationEntity = entities.find((e) => e.name === 'Application');
expect(applicationEntity).toBeDefined();

if (applicationEntity) {
expect(applicationEntity.name).toBe('Application');
expect(applicationEntity.description).toContain('interfaces with the REST API');
expect(applicationEntity.description).toContain(
'interfaces with the REST API'
);
expect(applicationEntity.attributes.length).toBeGreaterThan(0);

// Check that name attribute exists
const nameAttribute = applicationEntity.attributes.find(attr => attr.name === 'name');
const nameAttribute = applicationEntity.attributes.find(
(attr) => attr.name === 'name'
);
expect(nameAttribute).toBeDefined();
expect(nameAttribute?.type).toBe('String');
}
});
});
});
Loading