Skip to content

Commit

Permalink
create smartapi test files
Browse files Browse the repository at this point in the history
  • Loading branch information
rjawesome committed Jun 19, 2024
1 parent 6b794a9 commit 97c5be3
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/controllers/cron/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import smartapiCron from "./update_local_smartapi";
import testCron from "./test_smartapi";
import cacheClearCron from "./clear_edge_cache";

export default function scheduleCronJobs() {
smartapiCron();
cacheClearCron();
testCron();
}
132 changes: 132 additions & 0 deletions src/controllers/cron/test_smartapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import MetaKG, { SmartAPIKGOperationObject, TestExampleObject } from "@biothings-explorer/smartapi-kg";
import { QEdge2APIEdgeHandler, QEdge } from "@biothings-explorer/query_graph_handler";
import CallAPI from "@biothings-explorer/call-apis";
import { Telemetry, redisClient } from "@biothings-explorer/utils";
import Debug from "debug";
const debug = Debug("bte:biothings-explorer-trapi:cron");
import cron from "node-cron";
import path from "path";
import { stdout } from "process";
import { spanStatusfromHttpCode } from "@sentry/node";
const smartAPIPath = path.resolve(
__dirname,
process.env.STATIC_PATH ? `${process.env.STATIC_PATH}/data/smartapi_specs.json` : "../../../data/smartapi_specs.json",
);
const predicatesPath = path.resolve(
__dirname,
process.env.STATIC_PATH ? `${process.env.STATIC_PATH}/data/predicates.json` : "../../../data/predicates.json",
);

interface OpError {
op: string;
issue: Error;
}

function generateEdge(op: SmartAPIKGOperationObject, ex: TestExampleObject) {
return {
subject: {
categories: [op.association.input_type],
ids: [ex.qInput],
id: "n0"
},
object: {
categories: [op.association.output_type],
ids: [ex.oneOutput],
id: "n1"
},
predicates: ["biolink:" + op.association.predicate],
id: "e01",
frozen: true,
};
}

function generateId(op: SmartAPIKGOperationObject, ex: TestExampleObject) {
return `${op.association.api_name} [${ex.qInput}-${op.association.predicate}-${ex.oneOutput}]`;
}

async function runTests(debug = false): Promise<{errors: OpError[], opsCount: number }> {
let errors = [];
let opsCount = 0;
const metakg: MetaKG = global.metakg ? global.metakg : new MetaKG(smartAPIPath, predicatesPath);
if (!global.metakg) {
metakg.constructMetaKGSync(false);
}
const ops = metakg.ops;
for (const op of ops) {
if (op.testExamples && op.testExamples.length > 0) {
opsCount++;
}
}
if (debug) console.log(`Operation Count: ${opsCount}`);
let curCount = 0;
let errCount = 0;
for (const op of ops) {
if (op.testExamples && op.testExamples.length > 0) {
curCount++;
for (const example of op.testExamples) {
try {
const newMeta = new MetaKG(undefined, undefined, [op]);
const edge = new QEdge(generateEdge(op, example));
edge.subject.setEquivalentIDs({ [example.qInput]: { primaryID: example.qInput, equivalentIDs: [example.qInput], label: example.qInput, labelAliases: [], primaryTypes: [op.association.input_type], semanticTypes: [op.association.input_type] }})
const edgeConverter = new QEdge2APIEdgeHandler([edge], newMeta);
const APIEdges = await edgeConverter.convert([edge]);
const executor = new CallAPI(APIEdges, {}, redisClient);
const records = await executor.query(false, {});
if (records.filter(r => r.object.original === example.oneOutput).length <= 0) {
errors.push({ op: generateId(op, example), issue: new Error("Record is missing") });
errCount++;
}
} catch (error) {
errors.push({ op: generateId(op, example), issue: error });
errCount++;
}
}
if (debug) stdout.write("\r\r\r\r\r\r\r\r\r\r\r" + curCount.toString().padStart(4, '0') + " (" + errCount.toString().padStart(4, '0') + ")");
}
}
if (debug) console.log("");

return { errors, opsCount }
}

export default function testSmartApi() {
// Env set in manual sync script
const sync_and_exit = process.env.SYNC_AND_EXIT === "true";
if (sync_and_exit) {
console.log("Testing SmartAPI specs with subsequent exit...");
runTests(true).then(data => {
if (data.errors.length === 0) {
console.log(`Testing SmartAPI specs successful. ${data.opsCount} operations tested.`);
}
else {
console.log(`Testing SmartAPI specs failed. ${data.errors.length} operations failed.`);
data.errors.forEach(err => {
console.log(`${err.op}: ${err.issue.message}${err.issue.message = "Record is missing" ? "" : "\n"+err.issue.stack}`);
});
}
process.exit(0);
})
return;
}

cron.schedule("0 0 * * *", async () => {
debug(`Testing SmartAPI specs now at ${new Date().toUTCString()}!`);
const span = Telemetry.startSpan({ description: "smartapiTest" });
try {
const results = await runTests(false);
if (results.errors.length === 0) {
debug(`Testing SmartAPI specs successful. ${results.opsCount} operations tested.`);
}
else {
debug(`Testing SmartAPI specs failed. ${results.errors.length} operations failed (${results.opsCount} tested).`);
results.errors.forEach(err => {
debug(`${err.op}: ${err.issue.message}${err.issue.message = "Record is missing" ? "" : "\n"+err.issue.stack}`);
Telemetry.captureException(err.issue);
});
}
} catch (err) {
debug(`Testing SmartAPI specs failed! The error message is ${err.toString()}`);
}
span.finish();
});
}

0 comments on commit 97c5be3

Please sign in to comment.