test.js 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. /* globals describe, it, before, after */
  2. /* eslint no-loop-func:0 guard-for-in:0 */
  3. var networking = require("../lib/networking");
  4. var helpers = require("../lib/helpers");
  5. var logging = require("../lib/logging");
  6. var cleaner = require("../lib/cleaner");
  7. var request = require("request");
  8. var config = require("../config");
  9. var server = require("../lib/server");
  10. var assert = require("assert");
  11. var skins = require("../lib/skins");
  12. var cache = require("../lib/cache");
  13. var crc = require("crc").crc32;
  14. var fs = require("fs");
  15. // we don't want tests to fail because of slow internet
  16. config.server.http_timeout *= 3;
  17. // no spam
  18. if (process.env.VERBOSE_TEST !== "true") {
  19. logging.log = logging.debug = logging.warn = logging.error = function() {};
  20. }
  21. var uuids = fs.readFileSync("test/uuids.txt").toString().split(/\r?\n/);
  22. var names = fs.readFileSync("test/usernames.txt").toString().split(/\r?\n/);
  23. // Get a random UUID + name in order to prevent rate limiting
  24. var uuid = uuids[Math.round(Math.random() * (uuids.length - 1))];
  25. var name = names[Math.round(Math.random() * (names.length - 1))];
  26. // Let's hope these will never be assigned
  27. var steve_ids = [
  28. "fffffff0" + "fffffff0" + "fffffff0" + "fffffff0",
  29. "fffffff0" + "fffffff0" + "fffffff1" + "fffffff1",
  30. "fffffff0" + "fffffff1" + "fffffff0" + "fffffff1",
  31. "fffffff0" + "fffffff1" + "fffffff1" + "fffffff0",
  32. "fffffff1" + "fffffff0" + "fffffff0" + "fffffff1",
  33. "fffffff1" + "fffffff0" + "fffffff1" + "fffffff0",
  34. "fffffff1" + "fffffff1" + "fffffff0" + "fffffff0",
  35. "fffffff1" + "fffffff1" + "fffffff1" + "fffffff1",
  36. ];
  37. // Let's hope these will never be assigned
  38. var alex_ids = [
  39. "fffffff0" + "fffffff0" + "fffffff0" + "fffffff1",
  40. "fffffff0" + "fffffff0" + "fffffff1" + "fffffff0",
  41. "fffffff0" + "fffffff1" + "fffffff0" + "fffffff0",
  42. "fffffff0" + "fffffff1" + "fffffff1" + "fffffff1",
  43. "fffffff1" + "fffffff0" + "fffffff0" + "fffffff0",
  44. "fffffff1" + "fffffff0" + "fffffff1" + "fffffff1",
  45. "fffffff1" + "fffffff1" + "fffffff0" + "fffffff1",
  46. "fffffff1" + "fffffff1" + "fffffff1" + "fffffff0",
  47. ];
  48. // generates a 12 character random string
  49. function rid() {
  50. return Math.random().toString(36).substring(2, 14);
  51. }
  52. function getRandomInt(min, max) {
  53. return Math.floor(Math.random() * (max - min + 1)) + min;
  54. }
  55. var ids = [
  56. uuid.toLowerCase(),
  57. name.toLowerCase(),
  58. name.toUpperCase(),
  59. uuid.toUpperCase(),
  60. ];
  61. describe("Crafatar", function() {
  62. // we might have to make 2 HTTP requests
  63. this.timeout(config.server.http_timeout * 2 + 50);
  64. before(function() {
  65. cache.get_redis().flushall();
  66. // cause I don't know how big hard drives are these days
  67. config.cleaner.disk_limit = Infinity;
  68. config.cleaner.redis_limit = Infinity;
  69. cleaner.run();
  70. });
  71. describe("UUID/username", function() {
  72. it("non-hex uuid is invalid", function(done) {
  73. assert.strictEqual(helpers.id_valid("g098cb60fa8e427cb299793cbd302c9a"), false);
  74. done();
  75. });
  76. it("empty id is invalid", function(done) {
  77. assert.strictEqual(helpers.id_valid(""), false);
  78. done();
  79. });
  80. it("non-alphanumeric username is invalid", function(done) {
  81. assert.strictEqual(helpers.id_valid("usernäme"), false);
  82. done();
  83. });
  84. it("dashed username is invalid", function(done) {
  85. assert.strictEqual(helpers.id_valid("user-name"), false);
  86. done();
  87. });
  88. it(">16 length username is invalid", function(done) {
  89. assert.strictEqual(helpers.id_valid("ThisNameIsTooLong"), false);
  90. done();
  91. });
  92. it("lowercase uuid is valid", function(done) {
  93. assert.strictEqual(helpers.id_valid("0098cb60fa8e427cb299793cbd302c9a"), true);
  94. done();
  95. });
  96. it("uppercase uuid is valid", function(done) {
  97. assert.strictEqual(helpers.id_valid("1DCEF164FF0A47F2B9A691385C774EE7"), true);
  98. done();
  99. });
  100. it("dashed uuid is valid", function(done) {
  101. assert.strictEqual(helpers.id_valid("0098cb60-fa8e-427c-b299-793cbd302c9a"), true);
  102. done();
  103. });
  104. it("16 chars, underscored, capital, numbered username is valid", function(done) {
  105. assert.strictEqual(helpers.id_valid("__niceUs3rname__"), true);
  106. done();
  107. });
  108. it("1 char username is valid", function(done) {
  109. assert.strictEqual(helpers.id_valid("a"), true);
  110. done();
  111. });
  112. it("should not exist (uuid)", function(done) {
  113. var number = getRandomInt(0, 9).toString();
  114. networking.get_profile(rid(), Array(33).join(number), function(err, profile) {
  115. assert.ifError(err);
  116. assert.strictEqual(profile, null);
  117. done();
  118. });
  119. });
  120. it("should not exist (username)", function(done) {
  121. networking.get_username_url(rid(), "Steve", 0, function(err, profile) {
  122. assert.ifError(err);
  123. done();
  124. });
  125. });
  126. });
  127. describe("Avatar", function() {
  128. it("uuid's account should exist, but skin should not", function(done) {
  129. // profile "Alex" - hoping it'll never have a skin
  130. networking.get_profile(rid(), "ec561538f3fd461daff5086b22154bce", function(err, profile) {
  131. assert.ifError(err);
  132. assert.notStrictEqual(profile, null);
  133. networking.get_uuid_info(profile, "CAPE", function(url) {
  134. assert.strictEqual(url, null);
  135. done();
  136. });
  137. });
  138. });
  139. it("Username should default to MHF_Steve", function(done) {
  140. assert.strictEqual(skins.default_skin("TestUser"), "mhf_steve");
  141. done();
  142. });
  143. for (var a in alex_ids) {
  144. var alexid = alex_ids[a];
  145. (function(alex_id) {
  146. it("UUID " + alex_id + " should default to MHF_Alex", function(done) {
  147. assert.strictEqual(skins.default_skin(alex_id), "mhf_alex");
  148. done();
  149. });
  150. }(alexid));
  151. }
  152. for (var s in steve_ids) {
  153. var steveid = steve_ids[s];
  154. (function(steve_id) {
  155. it("UUID " + steve_id + " should default to MHF_Steve", function(done) {
  156. assert.strictEqual(skins.default_skin(steve_id), "mhf_steve");
  157. done();
  158. });
  159. }(steveid));
  160. }
  161. });
  162. describe("Errors", function() {
  163. it("should time out on uuid info download", function(done) {
  164. var original_timeout = config.server.http_timeout;
  165. config.server.http_timeout = 1;
  166. networking.get_profile(rid(), "069a79f444e94726a5befca90e38aaf5", function(err, profile) {
  167. assert.strictEqual(err.code, "ETIMEDOUT");
  168. config.server.http_timeout = original_timeout;
  169. done();
  170. });
  171. });
  172. it("should time out on username info download", function(done) {
  173. var original_timeout = config.server.http_timeout;
  174. config.server.http_timeout = 1;
  175. networking.get_username_url(rid(), "jomo", 0, function(err, url) {
  176. assert.strictEqual(err.code, "ETIMEDOUT");
  177. config.server.http_timeout = original_timeout;
  178. done();
  179. });
  180. });
  181. it("should time out on skin download", function(done) {
  182. var original_timeout = config.http_timeout;
  183. config.server.http_timeout = 1;
  184. networking.get_from(rid(), "http://textures.minecraft.net/texture/477be35554684c28bdeee4cf11c591d3c88afb77e0b98da893fd7bc318c65184", function(body, res, error) {
  185. assert.strictEqual(error.code, "ETIMEDOUT");
  186. config.server.http_timeout = original_timeout;
  187. done();
  188. });
  189. });
  190. it("should not find the skin", function(done) {
  191. assert.doesNotThrow(function() {
  192. networking.get_from(rid(), "http://textures.minecraft.net/texture/this-does-not-exist", function(img, response, err) {
  193. assert.strictEqual(err, null); // no error here, but it shouldn't throw exceptions
  194. done();
  195. });
  196. });
  197. });
  198. it("should not find the file", function(done) {
  199. skins.open_skin(rid(), "non/existent/path", function(err, img) {
  200. assert(err);
  201. done();
  202. });
  203. });
  204. });
  205. describe("Server", function() {
  206. // throws Exception when default headers are not in res.headers
  207. function assert_headers(res) {
  208. assert(res.headers["content-type"]);
  209. assert("" + res.headers["response-time"]);
  210. assert(res.headers["x-request-id"]);
  211. assert.equal(res.headers["access-control-allow-origin"], "*");
  212. assert.equal(res.headers["cache-control"], "max-age=" + config.caching.browser + ", public");
  213. }
  214. // throws Exception when +url+ is requested with +etag+
  215. // and it does not return 304 without a body
  216. function assert_cache(url, etag, callback) {
  217. request.get(url, {
  218. headers: {
  219. "If-None-Match": etag
  220. }
  221. }, function(error, res, body) {
  222. assert.ifError(error);
  223. assert.ifError(body);
  224. assert.equal(res.statusCode, 304);
  225. assert(res.headers.etag);
  226. assert_headers(res);
  227. callback();
  228. });
  229. }
  230. before(function(done) {
  231. server.boot(function() {
  232. done();
  233. });
  234. });
  235. it("should return 405 Method Not Allowed for POST", function(done) {
  236. request.post("http://localhost:3000", function(error, res, body) {
  237. assert.ifError(error);
  238. assert.strictEqual(res.statusCode, 405);
  239. done();
  240. });
  241. });
  242. it("should return correct HTTP response for home page", function(done) {
  243. var url = "http://localhost:3000";
  244. request.get(url, function(error, res, body) {
  245. assert.ifError(error);
  246. assert.strictEqual(res.statusCode, 200);
  247. assert_headers(res);
  248. assert(res.headers.etag);
  249. assert.strictEqual(res.headers["content-type"], "text/html; charset=utf-8");
  250. assert.strictEqual(res.headers.etag, '"' + crc(body) + '"');
  251. assert(body);
  252. assert_cache(url, res.headers.etag, function() {
  253. done();
  254. });
  255. });
  256. });
  257. it("should return correct HTTP response for assets", function(done) {
  258. var url = "http://localhost:3000/stylesheets/style.css";
  259. request.get(url, function(error, res, body) {
  260. assert.ifError(error);
  261. assert.strictEqual(res.statusCode, 200);
  262. assert_headers(res);
  263. assert(res.headers.etag);
  264. assert.strictEqual(res.headers["content-type"], "text/css");
  265. assert.strictEqual(res.headers.etag, '"' + crc(body) + '"');
  266. assert(body);
  267. assert_cache(url, res.headers.etag, function() {
  268. done();
  269. });
  270. });
  271. });
  272. it("should return correct HTTP response for URL encoded URLs", function(done) {
  273. var url = "http://localhost:3000/%61%76%61%74%61%72%73/%6a%6f%6d%6f"; // avatars/jomo
  274. request.get(url, function(error, res, body) {
  275. assert.ifError(error);
  276. assert.strictEqual(res.statusCode, 200);
  277. assert_headers(res);
  278. assert(res.headers.etag);
  279. assert.strictEqual(res.headers["content-type"], "image/png");
  280. assert(body);
  281. done();
  282. });
  283. });
  284. it("should not fail on simultaneous requests", function(done) {
  285. // do not change "constructor" !
  286. // it's a reserved property name, we're testing for that
  287. var sids = ["696a82ce41f44b51aa31b8709b8686f0", "constructor"];
  288. for (var j in sids) {
  289. var id = sids[j];
  290. var url = "http://localhost:3000/avatars/" + id;
  291. // 10 requests at once
  292. var requests = 10;
  293. var finished = 0;
  294. function partDone() {
  295. finished++;
  296. if (requests === finished) {
  297. done();
  298. }
  299. }
  300. function req() {
  301. request.get(url, function(error, res, body) {
  302. assert.ifError(error);
  303. assert.strictEqual(res.statusCode, 200);
  304. assert_headers(res);
  305. assert(res.headers.etag);
  306. assert.strictEqual(res.headers["content-type"], "image/png");
  307. assert(body);
  308. partDone();
  309. });
  310. }
  311. // make simultanous requests
  312. for (var k = 0; k < requests; k++) {
  313. req(k);
  314. }
  315. }
  316. });
  317. var server_tests = {
  318. "avatar with existing username": {
  319. url: "http://localhost:3000/avatars/jeb_?size=16",
  320. etag: '"a846b82963"',
  321. crc32: 1623808067
  322. },
  323. "avatar with non-existent username": {
  324. url: "http://localhost:3000/avatars/0?size=16",
  325. etag: '"mhf_steve"',
  326. crc32: [2416827277, 1243826040]
  327. },
  328. "avatar with non-existent username defaulting to mhf_alex": {
  329. url: "http://localhost:3000/avatars/0?size=16&default=mhf_alex",
  330. etag: '"mhf_alex"',
  331. crc32: [862751081, 809395677]
  332. },
  333. "avatar with non-existent username defaulting to username": {
  334. url: "http://localhost:3000/avatars/0?size=16&default=jeb_",
  335. crc32: 0,
  336. redirect: "/avatars/jeb_?size=16"
  337. },
  338. "avatar with non-existent username defaulting to uuid": {
  339. url: "http://localhost:3000/avatars/0?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  340. crc32: 0,
  341. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
  342. },
  343. "avatar with non-existent username defaulting to url": {
  344. url: "http://localhost:3000/avatars/0?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  345. crc32: 0,
  346. redirect: "http://example.com/CaseSensitive"
  347. },
  348. "overlay avatar with existing username": {
  349. url: "http://localhost:3000/avatars/jeb_?size=16&overlay",
  350. etag: '"a846b82963"',
  351. crc32: 646871998
  352. },
  353. "overlay avatar with non-existent username": {
  354. url: "http://localhost:3000/avatars/0?size=16&overlay",
  355. etag: '"mhf_steve"',
  356. crc32: [2416827277, 1243826040]
  357. },
  358. "overlay avatar with non-existent username defaulting to mhf_alex": {
  359. url: "http://localhost:3000/avatars/0?size=16&overlay&default=mhf_alex",
  360. etag: '"mhf_alex"',
  361. crc32: [862751081, 809395677]
  362. },
  363. "overlay avatar with non-existent username defaulting to username": {
  364. url: "http://localhost:3000/avatars/0?size=16&overlay&default=jeb_",
  365. crc32: 0,
  366. redirect: "/avatars/jeb_?size=16&overlay="
  367. },
  368. "overlay avatar with non-existent username defaulting to uuid": {
  369. url: "http://localhost:3000/avatars/0?size=16&overlay&default=853c80ef3c3749fdaa49938b674adae6",
  370. crc32: 0,
  371. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay="
  372. },
  373. "overlay avatar with non-existent username defaulting to url": {
  374. url: "http://localhost:3000/avatars/0?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  375. crc32: 0,
  376. redirect: "http://example.com/CaseSensitive"
  377. },
  378. "avatar with existing uuid": {
  379. url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
  380. etag: '"a846b82963"',
  381. crc32: 1623808067
  382. },
  383. "avatar with non-existent uuid": {
  384. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16",
  385. etag: '"mhf_steve"',
  386. crc32: [2416827277, 1243826040]
  387. },
  388. "avatar with non-existent uuid defaulting to mhf_alex": {
  389. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=mhf_alex",
  390. etag: '"mhf_alex"',
  391. crc32: [862751081, 809395677]
  392. },
  393. "avatar with non-existent uuid defaulting to username": {
  394. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_",
  395. crc32: 0,
  396. redirect: "/avatars/jeb_?size=16"
  397. },
  398. "avatar with non-existent uuid defaulting to uuid": {
  399. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  400. crc32: 0,
  401. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
  402. },
  403. "avatar with non-existent uuid defaulting to url": {
  404. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  405. crc32: 0,
  406. redirect: "http://example.com/CaseSensitive"
  407. },
  408. "overlay avatar with existing uuid": {
  409. url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay",
  410. etag: '"a846b82963"',
  411. crc32: 646871998
  412. },
  413. "overlay avatar with non-existent uuid": {
  414. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay",
  415. etag: '"mhf_steve"',
  416. crc32: [2416827277, 1243826040]
  417. },
  418. "overlay avatar with non-existent uuid defaulting to mhf_alex": {
  419. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=mhf_alex",
  420. etag: '"mhf_alex"',
  421. crc32: [862751081, 809395677]
  422. },
  423. "overlay avatar with non-existent uuid defaulting to username": {
  424. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_",
  425. crc32: 0,
  426. redirect: "/avatars/jeb_?size=16"
  427. },
  428. "overlay avatar with non-existent uuid defaulting to uuid": {
  429. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  430. crc32: 0,
  431. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
  432. },
  433. "overlay avatar with non-existent uuid defaulting to url": {
  434. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  435. crc32: 0,
  436. redirect: "http://example.com/CaseSensitive"
  437. },
  438. "cape with existing username": {
  439. url: "http://localhost:3000/capes/jeb_",
  440. etag: '"3f688e0e69"',
  441. crc32: [989800403, 1901140141]
  442. },
  443. "cape with non-existent username": {
  444. url: "http://localhost:3000/capes/0",
  445. crc32: 0
  446. },
  447. "cape with non-existent username defaulting to url": {
  448. url: "http://localhost:3000/capes/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  449. crc32: 0,
  450. redirect: "http://example.com/CaseSensitive"
  451. },
  452. "cape with existing uuid": {
  453. url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6",
  454. etag: '"3f688e0e69"',
  455. crc32: [989800403, 1901140141]
  456. },
  457. "cape with non-existent uuid": {
  458. url: "http://localhost:3000/capes/00000000000000000000000000000000",
  459. crc32: 0
  460. },
  461. "cape with non-existent uuid defaulting to url": {
  462. url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  463. crc32: 0,
  464. redirect: "http://example.com/CaseSensitive"
  465. },
  466. "skin with existing username": {
  467. url: "http://localhost:3000/skins/jeb_",
  468. etag: '"a846b82963"',
  469. crc32: 26500336
  470. },
  471. "skin with non-existent username": {
  472. url: "http://localhost:3000/skins/0",
  473. etag: '"mhf_steve"',
  474. crc32: 981937087
  475. },
  476. "skin with non-existent username defaulting to mhf_alex": {
  477. url: "http://localhost:3000/skins/0?default=mhf_alex",
  478. etag: '"mhf_alex"',
  479. crc32: 2298915739
  480. },
  481. "skin with non-existent username defaulting to username": {
  482. url: "http://localhost:3000/skins/0?size=16&default=jeb_",
  483. crc32: 0,
  484. redirect: "/skins/jeb_?size=16"
  485. },
  486. "skin with non-existent username defaulting to uuid": {
  487. url: "http://localhost:3000/skins/0?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  488. crc32: 0,
  489. redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16"
  490. },
  491. "skin with non-existent username defaulting to url": {
  492. url: "http://localhost:3000/skins/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  493. crc32: 0,
  494. redirect: "http://example.com/CaseSensitive"
  495. },
  496. "skin with existing uuid": {
  497. url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6",
  498. etag: '"a846b82963"',
  499. crc32: 26500336
  500. },
  501. "skin with non-existent uuid": {
  502. url: "http://localhost:3000/skins/00000000000000000000000000000000",
  503. etag: '"mhf_steve"',
  504. crc32: 981937087
  505. },
  506. "skin with non-existent uuid defaulting to mhf_alex": {
  507. url: "http://localhost:3000/skins/00000000000000000000000000000000?default=mhf_alex",
  508. etag: '"mhf_alex"',
  509. crc32: 2298915739
  510. },
  511. "skin with non-existent uuid defaulting to username": {
  512. url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=jeb_",
  513. crc32: 0,
  514. redirect: "/skins/jeb_?size=16"
  515. },
  516. "skin with non-existent uuid defaulting to uuid": {
  517. url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  518. crc32: 0,
  519. redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16"
  520. },
  521. "skin with non-existent uuid defaulting to url": {
  522. url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  523. crc32: 0,
  524. redirect: "http://example.com/CaseSensitive"
  525. },
  526. "head render with existing username": {
  527. url: "http://localhost:3000/renders/head/jeb_?scale=2",
  528. etag: '"a846b82963"',
  529. crc32: [3487896679, 3001090792]
  530. },
  531. "head render with non-existent username": {
  532. url: "http://localhost:3000/renders/head/0?scale=2",
  533. etag: '"mhf_steve"',
  534. crc32: [3257141069, 214248305]
  535. },
  536. "head render with non-existent username defaulting to mhf_alex": {
  537. url: "http://localhost:3000/renders/head/0?scale=2&default=mhf_alex",
  538. etag: '"mhf_alex"',
  539. crc32: [263450586, 3116770561]
  540. },
  541. "head render with non-existent username defaulting to username": {
  542. url: "http://localhost:3000/avatars/0?scale=2&default=jeb_",
  543. crc32: 0,
  544. redirect: "/avatars/jeb_?scale=2"
  545. },
  546. "head render with non-existent username defaulting to uuid": {
  547. url: "http://localhost:3000/avatars/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  548. crc32: 0,
  549. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?scale=2"
  550. },
  551. "head render with non-existent username defaulting to url": {
  552. url: "http://localhost:3000/renders/head/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  553. crc32: 0,
  554. redirect: "http://example.com/CaseSensitive"
  555. },
  556. "overlay head render with existing username": {
  557. url: "http://localhost:3000/renders/head/jeb_?scale=2&overlay",
  558. etag: '"a846b82963"',
  559. crc32: [762377383, 1726474987]
  560. },
  561. "overlay head render with non-existent username": {
  562. url: "http://localhost:3000/renders/head/0?scale=2&overlay",
  563. etag: '"mhf_steve"',
  564. crc32: [3257141069, 214248305]
  565. },
  566. "overlay head render with non-existent username defaulting to mhf_alex": {
  567. url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=mhf_alex",
  568. etag: '"mhf_alex"',
  569. crc32: [263450586, 3116770561]
  570. },
  571. "overlay head render with non-existent username defaulting to username": {
  572. url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=jeb_",
  573. crc32: 0,
  574. redirect: "/renders/head/jeb_?scale=2&overlay="
  575. },
  576. "overlay head render with non-existent username defaulting to uuid": {
  577. url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
  578. crc32: 0,
  579. redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay="
  580. },
  581. "overlay head render with non-existent username defaulting to url": {
  582. url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  583. crc32: 0,
  584. redirect: "http://example.com/CaseSensitive"
  585. },
  586. "head render with existing uuid": {
  587. url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2",
  588. etag: '"a846b82963"',
  589. crc32: [3487896679]
  590. },
  591. "head render with non-existent uuid": {
  592. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2",
  593. etag: '"mhf_steve"',
  594. crc32: [3257141069, 214248305]
  595. },
  596. "head render with non-existent uuid defaulting to mhf_alex": {
  597. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=mhf_alex",
  598. etag: '"mhf_alex"',
  599. crc32: [263450586, 3116770561]
  600. },
  601. "head render with non-existent uuid defaulting to username": {
  602. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=jeb_",
  603. crc32: 0,
  604. redirect: "/renders/head/jeb_?scale=2"
  605. },
  606. "head render with non-existent uuid defaulting to uuid": {
  607. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  608. crc32: 0,
  609. redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2"
  610. },
  611. "head render with non-existent uuid defaulting to url": {
  612. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  613. crc32: 0,
  614. redirect: "http://example.com/CaseSensitive"
  615. },
  616. "overlay head render with existing uuid": {
  617. url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
  618. etag: '"a846b82963"',
  619. crc32: [762377383]
  620. },
  621. "overlay head render with non-existent uuid": {
  622. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay",
  623. etag: '"mhf_steve"',
  624. crc32: [3257141069, 214248305]
  625. },
  626. "overlay head render with non-existent uuid defaulting to mhf_alex": {
  627. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
  628. etag: '"mhf_alex"',
  629. crc32: [263450586, 3116770561]
  630. },
  631. "overlay head with non-existent uuid defaulting to username": {
  632. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=jeb_",
  633. crc32: 0,
  634. redirect: "/renders/head/jeb_?scale=2&overlay="
  635. },
  636. "overlay head with non-existent uuid defaulting to uuid": {
  637. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
  638. crc32: 0,
  639. redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay="
  640. },
  641. "overlay head render with non-existent uuid defaulting to url": {
  642. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  643. crc32: 0,
  644. redirect: "http://example.com/CaseSensitive"
  645. },
  646. "body render with existing username": {
  647. url: "http://localhost:3000/renders/body/jeb_?scale=2",
  648. etag: '"a846b82963"',
  649. crc32: [3127075871, 2595192206]
  650. },
  651. "body render with non-existent username": {
  652. url: "http://localhost:3000/renders/body/0?scale=2",
  653. etag: '"mhf_steve"',
  654. crc32: [1046655221, 1620063267]
  655. },
  656. "body render with non-existent username defaulting to mhf_alex": {
  657. url: "http://localhost:3000/renders/body/0?scale=2&default=mhf_alex",
  658. etag: '"mhf_alex"',
  659. crc32: [549240598, 3952648540]
  660. },
  661. "body render with non-existent username defaulting to username": {
  662. url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_",
  663. crc32: 0,
  664. redirect: "/renders/body/jeb_?scale=2"
  665. },
  666. "body render with non-existent username defaulting to uuid": {
  667. url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  668. crc32: 0,
  669. redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2"
  670. },
  671. "body render with non-existent username defaulting to url": {
  672. url: "http://localhost:3000/renders/body/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  673. crc32: 0,
  674. redirect: "http://example.com/CaseSensitive"
  675. },
  676. "overlay body render with existing username": {
  677. url: "http://localhost:3000/renders/body/jeb_?scale=2&overlay",
  678. etag: '"a846b82963"',
  679. crc32: [699892097, 2732138694]
  680. },
  681. "overlay body render with non-existent username": {
  682. url: "http://localhost:3000/renders/body/0?scale=2&overlay",
  683. etag: '"mhf_steve"',
  684. crc32: [1046655221, 1620063267]
  685. },
  686. "overlay body render with non-existent username defaulting to mhf_alex": {
  687. url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=mhf_alex",
  688. etag: '"mhf_alex"',
  689. crc32: [549240598, 3952648540]
  690. },
  691. "overlay body render with non-existent username defaulting to username": {
  692. url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=jeb_",
  693. crc32: 0,
  694. redirect: "/renders/body/jeb_?scale=2&overlay="
  695. },
  696. "overlay body render with non-existent username defaulting to uuid": {
  697. url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
  698. crc32: 0,
  699. redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay="
  700. },
  701. "overlay body render with non-existent username defaulting to url": {
  702. url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  703. crc32: 0,
  704. redirect: "http://example.com/CaseSensitive"
  705. },
  706. "body render with existing uuid": {
  707. url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
  708. etag: '"a846b82963"',
  709. crc32: [3127075871]
  710. },
  711. "body render with non-existent uuid": {
  712. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2",
  713. etag: '"mhf_steve"',
  714. crc32: [1046655221, 1620063267]
  715. },
  716. "body render with non-existent uuid defaulting to mhf_alex": {
  717. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=mhf_alex",
  718. etag: '"mhf_alex"',
  719. crc32: [549240598, 3952648540]
  720. },
  721. "body render with non-existent uuid defaulting to username": {
  722. url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_",
  723. crc32: 0,
  724. redirect: "/renders/body/jeb_?scale=2"
  725. },
  726. "body render with non-existent uuid defaulting to uuid": {
  727. url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  728. crc32: 0,
  729. redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2"
  730. },
  731. "body render with non-existent uuid defaulting to url": {
  732. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  733. crc32: 0,
  734. redirect: "http://example.com/CaseSensitive"
  735. },
  736. "overlay body render with existing uuid": {
  737. url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
  738. etag: '"a846b82963"',
  739. crc32: [699892097]
  740. },
  741. "overlay body render with non-existent uuid": {
  742. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay",
  743. etag: '"mhf_steve"',
  744. crc32: [1046655221, 1620063267]
  745. },
  746. "overlay body render with non-existent uuid defaulting to mhf_alex": {
  747. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
  748. etag: '"mhf_alex"',
  749. crc32: [549240598, 3952648540]
  750. },
  751. "overlay body render with non-existent uuid defaulting to url": {
  752. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  753. crc32: 0,
  754. redirect: "http://example.com/CaseSensitive"
  755. },
  756. };
  757. for (var description in server_tests) {
  758. var loc = server_tests[description];
  759. (function(location) {
  760. it("should return correct HTTP response for " + description, function(done) {
  761. request.get(location.url, {followRedirect: false, encoding: null}, function(error, res, body) {
  762. assert.ifError(error);
  763. assert_headers(res);
  764. assert(res.headers["x-storage-type"]);
  765. assert.strictEqual(res.headers.etag, location.etag);
  766. var matches = false;
  767. if (location.crc32 instanceof Array) {
  768. for (var i = 0; i < location.crc32.length; i++) {
  769. if (location.crc32[i] === crc(body)) {
  770. matches = true;
  771. break;
  772. }
  773. }
  774. } else {
  775. matches = location.crc32 === crc(body);
  776. }
  777. try {
  778. assert.ok(matches);
  779. } catch(e) {
  780. throw new Error(crc(body) + " != " + location.crc32 + " | " + body.toString("base64"));
  781. }
  782. assert.strictEqual(res.headers.location, location.redirect);
  783. if (location.etag === undefined) {
  784. assert.strictEqual(res.statusCode, location.redirect ? 307 : 404);
  785. assert.strictEqual(res.headers["content-type"], "text/plain");
  786. done();
  787. } else {
  788. assert(res.headers.etag);
  789. assert.strictEqual(res.headers["content-type"], "image/png");
  790. assert.strictEqual(res.statusCode, 200);
  791. assert_cache(location.url, res.headers.etag, function() {
  792. done();
  793. });
  794. }
  795. });
  796. });
  797. }(loc));
  798. }
  799. it("should return a 422 (invalid size)", function(done) {
  800. var size = config.avatars.max_size + 1;
  801. request.get("http://localhost:3000/avatars/Jake_0?size=" + size, function(error, res, body) {
  802. assert.ifError(error);
  803. assert.strictEqual(res.statusCode, 422);
  804. done();
  805. });
  806. });
  807. it("should return a 422 (invalid scale)", function(done) {
  808. var scale = config.renders.max_scale + 1;
  809. request.get("http://localhost:3000/renders/head/Jake_0?scale=" + scale, function(error, res, body) {
  810. assert.ifError(error);
  811. assert.strictEqual(res.statusCode, 422);
  812. done();
  813. });
  814. });
  815. it("should return a 422 (invalid render type)", function(done) {
  816. request.get("http://localhost:3000/renders/invalid/Jake_0", function(error, res, body) {
  817. assert.ifError(error);
  818. assert.strictEqual(res.statusCode, 422);
  819. done();
  820. });
  821. });
  822. // testing all paths for Invalid UserID
  823. var locations = ["avatars", "skins", "capes", "renders/body", "renders/head"];
  824. for (var l in locations) {
  825. loc = locations[l];
  826. (function(location) {
  827. it("should return a 422 (invalid id " + location + ")", function(done) {
  828. request.get("http://localhost:3000/" + location + "/thisisaninvaliduuid", function(error, res, body) {
  829. assert.ifError(error);
  830. assert.strictEqual(res.statusCode, 422);
  831. done();
  832. });
  833. });
  834. it("should return a 404 (invalid path " + location + ")", function(done) {
  835. request.get("http://localhost:3000/" + location + "/853c80ef3c3749fdaa49938b674adae6/invalid", function(error, res, body) {
  836. assert.ifError(error);
  837. assert.strictEqual(res.statusCode, 404);
  838. done();
  839. });
  840. });
  841. }(loc));
  842. }
  843. after(function(done) {
  844. server.close(function() {
  845. done();
  846. });
  847. });
  848. });
  849. // we have to make sure that we test both a 32x64 and 64x64 skin
  850. describe("Networking: Render", function() {
  851. it("should not fail (username, 32x64 skin)", function(done) {
  852. helpers.get_render(rid(), "md_5", 6, true, true, function(err, hash, img) {
  853. assert.strictEqual(err, null);
  854. done();
  855. });
  856. });
  857. it("should not fail (username, 64x64 skin)", function(done) {
  858. helpers.get_render(rid(), "Jake_0", 6, true, true, function(err, hash, img) {
  859. assert.strictEqual(err, null);
  860. done();
  861. });
  862. });
  863. });
  864. describe("Networking: Cape", function() {
  865. it("should not fail (guaranteed cape)", function(done) {
  866. helpers.get_cape(rid(), "Dinnerbone", function(err, hash, status, img) {
  867. assert.strictEqual(err, null);
  868. done();
  869. });
  870. });
  871. it("should already exist", function(done) {
  872. before(function() {
  873. cache.get_redis().flushall();
  874. });
  875. helpers.get_cape(rid(), "Dinnerbone", function(err, hash, status, img) {
  876. assert.strictEqual(err, null);
  877. done();
  878. });
  879. });
  880. it("should not be found", function(done) {
  881. helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
  882. assert.ifError(err);
  883. assert.strictEqual(img, null);
  884. done();
  885. });
  886. });
  887. });
  888. describe("Networking: Skin", function() {
  889. it("should not fail", function(done) {
  890. helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
  891. assert.strictEqual(err, null);
  892. done();
  893. });
  894. });
  895. it("should already exist", function(done) {
  896. before(function() {
  897. cache.get_redis().flushall();
  898. });
  899. helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
  900. assert.strictEqual(err, null);
  901. done();
  902. });
  903. });
  904. });
  905. // DRY with uuid and username tests
  906. for (var i in ids) {
  907. var iid = ids[i];
  908. var iid_type = iid.length > 16 ? "uuid" : "name";
  909. // needs an anonymous function because id and id_type aren't constant
  910. (function(id, id_type) {
  911. describe("Networking: Avatar", function() {
  912. before(function() {
  913. cache.get_redis().flushall();
  914. console.log("\n\nRunning tests with " + id_type + " '" + id + "'\n\n");
  915. });
  916. it("should be downloaded", function(done) {
  917. helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
  918. assert.ifError(err);
  919. assert.strictEqual(status, 2);
  920. done();
  921. });
  922. });
  923. it("should be cached", function(done) {
  924. helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
  925. assert.ifError(err);
  926. assert.strictEqual(status === 0 || status === 1, true);
  927. done();
  928. });
  929. });
  930. if (id.length > 16) {
  931. console.log("can't run 'checked' test due to Mojang's rate limits :(");
  932. } else {
  933. it("should be checked", function(done) {
  934. var original_cache_time = config.caching.local;
  935. config.caching.local = 0;
  936. helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
  937. assert.ifError(err);
  938. assert.strictEqual(status, 3);
  939. config.caching.local = original_cache_time;
  940. done();
  941. });
  942. });
  943. }
  944. });
  945. describe("Networking: Skin", function() {
  946. it("should not fail (uuid)", function(done) {
  947. helpers.get_skin(rid(), id, function(err, hash, status, img) {
  948. assert.strictEqual(err, null);
  949. done();
  950. });
  951. });
  952. });
  953. describe("Networking: Render", function() {
  954. it("should not fail (full body)", function(done) {
  955. helpers.get_render(rid(), id, 6, true, true, function(err, hash, img) {
  956. assert.ifError(err);
  957. done();
  958. });
  959. });
  960. it("should not fail (only head)", function(done) {
  961. helpers.get_render(rid(), id, 6, true, false, function(err, hash, img) {
  962. assert.ifError(err);
  963. done();
  964. });
  965. });
  966. });
  967. describe("Networking: Cape", function() {
  968. it("should not fail (possible cape)", function(done) {
  969. helpers.get_cape(rid(), id, function(err, hash, status, img) {
  970. assert.ifError(err);
  971. done();
  972. });
  973. });
  974. });
  975. describe("Errors", function() {
  976. before(function() {
  977. cache.get_redis().flushall();
  978. });
  979. if (id_type === "uuid") {
  980. it("uuid should be rate limited", function(done) {
  981. networking.get_profile(rid(), id, function() {
  982. networking.get_profile(rid(), id, function(err, profile) {
  983. assert.strictEqual(err.toString(), "HTTP: 429");
  984. assert.strictEqual(profile, null);
  985. done();
  986. });
  987. });
  988. });
  989. } else {
  990. it("username should NOT be rate limited (username)", function(done) {
  991. helpers.get_avatar(rid(), id, false, 160, function() {
  992. helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
  993. assert.strictEqual(err, null);
  994. done();
  995. });
  996. });
  997. });
  998. }
  999. });
  1000. }(iid, iid_type));
  1001. }
  1002. });