@team-internet/apiconnector
Version:
Node.js SDK for the insanely fast CentralNic Reseller (fka RRPProxy) API
611 lines (564 loc) • 22.2 kB
text/typescript
import { expect, use } from "chai"; // Using Expect style
import chaiAsPromised from "chai-as-promised";
import "mocha";
import nock from "nock";
import {
APIClient,
CNR_CONNECTION_URL_LIVE,
CNR_CONNECTION_URL_OTE,
CNR_CONNECTION_URL_PROXY,
} from "../src/apiclient.ts";
import { Response } from "../src/response.ts";
import { ResponseTemplateManager } from "../src/responsetemplatemanager.ts";
use(chaiAsPromised);
const apiScript = "/api/call.cgi";
const oteHost = CNR_CONNECTION_URL_OTE.replace(apiScript, "");
const rtm = ResponseTemplateManager.getInstance();
const cmd = { COMMAND: "StatusAccount" };
let cl: APIClient;
after(() => {
nock.cleanAll();
});
before(() => {
cl = new APIClient();
rtm
.addTemplate(
"login200",
"[RESPONSE]\r\nproperty[expiration date][0] = 2024-09-19 10:52:51\r\nproperty[sessionid][0] = bb7a884b09b9a674fb4a22211758ce87\r\ndescription = Command completed successfully\r\ncode = 200\r\nqueuetime = 0.004\r\nruntime = 0.023\r\nEOF\r\n",
)
.addTemplate(
"login500",
rtm.generateTemplate("530", "Authentication failed"),
)
.addTemplate(
"OK",
rtm.generateTemplate("200", "Command completed successfully"),
)
.addTemplate(
"CHECKS",
"[RESPONSE]\r\nproperty[total][0] = 4\r\nproperty[first][0] = 0\r\nproperty[domain][0] = cnic-ssl-test1.com\r\nproperty[domain][1] = cnic-ssl-test2.com\r\nproperty[count][0] = 2\r\nproperty[last][0] = 1\r\nproperty[limit][0] = 2\r\ndescription = Command completed successfully\r\ncode = 200\r\nqueuetime = 0\r\nruntime = 0.007\r\nEOF\r\n",
)
.addTemplate(
"listP0",
"[RESPONSE]\r\nproperty[total][0] = 4\r\nproperty[first][0] = 0\r\nproperty[domain][0] = cnic-ssl-test1.com\r\nproperty[domain][1] = cnic-ssl-test2.com\r\nproperty[count][0] = 2\r\nproperty[last][0] = 1\r\nproperty[limit][0] = 2\r\ndescription = Command completed successfully\r\ncode = 200\r\nqueuetime = 0\r\nruntime = 0.007\r\nEOF\r\n",
)
.addTemplate(
"listP1",
"[RESPONSE]\r\nproperty[total][0] = 4\r\nproperty[first][0] = 2\r\nproperty[domain][0] = emailcustomization.com\r\nproperty[domain][1] = test-keysysbe0123.be\r\nproperty[count][0] = 2\r\nproperty[last][0] = 3\r\nproperty[limit][0] = 2\r\ndescription = Command completed successfully\r\ncode = 200\r\nqueuetime = 0\r\nruntime = 0.006\r\nEOF\r\n",
)
.addTemplate(
"listFP0",
"[RESPONSE]\r\nproperty[total][0] = 4\r\nproperty[first][0] = 0\r\nproperty[domain][0] = cnic-ssl-test1.com\r\nproperty[count][0] = 1\r\nproperty[last][0] = 0\r\nproperty[limit][0] = 1\r\ndescription = Command completed successfully\r\ncode = 200\r\nqueuetime = 0\r\nruntime = 0.009\r\nEOF\r\n",
)
.addTemplate(
"listFP1",
"[RESPONSE]\r\nproperty[total][0] = 4\r\nproperty[first][0] = 1\r\nproperty[domain][0] = cnic-ssl-test2.com\r\nproperty[count][0] = 1\r\nproperty[last][0] = 1\r\nproperty[limit][0] = 1\r\ndescription = Command completed successfully\r\ncode = 200\r\nqueuetime = 0\r\nruntime = 0.007\r\nEOF\r\n",
)
.addTemplate(
"listFP2",
"[RESPONSE]\r\nproperty[total][0] = 4\r\nproperty[first][0] = 2\r\nproperty[domain][0] = emailcustomization.com\r\nproperty[count][0] = 1\r\nproperty[last][0] = 2\r\nproperty[limit][0] = 1\r\ndescription = Command completed successfully\r\ncode = 200\r\nqueuetime = 0\r\nruntime = 0.007\r\nEOF\r\n",
)
.addTemplate(
"subUserSet",
"[RESPONSE]\r\nproperty[amount][0] = 670.77\r\nproperty[credit][0] = 0.00\r\nproperty[credit threshold][0] = 0.00\r\nproperty[currency][0] = USD\r\nproperty[registrar][0] = sub1\r\nproperty[vat][0] = 0.00\r\ndescription = Command completed successfully\r\ncode = 200\r\nruntime = 0.008\r\nqueuetime = 0\r\nEOF\r\n",
)
.addTemplate(
"subUserReset",
"[RESPONSE]\r\nproperty[amount][0] = 670.77\r\nproperty[credit][0] = 0.00\r\nproperty[credit threshold][0] = 0.00\r\nproperty[currency][0] = USD\r\nproperty[registrar][0] = test\r\nproperty[vat][0] = 0.00\r\ndescription = Command completed successfully\r\ncode = 200\r\nruntime = 0.008\r\nqueuetime = 0\r\nEOF\r\n",
);
});
describe("APIClient class", function () {
this.timeout(APIClient.socketTimeout);
this.slow(1000);
describe("#.getPOSTData", () => {
it("test object input with special chars", () => {
const validate =
"s_command=AUTH%3Dgwrgwqg%25%26%5C44t3%2A%0ACOMMAND%3DModifyDomain";
const enc = cl.getPOSTData({
AUTH: "gwrgwqg%&\\44t3*",
COMMAND: "ModifyDomain",
});
expect(enc).to.equal(validate);
});
it("test string input", () => {
const enc = cl.getPOSTData("gregergege");
expect(enc).to.equal("s_command=gregergege");
});
it("test object input with null value in parameter", () => {
const validate = "s_command=COMMAND%3DModifyDomain";
const enc = cl.getPOSTData({
AUTH: null,
COMMAND: "ModifyDomain",
});
expect(enc).to.equal(validate);
});
it("test object input with undefined value in parameter", () => {
const validate = "s_command=COMMAND%3DModifyDomain";
const enc = cl.getPOSTData({
AUTH: undefined,
COMMAND: "ModifyDomain",
});
expect(enc).to.equal(validate);
});
it("test data getting secured correctly", () => {
const encodedTestUserName = encodeURIComponent(
process.env.CNR_TEST_USER || "",
);
const validate =
"s_login=" +
encodedTestUserName +
"&s_pw=***&s_command=COMMAND%3DCheckAuthentication%0APASSWORD%3D%2A%2A%2A%0ASUBUSER%3D" +
encodedTestUserName;
cl.setCredentials(
process.env.CNR_TEST_USER || "",
process.env.CNR_TEST_PASSWORD || "",
);
const enc = cl.getPOSTData(
{
COMMAND: "CheckAuthentication",
PASSWORD: "test.passw0rd",
SUBUSER: process.env.CNR_TEST_USER,
},
true,
);
cl.setCredentials("", "");
expect(enc).to.equal(validate);
});
});
describe("#.enableDebugMode", () => {
it("activate debug mode", () => {
cl.enableDebugMode();
});
});
describe("#.disableDebugMode", () => {
it("deactivate debug mode", () => {
cl.disableDebugMode();
});
});
describe("#.getURL", () => {
it("validate default socket url", () => {
const url = cl.getURL();
expect(url).to.equal(CNR_CONNECTION_URL_LIVE);
});
});
describe("#.getUserAgent", () => {
it("validate response", () => {
const ua = cl.getUserAgent();
expect(ua).to.equal(
`NODE-SDK (${process.platform}; ${
process.arch
}; rv:${cl.getVersion()}) node/${process.version}`,
);
});
});
describe("#.setUserAgent", () => {
it("validate response", () => {
const cls = cl.setUserAgent("WHMCS", "7.7.0");
const ua = cl.getUserAgent();
expect(cls).to.be.instanceOf(APIClient);
expect(ua).to.equal(
`WHMCS (${process.platform}; ${
process.arch
}; rv:7.7.0) node-sdk/${cl.getVersion()} node/${process.version}`,
);
});
it("validate agent string including modules", () => {
const cls = cl.setUserAgent("WHMCS", "7.7.0", [
"reg/2.6.2",
"ssl/7.2.2",
"dc/8.2.2",
]);
const ua = cl.getUserAgent();
expect(cls).to.be.instanceOf(APIClient);
expect(ua).to.equal(
`WHMCS (${process.platform}; ${
process.arch
}; rv:7.7.0) reg/2.6.2 ssl/7.2.2 dc/8.2.2 node-sdk/${cl.getVersion()} node/${
process.version
}`,
);
});
});
describe("#.setURL", () => {
it("validate http socket url", () => {
const url = cl.setURL(CNR_CONNECTION_URL_PROXY).getURL();
expect(url).to.equal(CNR_CONNECTION_URL_PROXY);
cl.setURL(CNR_CONNECTION_URL_LIVE);
});
});
describe("#.saveSession/reuseSession", () => {
after(() => {
cl.reuseSession({});
});
it("validate correct settings", async () => {
const sessionobj = {};
const tpl = new Response(rtm.getTemplate("login200").getPlain(), cmd);
nock(oteHost).post(apiScript).reply(200, tpl.getPlain());
cl.useOTESystem().setCredentials(
process.env.CNR_TEST_USER || "",
process.env.CNR_TEST_PASSWORD || "",
);
const r = await cl.login();
cl.saveSession(sessionobj);
expect(r).to.be.instanceOf(Response);
expect(r.isSuccess()).to.be.true;
const rec = r.getRecord(0);
expect(rec).not.to.be.null;
const cl2 = new APIClient();
cl2.reuseSession(sessionobj);
const tmp = cl2.getPOSTData({
COMMAND: "StatusAccount",
});
expect(tmp).to.include("s_sessionid");
});
});
describe("#.setCredentials", () => {
it("validate getPOSTData response [credentials set] ", () => {
cl.setCredentials("myaccountid", "mypassword");
const tmp = cl.getPOSTData({
COMMAND: "StatusAccount",
});
expect(tmp).to.equal(
"s_login=myaccountid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount",
);
});
it("validate getPOSTData response [session reset]", () => {
cl.setCredentials("", "");
const tmp = cl.getPOSTData({
COMMAND: "StatusAccount",
});
expect(tmp).to.equal("s_command=COMMAND%3DStatusAccount");
});
});
describe("#.setRoleCredentials", () => {
it("validate getPOSTData response [role credentials set] ", () => {
cl.setRoleCredentials("myaccountid", "myroleid", "mypassword");
const tmp = cl.getPOSTData({
COMMAND: "StatusAccount",
});
expect(tmp).to.equal(
"s_login=myaccountid%3Amyroleid&s_pw=mypassword&s_command=COMMAND%3DStatusAccount",
);
});
it("validate getPOSTData response [role credentials reset]", () => {
cl.setRoleCredentials("", "", "");
const tmp = cl.getPOSTData({
COMMAND: "StatusAccount",
});
expect(tmp).to.equal("s_command=COMMAND%3DStatusAccount");
});
});
describe("#.login", () => {
it("validate against mocked API response [login succeeded; no role used]", async () => {
const tpl = new Response(rtm.getTemplate("login200").getPlain(), cmd);
nock(oteHost).post(apiScript).reply(200, tpl.getPlain());
cl.useOTESystem().setCredentials(
process.env.CNR_TEST_USER || "",
process.env.CNR_TEST_PASSWORD || "",
);
const r = await cl.login();
expect(r).to.be.instanceOf(Response);
expect(r.isSuccess()).to.be.true;
const rec = r.getRecord(0);
expect(rec).not.to.be.null;
if (rec) {
const rec2 = tpl.getRecord(0);
expect(rec2).not.to.be.null;
if (rec2) {
expect(rec.getDataByKey("SESSIONID")).to.equal(
rec2.getDataByKey("SESSIONID"),
);
}
}
});
it("validate against mocked API response [login failed; wrong credentials]", async () => {
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("login500").getPlain());
cl.setCredentials("test.user", "WRONGPASSWORD");
const r = await cl.login();
expect(r).to.be.instanceOf(Response);
expect(r.isError()).to.be.true;
});
// deactivated as delayConnection is not working together with node-fetch
it.skip("validate against mocked API response [login failed; http timeout]", async () => {
nock.cleanAll();
const tpl = rtm.getTemplate("httperror");
nock(oteHost)
.post(apiScript)
.delayConnection(APIClient.socketTimeout + 1000)
.reply(200, tpl.getPlain());
cl.setCredentials("test.user", "WRONGPASSWORD");
const r = await cl.login();
expect(r).to.be.instanceOf(Response);
expect(r.isTmpError()).to.be.true;
expect(r.getDescription()).to.equal(tpl.getDescription());
});
it("validate against mocked API response [login succeeded; no session returned] ", async () => {
// this case cannot really happen as the api always returns SESSIONID property.
// this case is just to increase coverage
const tpl = new Response(rtm.getTemplate("OK").getPlain(), cmd);
nock(oteHost).post(apiScript).reply(200, tpl.getPlain());
cl.useOTESystem().setCredentials(
process.env.CNR_TEST_USER || "",
process.env.CNR_TEST_PASSWORD || "",
);
const r = await cl.login();
expect(r).to.be.instanceOf(Response);
expect(r.isSuccess()).to.be.true;
const rec = r.getRecord(0);
expect(rec).to.be.null;
});
});
describe("#.logout", () => {
it("validate against mocked API response [logout succeeded]", async () => {
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("OK").getPlain());
// Perform login to establish a session
cl.reuseSession({
socketconfig: { login: "myaccountid", session: "12345678" },
});
// Perform logout
const logoutResponse = await cl.logout();
console.log(
"Logout Response:",
cl.getPOSTData({ COMMAND: "StatusAccount" }),
);
expect(logoutResponse).to.be.instanceOf(Response);
expect(logoutResponse.isSuccess()).to.be.true;
});
it("validate against mocked API response [logout failed; session no longer exists]", async () => {
const tpl = new Response(rtm.getTemplate("login200").getPlain(), cmd);
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("login500").getPlain());
const rec2 = tpl.getRecord(0);
expect(rec2).not.to.be.null;
if (rec2) {
const sessid = rec2.getDataByKey("SESSIONID");
expect(sessid).not.to.be.null;
if (sessid) {
cl.enableDebugMode();
const r = await cl.logout();
expect(r).to.be.instanceOf(Response);
expect(r.isError()).to.be.true;
}
}
});
});
describe("#.request", () => {
// TODO additional test for statusMessage - not supported through nock [https://github.com/nock/nock/issues/558]
it("validate against mocked API response [200 < r.statusCode > 299]", async () => {
const tpl2 = new Response(rtm.getTemplate("httperror").getPlain(), cmd);
nock(oteHost)
.post(apiScript)
.reply(404, rtm.getTemplate("404").getPlain());
cl.enableDebugMode()
.setCredentials(
process.env.CNR_TEST_USER || "",
process.env.CNR_TEST_PASSWORD || "",
)
.useOTESystem();
const r = await cl.request(cmd);
expect(r).to.be.instanceOf(Response);
expect(r.isTmpError()).to.be.true;
expect(r.getCode()).to.equal(tpl2.getCode());
expect(r.getDescription()).to.equal(tpl2.getDescription());
});
it("validate against mocked API response [200 < r.statusCode > 299, no debug]", async () => {
const tpl2 = new Response(rtm.getTemplate("httperror").getPlain(), cmd);
nock(oteHost)
.post(apiScript)
.reply(404, rtm.getTemplate("404").getPlain());
cl.disableDebugMode();
const r = await cl.request(cmd);
expect(r).to.be.instanceOf(Response);
expect(r.isTmpError()).to.be.true;
expect(r.getCode()).to.equal(tpl2.getCode());
expect(r.getDescription()).to.equal(tpl2.getDescription());
});
it("test if flattening of nested array / bulk parameters works", async () => {
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("OK").getPlain());
cl.enableDebugMode();
const r = await cl.request({
COMMAND: "CheckDomains",
DOMAIN: ["example.com", "example.net"],
});
expect(r).to.be.instanceOf(Response);
const mycmd = r.getCommand();
const keys = Object.keys(mycmd);
expect(keys.includes("DOMAIN")).to.be.false;
expect(keys.includes("DOMAIN0")).to.be.true;
expect(keys.includes("DOMAIN1")).to.be.true;
expect(mycmd.DOMAIN0).to.equal("example.com");
expect(mycmd.DOMAIN1).to.equal("example.net");
});
it("test if auto-idn convert works on domains", async () => {
nock.cleanAll();
const r = await cl.request({
COMMAND: "CheckDomains",
DOMAIN: ["example.com", "dömäin.example", "example.net"],
});
expect(r).to.be.instanceOf(Response);
const mycmd = r.getCommand();
const keys = Object.keys(mycmd);
expect(keys.includes("DOMAIN")).to.be.false;
expect(keys.includes("DOMAIN0")).to.be.true;
expect(keys.includes("DOMAIN1")).to.be.true;
expect(keys.includes("DOMAIN2")).to.be.true;
expect(mycmd.DOMAIN0).to.equal("example.com");
expect(mycmd.DOMAIN1).to.equal("dömäin.example");
expect(mycmd.DOMAIN2).to.equal("example.net");
});
it("test if auto-idn convert works on dnszone", () => {
cl.setRoleCredentials("myaccountid", "myroleid", "mypassword");
const tmp = cl.getPOSTData({
COMMAND: "StatusDNSZone",
DNSZONE: "hallööö.com",
});
expect(tmp).to.equal(
"s_login=myaccountid%3Amyroleid&s_pw=mypassword&s_command=COMMAND%3DStatusDNSZone%0ADNSZONE%3Dhall%C3%B6%C3%B6%C3%B6.com",
);
});
});
describe("#.requestNextResponsePage", () => {
it("validate against mocked API response [no LAST set]", async () => {
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("listP1").getPlain());
const r = new Response(rtm.getTemplate("listP0").getPlain(), {
COMMAND: "QueryDomainList",
LIMIT: 2,
FIRST: 0,
});
const nr = await cl.requestNextResponsePage(r);
expect(r.isSuccess()).to.be.true;
expect(r.getRecordsLimitation()).to.equal(2);
expect(r.getRecordsCount()).to.equal(2);
expect(r.getFirstRecordIndex()).to.equal(0);
expect(r.getLastRecordIndex()).to.equal(1);
expect(nr).not.to.be.null;
if (nr) {
expect(nr.isSuccess()).to.be.true;
expect(nr.getRecordsLimitation()).to.equal(2);
expect(nr.getRecordsCount()).to.equal(2);
expect(nr.getFirstRecordIndex()).to.equal(2);
expect(nr.getLastRecordIndex()).to.equal(3);
}
});
it("validate against mocked API response [LAST set]", () => {
const r = new Response(rtm.getTemplate("listP0").getPlain(), {
COMMAND: "QueryDomainList",
LIMIT: 2,
FIRST: 0,
LAST: 1,
});
return expect(cl.requestNextResponsePage(r)).to.be.rejectedWith(
Error,
"Parameter LAST in use. Please remove it to avoid issues in requestNextPage.",
);
});
it("validate against mocked API response [no FIRST set]", async () => {
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("listP1").getPlain());
cl.disableDebugMode();
const r = new Response(rtm.getTemplate("listP0").getPlain(), {
COMMAND: "QueryDomainList",
LIMIT: 2,
});
const nr = await cl.requestNextResponsePage(r);
expect(r.isSuccess()).to.be.true;
expect(r.getRecordsLimitation()).to.equal(2);
expect(r.getRecordsCount()).to.equal(2);
expect(r.getFirstRecordIndex()).to.equal(0);
expect(r.getLastRecordIndex()).to.equal(1);
expect(nr).not.to.be.null;
if (nr) {
expect(nr.isSuccess()).to.be.true;
expect(nr.getRecordsLimitation()).to.equal(2);
expect(nr.getRecordsCount()).to.equal(2);
expect(nr.getFirstRecordIndex()).to.equal(2);
expect(nr.getLastRecordIndex()).to.equal(3);
}
});
});
describe("#.requestAllResponsePages", () => {
it("validate against mocked API response [success case]", async () => {
let reqcount = 0;
const scope = nock(oteHost)
.persist()
.post(apiScript)
.reply(200, () => {
reqcount++;
if (reqcount === 1) {
return rtm.getTemplate("listFP0").getPlain();
}
if (reqcount === 2) {
return rtm.getTemplate("listFP1").getPlain();
}
return rtm.getTemplate("listFP2").getPlain();
});
const nr = await cl.requestAllResponsePages({
COMMAND: "QueryDomainList",
FIRST: 0,
LIMIT: 1,
});
expect(nr.length).to.equal(4);
scope.persist(false);
});
});
describe("#.setUserView", () => {
it("validate against mocked API response", async () => {
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("subUserSet").getPlain());
cl.setUserView("sub1");
cl.enableDebugMode();
const r = await cl.request({ COMMAND: "StatusAccount" });
console.log("Response:", r);
expect(r).to.be.instanceOf(Response);
expect(r.isSuccess()).to.be.true;
});
});
describe("#.resetUserView", () => {
it("validate against mocked API response", async () => {
nock(oteHost)
.post(apiScript)
.reply(200, rtm.getTemplate("subUserReset").getPlain());
cl.resetUserView();
const r = await cl.request({ COMMAND: "StatusAccount" });
expect(r).to.be.instanceOf(Response);
expect(r.isSuccess()).to.be.true;
});
});
describe("#.setProxy", () => {
it("test setting proxy works", () => {
cl.setProxy("127.0.0.1");
expect(cl.getProxy()).to.equal("127.0.0.1");
cl.setProxy("");
});
});
describe("#.setReferer", () => {
it("test setting referer works", () => {
cl.setReferer("https://www.centralnicreseller.com");
expect(cl.getReferer()).to.equal("https://www.centralnicreseller.com");
cl.setReferer("");
});
});
describe("#.useHighPerformanceConnectionSetup", () => {
it("test setting high performance connection setup works", () => {
cl.useHighPerformanceConnectionSetup();
expect(cl.getURL()).to.equal(CNR_CONNECTION_URL_PROXY);
});
});
describe("#.useDefaultConnectionSetup", () => {
it("test setting default connection setup works", () => {
cl.useDefaultConnectionSetup();
expect(cl.getURL()).to.equal(CNR_CONNECTION_URL_LIVE);
});
});
});