Refactor getFactorioLocale

Move getFactorioLocale to the new lib/factorio namespace and refactor it
to use async functions instead of callbacks.  Split tests into
integration with factorio and testing of the function itself.

Also removes the cacheing from the function itself as this breaks the
isolated testing of it.
This commit is contained in:
Hornwitser committed 2019-10-14 18:56:46 +02:00
1 parent a01b971ba2
commit 93480c8b29
8 files changed
+125 -132

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
module.exports = {
...require('lib/factorio/locale'),
...require('lib/factorio/server'),
};
+28
View File
@@ -0,0 +1,28 @@
/**
* Function for parsing factorio locale from a local factorio install
*/
"use strict";
const fs = require("fs-extra");
const path = require("path");
const ini = require("ini");
/**
* Gets factorios locale as an object, does not respect mods.
*
* @param {string} factorioDirectory
* @param {string} languageCode
* @param {function} callback
*/
async function getLocale(dataDir, languageCode) {
if (typeof dataDir !== "string") throw new TypeError("dataDir must be a string");
if (typeof languageCode !== "string") throw new TypeError("languageCode must be a string");
let localeFilePath = path.join(dataDir, "base", "locale", languageCode, "base.cfg");
let content = await fs.readFile(localeFilePath, "utf8");
return ini.parse(content);
}
module.exports = {
getLocale,
};
-53
View File
@@ -1,53 +0,0 @@
/**
Module for grabbing factorio locale from a local factorio install
@module getFactorioLocale
*/
const fs = require("fs");
const ini = require("ini");
const cache = {};
/**
Gets raw factorio locale from the .ini file.
Not available outside of module through exports.
@memberof getFactorioLocale
@inner
@param {string} factorioDirectory
@param {string} languageCode
@param {function} callback
*/
function getLocale(factorioDirectory, languageCode, callback){
var localeFile = factorioDirectory + '/data/base/locale/' + languageCode + '/base.cfg';
fs.readFile(localeFile, "utf8", (err, rawLocale) => {
callback(err, rawLocale);
});
}
/**
Gets factorios locale as an object, does not respect mods.
@param {string} factorioDirectory
@param {string} languageCode
@param {function} callback
*/
module.exports.asObject = function getLocaleAsObject(factorioDirectory, languageCode, callback){
if(typeof factorioDirectory != "string" || typeof languageCode != "string" || typeof callback != "function"){
throw new Error("Error: wrong parameters provided");
}
if(!cache[languageCode]){
getLocale(factorioDirectory, languageCode, (err, rawLocale) => {
if(err){
callback(err);
} else {
let processedLocale = ini.parse(rawLocale);
cache[languageCode] = processedLocale;
callback(undefined, processedLocale);
}
});
} else {
callback(undefined, cache[languageCode]);
}
}
+12 -5
View File
@@ -32,9 +32,9 @@ let config = {};
// homebrew modules
const generateSSLcert = require("lib/generateSSLcert");
const getFactorioLocale = require("lib/getFactorioLocale");
const pluginManager = require("lib/manager/pluginManager.js");
const database = require("lib/database");
const factorio = require("lib/factorio");
// homemade express middleware for token auth
const authenticate = require("lib/authenticate");
@@ -670,19 +670,26 @@ app.post("/api/runCommand", authenticate.middleware, function(req,res) {
res.status(400).send({auth: true, message: "Error: invalid request.body"});
}
});
var localeCache;
/**
GET endpoint. Returns factorio's base locale as a JSON object.
@memberof clusterioMaster
@instance
@alias api/getFactorioLocale
@returns {object{}} 2 deep nested object with base game factorio locale as key:value pairs
@returns {object<string, object>} 2 deep nested object with base game factorio locale as key:value pairs
*/
app.get("/api/getFactorioLocale", function(req,res){
endpointHitCounter.labels(req.route.path).inc();
getFactorioLocale.asObject(config.factorioDirectory, "en", (err, factorioLocale) => {
res.send(factorioLocale);
});
if (!localeCache) {
factorio.getLocale(path.join(config.factorioDirectory, "data"), "en").then(locale => {
localeCache = locale;
res.send(localeCache);
});
} else {
res.send(localeCache);
}
});
/* Websockets for plugins */
@@ -0,0 +1,4 @@
; Test data for getLocale
[test]
key-a=1
key-b=2
+62
View File
@@ -0,0 +1,62 @@
const assert = require("assert").strict;
const path = require("path");
const factorio = require("lib/factorio");
describe("Integration of lib/factorio/locale", function() {
describe("getLocale()", function() {
let locale;
before(async function() {
locale = await factorio.getLocale(path.join("factorio", "data"), "en");
});
it("returns a nested object with base game locale information", async function() {
assert.equal(typeof locale, "object");
assert.equal(locale["entity-name"]["fish"], "Fish");
assert.equal(locale["entity-name"]["small-lamp"], "Lamp");
let empty_keys = 0;
Object.keys(locale).forEach(key => {
// first level of the nested object is always an object
assert.equal(typeof locale[key], "object");
Object.keys(locale[key]).forEach(key2 => {
// second level of the nested object is always a string, nearly always truthy
if (!locale[key][key2]) {
empty_keys++;
}
assert.equal(typeof locale[key][key2], "string");
});
});
assert(empty_keys < 10, `Got unusually high amount (${empty_keys}) of empty keys`);
});
it("has some duplicate keys", async function() {
let arrayOfKeys = [];
// collect all keys in a single array
Object.keys(locale).forEach(key => {
arrayOfKeys.push(key);
Object.keys(locale[key]).forEach(key2 => {
arrayOfKeys.push(key2);
});
});
// check for duplicates
let duplicateKeys = [];
let i = 0;
arrayOfKeys.forEach(key => {
let o = 0;
arrayOfKeys.forEach(key2 => {
if(i != o && key == key2){
duplicateKeys.push(key);
}
o++
});
i++;
});
// we got 704 but this is a very unexact number
assert(duplicateKeys.length > 10)
});
});
});
+18
View File
@@ -0,0 +1,18 @@
const assert = require("assert").strict;
const path = require("path");
const factorio = require("lib/factorio");
describe("lib/factorio/locale", function() {
describe("getLocale()", function() {
it("verifies types of the arguments", async function() {
await assert.rejects(factorio.getLocale(1, "a"), new TypeError("dataDir must be a string"));
await assert.rejects(factorio.getLocale("a", 1), new TypeError("languageCode must be a string"));
});
it("returns a nested object with locale definitions", async function() {
let locale = await factorio.getLocale(path.join("test", "file", "factorio", "data"), "en");
assert.deepEqual(locale, { test: { "key-a": "1", "key-b": "2" } });
});
});;
});
-74
View File
@@ -1,74 +0,0 @@
const assert = require("assert");
const util = require("util");
const getFactorioLocale = require("lib/getFactorioLocale");
const asObjectAsync = util.promisify(getFactorioLocale.asObject);
describe("getFactorioLocale.js", function() {
describe("asObject()", function() {
it("requires 2 strings and a function as parameters", function() {
function validate(err) {
return err.message == "Error: wrong parameters provided"
}
let message = "getFactorioLocale is responding incorrectly to invalid arguments"
let callback = (err,object) => true;
assert.throws(() => getFactorioLocale.asObject(), validate, message);
assert.throws(() => getFactorioLocale.asObject("hello", "I forgot my callback"), validate, message);
assert.throws(() => getFactorioLocale.asObject(undefined, undefined, callback), validate, message);
// the proper way to do it
getFactorioLocale.asObject("factorio_0.15.27", "en", callback);
});
it("returns a nested object with base game locale information", async function() {
let object = await asObjectAsync("factorio", "en");
assert.equal(typeof object, "object");
assert.equal(object["entity-name"]["fish"], "Fish");
assert.equal(object["entity-name"]["small-lamp"], "Lamp");
let empty_keys = 0;
Object.keys(object).forEach(key => {
// first level of the nested object is always an object
assert.equal(typeof object[key], "object");
Object.keys(object[key]).forEach(key2 => {
// second level of the nested object is always a string, nearly always truthy
if (!object[key][key2]) {
empty_keys++;
}
assert.equal(typeof object[key][key2], "string");
});
});
assert(empty_keys < 10, `Got unusually high amount (${empty_keys}) of empty keys`);
});
it("has some duplicate keys", async function() {
let object = await asObjectAsync("factorio", "en");
let arrayOfKeys = [];
// collect all keys in a single array
Object.keys(object).forEach(key => {
arrayOfKeys.push(key);
Object.keys(object[key]).forEach(key2 => {
arrayOfKeys.push(key2);
});
});
// check for duplicates
let duplicateKeys = [];
let i = 0;
arrayOfKeys.forEach(key => {
let o = 0;
arrayOfKeys.forEach(key2 => {
if(i != o && key == key2){
duplicateKeys.push(key);
}
o++
});
i++;
});
// we got 704 but this is a very unexact number
assert(duplicateKeys.length > 10)
});
});
});