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