UNPKG

apigeelint

Version:

Node module and tool to lint a bundle for an Apigee API Proxy or sharedflow.

327 lines (298 loc) 9.94 kB
/* Copyright © 2019-2026 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* global describe, it */ const assert = require("node:assert"), fs = require("node:fs"), path = require("node:path"), debug = require("debug")("apigeelint:EP002-test"), bl = require("../../lib/package/bundleLinter.js"); describe(`EP002 - apiproxy bundle with misplaced elements`, () => { let items = null, ep002Errors, lastRunProxy; // sets ep002Errors as side-effect . const insure = (proxydir, cb) => { if (!cb) { cb = proxydir; proxydir = `EP002`; } /* * Tests must not run the linter outside of the scope of an it() , * because then the mocha --grep does not do what you want. * This method insures we run the lint once, but only within * the scope of it(). **/ // avoid re-run if not necessary if (items == null || lastRunProxy != proxydir) { const proxyPath = path.resolve( __dirname, `../fixtures/resources/${proxydir}/apiproxy`, ); const configuration = { debug: true, source: { type: "filesystem", path: proxyPath, bundleType: "apiproxy", }, profile: "apigeex", excluded: {}, setExitCode: false, output: () => {}, // suppress output }; debug(`running and caching for ${proxydir}`); assert.ok(fs.existsSync(proxyPath)); bl.lint(configuration, (bundle) => { lastRunProxy = proxydir; items = bundle.getReport(); debug(`#items ${items.length}`); ep002Errors = items.filter( (item) => item.messages && item.messages.length && item.messages.find((m) => m.ruleId == "EP002"), ); cb(); }); } else { debug(`returning cached results for ${proxydir}`); cb(); } }; it("should generate the expected errors", () => { insure(() => { assert.ok(items); assert.ok(items.length); assert.equal(ep002Errors.length, 2); }); }); it("should generate the correct messages for proxy endpoint 1", () => { insure(() => { const proxyEp1Errors = ep002Errors.filter((item) => item.filePath.endsWith( path.normalize("/apiproxy/proxies/proxy-endpoint-1.xml"), ), ); assert.ok(proxyEp1Errors); assert.equal(proxyEp1Errors.length, 1); let expectedErrors = [ "Extra FaultRules element", "Invalid Framjo element", "Misplaced DefaultFaultRule element child of Framjo", "Misplaced Step element child of PostClientFlow", "Invalid Flow element", ]; assert.equal( proxyEp1Errors[0].messages.length, expectedErrors.length, "number of errors", ); proxyEp1Errors[0].messages.forEach((msg) => { assert.ok(msg.message); assert.ok(expectedErrors.includes(msg.message)); // disallow repeats expectedErrors = expectedErrors.filter((item) => item != msg.message); }); assert.equal(expectedErrors.length, 0); }); }); it("should generate the correct messages for proxy endpoint 2", () => { insure(() => { const proxyEp2Errors = ep002Errors.filter((item) => item.filePath.endsWith("/apiproxy/proxies/proxy-endpoint-2.xml"), ); assert.ok(proxyEp2Errors); assert.equal(proxyEp2Errors.length, 0); // assert.equal(proxyEp2Errors[0].messages.length, expectedErrors.length, "number of errors"); // proxyEp2Errors[0].messages.forEach( msg => { // assert.ok(msg.message); // assert.ok(expectedErrors.includes(msg.message)); // // disallow repeats // expectedErrors = expectedErrors.filter( item => item != msg.message); // }); // assert.equal(expectedErrors.length, 0); }); }); it("should generate the correct messages for the target endpoint", () => { insure(() => { const targetErrors = ep002Errors.filter((item) => item.filePath.endsWith(path.normalize("/apiproxy/targets/http-1.xml")), ); assert.ok(targetErrors); assert.equal(targetErrors.length, 1); let expectedErrors = [ "Extra Flows element", "Misplaced 'SocketReadTimeoutInSec' element child of Request", "Misplaced 'HTTPMonitor' element child of HTTPTargetConnection", "Misplaced 'ThisIsBogus' element child of HealthMonitor", "Invalid MisPlaced element", "LocalTargetConnection element conflicts with HTTPTargetConnection on line 26", "Invalid RouteRule element", "Misplaced 'ConnectTimeoutInMin' element child of Request", "Misplaced 'Status' element child of SuccessResponse", "Redundant HealthMonitor element", "TCPMonitor element conflicts with HTTPMonitor on line 47", ]; debug(targetErrors[0].messages); const ep002Messages = targetErrors[0].messages.filter( (m) => m.ruleId == "EP002", ); assert.equal( ep002Messages.length, expectedErrors.length, "number of errors", ); ep002Messages.forEach((msg) => { assert.ok(msg.message); assert.ok(expectedErrors.includes(msg.message), msg.message); // disallow repeats expectedErrors = expectedErrors.filter((item) => item != msg.message); }); }); }); it("should generate the correct messages for the target endpoint with URL", () => { insure(() => { const targetErrors = ep002Errors.filter((item) => { const normalizedPath = item.filePath.split(path.sep).join("/"); return normalizedPath.endsWith("/apiproxy/targets/http-2.xml"); }); assert.ok(targetErrors); assert.equal(targetErrors.length, 0); }); }); it("should generate the correct messages for the SSLInfo element in the TargetEndpoint", () => { insure("EP002-issue-437", () => { debug(ep002Errors); const errorsofInterest = ep002Errors.filter((item) => { const normalizedPath = item.filePath.split(path.sep).join("/"); debug(`checking ${normalizedPath}`); return normalizedPath.endsWith("/apiproxy/targets/target1.xml"); }); assert.ok(errorsofInterest); assert.equal(errorsofInterest.length, 1); const ep002Messages = errorsofInterest[0].messages.filter( (m) => m.ruleId == "EP002", ); assert.equal(ep002Messages.length, 2, "number of errors"); ep002Messages.forEach((msg) => assert.equal(msg.message, "Invalid IgnoreValidationError element"), ); }); }); }); describe(`EP002 - sharedflowbundle with no misplaced elements`, () => { const configuration = { debug: true, source: { type: "filesystem", path: path.resolve( __dirname, "../fixtures/resources/ExtractVariables-Attachment/sharedflowbundle", ), bundleType: "sharedflowbundle", }, profile: "apigeex", excluded: {}, setExitCode: false, output: () => {}, // suppress output }; let items = null; const insure = (cb) => { if (items == null) { bl.lint(configuration, (bundle) => { items = bundle.getReport(); cb(); }); } else { cb(); } }; it("should generate some errors", () => { insure(() => { assert.ok(items); assert.ok(items.length); const itemsWithErrors = items.filter( (item) => item.messages && item.messages.length, ); assert.equal(itemsWithErrors.length, 1); }); }); it("should generate no EP002 errors", () => { insure(() => { const ep002Errors = items.filter( (item) => item.messages && item.messages.length && item.messages.find((m) => m.ruleId == "EP002"), ); assert.equal(ep002Errors.length, 0); }); }); }); describe(`EP002 - EventFlow within API Proxy and Target Endpoints`, () => { const configuration = { debug: true, source: { type: "filesystem", path: path.resolve( __dirname, "../fixtures/resources/EP002-eventflow/apiproxy", ), bundleType: "apiproxy", }, profile: "apigeex", excluded: {}, setExitCode: false, output: () => {}, // suppress output }; let items = null; const insure = (cb) => { if (items == null) { bl.lint(configuration, (bundle) => { items = bundle.getReport(); cb(); }); } else { cb(); } }; it("should generate some errors", () => { insure(() => { assert.ok(items); assert.ok(items.length); const itemsWithErrors = items.filter( (item) => item.messages && item.messages.length, ); assert.equal(itemsWithErrors.length, 2); }); }); it("should generate one EP002 error", () => { insure(() => { const ep002Errors = items.filter( (item) => item.messages && item.messages.length && item.messages.find((m) => m.ruleId == "EP002"), ); assert.equal(ep002Errors.length, 1); const message = ep002Errors?.[0]?.messages?.[0]; assert.ok(message); assert.equal( message.message, "Misplaced Request element child of EventFlow", ); assert.equal(message.line, 17); assert.equal(message.column, 5); }); }); });