test.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  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 not find the file", function(done) {
  196. skins.open_skin(rid, "non/existent/path", function(err, img) {
  197. assert(err);
  198. done();
  199. });
  200. });
  201. });
  202. describe("Server", function() {
  203. // throws Exception when default headers are not in res.headers
  204. function assert_headers(res) {
  205. assert(res.headers["content-type"]);
  206. assert("" + res.headers["response-time"]);
  207. assert(res.headers["x-request-id"]);
  208. assert.equal(res.headers["access-control-allow-origin"], "*");
  209. assert.equal(res.headers["cache-control"], "max-age=" + config.caching.browser + ", public");
  210. }
  211. // throws Exception when +url+ is requested with +etag+
  212. // and it does not return 304 without a body
  213. function assert_cache(url, etag, callback) {
  214. request.get(url, {
  215. headers: {
  216. "If-None-Match": etag
  217. }
  218. }, function(error, res, body) {
  219. assert.ifError(error);
  220. assert.ifError(body);
  221. assert.equal(res.statusCode, 304);
  222. assert(res.headers.etag);
  223. assert_headers(res);
  224. callback();
  225. });
  226. }
  227. before(function(done) {
  228. server.boot(function() {
  229. done();
  230. });
  231. });
  232. it("should return 405 Method Not Allowed for POST", function(done) {
  233. request.post("http://localhost:3000", function(error, res, body) {
  234. assert.ifError(error);
  235. assert.strictEqual(res.statusCode, 405);
  236. done();
  237. });
  238. });
  239. it("should return correct HTTP response for home page", function(done) {
  240. var url = "http://localhost:3000";
  241. request.get(url, function(error, res, body) {
  242. assert.ifError(error);
  243. assert.strictEqual(res.statusCode, 200);
  244. assert_headers(res);
  245. assert(res.headers.etag);
  246. assert.strictEqual(res.headers["content-type"], "text/html; charset=utf-8");
  247. assert.strictEqual(res.headers.etag, '"' + crc(body) + '"');
  248. assert(body);
  249. assert_cache(url, res.headers.etag, function() {
  250. done();
  251. });
  252. });
  253. });
  254. it("should return correct HTTP response for assets", function(done) {
  255. var url = "http://localhost:3000/stylesheets/style.css";
  256. request.get(url, function(error, res, body) {
  257. assert.ifError(error);
  258. assert.strictEqual(res.statusCode, 200);
  259. assert_headers(res);
  260. assert(res.headers.etag);
  261. assert.strictEqual(res.headers["content-type"], "text/css");
  262. assert.strictEqual(res.headers.etag, '"' + crc(body) + '"');
  263. assert(body);
  264. assert_cache(url, res.headers.etag, function() {
  265. done();
  266. });
  267. });
  268. });
  269. it("should return correct HTTP response for URL encoded URLs", function(done) {
  270. var url = "http://localhost:3000/%61%76%61%74%61%72%73/%6a%6f%6d%6f"; // avatars/jomo
  271. request.get(url, function(error, res, body) {
  272. assert.ifError(error);
  273. assert.strictEqual(res.statusCode, 200);
  274. assert_headers(res);
  275. assert(res.headers.etag);
  276. assert.strictEqual(res.headers["content-type"], "image/png");
  277. assert(body);
  278. done();
  279. });
  280. });
  281. it("should not fail on simultaneous requests", function(done) {
  282. var url = "http://localhost:3000/avatars/696a82ce41f44b51aa31b8709b8686f0";
  283. // 10 requests at once
  284. var requests = 10;
  285. var finished = 0;
  286. function partDone() {
  287. finished++;
  288. if (requests === finished) {
  289. done();
  290. }
  291. }
  292. function req() {
  293. request.get(url, function(error, res, body) {
  294. assert.ifError(error);
  295. assert.strictEqual(res.statusCode, 200);
  296. assert_headers(res);
  297. assert(res.headers.etag);
  298. assert.strictEqual(res.headers["content-type"], "image/png");
  299. assert(body);
  300. partDone();
  301. });
  302. }
  303. // make simultanous requests
  304. for (var j = 0; j < requests; j++) {
  305. req(j);
  306. }
  307. });
  308. var server_tests = {
  309. "avatar with existing username": {
  310. url: "http://localhost:3000/avatars/jeb_?size=16",
  311. etag: '"a846b82963"',
  312. crc32: 1623808067
  313. },
  314. "avatar with non-existent username": {
  315. url: "http://localhost:3000/avatars/0?size=16",
  316. etag: '"steve"',
  317. crc32: [2416827277, 1243826040]
  318. },
  319. "avatar with non-existent username defaulting to alex": {
  320. url: "http://localhost:3000/avatars/0?size=16&default=alex",
  321. etag: '"alex"',
  322. crc32: [862751081, 809395677]
  323. },
  324. "avatar with non-existent username defaulting to username": {
  325. url: "http://localhost:3000/avatars/0?size=16&default=jeb_",
  326. crc32: 0,
  327. redirect: "/avatars/jeb_?size=16"
  328. },
  329. "avatar with non-existent username defaulting to uuid": {
  330. url: "http://localhost:3000/avatars/0?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  331. crc32: 0,
  332. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
  333. },
  334. "avatar with non-existent username defaulting to url": {
  335. url: "http://localhost:3000/avatars/0?size=16&default=http%3A%2F%2Fexample.com",
  336. crc32: 0,
  337. redirect: "http://example.com"
  338. },
  339. "helm avatar with existing username": {
  340. url: "http://localhost:3000/avatars/jeb_?size=16&helm",
  341. etag: '"a846b82963"',
  342. crc32: 646871998
  343. },
  344. "helm avatar with non-existent username": {
  345. url: "http://localhost:3000/avatars/0?size=16&helm",
  346. etag: '"steve"',
  347. crc32: [2416827277, 1243826040]
  348. },
  349. "helm avatar with non-existent username defaulting to alex": {
  350. url: "http://localhost:3000/avatars/0?size=16&helm&default=alex",
  351. etag: '"alex"',
  352. crc32: [862751081, 809395677]
  353. },
  354. "helm avatar with non-existent username defaulting to username": {
  355. url: "http://localhost:3000/avatars/0?size=16&helm&default=jeb_",
  356. crc32: 0,
  357. redirect: "/avatars/jeb_?size=16&helm="
  358. },
  359. "helm avatar with non-existent username defaulting to uuid": {
  360. url: "http://localhost:3000/avatars/0?size=16&helm&default=853c80ef3c3749fdaa49938b674adae6",
  361. crc32: 0,
  362. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&helm="
  363. },
  364. "helm avatar with non-existent username defaulting to url": {
  365. url: "http://localhost:3000/avatars/0?size=16&helm&default=http%3A%2F%2Fexample.com",
  366. crc32: 0,
  367. redirect: "http://example.com"
  368. },
  369. "avatar with existing uuid": {
  370. url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
  371. etag: '"a846b82963"',
  372. crc32: 1623808067
  373. },
  374. "avatar with non-existent uuid": {
  375. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16",
  376. etag: '"steve"',
  377. crc32: [2416827277, 1243826040]
  378. },
  379. "avatar with non-existent uuid defaulting to alex": {
  380. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=alex",
  381. etag: '"alex"',
  382. crc32: [862751081, 809395677]
  383. },
  384. "avatar with non-existent uuid defaulting to username": {
  385. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_",
  386. crc32: 0,
  387. redirect: "/avatars/jeb_?size=16"
  388. },
  389. "avatar with non-existent uuid defaulting to uuid": {
  390. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  391. crc32: 0,
  392. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
  393. },
  394. "avatar with non-existent uuid defaulting to url": {
  395. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http%3A%2F%2Fexample.com",
  396. crc32: 0,
  397. redirect: "http://example.com"
  398. },
  399. "helm avatar with existing uuid": {
  400. url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&helm",
  401. etag: '"a846b82963"',
  402. crc32: 646871998
  403. },
  404. "helm avatar with non-existent uuid": {
  405. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm",
  406. etag: '"steve"',
  407. crc32: [2416827277, 1243826040]
  408. },
  409. "helm avatar with non-existent uuid defaulting to alex": {
  410. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=alex",
  411. etag: '"alex"',
  412. crc32: [862751081, 809395677]
  413. },
  414. "helm avatar with non-existent uuid defaulting to username": {
  415. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_",
  416. crc32: 0,
  417. redirect: "/avatars/jeb_?size=16"
  418. },
  419. "helm avatar with non-existent uuid defaulting to uuid": {
  420. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  421. crc32: 0,
  422. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
  423. },
  424. "helm avatar with non-existent uuid defaulting to url": {
  425. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&helm&default=http%3A%2F%2Fexample.com",
  426. crc32: 0,
  427. redirect: "http://example.com"
  428. },
  429. "cape with existing username": {
  430. url: "http://localhost:3000/capes/jeb_",
  431. etag: '"3f688e0e69"',
  432. crc32: [989800403, 1901140141]
  433. },
  434. "cape with non-existent username": {
  435. url: "http://localhost:3000/capes/0",
  436. crc32: 0
  437. },
  438. "cape with non-existent username defaulting to url": {
  439. url: "http://localhost:3000/capes/0?default=http%3A%2F%2Fexample.com",
  440. crc32: 0,
  441. redirect: "http://example.com"
  442. },
  443. "cape with existing uuid": {
  444. url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6",
  445. etag: '"3f688e0e69"',
  446. crc32: [989800403, 1901140141]
  447. },
  448. "cape with non-existent uuid": {
  449. url: "http://localhost:3000/capes/00000000000000000000000000000000",
  450. crc32: 0
  451. },
  452. "cape with non-existent uuid defaulting to url": {
  453. url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com",
  454. crc32: 0,
  455. redirect: "http://example.com"
  456. },
  457. "skin with existing username": {
  458. url: "http://localhost:3000/skins/jeb_",
  459. etag: '"a846b82963"',
  460. crc32: 26500336
  461. },
  462. "skin with non-existent username": {
  463. url: "http://localhost:3000/skins/0",
  464. etag: '"steve"',
  465. crc32: 981937087
  466. },
  467. "skin with non-existent username defaulting to alex": {
  468. url: "http://localhost:3000/skins/0?default=alex",
  469. etag: '"alex"',
  470. crc32: 2298915739
  471. },
  472. "skin with non-existent username defaulting to username": {
  473. url: "http://localhost:3000/skins/0?size=16&default=jeb_",
  474. crc32: 0,
  475. redirect: "/skins/jeb_?size=16"
  476. },
  477. "skin with non-existent username defaulting to uuid": {
  478. url: "http://localhost:3000/skins/0?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  479. crc32: 0,
  480. redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16"
  481. },
  482. "skin with non-existent username defaulting to url": {
  483. url: "http://localhost:3000/skins/0?default=http%3A%2F%2Fexample.com",
  484. crc32: 0,
  485. redirect: "http://example.com"
  486. },
  487. "skin with existing uuid": {
  488. url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6",
  489. etag: '"a846b82963"',
  490. crc32: 26500336
  491. },
  492. "skin with non-existent uuid": {
  493. url: "http://localhost:3000/skins/00000000000000000000000000000000",
  494. etag: '"steve"',
  495. crc32: 981937087
  496. },
  497. "skin with non-existent uuid defaulting to alex": {
  498. url: "http://localhost:3000/skins/00000000000000000000000000000000?default=alex",
  499. etag: '"alex"',
  500. crc32: 2298915739
  501. },
  502. "skin with non-existent uuid defaulting to username": {
  503. url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=jeb_",
  504. crc32: 0,
  505. redirect: "/skins/jeb_?size=16"
  506. },
  507. "skin with non-existent uuid defaulting to uuid": {
  508. url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  509. crc32: 0,
  510. redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16"
  511. },
  512. "skin with non-existent uuid defaulting to url": {
  513. url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com",
  514. crc32: 0,
  515. redirect: "http://example.com"
  516. },
  517. "head render with existing username": {
  518. url: "http://localhost:3000/renders/head/jeb_?scale=2",
  519. etag: '"a846b82963"',
  520. crc32: [353633671, 370672768]
  521. },
  522. "head render with non-existent username": {
  523. url: "http://localhost:3000/renders/head/0?scale=2",
  524. etag: '"steve"',
  525. crc32: [883439147, 433083528]
  526. },
  527. "head render with non-existent username defaulting to alex": {
  528. url: "http://localhost:3000/renders/head/0?scale=2&default=alex",
  529. etag: '"alex"',
  530. crc32: [1240086237, 1108800327]
  531. },
  532. "head render with non-existent username defaulting to username": {
  533. url: "http://localhost:3000/avatars/0?scale=2&default=jeb_",
  534. crc32: 0,
  535. redirect: "/avatars/jeb_?scale=2"
  536. },
  537. "head render with non-existent username defaulting to uuid": {
  538. url: "http://localhost:3000/avatars/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  539. crc32: 0,
  540. redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?scale=2"
  541. },
  542. "head render with non-existent username defaulting to url": {
  543. url: "http://localhost:3000/renders/head/0?scale=2&default=http%3A%2F%2Fexample.com",
  544. crc32: 0,
  545. redirect: "http://example.com"
  546. },
  547. "helm head render with existing username": {
  548. url: "http://localhost:3000/renders/head/jeb_?scale=2&helm",
  549. etag: '"a846b82963"',
  550. crc32: [3456497067, 3490318764]
  551. },
  552. "helm head render with non-existent username": {
  553. url: "http://localhost:3000/renders/head/0?scale=2&helm",
  554. etag: '"steve"',
  555. crc32: [1858563554, 2647471936]
  556. },
  557. "helm head render with non-existent username defaulting to alex": {
  558. url: "http://localhost:3000/renders/head/0?scale=2&helm&default=alex",
  559. etag: '"alex"',
  560. crc32: [2963161105, 1769904825]
  561. },
  562. "helm head render with non-existent username defaulting to username": {
  563. url: "http://localhost:3000/renders/head/0?scale=2&helm&default=jeb_",
  564. crc32: 0,
  565. redirect: "/renders/head/jeb_?scale=2&helm="
  566. },
  567. "helm head render with non-existent username defaulting to uuid": {
  568. url: "http://localhost:3000/renders/head/0?scale=2&helm&default=853c80ef3c3749fdaa49938b674adae6",
  569. crc32: 0,
  570. redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&helm="
  571. },
  572. "helm head render with non-existent username defaulting to url": {
  573. url: "http://localhost:3000/renders/head/0?scale=2&helm&default=http%3A%2F%2Fexample.com",
  574. crc32: 0,
  575. redirect: "http://example.com"
  576. },
  577. "head render with existing uuid": {
  578. url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2",
  579. etag: '"a846b82963"',
  580. crc32: [353633671, 370672768]
  581. },
  582. "head render with non-existent uuid": {
  583. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2",
  584. etag: '"steve"',
  585. crc32: [883439147, 433083528]
  586. },
  587. "head render with non-existent uuid defaulting to alex": {
  588. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=alex",
  589. etag: '"alex"',
  590. crc32: [1240086237, 1108800327]
  591. },
  592. "head render with non-existent uuid defaulting to username": {
  593. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=jeb_",
  594. crc32: 0,
  595. redirect: "/renders/head/jeb_?scale=2"
  596. },
  597. "head render with non-existent uuid defaulting to uuid": {
  598. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  599. crc32: 0,
  600. redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2"
  601. },
  602. "head render with non-existent uuid defaulting to url": {
  603. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com",
  604. crc32: 0,
  605. redirect: "http://example.com"
  606. },
  607. "helm head render with existing uuid": {
  608. url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&helm",
  609. etag: '"a846b82963"',
  610. crc32: [3456497067, 3490318764]
  611. },
  612. "helm head render with non-existent uuid": {
  613. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm",
  614. etag: '"steve"',
  615. crc32: [1858563554, 2647471936]
  616. },
  617. "helm head render with non-existent uuid defaulting to alex": {
  618. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm&default=alex",
  619. etag: '"alex"',
  620. crc32: [2963161105, 1769904825]
  621. },
  622. "helm head with non-existent uuid defaulting to username": {
  623. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm&default=jeb_",
  624. crc32: 0,
  625. redirect: "/renders/head/jeb_?scale=2&helm="
  626. },
  627. "helm head with non-existent uuid defaulting to uuid": {
  628. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm&default=853c80ef3c3749fdaa49938b674adae6",
  629. crc32: 0,
  630. redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&helm="
  631. },
  632. "helm head render with non-existent uuid defaulting to url": {
  633. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&helm&default=http%3A%2F%2Fexample.com",
  634. crc32: 0,
  635. redirect: "http://example.com"
  636. },
  637. "body render with existing username": {
  638. url: "http://localhost:3000/renders/body/jeb_?scale=2",
  639. etag: '"a846b82963"',
  640. crc32: [1291941229, 2628108474]
  641. },
  642. "body render with non-existent username": {
  643. url: "http://localhost:3000/renders/body/0?scale=2",
  644. etag: '"steve"',
  645. crc32: [2652947188, 2115706574]
  646. },
  647. "body render with non-existent username defaulting to alex": {
  648. url: "http://localhost:3000/renders/body/0?scale=2&default=alex",
  649. etag: '"alex"',
  650. crc32: [407932087, 2516216042]
  651. },
  652. "body render with non-existent username defaulting to username": {
  653. url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_",
  654. crc32: 0,
  655. redirect: "/renders/body/jeb_?scale=2"
  656. },
  657. "body render with non-existent username defaulting to uuid": {
  658. url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  659. crc32: 0,
  660. redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2"
  661. },
  662. "body render with non-existent username defaulting to url": {
  663. url: "http://localhost:3000/renders/body/0?scale=2&default=http%3A%2F%2Fexample.com",
  664. crc32: 0,
  665. redirect: "http://example.com"
  666. },
  667. "helm body render with existing username": {
  668. url: "http://localhost:3000/renders/body/jeb_?scale=2&helm",
  669. etag: '"a846b82963"',
  670. crc32: [3556188297, 4269754007]
  671. },
  672. "helm body render with non-existent username": {
  673. url: "http://localhost:3000/renders/body/0?scale=2&helm",
  674. etag: '"steve"',
  675. crc32: [272191039, 542896675]
  676. },
  677. "helm body render with non-existent username defaulting to alex": {
  678. url: "http://localhost:3000/renders/body/0?scale=2&helm&default=alex",
  679. etag: '"alex"',
  680. crc32: [737759773, 66512449]
  681. },
  682. "helm body render with non-existent username defaulting to username": {
  683. url: "http://localhost:3000/renders/body/0?scale=2&helm&default=jeb_",
  684. crc32: 0,
  685. redirect: "/renders/body/jeb_?scale=2&helm="
  686. },
  687. "helm body render with non-existent username defaulting to uuid": {
  688. url: "http://localhost:3000/renders/body/0?scale=2&helm&default=853c80ef3c3749fdaa49938b674adae6",
  689. crc32: 0,
  690. redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&helm="
  691. },
  692. "helm body render with non-existent username defaulting to url": {
  693. url: "http://localhost:3000/renders/body/0?scale=2&helm&default=http%3A%2F%2Fexample.com",
  694. crc32: 0,
  695. redirect: "http://example.com"
  696. },
  697. "body render with existing uuid": {
  698. url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
  699. etag: '"a846b82963"',
  700. crc32: [1291941229, 2628108474]
  701. },
  702. "body render with non-existent uuid": {
  703. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2",
  704. etag: '"steve"',
  705. crc32: [2652947188, 2115706574]
  706. },
  707. "body render with non-existent uuid defaulting to alex": {
  708. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=alex",
  709. etag: '"alex"',
  710. crc32: [407932087, 2516216042]
  711. },
  712. "body render with non-existent uuid defaulting to username": {
  713. url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_",
  714. crc32: 0,
  715. redirect: "/renders/body/jeb_?scale=2"
  716. },
  717. "body render with non-existent uuid defaulting to uuid": {
  718. url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  719. crc32: 0,
  720. redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2"
  721. },
  722. "body render with non-existent uuid defaulting to url": {
  723. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com",
  724. crc32: 0,
  725. redirect: "http://example.com"
  726. },
  727. "helm body render with existing uuid": {
  728. url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&helm",
  729. etag: '"a846b82963"',
  730. crc32: [3556188297, 4269754007]
  731. },
  732. "helm body render with non-existent uuid": {
  733. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm",
  734. etag: '"steve"',
  735. crc32: [272191039, 542896675]
  736. },
  737. "helm body render with non-existent uuid defaulting to alex": {
  738. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=alex",
  739. etag: '"alex"',
  740. crc32: [737759773, 66512449]
  741. },
  742. "helm body render with non-existent uuid defaulting to url": {
  743. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&helm&default=http%3A%2F%2Fexample.com",
  744. crc32: 0,
  745. redirect: "http://example.com"
  746. },
  747. };
  748. for (var description in server_tests) {
  749. var loc = server_tests[description];
  750. (function(location) {
  751. it("should return correct HTTP response for " + description, function(done) {
  752. request.get(location.url, {followRedirect: false, encoding: null}, function(error, res, body) {
  753. assert.ifError(error);
  754. assert_headers(res);
  755. assert(res.headers["x-storage-type"]);
  756. assert.strictEqual(res.headers.etag, location.etag);
  757. var matches = false;
  758. if (location.crc32 instanceof Array) {
  759. for (var i = 0; i < location.crc32.length; i++) {
  760. if (location.crc32[i] === crc(body)) {
  761. matches = true;
  762. break;
  763. }
  764. }
  765. } else {
  766. matches = (location.crc32 === crc(body));
  767. }
  768. try {
  769. assert.ok(matches);
  770. } catch(e) {
  771. throw new Error(crc(body) + " != " + location.crc32);
  772. }
  773. assert.strictEqual(res.headers.location, location.redirect);
  774. if (location.etag === undefined) {
  775. assert.strictEqual(res.statusCode, location.redirect ? 307 : 404);
  776. assert.strictEqual(res.headers["content-type"], "text/plain");
  777. done();
  778. } else {
  779. assert(res.headers.etag);
  780. assert.strictEqual(res.headers["content-type"], "image/png");
  781. assert.strictEqual(res.statusCode, 200);
  782. assert_cache(location.url, res.headers.etag, function() {
  783. done();
  784. });
  785. }
  786. });
  787. });
  788. }(loc));
  789. }
  790. it("should return a 422 (invalid size)", function(done) {
  791. var size = config.avatars.max_size + 1;
  792. request.get("http://localhost:3000/avatars/Jake_0?size=" + size, function(error, res, body) {
  793. assert.ifError(error);
  794. assert.strictEqual(res.statusCode, 422);
  795. done();
  796. });
  797. });
  798. it("should return a 422 (invalid scale)", function(done) {
  799. var scale = config.renders.max_scale + 1;
  800. request.get("http://localhost:3000/renders/head/Jake_0?scale=" + scale, function(error, res, body) {
  801. assert.ifError(error);
  802. assert.strictEqual(res.statusCode, 422);
  803. done();
  804. });
  805. });
  806. it("should return a 422 (invalid render type)", function(done) {
  807. request.get("http://localhost:3000/renders/invalid/Jake_0", function(error, res, body) {
  808. assert.ifError(error);
  809. assert.strictEqual(res.statusCode, 422);
  810. done();
  811. });
  812. });
  813. // testing all paths for Invalid UserID
  814. var locations = ["avatars", "skins", "capes", "renders/body", "renders/head"];
  815. for (var l in locations) {
  816. loc = locations[l];
  817. (function(location) {
  818. it("should return a 422 (invalid id " + location + ")", function(done) {
  819. request.get("http://localhost:3000/" + location + "/thisisaninvaliduuid", function(error, res, body) {
  820. assert.ifError(error);
  821. assert.strictEqual(res.statusCode, 422);
  822. done();
  823. });
  824. });
  825. it("should return a 404 (invalid path " + location + ")", function(done) {
  826. request.get("http://localhost:3000/" + location + "/853c80ef3c3749fdaa49938b674adae6/invalid", function(error, res, body) {
  827. assert.ifError(error);
  828. assert.strictEqual(res.statusCode, 404);
  829. done();
  830. });
  831. });
  832. }(loc));
  833. }
  834. after(function(done) {
  835. server.close(function() {
  836. done();
  837. });
  838. });
  839. });
  840. // we have to make sure that we test both a 32x64 and 64x64 skin
  841. describe("Networking: Render", function() {
  842. it("should not fail (username, 32x64 skin)", function(done) {
  843. helpers.get_render(rid, "md_5", 6, true, true, function(err, hash, img) {
  844. assert.strictEqual(err, null);
  845. done();
  846. });
  847. });
  848. it("should not fail (username, 64x64 skin)", function(done) {
  849. helpers.get_render(rid, "Jake_0", 6, true, true, function(err, hash, img) {
  850. assert.strictEqual(err, null);
  851. done();
  852. });
  853. });
  854. });
  855. describe("Networking: Cape", function() {
  856. it("should not fail (guaranteed cape)", function(done) {
  857. helpers.get_cape(rid, "Dinnerbone", function(err, hash, status, img) {
  858. assert.strictEqual(err, null);
  859. done();
  860. });
  861. });
  862. it("should already exist", function(done) {
  863. before(function() {
  864. cache.get_redis().flushall();
  865. });
  866. helpers.get_cape(rid, "Dinnerbone", function(err, hash, status, img) {
  867. assert.strictEqual(err, null);
  868. done();
  869. });
  870. });
  871. it("should not be found", function(done) {
  872. helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) {
  873. assert.ifError(err);
  874. assert.strictEqual(img, null);
  875. done();
  876. });
  877. });
  878. });
  879. describe("Networking: Skin", function() {
  880. it("should not fail", function(done) {
  881. helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) {
  882. assert.strictEqual(err, null);
  883. done();
  884. });
  885. });
  886. it("should already exist", function(done) {
  887. before(function() {
  888. cache.get_redis().flushall();
  889. });
  890. helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) {
  891. assert.strictEqual(err, null);
  892. done();
  893. });
  894. });
  895. });
  896. // DRY with uuid and username tests
  897. for (var i in ids) {
  898. var iid = ids[i];
  899. var iid_type = iid.length > 16 ? "uuid" : "name";
  900. // needs an anonymous function because id and id_type aren't constant
  901. (function(id, id_type) {
  902. describe("Networking: Avatar", function() {
  903. before(function() {
  904. cache.get_redis().flushall();
  905. console.log("\n\nRunning tests with " + id_type + " '" + id + "'\n\n");
  906. });
  907. it("should be downloaded", function(done) {
  908. helpers.get_avatar(rid, id, false, 160, function(err, status, image) {
  909. assert.ifError(err);
  910. assert.strictEqual(status, 2);
  911. done();
  912. });
  913. });
  914. it("should be cached", function(done) {
  915. helpers.get_avatar(rid, id, false, 160, function(err, status, image) {
  916. assert.ifError(err);
  917. assert.strictEqual(status === 0 || status === 1, true);
  918. done();
  919. });
  920. });
  921. if (id.length > 16) {
  922. console.log("can't run 'checked' test due to Mojang's rate limits :(");
  923. } else {
  924. it("should be checked", function(done) {
  925. var original_cache_time = config.caching.local;
  926. config.caching.local = 0;
  927. helpers.get_avatar(rid, id, false, 160, function(err, status, image) {
  928. assert.ifError(err);
  929. assert.strictEqual(status, 3);
  930. config.caching.local = original_cache_time;
  931. done();
  932. });
  933. });
  934. }
  935. });
  936. describe("Networking: Skin", function() {
  937. it("should not fail (uuid)", function(done) {
  938. helpers.get_skin(rid, id, function(err, hash, status, img) {
  939. assert.strictEqual(err, null);
  940. done();
  941. });
  942. });
  943. });
  944. describe("Networking: Render", function() {
  945. it("should not fail (full body)", function(done) {
  946. helpers.get_render(rid, id, 6, true, true, function(err, hash, img) {
  947. assert.ifError(err);
  948. done();
  949. });
  950. });
  951. it("should not fail (only head)", function(done) {
  952. helpers.get_render(rid, id, 6, true, false, function(err, hash, img) {
  953. assert.ifError(err);
  954. done();
  955. });
  956. });
  957. });
  958. describe("Networking: Cape", function() {
  959. it("should not fail (possible cape)", function(done) {
  960. helpers.get_cape(rid, id, function(err, hash, status, img) {
  961. assert.ifError(err);
  962. done();
  963. });
  964. });
  965. });
  966. describe("Errors", function() {
  967. before(function() {
  968. cache.get_redis().flushall();
  969. });
  970. if (id_type === "uuid") {
  971. it("uuid should be rate limited", function(done) {
  972. networking.get_profile(rid, id, function() {
  973. networking.get_profile(rid, id, function(err, profile) {
  974. assert.strictEqual(err, "TooManyRequests");
  975. assert.strictEqual(profile.error, "TooManyRequestsException");
  976. done();
  977. });
  978. });
  979. });
  980. } else {
  981. it("username should NOT be rate limited (username)", function(done) {
  982. helpers.get_avatar(rid, id, false, 160, function() {
  983. helpers.get_avatar(rid, id, false, 160, function(err, status, image) {
  984. assert.strictEqual(err, null);
  985. done();
  986. });
  987. });
  988. });
  989. }
  990. });
  991. }(iid, iid_type));
  992. }
  993. });