info-endpoint.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. const { XMLHttpRequest } = require("xmlhttprequest");
  2. // const jsl = require("svjsl");
  3. const semver = require("semver");
  4. const settings = require("../settings");
  5. const meta = {
  6. name: "Info",
  7. category: "Endpoints"
  8. };
  9. const baseURL = `http://127.0.0.1:${settings.httpServer.port}`;
  10. /**
  11. * @typedef {Object} UnitTestResult
  12. * @prop {Object} meta
  13. * @prop {String} meta.name
  14. * @prop {String} meta.category
  15. * @prop {Array<String>} errors
  16. */
  17. /**
  18. * Runs this unit test
  19. * @returns {Promise<UnitTestResult>}
  20. */
  21. function run()
  22. {
  23. return new Promise((resolve, reject) => {
  24. let errors = [];
  25. let run = () => new Promise(xhrResolve => {
  26. let xhr = new XMLHttpRequest();
  27. xhr.open("GET", `${baseURL}/info`);
  28. xhr.onreadystatechange = () => {
  29. if(xhr.readyState == 4)
  30. {
  31. if(xhr.status >= 200 && xhr.status < 300)
  32. {
  33. let resp = JSON.parse(xhr.responseText);
  34. let packageJSON = require("../package.json");
  35. //#SECTION error
  36. if(resp.error == true)
  37. {
  38. errors.push(`"error" parameter is set to "true" - error message: ${resp.message}`);
  39. return xhrResolve();
  40. }
  41. //#SECTION version
  42. if(!semver.eq(resp.version, packageJSON.version))
  43. errors.push(`API version (${resp.version}) doesn't match version in package.json (${packageJSON.version})`);
  44. //#SECTION joke count
  45. if(!resp.jokes.totalCount || isNaN(parseInt(resp.jokes.totalCount)))
  46. errors.push(`API supplied no "totalCount" param or it is not a number`);
  47. //#SECTION categories
  48. let possibleCats = [settings.jokes.possible.anyCategoryName, ...settings.jokes.possible.categories];
  49. if(!arraysEqual(possibleCats, resp.jokes.categories))
  50. errors.push(`API's categories (${resp.jokes.categories.map(c => c.substring(0, 5)).join(", ")}) differ from the local ones (${possibleCats.map(c => c.substring(0, 5)).join(", ")})`);
  51. //#SECTION flag
  52. let possibleFlags = settings.jokes.possible.flags;
  53. if(!arraysEqual(possibleFlags, resp.jokes.flags))
  54. errors.push(`API's flags (${resp.jokes.flags.join(", ")}) differ from the local ones (${possibleFlags.join(", ")})`);
  55. //#SECTION types
  56. let possibleTypes = settings.jokes.possible.types;
  57. if(!arraysEqual(possibleTypes, resp.jokes.types))
  58. errors.push(`API's types (${resp.jokes.types.join(", ")}) differ from the local ones (${possibleTypes.join(", ")})`);
  59. //#SECTION formats
  60. let possibleFormats = settings.jokes.possible.formats;
  61. if(!arraysEqual(possibleFormats, resp.formats))
  62. errors.push(`API's formats (${resp.formats.join(", ")}) differ from the local ones (${possibleFormats.join(", ")})`);
  63. //#SECTION joke languages
  64. if(!resp.jokeLanguages || isNaN(parseInt(resp.jokeLanguages)))
  65. errors.push(`API supplied no "jokeLanguages" param or it is not a number`);
  66. //#SECTION system languages
  67. if(!resp.systemLanguages || isNaN(parseInt(resp.systemLanguages)))
  68. errors.push(`API supplied no "systemLanguages" param or it is not a number`);
  69. //#SECTION info string
  70. if(typeof resp.info != "string")
  71. errors.push(`API supplied no "info" param or it is not a string`);
  72. //#SECTION timestamp
  73. let resTS = parseInt(resp.timestamp);
  74. let localTS = parseInt(new Date().getTime());
  75. let tsRange = [localTS - 600000, localTS + 600000];
  76. if(resTS < tsRange[0] || resTS > tsRange[1])
  77. errors.push(`API system's time is out of sync by more than 10 minutes`);
  78. return xhrResolve();
  79. }
  80. else
  81. {
  82. errors.push(`Couldn't reach endpoint - HTTP status: ${xhr.status}`);
  83. return xhrResolve();
  84. }
  85. }
  86. };
  87. xhr.send();
  88. });
  89. run().then(() => {
  90. if(errors.length == 0)
  91. return resolve({ meta });
  92. else
  93. return reject({ meta, errors });
  94. });
  95. });
  96. }
  97. /**
  98. * Checks if two arrays contain the same elements (order is ignored)
  99. * @author [canbax](https://stackoverflow.com/a/55614659/8602926)
  100. * @param {Array<*>} a1
  101. * @param {Array<*>} a2
  102. */
  103. function arraysEqual(a1, a2) {
  104. const superSet = {};
  105. for(const i of a1)
  106. {
  107. const e = i + typeof i;
  108. superSet[e] = 1;
  109. }
  110. for(const i of a2)
  111. {
  112. const e = i + typeof i;
  113. if(!superSet[e])
  114. return false;
  115. superSet[e] = 2;
  116. }
  117. for(let e in superSet)
  118. {
  119. if(superSet[e] === 1)
  120. return false;
  121. }
  122. return true;
  123. }
  124. module.exports = { meta, run };