From 55c2838a854f34804ba046a54ebc7a6e50b60b52 Mon Sep 17 00:00:00 2001 From: Donald Merand Date: Mon, 24 Aug 2026 12:08:24 -0400 Subject: [PATCH] Test remote contract validation policies Assisted-By: devx/533b856c-5f30-42c9-8965-a066782729a4 --- .../fetch-extension-specifications.test.ts | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/packages/app/src/cli/services/generate/fetch-extension-specifications.test.ts b/packages/app/src/cli/services/generate/fetch-extension-specifications.test.ts index 189242534d7..7176ede2e82 100644 --- a/packages/app/src/cli/services/generate/fetch-extension-specifications.test.ts +++ b/packages/app/src/cli/services/generate/fetch-extension-specifications.test.ts @@ -1,8 +1,82 @@ import {fetchSpecifications} from './fetch-extension-specifications.js' import {testDeveloperPlatformClient, testOrganizationApp} from '../../models/app/app.test-data.js' +import {RemoteSpecification} from '../../api/graphql/extension_specifications.js' import {describe, expect, test} from 'vitest' describe('fetchExtensionSpecifications', () => { + test('strips extra root configuration properties defined by the remote contract', async () => { + // Given + const specifications = await fetchSpecifications({ + developerPlatformClient: testDeveloperPlatformClient(), + app: testOrganizationApp(), + }) + const specification = specifications.find((spec) => spec.identifier === 'remote_only_extension_schema_config_style') + + // When + const result = specification!.parseConfigurationObject({pattern: 'pattern', extra: 'extra'}) + + // Then + expect(result).toEqual({ + state: 'ok', + data: {pattern: 'pattern'}, + errors: undefined, + }) + }) + + test('rejects extra root extension properties defined by the remote contract', async () => { + // Given + const specifications = await fetchSpecifications({ + developerPlatformClient: testDeveloperPlatformClient(), + app: testOrganizationApp(), + }) + const specification = specifications.find((spec) => spec.identifier === 'remote_only_extension_schema') + + // When + const result = specification!.parseConfigurationObject({pattern: 'pattern', extra: 'extra'}) + + // Then + expect(result.state).toBe('error') + expect(result.errors).toContainEqual( + expect.objectContaining({path: ['extra'], message: expect.stringContaining('No additional properties allowed')}), + ) + }) + + test('retains remote required fields for configuration modules', async () => { + // Given + const remoteSpecification: RemoteSpecification = { + name: 'Required configuration module', + externalName: 'Required Configuration Module', + identifier: 'remote_configuration_with_required_field', + externalIdentifier: 'remote_configuration_with_required_field', + gated: false, + experience: 'configuration', + managementExperience: 'cli', + registrationLimit: 1, + uidStrategy: 'single', + validationSchema: { + jsonSchema: '{"type":"object","properties":{"section":{"type":"object"}},"required":["section"]}', + }, + } + const specifications = await fetchSpecifications({ + developerPlatformClient: testDeveloperPlatformClient({ + specifications: () => Promise.resolve([remoteSpecification]), + }), + app: testOrganizationApp(), + }) + const specification = specifications.find((spec) => spec.identifier === remoteSpecification.identifier) + + // When + const result = specification!.parseConfigurationObject({}) + + // Then + // This documents the existing strip-policy gap; it does not endorse required fields in remote contracts. + expect(result).toEqual({ + state: 'error', + data: undefined, + errors: [{path: ['section'], message: 'Required'}], + }) + }) + test('returns the filtered and mapped results including theme', async () => { // Given/When const got = await fetchSpecifications({