test.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. /* globals describe, it, before, after */
  2. /* eslint no-loop-func:0 guard-for-in:0 */
  3. // no spam
  4. var logging = require("../lib/logging");
  5. if (process.env.VERBOSE_TEST !== "true") {
  6. logging.log = logging.debug = logging.warn = logging.error = function() {};
  7. }
  8. var networking = require("../lib/networking");
  9. var helpers = require("../lib/helpers");
  10. var request = require("request");
  11. var config = require("../config");
  12. var server = require("../lib/server");
  13. var assert = require("assert");
  14. var skins = require("../lib/skins");
  15. var cache = require("../lib/cache");
  16. var crc = require("crc").crc32;
  17. var fs = require("fs");
  18. // we don't want tests to fail because of slow internet
  19. config.server.http_timeout *= 3;
  20. var uuids = fs.readFileSync("test/uuids.txt").toString().split(/\r?\n/);
  21. // Get a random UUIDto prevent rate limiting
  22. var uuid = uuids[Math.round(Math.random() * (uuids.length - 1))];
  23. // Let's hope these will never be assigned
  24. var steve_ids = [
  25. "fffffff0" + "fffffff0" + "fffffff0" + "fffffff0",
  26. "fffffff0" + "fffffff0" + "fffffff1" + "fffffff1",
  27. "fffffff0" + "fffffff1" + "fffffff0" + "fffffff1",
  28. "fffffff0" + "fffffff1" + "fffffff1" + "fffffff0",
  29. "fffffff1" + "fffffff0" + "fffffff0" + "fffffff1",
  30. "fffffff1" + "fffffff0" + "fffffff1" + "fffffff0",
  31. "fffffff1" + "fffffff1" + "fffffff0" + "fffffff0",
  32. "fffffff1" + "fffffff1" + "fffffff1" + "fffffff1",
  33. ];
  34. // Let's hope these will never be assigned
  35. var alex_ids = [
  36. "fffffff0" + "fffffff0" + "fffffff0" + "fffffff1",
  37. "fffffff0" + "fffffff0" + "fffffff1" + "fffffff0",
  38. "fffffff0" + "fffffff1" + "fffffff0" + "fffffff0",
  39. "fffffff0" + "fffffff1" + "fffffff1" + "fffffff1",
  40. "fffffff1" + "fffffff0" + "fffffff0" + "fffffff0",
  41. "fffffff1" + "fffffff0" + "fffffff1" + "fffffff1",
  42. "fffffff1" + "fffffff1" + "fffffff0" + "fffffff1",
  43. "fffffff1" + "fffffff1" + "fffffff1" + "fffffff0",
  44. ];
  45. // generates a 12 character random string
  46. function rid() {
  47. return Math.random().toString(36).substring(2, 14);
  48. }
  49. function getRandomInt(min, max) {
  50. return Math.floor(Math.random() * (max - min + 1)) + min;
  51. }
  52. describe("Crafatar", function() {
  53. // we might have to make 2 HTTP requests
  54. this.timeout(config.server.http_timeout * 2 + 50);
  55. before(function(done) {
  56. console.log("Flushing and waiting for redis ...");
  57. cache.get_redis().flushall(function() {
  58. console.log("Redis flushed!");
  59. done();
  60. });
  61. });
  62. describe("UUID/username", function() {
  63. it("non-hex uuid is invalid", function(done) {
  64. assert.strictEqual(helpers.id_valid("g098cb60fa8e427cb299793cbd302c9a"), false);
  65. done();
  66. });
  67. it("empty id is invalid", function(done) {
  68. assert.strictEqual(helpers.id_valid(""), false);
  69. done();
  70. });
  71. it("lowercase uuid is valid", function(done) {
  72. assert.strictEqual(helpers.id_valid("0098cb60fa8e427cb299793cbd302c9a"), true);
  73. done();
  74. });
  75. it("uppercase uuid is valid", function(done) {
  76. assert.strictEqual(helpers.id_valid("1DCEF164FF0A47F2B9A691385C774EE7"), true);
  77. done();
  78. });
  79. it("dashed uuid is not valid", function(done) {
  80. assert.strictEqual(helpers.id_valid("0098cb60-fa8e-427c-b299-793cbd302c9a"), false);
  81. done();
  82. });
  83. it("username is invalid", function(done) {
  84. assert.strictEqual(helpers.id_valid("__niceUs3rname__"), false);
  85. done();
  86. });
  87. it("username alex is invalid", function(done) {
  88. assert.strictEqual(helpers.id_valid("alex"), false);
  89. done();
  90. });
  91. it("username mhf_alex is invalid", function(done) {
  92. assert.strictEqual(helpers.id_valid("mhf_alex"), false);
  93. done();
  94. });
  95. it("username steve is invalid", function(done) {
  96. assert.strictEqual(helpers.id_valid("steve"), false);
  97. done();
  98. });
  99. it("username mhf_steve is invalid", function(done) {
  100. assert.strictEqual(helpers.id_valid("mhf_steve"), false);
  101. done();
  102. });
  103. it(">16 length username is invalid", function(done) {
  104. assert.strictEqual(helpers.id_valid("ThisNameIsTooLong"), false);
  105. done();
  106. });
  107. it("should not exist (uuid)", function(done) {
  108. var number = getRandomInt(0, 9).toString();
  109. networking.get_profile(rid(), Array(33).join(number), function(err, profile) {
  110. assert.ifError(err);
  111. assert.strictEqual(profile, null);
  112. done();
  113. });
  114. });
  115. });
  116. describe("Avatar", function() {
  117. for (var a in alex_ids) {
  118. var alexid = alex_ids[a];
  119. (function(alex_id) {
  120. it("UUID " + alex_id + " should default to MHF_Alex", function(done) {
  121. assert.strictEqual(skins.default_skin(alex_id), "mhf_alex");
  122. done();
  123. });
  124. }(alexid));
  125. }
  126. for (var s in steve_ids) {
  127. var steveid = steve_ids[s];
  128. (function(steve_id) {
  129. it("UUID " + steve_id + " should default to MHF_Steve", function(done) {
  130. assert.strictEqual(skins.default_skin(steve_id), "mhf_steve");
  131. done();
  132. });
  133. }(steveid));
  134. }
  135. });
  136. describe("Errors", function() {
  137. it("should time out on uuid info download", function(done) {
  138. var original_timeout = config.server.http_timeout;
  139. config.server.http_timeout = 1;
  140. networking.get_profile(rid(), "069a79f444e94726a5befca90e38aaf5", function(err, profile) {
  141. assert.notStrictEqual(["ETIMEDOUT", "ESOCKETTIMEDOUT"].indexOf(err.code), -1);
  142. config.server.http_timeout = original_timeout;
  143. done();
  144. });
  145. });
  146. it("should time out on skin download", function(done) {
  147. var original_timeout = config.http_timeout;
  148. config.server.http_timeout = 1;
  149. networking.get_from(rid(), "http://textures.minecraft.net/texture/477be35554684c28bdeee4cf11c591d3c88afb77e0b98da893fd7bc318c65184", function(body, res, error) {
  150. assert.notStrictEqual(["ETIMEDOUT", "ESOCKETTIMEDOUT"].indexOf(error.code), -1);
  151. config.server.http_timeout = original_timeout;
  152. done();
  153. });
  154. });
  155. it("should not find the skin", function(done) {
  156. assert.doesNotThrow(function() {
  157. networking.get_from(rid(), "http://textures.minecraft.net/texture/this-does-not-exist", function(img, response, err) {
  158. assert.strictEqual(err, null); // no error here, but it shouldn't throw exceptions
  159. done();
  160. });
  161. });
  162. });
  163. it("should not find the file", function(done) {
  164. skins.open_skin(rid(), "non/existent/path", function(err, img) {
  165. assert(err);
  166. done();
  167. });
  168. });
  169. });
  170. describe("Server", function() {
  171. // throws Exception when default headers are not in res.headers
  172. function assert_headers(res) {
  173. assert(res.headers["content-type"]);
  174. assert("" + res.headers["response-time"]);
  175. assert(res.headers["x-request-id"]);
  176. assert.equal(res.headers["access-control-allow-origin"], "*");
  177. assert.equal(res.headers["cache-control"], "max-age=" + config.caching.browser);
  178. }
  179. // throws Exception when +url+ is requested with +etag+
  180. // and it does not return 304 without a body
  181. function assert_cache(url, etag, callback) {
  182. request.get(url, {
  183. headers: {
  184. "If-None-Match": etag,
  185. },
  186. }, function(error, res, body) {
  187. assert.ifError(error);
  188. assert.strictEqual(body, '');
  189. assert.equal(res.statusCode, 304);
  190. assert_headers(res);
  191. callback();
  192. });
  193. }
  194. before(function(done) {
  195. server.boot(function() {
  196. done();
  197. });
  198. });
  199. it("should return 405 Method Not Allowed for POST", function(done) {
  200. request.post("http://localhost:3000", function(error, res, body) {
  201. assert.ifError(error);
  202. assert.strictEqual(res.statusCode, 405);
  203. done();
  204. });
  205. });
  206. it("should return correct HTTP response for home page", function(done) {
  207. var url = "http://localhost:3000";
  208. request.get(url, function(error, res, body) {
  209. assert.ifError(error);
  210. assert.strictEqual(res.statusCode, 200);
  211. assert_headers(res);
  212. assert(res.headers.etag);
  213. assert.strictEqual(res.headers["content-type"], "text/html; charset=utf-8");
  214. assert.strictEqual(res.headers.etag, '"' + crc(body) + '"');
  215. assert(body);
  216. assert_cache(url, res.headers.etag, function() {
  217. done();
  218. });
  219. });
  220. });
  221. it("should return correct HTTP response for assets", function(done) {
  222. var url = "http://localhost:3000/stylesheets/style.css";
  223. request.get(url, function(error, res, body) {
  224. assert.ifError(error);
  225. assert.strictEqual(res.statusCode, 200);
  226. assert_headers(res);
  227. assert(res.headers.etag);
  228. assert.strictEqual(res.headers["content-type"], "text/css");
  229. assert.strictEqual(res.headers.etag, '"' + crc(body) + '"');
  230. assert(body);
  231. assert_cache(url, res.headers.etag, function() {
  232. done();
  233. });
  234. });
  235. });
  236. it("should return correct HTTP response for URL encoded URLs", function(done) {
  237. var url = "http://localhost:3000/%61%76%61%74%61%72%73/%61%65%37%39%35%61%61%38%36%33%32%37%34%30%38%65%39%32%61%62%32%35%63%38%61%35%39%66%33%62%61%31"; // avatars/ae795aa86327408e92ab25c8a59f3ba1
  238. request.get(url, function(error, res, body) {
  239. assert.ifError(error);
  240. assert.strictEqual(res.statusCode, 200);
  241. assert_headers(res);
  242. assert(res.headers.etag);
  243. assert.strictEqual(res.headers["content-type"], "image/png");
  244. assert(body);
  245. done();
  246. });
  247. });
  248. it("should not fail on simultaneous requests", function(done) {
  249. var url = "http://localhost:3000/avatars/696a82ce41f44b51aa31b8709b8686f0";
  250. // 10 requests at once
  251. var requests = 10;
  252. var finished = 0;
  253. function partDone() {
  254. finished++;
  255. if (requests === finished) {
  256. done();
  257. }
  258. }
  259. function req() {
  260. request.get(url, function(error, res, body) {
  261. assert.ifError(error);
  262. assert.strictEqual(res.statusCode, 200);
  263. assert_headers(res);
  264. assert(res.headers.etag);
  265. assert.strictEqual(res.headers["content-type"], "image/png");
  266. assert(body);
  267. partDone();
  268. });
  269. }
  270. // make simultanous requests
  271. for (var k = 0; k < requests; k++) {
  272. req(k);
  273. }
  274. });
  275. var server_tests = {
  276. "avatar with existing uuid": {
  277. url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
  278. crc32: [4264176600],
  279. },
  280. "avatar with existing dashed uuid": {
  281. url: "http://localhost:3000/avatars/853c80ef-3c37-49fd-aa49938b674adae6?size=16",
  282. crc32: [4264176600],
  283. },
  284. "avatar with non-existent uuid": {
  285. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16",
  286. crc32: [3348154329],
  287. },
  288. "avatar with non-existent uuid defaulting to mhf_alex": {
  289. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=mhf_alex",
  290. crc32: [73899130],
  291. },
  292. "avatar with non-existent uuid defaulting to uuid": {
  293. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  294. crc32: [0],
  295. redirect: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
  296. },
  297. "avatar with non-existent uuid defaulting to url": {
  298. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  299. crc32: [0],
  300. redirect: "http://example.com/CaseSensitive",
  301. },
  302. "overlay avatar with existing uuid": {
  303. url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay",
  304. crc32: [575355728],
  305. },
  306. "overlay avatar with non-existent uuid": {
  307. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay",
  308. crc32: [3348154329],
  309. },
  310. "overlay avatar with non-existent uuid defaulting to mhf_alex": {
  311. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=mhf_alex",
  312. crc32: [73899130],
  313. },
  314. "overlay avatar with non-existent uuid defaulting to uuid": {
  315. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  316. crc32: [0],
  317. redirect: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
  318. },
  319. "overlay avatar with non-existent uuid defaulting to url": {
  320. url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  321. crc32: [0],
  322. redirect: "http://example.com/CaseSensitive",
  323. },
  324. "cape with existing uuid": {
  325. url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6",
  326. crc32: [985789174, 2099310578],
  327. },
  328. "cape with non-existent uuid": {
  329. url: "http://localhost:3000/capes/00000000000000000000000000000000",
  330. crc32: [0],
  331. },
  332. "cape with non-existent uuid defaulting to url": {
  333. url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  334. crc32: [0],
  335. redirect: "http://example.com/CaseSensitive",
  336. },
  337. "skin with existing uuid": {
  338. url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6",
  339. crc32: [1759176487],
  340. },
  341. "skin with non-existent uuid": {
  342. url: "http://localhost:3000/skins/00000000000000000000000000000000",
  343. crc32: [1853029228],
  344. },
  345. "skin with non-existent uuid defaulting to mhf_alex": {
  346. url: "http://localhost:3000/skins/00000000000000000000000000000000?default=mhf_alex",
  347. crc32: [427506205],
  348. },
  349. "skin with non-existent uuid defaulting to uuid": {
  350. url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
  351. crc32: [0],
  352. redirect: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6?size=16",
  353. },
  354. "skin with non-existent uuid defaulting to url": {
  355. url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  356. crc32: [0],
  357. redirect: "http://example.com/CaseSensitive",
  358. },
  359. "head render with existing uuid": {
  360. url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2",
  361. crc32: [1168786201],
  362. },
  363. "head render with non-existent uuid": {
  364. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2",
  365. crc32: [3800926063],
  366. },
  367. "head render with non-existent uuid defaulting to mhf_alex": {
  368. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=mhf_alex",
  369. crc32: [4027858557],
  370. },
  371. "head render with non-existent uuid defaulting to uuid": {
  372. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  373. crc32: [0],
  374. redirect: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2",
  375. },
  376. "head render with non-existent uuid defaulting to url": {
  377. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  378. crc32: [0],
  379. redirect: "http://example.com/CaseSensitive",
  380. },
  381. "overlay head render with existing uuid": {
  382. url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
  383. crc32: [2880579826],
  384. },
  385. "overlay head render with non-existent uuid": {
  386. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay",
  387. crc32: [3800926063],
  388. },
  389. "overlay head render with non-existent uuid defaulting to mhf_alex": {
  390. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
  391. crc32: [4027858557],
  392. },
  393. "overlay head with non-existent uuid defaulting to uuid": {
  394. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
  395. crc32: [0],
  396. redirect: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=",
  397. },
  398. "overlay head render with non-existent uuid defaulting to url": {
  399. url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  400. crc32: [0],
  401. redirect: "http://example.com/CaseSensitive",
  402. },
  403. "body render with existing uuid": {
  404. url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
  405. crc32: [1144887125],
  406. },
  407. "body render with non-existent uuid": {
  408. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2",
  409. crc32: [996962526],
  410. },
  411. "body render with non-existent uuid defaulting to mhf_alex": {
  412. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=mhf_alex",
  413. crc32: [4280894468],
  414. },
  415. "body render with non-existent uuid defaulting to uuid": {
  416. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
  417. crc32: [0],
  418. redirect: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
  419. },
  420. "body render with non-existent uuid defaulting to url": {
  421. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  422. crc32: [0],
  423. redirect: "http://example.com/CaseSensitive",
  424. },
  425. "overlay body render with existing uuid": {
  426. url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
  427. crc32: [1107696668],
  428. },
  429. "overlay body render with non-existent uuid": {
  430. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay",
  431. crc32: [996962526],
  432. },
  433. "overlay body render with non-existent uuid defaulting to mhf_alex": {
  434. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
  435. crc32: [4280894468],
  436. },
  437. "overlay body render with non-existent uuid defaulting to url": {
  438. url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
  439. crc32: [0],
  440. redirect: "http://example.com/CaseSensitive",
  441. },
  442. };
  443. for (var description in server_tests) {
  444. var loc = server_tests[description];
  445. (function(location) {
  446. it("should return correct HTTP response for " + description, function(done) {
  447. request.get(location.url, {followRedirect: false, encoding: null}, function(error, res, body) {
  448. assert.ifError(error);
  449. assert_headers(res);
  450. assert(res.headers["x-storage-type"]);
  451. var hash = crc(body);
  452. var matches = false;
  453. for (var c = 0; c < location.crc32.length; c++) {
  454. if (location.crc32[c] === hash) {
  455. matches = true;
  456. break;
  457. }
  458. }
  459. try {
  460. assert(matches);
  461. } catch(e) {
  462. throw new Error(hash + " != " + location.crc32 + " | " + body.toString("base64"));
  463. }
  464. assert.strictEqual(res.headers.location, location.redirect);
  465. if (location.crc32[0] === 0) {
  466. assert.strictEqual(res.statusCode, location.redirect ? 307 : 404);
  467. assert.ifError(res.headers.etag); // etag must not be present on non-200
  468. assert.strictEqual(res.headers["content-type"], "text/plain");
  469. done();
  470. } else {
  471. assert.strictEqual(res.headers["content-type"], "image/png");
  472. assert.strictEqual(res.statusCode, 200);
  473. assert(res.headers.etag);
  474. assert.strictEqual(res.headers.etag, '"' + hash + '"');
  475. assert_cache(location.url, res.headers.etag, function() {
  476. done();
  477. });
  478. }
  479. });
  480. });
  481. }(loc));
  482. }
  483. it("should return 304 on server error", function(done) {
  484. var original_debug = config.server.debug_enabled;
  485. var original_timeout = config.server.http_timeout;
  486. config.server.debug_enabled = false;
  487. config.server.http_timeout = 1;
  488. request.get({url: "http://localhost:3000/avatars/0000000000000000000000000000000f", headers: {"If-None-Match": '"some-etag"'}}, function(error, res, body) {
  489. assert.ifError(error);
  490. assert.strictEqual(body, '');
  491. assert.strictEqual(res.statusCode, 304);
  492. config.server.debug_enabled = original_debug;
  493. config.server.http_timeout = original_timeout;
  494. done();
  495. });
  496. });
  497. it("should return a 422 (invalid size)", function(done) {
  498. var size = config.avatars.max_size + 1;
  499. request.get("http://localhost:3000/avatars/2d5aa9cdaeb049189930461fc9b91cc5?size=" + size, function(error, res, body) {
  500. assert.ifError(error);
  501. assert.strictEqual(res.statusCode, 422);
  502. done();
  503. });
  504. });
  505. it("should return a 422 (invalid scale)", function(done) {
  506. var scale = config.renders.max_scale + 1;
  507. request.get("http://localhost:3000/renders/head/2d5aa9cdaeb049189930461fc9b91cc5?scale=" + scale, function(error, res, body) {
  508. assert.ifError(error);
  509. assert.strictEqual(res.statusCode, 422);
  510. done();
  511. });
  512. });
  513. it("should return a 422 (invalid render type)", function(done) {
  514. request.get("http://localhost:3000/renders/invalid/2d5aa9cdaeb049189930461fc9b91cc5", function(error, res, body) {
  515. assert.ifError(error);
  516. assert.strictEqual(res.statusCode, 422);
  517. done();
  518. });
  519. });
  520. // testing all paths for Invalid UUID
  521. var locations = ["avatars", "skins", "capes", "renders/body", "renders/head"];
  522. for (var l in locations) {
  523. loc = locations[l];
  524. (function(location) {
  525. it("should return a 422 (invalid uuid " + location + ")", function(done) {
  526. request.get("http://localhost:3000/" + location + "/thisisaninvaliduuid", function(error, res, body) {
  527. assert.ifError(error);
  528. assert.strictEqual(res.statusCode, 422);
  529. done();
  530. });
  531. });
  532. it("should return a 404 (invalid path " + location + ")", function(done) {
  533. request.get("http://localhost:3000/" + location + "/853c80ef3c3749fdaa49938b674adae6/invalid", function(error, res, body) {
  534. assert.ifError(error);
  535. assert.strictEqual(res.statusCode, 404);
  536. done();
  537. });
  538. });
  539. }(loc));
  540. }
  541. it("should return /public resources", function(done) {
  542. request.get("http://localhost:3000/javascript/crafatar.js", function(error, res, body) {
  543. assert.ifError(error);
  544. assert.strictEqual(res.statusCode, 200);
  545. done();
  546. });
  547. });
  548. it("should not allow path traversal on /public", function(done) {
  549. request.get("http://localhost:3000/../server.js", function(error, res, body) {
  550. assert.ifError(error);
  551. assert.strictEqual(res.statusCode, 404);
  552. done();
  553. });
  554. });
  555. it("should not allow encoded path traversal on /public", function(done) {
  556. request.get("http://localhost:3000/%2E%2E/server.js", function(error, res, body) {
  557. assert.ifError(error);
  558. assert.strictEqual(res.statusCode, 404);
  559. done();
  560. });
  561. });
  562. });
  563. // we have to make sure that we test both a 32x64 and 64x64 skin
  564. describe("Networking: Render", function() {
  565. it("should not fail (uuid, 32x64 skin)", function(done) {
  566. helpers.get_render(rid(), "af74a02d19cb445bb07f6866a861f783", 6, true, true, function(err, hash, img) {
  567. assert.strictEqual(err, null);
  568. done();
  569. });
  570. });
  571. it("should not fail (uuid, 64x64 skin)", function(done) {
  572. helpers.get_render(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", 6, true, true, function(err, hash, img) {
  573. assert.strictEqual(err, null);
  574. done();
  575. });
  576. });
  577. });
  578. describe("Networking: Cape", function() {
  579. it("should not fail (guaranteed cape)", function(done) {
  580. helpers.get_cape(rid(), "61699b2ed3274a019f1e0ea8c3f06bc6", function(err, hash, status, img) {
  581. assert.strictEqual(err, null);
  582. done();
  583. });
  584. });
  585. it("should already exist", function(done) {
  586. before(function() {
  587. cache.get_redis().flushall();
  588. });
  589. helpers.get_cape(rid(), "61699b2ed3274a019f1e0ea8c3f06bc6", function(err, hash, status, img) {
  590. assert.strictEqual(err, null);
  591. done();
  592. });
  593. });
  594. it("should not be found", function(done) {
  595. helpers.get_cape(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", function(err, hash, status, img) {
  596. assert.ifError(err);
  597. assert.strictEqual(img, null);
  598. done();
  599. });
  600. });
  601. });
  602. describe("Networking: Skin", function() {
  603. it("should not fail", function(done) {
  604. helpers.get_cape(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", function(err, hash, status, img) {
  605. assert.strictEqual(err, null);
  606. done();
  607. });
  608. });
  609. it("should already exist", function(done) {
  610. before(function() {
  611. cache.get_redis().flushall();
  612. });
  613. helpers.get_cape(rid(), "2d5aa9cdaeb049189930461fc9b91cc5", function(err, hash, status, img) {
  614. assert.strictEqual(err, null);
  615. done();
  616. });
  617. });
  618. });
  619. describe("Networking: Avatar", function() {
  620. before(function() {
  621. cache.get_redis().flushall();
  622. });
  623. it("should be downloaded", function(done) {
  624. helpers.get_avatar(rid(), uuid, false, 160, function(err, status, image) {
  625. assert.ifError(err);
  626. assert.strictEqual(status, 2);
  627. done();
  628. });
  629. });
  630. it("should be cached", function(done) {
  631. helpers.get_avatar(rid(), uuid, false, 160, function(err, status, image) {
  632. assert.ifError(err);
  633. assert.strictEqual(status === 0 || status === 1, true);
  634. done();
  635. });
  636. });
  637. });
  638. describe("Networking: Skin", function() {
  639. it("should not fail (uuid)", function(done) {
  640. helpers.get_skin(rid(), uuid, function(err, hash, status, img) {
  641. assert.strictEqual(err, null);
  642. done();
  643. });
  644. });
  645. });
  646. describe("Networking: Render", function() {
  647. it("should not fail (full body)", function(done) {
  648. helpers.get_render(rid(), uuid, 6, true, true, function(err, hash, img) {
  649. assert.ifError(err);
  650. done();
  651. });
  652. });
  653. it("should not fail (only head)", function(done) {
  654. helpers.get_render(rid(), uuid, 6, true, false, function(err, hash, img) {
  655. assert.ifError(err);
  656. done();
  657. });
  658. });
  659. });
  660. describe("Networking: Cape", function() {
  661. it("should not fail (possible cape)", function(done) {
  662. helpers.get_cape(rid(), uuid, function(err, hash, status, img) {
  663. assert.ifError(err);
  664. done();
  665. });
  666. });
  667. });
  668. describe("Errors", function() {
  669. before(function() {
  670. cache.get_redis().flushall();
  671. });
  672. // Mojang has changed its rate limiting, so we no longer expect to hit the rate limit
  673. // it("uuid SHOULD be rate limited", function(done) {
  674. // networking.get_profile(rid(), uuid, function() {
  675. // networking.get_profile(rid(), uuid, function(err, profile) {
  676. // assert.strictEqual(err.toString(), "HTTP: 429");
  677. // assert.strictEqual(profile, null);
  678. // done();
  679. // });
  680. // });
  681. // });
  682. it("CloudFront rate limit is handled", function(done) {
  683. var original_rate_limit = config.server.sessions_rate_limit;
  684. config.server.sessions_rate_limit = 1;
  685. networking.get_profile(rid(), uuid, function() {
  686. networking.get_profile(rid(), uuid, function(err, profile) {
  687. assert.strictEqual(err.code, "RATELIMIT");
  688. config.server.sessions_rate_limit = original_rate_limit;
  689. done();
  690. });
  691. });
  692. });
  693. });
  694. after(function(done) {
  695. server.close(function() {
  696. cache.get_redis().quit();
  697. done();
  698. });
  699. });
  700. });