From 292bc0fc810a0d0f0cce6813b0631ff25099cc05 Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Wed, 3 Sep 2025 21:09:37 -0400 Subject: [PATCH] fix: api version json response (#1653) ## Summary by CodeRabbit * **New Features** * Version command now supports JSON output via a -j/--json flag, returning version, build (when available), and a combined value. Default human-readable output remains unchanged. * **Tests** * Added comprehensive tests for version command behavior across human-readable and JSON modes, including scenarios with and without build metadata. * **Chores** * Bumped API configuration version from 4.18.1 to 4.18.2. --- api/dev/configs/api.json | 2 +- .../cli/__test__/version.command.test.ts | 111 ++++++++++++++++++ api/src/unraid-api/cli/version.command.ts | 31 ++++- 3 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 api/src/unraid-api/cli/__test__/version.command.test.ts diff --git a/api/dev/configs/api.json b/api/dev/configs/api.json index 0a837375c..bb7363f23 100644 --- a/api/dev/configs/api.json +++ b/api/dev/configs/api.json @@ -1,5 +1,5 @@ { - "version": "4.18.1", + "version": "4.18.2", "extraOrigins": [], "sandbox": true, "ssoSubIds": [], diff --git a/api/src/unraid-api/cli/__test__/version.command.test.ts b/api/src/unraid-api/cli/__test__/version.command.test.ts new file mode 100644 index 000000000..0d62f1f6c --- /dev/null +++ b/api/src/unraid-api/cli/__test__/version.command.test.ts @@ -0,0 +1,111 @@ +import { Test, TestingModule } from '@nestjs/testing'; + +import { afterEach, beforeEach, describe, expect, it, MockInstance, vi } from 'vitest'; + +import { LogService } from '@app/unraid-api/cli/log.service.js'; +import { VersionCommand } from '@app/unraid-api/cli/version.command.js'; + +let API_VERSION_MOCK = '4.18.2+build123'; + +vi.mock('@app/environment.js', async (importOriginal) => { + const actual = (await importOriginal()) as any; + return { + ...actual, + get API_VERSION() { + return API_VERSION_MOCK; + }, + }; +}); + +describe('VersionCommand', () => { + let command: VersionCommand; + let logService: LogService; + let consoleLogSpy: MockInstance; + + beforeEach(async () => { + API_VERSION_MOCK = '4.18.2+build123'; // Reset to default before each test + consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + VersionCommand, + { + provide: LogService, + useValue: { + info: vi.fn(), + }, + }, + ], + }).compile(); + + command = module.get(VersionCommand); + logService = module.get(LogService); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('run', () => { + it('should output version with logger when no options provided', async () => { + await command.run([]); + + expect(logService.info).toHaveBeenCalledWith('Unraid API v4.18.2+build123'); + expect(consoleLogSpy).not.toHaveBeenCalled(); + }); + + it('should output version with logger when json option is false', async () => { + await command.run([], { json: false }); + + expect(logService.info).toHaveBeenCalledWith('Unraid API v4.18.2+build123'); + expect(consoleLogSpy).not.toHaveBeenCalled(); + }); + + it('should output JSON when json option is true', async () => { + await command.run([], { json: true }); + + expect(logService.info).not.toHaveBeenCalled(); + expect(consoleLogSpy).toHaveBeenCalledWith( + JSON.stringify({ + version: '4.18.2', + build: 'build123', + combined: '4.18.2+build123', + }) + ); + }); + + it('should handle version without build info', async () => { + API_VERSION_MOCK = '4.18.2'; // Set version without build info + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + VersionCommand, + { + provide: LogService, + useValue: { + info: vi.fn(), + }, + }, + ], + }).compile(); + + const commandWithoutBuild = module.get(VersionCommand); + + await commandWithoutBuild.run([], { json: true }); + + expect(consoleLogSpy).toHaveBeenCalledWith( + JSON.stringify({ + version: '4.18.2', + build: undefined, + combined: '4.18.2', + }) + ); + }); + }); + + describe('parseJson', () => { + it('should return true', () => { + expect(command.parseJson()).toBe(true); + }); + }); +}); diff --git a/api/src/unraid-api/cli/version.command.ts b/api/src/unraid-api/cli/version.command.ts index 05a0c2800..fd38dd8d7 100644 --- a/api/src/unraid-api/cli/version.command.ts +++ b/api/src/unraid-api/cli/version.command.ts @@ -1,14 +1,37 @@ -import { Command, CommandRunner } from 'nest-commander'; +import { Command, CommandRunner, Option } from 'nest-commander'; import { API_VERSION } from '@app/environment.js'; import { LogService } from '@app/unraid-api/cli/log.service.js'; -@Command({ name: 'version' }) +interface VersionOptions { + json?: boolean; +} + +@Command({ name: 'version', description: 'Display API version information' }) export class VersionCommand extends CommandRunner { constructor(private readonly logger: LogService) { super(); } - async run(): Promise { - this.logger.info(`Unraid API v${API_VERSION}`); + + @Option({ + flags: '-j, --json', + description: 'Output version information as JSON', + }) + parseJson(): boolean { + return true; + } + + async run(passedParam: string[], options?: VersionOptions): Promise { + if (options?.json) { + const [baseVersion, buildInfo] = API_VERSION.split('+'); + const versionInfo = { + version: baseVersion || API_VERSION, + build: buildInfo || undefined, + combined: API_VERSION, + }; + console.log(JSON.stringify(versionInfo)); + } else { + this.logger.info(`Unraid API v${API_VERSION}`); + } } }