test.js 41 KB

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