| | import fs from 'fs' |
| | import path from 'path' |
| |
|
| | import { beforeAll, describe, expect, test } from 'vitest' |
| | import walk from 'walk-sync' |
| | import { isPlainObject, difference } from 'lodash-es' |
| |
|
| | import { isApiVersioned, allVersions } from '@/versions/lib/all-versions' |
| | import getRest from '../lib/index' |
| | import readFrontmatter from '@/frame/lib/read-frontmatter' |
| | import frontmatter from '@/frame/lib/frontmatter' |
| | import getApplicableVersions from '../../versions/lib/get-applicable-versions' |
| | import { getAutomatedMarkdownFiles } from '../scripts/test-open-api-schema' |
| | import { nonAutomatedRestPaths } from '../lib/config' |
| |
|
| | const schemasPath = 'src/rest/data' |
| |
|
| | |
| | async function getFlatListOfOperations(version: string): Promise<any[]> { |
| | const flatList = [] |
| |
|
| | if (isApiVersioned(version)) { |
| | const apiVersions = allVersions[version].apiVersions |
| |
|
| | for (const apiVersion of apiVersions) { |
| | const operations = await getRest(version, apiVersion) |
| | flatList.push(...createCategoryList(operations)) |
| | } |
| | } else { |
| | const operations = await getRest(version) |
| | flatList.push(...createCategoryList(operations)) |
| | } |
| |
|
| | return flatList |
| | } |
| |
|
| | |
| | function createCategoryList(operations: any): any[] { |
| | const catSubCatList = [] |
| | for (const category of Object.keys(operations)) { |
| | const subcategories = Object.keys(operations[category]) |
| | for (const subcategory of subcategories) { |
| | catSubCatList.push(...operations[category][subcategory]) |
| | } |
| | } |
| |
|
| | return catSubCatList |
| | } |
| |
|
| | describe('markdown for each rest version', () => { |
| | |
| | const allCategories = new Set<string>() |
| | |
| | const openApiSchema: Record<string, any> = {} |
| | |
| | const categoryApplicableVersions: Record<string, any> = {} |
| |
|
| | function getApplicableVersionFromFile(file: string) { |
| | const currentFile = fs.readFileSync(file, 'utf8') |
| | |
| | const { data } = frontmatter(currentFile) as { data: any } |
| | return getApplicableVersions(data.versions, file) |
| | } |
| |
|
| | function getCategorySubcategory(file: string) { |
| | const fileSplit = file.split('/') |
| | const cat = fileSplit[fileSplit.length - 2] |
| | const subCat = fileSplit[fileSplit.length - 1].replace('.md', '') |
| | return { category: cat, subCategory: subCat } |
| | } |
| |
|
| | beforeAll(async () => { |
| | for (const version in allVersions) { |
| | if (isApiVersioned(version)) { |
| | for (const apiVersion of allVersions[version].apiVersions) { |
| | const apiOperations = await getRest(version, apiVersion) |
| | for (const category of Object.keys(apiOperations)) { |
| | allCategories.add(category) |
| | } |
| | openApiSchema[version] = apiOperations |
| | } |
| | } else { |
| | const apiOperations = await getRest(version) |
| | for (const category of Object.keys(apiOperations)) { |
| | allCategories.add(category) |
| | } |
| | openApiSchema[version] = apiOperations |
| | } |
| | } |
| |
|
| | |
| | |
| | for (const file of walk('content/rest', { includeBasePath: true, directories: false }).filter( |
| | (filename) => filename.includes('index.md'), |
| | )) { |
| | const applicableVersions = getApplicableVersionFromFile(file) |
| | const { category } = getCategorySubcategory(file) |
| | categoryApplicableVersions[category] = applicableVersions |
| | } |
| | }) |
| |
|
| | test('markdown file exists for every operationId prefix in all versions of the OpenAPI schema', async () => { |
| | |
| | const filenames = new Set( |
| | getAutomatedMarkdownFiles('content/rest') |
| | |
| | .map((filename) => filename.split('/')[2]) |
| | .sort(), |
| | ) |
| |
|
| | const missingResource = |
| | 'Found a markdown file in content/rest that is not represented by an OpenAPI REST operation category.' |
| | expect(difference([...filenames], [...allCategories]), missingResource).toEqual([]) |
| |
|
| | const missingFile = |
| | 'Found an OpenAPI REST operation category that is not represented by a markdown file in content/rest.' |
| | expect(difference([...allCategories], [...filenames]), missingFile).toEqual([]) |
| | }) |
| |
|
| | test('category and subcategory exist in OpenAPI schema for every applicable version in markdown frontmatter', async () => { |
| | const automatedFiles = getAutomatedMarkdownFiles('content/rest') |
| | for (const file of automatedFiles) { |
| | const applicableVersions = getApplicableVersionFromFile(file) |
| | const { category, subCategory } = getCategorySubcategory(file) |
| |
|
| | for (const version of applicableVersions) { |
| | expect( |
| | Object.keys(openApiSchema[version][category]), |
| | `The REST version: ${version}'s category: ${category} does not include the subcategory: ${subCategory}. Please check file: ${file}`, |
| | ).toContain(subCategory) |
| | expect( |
| | categoryApplicableVersions[category], |
| | `The versions that apply to category ${category} does not contain the ${version}, as is expected. Please check the versions for file ${file} or look at the index that governs that file (in its parent directory).`, |
| | ).toContain(version) |
| | } |
| | } |
| | }) |
| | }) |
| |
|
| | describe('rest file structure', () => { |
| | test('children of content/rest/index.md are in alphabetical order', async () => { |
| | const indexContent = fs.readFileSync('content/rest/index.md', 'utf8') |
| | |
| | const { data } = readFrontmatter(indexContent) as { data: any } |
| | const nonAutomatedChildren = nonAutomatedRestPaths.map((child: string) => |
| | child.replace('/rest', ''), |
| | ) |
| | const sortableChildren = data.children.filter( |
| | (child: string) => !nonAutomatedChildren.includes(child), |
| | ) |
| | expect(sortableChildren).toStrictEqual([...sortableChildren].sort()) |
| | }) |
| | }) |
| |
|
| | describe('OpenAPI schema validation', () => { |
| | |
| | |
| | |
| | |
| | test('every OpenAPI version must have a schema file in the docs', async () => { |
| | const decoratedFilenames = walk(schemasPath).map((filename) => path.basename(filename, '.json')) |
| | const openApiBaseNames = Object.values(allVersions).map((version) => version.openApiVersionName) |
| | for (const openApiBaseName of openApiBaseNames) { |
| | |
| | |
| | expect( |
| | decoratedFilenames.some((versionFile) => versionFile.startsWith(openApiBaseName)), |
| | ).toBe(true) |
| | } |
| | }) |
| |
|
| | test('operations object structure organized by version, category, and subcategory', async () => { |
| | for (const version in allVersions) { |
| | const operations = await getFlatListOfOperations(version) |
| | expect(operations.every((operation) => operation.verb)).toBe(true) |
| | } |
| | }) |
| |
|
| | test('no wrongly detected AppleScript syntax highlighting in schema data', async () => { |
| | const countAssertions = Object.keys(allVersions) |
| | .map((version) => allVersions[version].apiVersions.length) |
| | .reduce((prevLength, currLength) => prevLength + currLength) |
| |
|
| | expect.assertions(countAssertions) |
| | await Promise.all( |
| | Object.keys(allVersions).map(async (version) => { |
| | for (const apiVersion of allVersions[version].apiVersions) { |
| | const operations = await getRest(version, apiVersion) |
| | expect(JSON.stringify(operations).includes('hljs language-applescript')).toBe(false) |
| | } |
| | }), |
| | ) |
| | }) |
| | }) |
| |
|
| | async function findOperation(version: string, method: string, requestPath: string) { |
| | const allOperations = await getFlatListOfOperations(version) |
| | return allOperations.find((operation) => { |
| | return ( |
| | operation.requestPath === requestPath && operation.verb.toLowerCase() === method.toLowerCase() |
| | ) |
| | }) |
| | } |
| |
|
| | describe('code examples are defined', () => { |
| | test('GET', async () => { |
| | for (const version in allVersions) { |
| | if (version === 'enterprise-server@3.2' || version === 'enterprise-server@3.1') continue |
| |
|
| | let domain = 'https://api.github.com' |
| | if (version.includes('enterprise-server')) { |
| | domain = 'http(s)://HOSTNAME/api/v3' |
| | } |
| |
|
| | const operation = await findOperation(version, 'GET', '/repos/{owner}/{repo}') |
| | expect(operation.serverUrl).toBe(domain) |
| | expect(isPlainObject(operation)).toBe(true) |
| | expect(operation.codeExamples).toBeDefined() |
| | |
| | for (const example of operation.codeExamples as any[]) { |
| | expect(isPlainObject(example.request)).toBe(true) |
| | expect(isPlainObject(example.response)).toBe(true) |
| | } |
| | } |
| | }) |
| | }) |
| |
|