From f5d39e61d8dea60b788964f8f11b1dc782698ebe Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Fri, 25 Apr 2025 07:08:58 +0200 Subject: [PATCH] test: ensure file formats --- .github/workflows/renovate.yml | 5 --- .github/workflows/test.yml | 23 +++++++++++ __tests__/apps.test.ts | 71 +++++++++++++++++++++++++++++++++ bun.lockb | Bin 2715 -> 8675 bytes package.json | 9 ++++- tsconfig.json | 5 ++- 6 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 __tests__/apps.test.ts diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml index 77d2c04..641324e 100644 --- a/.github/workflows/renovate.yml +++ b/.github/workflows/renovate.yml @@ -22,11 +22,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 22 - - name: Install bun uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..535cc75 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: bun install + + - name: Run tests + run: bun test diff --git a/__tests__/apps.test.ts b/__tests__/apps.test.ts new file mode 100644 index 0000000..25a335f --- /dev/null +++ b/__tests__/apps.test.ts @@ -0,0 +1,71 @@ +import { expect, test, describe } from "bun:test"; +import { appInfoSchema, dynamicComposeSchema } from '@runtipi/common/schemas' +import { fromError } from 'zod-validation-error'; +import fs from 'node:fs' +import path from 'node:path' + +const getApps = async () => { + const appsDir = await fs.promises.readdir(path.join(process.cwd(), 'apps')) + return appsDir +}; + +const getFile = async (app: string, file: string) => { + const filePath = path.join(process.cwd(), 'apps', app, file) + try { + const file = await fs.promises.readFile(filePath, 'utf-8') + return file + } catch (err) { + return null + } +} + +describe("each app should have the required files", async () => { + const apps = await getApps() + + for (const app of apps) { + const files = ['config.json', 'docker-compose.json', 'metadata/logo.jpg', 'metadata/description.md'] + + for (const file of files) { + test(`app ${app} should have ${file}`, async () => { + const fileContent = await getFile(app, file) + expect(fileContent).not.toBeNull() + }) + } + } +}) + +describe("each app should have a valid config.json", async () => { + const apps = await getApps() + + for (const app of apps) { + test(`app ${app} should have a valid config.json`, async () => { + const fileContent = await getFile(app, 'config.json') + const parsed = appInfoSchema.omit({ urn: true }).safeParse(JSON.parse(fileContent || '{}')) + + if (!parsed.success) { + const validationError = fromError(parsed.error); + console.error(`Error parsing config.json for app ${app}:`, validationError.toString()); + } + + expect(parsed.success).toBe(true) + }) + } +}) + +describe("each app should have a valid docker-compose.json", async () => { + const apps = await getApps() + + for (const app of apps) { + test(`app ${app} should have a valid docker-compose.json`, async () => { + const fileContent = await getFile(app, 'docker-compose.json') + const parsed = dynamicComposeSchema.safeParse(JSON.parse(fileContent || '{}')) + + if (!parsed.success) { + const validationError = fromError(parsed.error); + console.error(`Error parsing docker-compose.json for app ${app}:`, validationError.toString()); + } + + expect(parsed.success).toBe(true) + }) + } +}); diff --git a/bun.lockb b/bun.lockb index f7bcbffa5789067da817b6667b2a1dd384cef970..2c1bfe844b0d0f273ac0453eb75ce06e1ac7aa60 100755 GIT binary patch literal 8675 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 W{?5Ph9&oPwt=7q%8}a|%_kRF_=;t>8 delta 651 zcmaFtJX>^vp6359DarLm>N3ynzbvnJ@chyIS2}DTG;I|cE)_-`pZ`~mn~MPq*d~U{ zXXUX$I1o~RfuTVj$m9mn$v~P1NCU+f_<{6WAT0!>tMXHTECz=ALR%GeF4mPy)Q{>_ z+iGjcnQwFKRp6}M+pcb6D=T@_Z1L7=W-NQVpsv~D*X}O$2Pa=(^kC9rm@LI+$CS@7 zISNQ^V3<6M)sE>u!{l2)%7<}s6qDKH2aGI}1(;YunSgdM0x>hlG9UoaATd@T28km( z93)PZ`pK~z1(SXGyeCU=@=jjL=gr7Gc_ME((8iZQl6A5qr~l?+!Fh~a8=$5iVVmqN zY9saUKLh}+0o(rpD$T+^d6}q|7|3ZXP$w9$gA_3^cz~polt5A-*TP&5QU>xt1yq>` z$K-WlYQC^Qh6Nr6W1NAWg`NQe11vgV!3>l!GSV|N(KAd28Uu44O{s;Qf}uijW>so_nhuban3I{F zm!HO!T9KGrkdvyLSWr-0l3$eSP*j>%l39?cpPZkYo1f=UQdy8%te=;kk~;aKg3{z% Tg@(ze