test.js 36 KB

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