Skip to content

Commit

Permalink
In v9, we could pass a string directly as the response body and it wo…
Browse files Browse the repository at this point in the history
…uld be handled correctly

In v10, we need to be more explicit about how we handle the response

Amended how the responses are handled to fit the pattern in v10.
  • Loading branch information
rocketstack-matt committed Feb 23, 2025
1 parent 7558986 commit 0f0dcc9
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 20 deletions.
85 changes: 68 additions & 17 deletions shared/src/commands/validate/validate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,12 @@ describe('validate pattern and architecture', () => {
beforeEach(() => {
mockRunFunction.mockReturnValue([]);
jest.useFakeTimers();
fetchMock.reset();
});

afterEach(() => {
fetchMock.restore();
jest.clearAllMocks();
});

it('throws error when the the Pattern and the Architecture are undefined or an empty string', async () => {
Expand Down Expand Up @@ -286,8 +288,7 @@ describe('validate pattern and architecture', () => {

it('throws error when the architecture URL returns a 404', async () => {
const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway.json'), 'utf8');

fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });
fetchMock.mock('https://does-not-exist/api-gateway-implementation.json', 404);

await expect(validate('https://does-not-exist/api-gateway-implementation.json', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled))
Expand All @@ -299,8 +300,8 @@ describe('validate pattern and architecture', () => {
const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway.json'), 'utf8');

const markdown = ' #This is markdown';
fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('https://url/with/non/json/response', markdown);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });
fetchMock.mock('https://url/with/non/json/response', { status: 200, body: markdown, headers: { 'content-type': 'text/markdown' } });

await expect(validate('https://url/with/non/json/response', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled))
.rejects
Expand All @@ -317,13 +318,20 @@ describe('validate pattern and architecture', () => {
it('has error when the architecture does not match the json schema', async () => {

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const apiGatewayArchitecture = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway-implementation-that-does-not-match-schema.json'), 'utf8');
fetchMock.mock('https://exist/api-gateway-implementation.json', apiGatewayArchitecture);
fetchMock.mock('https://exist/api-gateway-implementation.json', { status: 200, body: apiGatewayArchitecture, headers: { 'content-type': 'application/json' } });

const response = await validate('https://exist/api-gateway-implementation.json', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled);

console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});

expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).toBeTruthy();
Expand All @@ -346,12 +354,18 @@ describe('validate pattern and architecture', () => {
mockRunFunction.mockReturnValue(expectedSpectralOutput);

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const apiGatewayArchitecture = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway-implementation-that-does-not-pass-the-spectral-validation.json'), 'utf8');
fetchMock.mock('https://exist/api-gateway-implementation.json', apiGatewayArchitecture);
fetchMock.mock('https://exist/api-gateway-implementation.json', { status: 200, body: apiGatewayArchitecture, headers: { 'content-type': 'application/json' } });

const response = await validate('https://exist/api-gateway-implementation.json', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled);
console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});
expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).toBeTruthy();
Expand All @@ -373,12 +387,18 @@ describe('validate pattern and architecture', () => {
mockRunFunction.mockReturnValue(expectedSpectralOutput);

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway-with-no-relationships.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const apiGatewayArchitecture = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway-implementation.json'), 'utf8');
fetchMock.mock('https://exist/api-gateway-implementation.json', apiGatewayArchitecture);
fetchMock.mock('https://exist/api-gateway-implementation.json', { status: 200, body: apiGatewayArchitecture, headers: { 'content-type': 'application/json' } });

const response = await validate('https://exist/api-gateway-implementation.json', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled);
console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});
expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).toBeTruthy();
Expand All @@ -400,12 +420,18 @@ describe('validate pattern and architecture', () => {
mockRunFunction.mockReturnValue(expectedSpectralOutput);

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const apiGatewayArchitecture = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway-implementation.json'), 'utf8');
fetchMock.mock('https://exist/api-gateway-implementation.json', apiGatewayArchitecture);
fetchMock.mock('https://exist/api-gateway-implementation.json', { status: 200, body: apiGatewayArchitecture, headers: { 'content-type': 'application/json' } });

const response = await validate('https://exist/api-gateway-implementation.json', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled);
console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});
expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).not.toBeTruthy();
Expand All @@ -423,6 +449,7 @@ describe('validate pattern only', () => {

afterEach(() => {
fetchMock.restore();
jest.clearAllMocks();
});

it('has errors when the pattern does not pass all the spectral validations ', async () => {
Expand All @@ -439,9 +466,15 @@ describe('validate pattern only', () => {
mockRunFunction.mockReturnValue(expectedSpectralOutput);

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const response = await validate('', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled);
console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});
expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).toBeTruthy();
Expand All @@ -456,9 +489,15 @@ describe('validate pattern only', () => {
mockRunFunction.mockReturnValue(expectedSpectralOutput);

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/bad-schema/bad-json-schema.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway.json', apiGateway);
fetchMock.mock('http://exist/api-gateway.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const response = await validate('', 'http://exist/api-gateway.json', metaSchemaLocation, debugDisabled);
console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});
expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).toBeTruthy();
Expand All @@ -474,6 +513,7 @@ describe('validate - architecture only', () => {

afterEach(() => {
fetchMock.restore();
jest.clearAllMocks();
});

it('throw error when the architecture cannot be found', async () => {
Expand All @@ -498,9 +538,15 @@ describe('validate - architecture only', () => {
mockRunFunction.mockReturnValue(expectedSpectralOutput);

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway-implementation.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway-implementation.json', apiGateway);
fetchMock.mock('http://exist/api-gateway-implementation.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const response = await validate('http://exist/api-gateway-implementation.json', '', metaSchemaLocation, debugDisabled);
console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});
expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).toBeTruthy();
Expand All @@ -514,10 +560,15 @@ describe('validate - architecture only', () => {
mockRunFunction.mockReturnValue(expectedSpectralOutput);

const apiGateway = readFileSync(path.resolve(__dirname, '../../../test_fixtures/api-gateway-implementation.json'), 'utf8');
fetchMock.mock('http://exist/api-gateway-implementation.json', apiGateway);
fetchMock.mock('http://exist/api-gateway-implementation.json', { status: 200, body: apiGateway, headers: { 'content-type': 'application/json' } });

const response = await validate('http://exist/api-gateway-implementation.json', '', metaSchemaLocation, debugDisabled);

console.log('Response:', {
hasErrors: response.hasErrors,
hasWarnings: response.hasWarnings,
jsonSchemaValidations: response.jsonSchemaValidationOutputs,
spectralValidations: response.spectralSchemaValidationOutputs
});
expect(response).not.toBeNull();
expect(response).not.toBeUndefined();
expect(response.hasErrors).not.toBeTruthy();
Expand Down
8 changes: 6 additions & 2 deletions shared/src/commands/validate/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,12 @@ async function loadFileFromUrl(fileUrl: string) {
if (!res.ok) {
throw new Error(`The http request to ${fileUrl} did not succeed. Status code ${res.status}`);
}
const body = await res.json();
return body;
const text = await res.text();
try {
return JSON.parse(text);
} catch (e) {

Check failure on line 331 in shared/src/commands/validate/validate.ts

View workflow job for this annotation

GitHub Actions / Build, Test, and Lint Shared Module

'e' is defined but never used. Allowed unused caught errors must match /^_/u
throw new Error(`Response from ${fileUrl} is not valid JSON: ${text}`);
}
}

/**
Expand Down
3 changes: 2 additions & 1 deletion shared/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
"compilerOptions": {
"outDir": "./dist",
"rootDir": "src",
"module": "commonjs",
"module": "es2022",
"target": "es2021",
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
Expand Down

0 comments on commit 0f0dcc9

Please sign in to comment.