AbdulElahGwaith's picture
Upload folder using huggingface_hub
88df9e4 verified
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'
// Operations have dynamic structure from OpenAPI schema - using any to avoid complex type definitions
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
}
// OpenAPI operations object has dynamic structure based on categories and subcategories
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', () => {
// Unique set of all categories across all versions of the OpenAPI schema
const allCategories = new Set<string>()
// Entire schema including categories and subcategories - using any due to dynamic OpenAPI structure
const openApiSchema: Record<string, any> = {}
// All applicable version of categories based on frontmatter in the categories index.md file
const categoryApplicableVersions: Record<string, any> = {}
function getApplicableVersionFromFile(file: string) {
const currentFile = fs.readFileSync(file, 'utf8')
// Frontmatter data structure is dynamic based on file content
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
}
}
// Read the versions from each index.md file to build a list of
// applicable versions for each category
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 () => {
// List of categories derived from disk
const filenames = new Set(
getAutomatedMarkdownFiles('content/rest')
// Gets just category level files (paths directly under /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')
// Frontmatter data structure is dynamic based on file content
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', () => {
// ensure every version defined in allVersions has a correlating static
// decorated file, while allowing decorated files to exist when a version
// is not yet defined in allVersions (e.g., a GHEC static file can exist
// even though the version is not yet supported in the docs)
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) {
// Because the rest calendar dates now have latest, next, or calendar date attached to the name, we're
// now checking if the decorated file names now start with an openApiBaseName
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()
// Code examples have dynamic structure from OpenAPI schema
for (const example of operation.codeExamples as any[]) {
expect(isPlainObject(example.request)).toBe(true)
expect(isPlainObject(example.response)).toBe(true)
}
}
})
})