/* eslint-disable @typescript-eslint/naming-convention -- We are dealing with repository names, which are in kebab-case */ /** * @copyright NHCarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import { readFile } from "node:fs/promises"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { parse } from "yaml"; import type { Projects } from "../src/interfaces/projects.js"; interface Repository { id: number; owner: { id: number; login: string; login_name: string; source_id: number; full_name: string; email: string; avatar_url: string; html_url: string; language: string; is_admin: boolean; last_login: string; created: string; restricted: boolean; active: boolean; prohibit_login: boolean; location: string; website: string; description: string; visibility: string; followers_count: number; following_count: number; starred_repos_count: number; username: string; }; name: string; full_name: string; description: string; empty: boolean; private: boolean; fork: boolean; template: boolean; mirror: boolean; size: number; language: string; languages_url: string; html_url: string; url: string; link: string; ssh_url: string; clone_url: string; original_url: string; website: string; stars_count: number; forks_count: number; watchers_count: number; open_issues_count: number; open_pr_counter: number; release_counter: number; default_branch: string; archived: boolean; created_at: string; updated_at: string; archived_at: string; permissions: { admin: boolean; push: boolean; pull: boolean; }; has_issues: boolean; internal_tracker: { enable_time_tracker: boolean; allow_only_contributors_to_track_time: boolean; enable_issue_dependencies: boolean; }; has_wiki: boolean; has_pull_requests: boolean; has_projects: boolean; projects_mode: string; has_releases: boolean; has_packages: boolean; has_actions: boolean; ignore_whitespace_conflicts: boolean; allow_merge_commits: boolean; allow_rebase: boolean; allow_rebase_explicit: boolean; allow_squash_merge: boolean; allow_fast_forward_only_merge: boolean; allow_rebase_update: boolean; default_delete_branch_after_merge: boolean; default_merge_style: string; default_allow_maintainer_edit: boolean; avatar_url: string; internal: boolean; mirror_interval: string; object_format_name: string; mirror_updated: string; topics: Array; licenses: Array; } const getRepositories = async(): Promise> => { const repos: Array = []; const orgs = [ "nhcarrigan", "nhcarrigan-games", ]; for (const org of orgs) { const response = await fetch(`https://git.nhcarrigan.com/api/v1/orgs/${org}/repos?limit=50`); const data = await response.json(); repos.push(...data); if (data.length === 50) { let page = 2; while (true) { const responseInner = await fetch(`https://git.nhcarrigan.com/api/v1/orgs/${org}/repos?limit=50&page=${page}`); const dataInner = await responseInner.json(); repos.push(...dataInner); if (dataInner.length < 50) { break; } page = page + 1; } } } return repos; }; const repoNameMap = { "a4p-bot": "Artists4Palestine Bot", "beccalia-origins": "Beccalia: Origins", "beccalia-prologue": "Beccalia: Prologue", "blog": "Naomi's Blog", "data": "Data API", "docs": "NHCarrigan Documentation", "eslint-config": "ESLint Config", "fcc-review-generator": "freeCodeCamp Review Generator", "life-of-a-naomi": "Life of a Naomi", "naomis-adventure-1": "Naomi's Adventure I: An Isekai Story", "ruu-goblin-quest": "Ruu's Goblin Quest", "typescript-config": "TypeScript Config", "vscode-themes": "Naomi's VSCode Themes", }; const excludedRepos = new Set([ "template", "espanso", "rig-task-bot", "security", "nginx-configs", ".profile", ".gitea", "womp-womp", "sakura", ]); const convertKebabCaseToTitleCase = (string_: string): string => { return string_.replaceAll("-", " ").replaceAll(/\b\w/g, (char) => { return char.toUpperCase(); }); }; describe("projects data", () => { it("should include all repositories", async() => { expect.hasAssertions(); const repos = await getRepositories(); const data = await readFile( join(import.meta.dirname, "..", "data", "projects.yml"), "utf8", ); const parsed = parse(data) as Projects; expect(parsed, `Parsed projects data should be defined`).toBeDefined(); expect(Array.isArray(parsed), `Parsed projects data should be an array`).toBeTruthy(); expect(parsed.length, `There should be at least one project`).toBeGreaterThan(0); for (const repo of repos) { if (excludedRepos.has(repo.name)) { continue; } const project = parsed.find((p) => { return repo.name in repoNameMap ? p.name === repoNameMap[repo.name] : p.name === convertKebabCaseToTitleCase(repo.name); }); expect(project, `Project should be defined for repository ${convertKebabCaseToTitleCase(repo.name)}`).toBeDefined(); } }); });