diff --git a/modules/tasks/doc/tasks.yml b/modules/tasks/doc/tasks.yml index d2b6aebee..4df06707b 100644 --- a/modules/tasks/doc/tasks.yml +++ b/modules/tasks/doc/tasks.yml @@ -7,6 +7,7 @@ paths: get: tags: - Tasks + operationId: getTaskStats summary: Get task statistics description: Returns the estimated total number of tasks. Public endpoint — no authentication required. responses: @@ -29,6 +30,7 @@ paths: get: tags: - Tasks + operationId: listTasks summary: List tasks description: Returns all tasks scoped to the current organization. Requires authentication. security: @@ -59,6 +61,7 @@ paths: post: tags: - Tasks + operationId: createTask summary: Create a task description: Creates a new task scoped to the current organization. The requesting user is set as the task owner. security: @@ -94,6 +97,7 @@ paths: get: tags: - Tasks + operationId: getTask summary: Get a task description: Returns a single task by ID. Ownership is enforced by CASL policy. security: @@ -123,6 +127,7 @@ paths: put: tags: - Tasks + operationId: updateTask summary: Update a task description: Updates an existing task. All fields are optional — only provided fields are updated. Ownership is enforced by CASL policy. security: @@ -160,6 +165,7 @@ paths: delete: tags: - Tasks + operationId: deleteTask summary: Delete a task description: Deletes a task by ID. Ownership is enforced by CASL policy. security: diff --git a/modules/tasks/tests/tasks.openapi-operationid.unit.tests.js b/modules/tasks/tests/tasks.openapi-operationid.unit.tests.js new file mode 100644 index 000000000..1d5a766e6 --- /dev/null +++ b/modules/tasks/tests/tasks.openapi-operationid.unit.tests.js @@ -0,0 +1,29 @@ +import { describe, test, expect } from '@jest/globals'; +import yaml from 'js-yaml'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('modules/tasks/doc/tasks.yml — OpenAPI operationIds:', () => { + const specPath = path.resolve(__dirname, '../doc/tasks.yml'); + const spec = yaml.load(fs.readFileSync(specPath, 'utf8')); + + test('every operation has a unique operationId', () => { + const operationIds = []; + for (const [, pathItem] of Object.entries(spec.paths ?? {})) { + for (const [method, operation] of Object.entries(pathItem)) { + if (['get', 'post', 'put', 'patch', 'delete'].includes(method)) { + expect(operation.operationId).toBeDefined(); + expect(typeof operation.operationId).toBe('string'); + operationIds.push(operation.operationId); + } + } + } + // Uniqueness check + expect(new Set(operationIds).size).toBe(operationIds.length); + // At least one operationId (tasks.yml is not empty) + expect(operationIds.length).toBeGreaterThan(0); + }); +});