Index: binaries/data/mods/public/simulation/components/tests/test_UpgradeModification.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/tests/test_UpgradeModification.js
@@ -0,0 +1,143 @@
+Engine.LoadHelperScript("Player.js");
+Engine.LoadHelperScript("ValueModification.js");
+Resources = {
+ "GetCodes": () => ["food", "metal", "stone", "wood"],
+ "GetResource": () => ({}),
+ "BuildSchema": (type) => {
+ let schema = "";
+ for (let res of Resources.GetCodes())
+ schema +=
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+ return "" + schema + "";
+ }
+};
+
+Engine.LoadComponentScript("interfaces/AuraManager.js");
+Engine.LoadComponentScript("interfaces/EntityLimits.js");
+Engine.LoadComponentScript("interfaces/Player.js");
+Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
+Engine.LoadComponentScript("interfaces/TechnologyManager.js");
+Engine.LoadComponentScript("interfaces/Timer.js");
+Engine.LoadComponentScript("interfaces/Upgrade.js");
+
+Engine.LoadComponentScript("TechnologyManager.js");
+Engine.LoadComponentScript("Player.js");
+Engine.LoadComponentScript("PlayerManager.js");
+Engine.LoadComponentScript("Upgrade.js");
+
+// Input (bare minimum needed for tests)
+let techs = {
+ "alter_tower_upgrade_cost": {
+ "modifications": [
+ { "value": "Upgrade/Cost/stone", "add": 60.0 },
+ { "value": "Upgrade/Cost/wood", "multiply": 0.5 },
+ { "value": "Upgrade/Time", "replace": 90 }
+ ],
+ "affects": ["Tower"]
+ }
+};
+let template = {
+ "Identity": {
+ "Classes": { '@datatype': "tokens", "_string": "Tower" },
+ "VisibleClasses": { '@datatype': "tokens", "_string": "" }
+ },
+ "Upgrade": {
+ "Tower": {
+ "Cost": { "stone": "100", "wood": "50" },
+ "Entity": "structures/{civ}_defense_tower",
+ "RequiredTechnology": "phase_town",
+ "Time": "100",
+ "Tooltip": "Reinforce with stone and upgrade to a defense tower."
+ }
+ }
+};
+let civCode = "pony";
+
+/**
+ * Initialise various bits
+ */
+// System Entities
+AddMock(SYSTEM_ENTITY, IID_AuraManager, null);
+AddMock(SYSTEM_ENTITY, IID_DataTemplateManager, {
+ "GetAllTechs": () => techs,
+ "GetTechnologyTemplate": (template) => techs[template] || {}
+});
+AddMock(SYSTEM_ENTITY, IID_RangeManager, {
+ "GetEntitiesByPlayer": (pID) => {[]}
+});
+AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
+ "GetCurrentTemplateName": (ent) => "{civ}_sentry_tower",
+ "GetTemplate": () => template
+});
+AddMock(SYSTEM_ENTITY, IID_Timer, {
+ "SetInterval": () => 1,
+ "CancelTimer": () => {}
+});
+let cmpPlayerManager = ConstructComponent(SYSTEM_ENTITY, "PlayerManager");
+
+// Init Player
+let cmpPlayer = ConstructComponent(10, "Player", {
+ "SpyCostMultiplier": 1.0
+});
+let cmpTechnologyManager = ConstructComponent(10, "TechnologyManager");
+AddMock(10, IID_EntityLimits, null);
+AddMock(10, IID_StatisticsTracker, null);
+
+// Add the player twice, so as to get a playerID that's > 0, then make sure of that
+// (Reason for this is because of globalscripts/Templates line 90)
+cmpPlayerManager.AddPlayer(10);
+cmpPlayerManager.AddPlayer(10);
+TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetPlayerID(), 1);
+
+// Create an entity with an Upgrade Component
+AddMock(20, IID_Ownership, {
+ "GetOwner": () => cmpPlayer.GetPlayerID()
+});
+AddMock(20, IID_Identity, {
+ "GetClassesList": () => GetIdentityClasses(template.Identity),
+ "GetCiv": () => civCode
+});
+let cmpUpgrade = ConstructComponent(20, "Upgrade", template.Upgrade);
+cmpUpgrade.owner = cmpPlayer.GetPlayerID();
+
+
+/**
+ * Now to start the test proper
+ * To start with, no techs are researched...
+ */
+// T1: Check the cost of the upgrade without a player value being passed (as it would be in the structree)
+let parsed_template = GetTemplateDataHelper(template, null, {}, Resources);
+TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, {"stone": 100, "wood": 50, "time": 100});
+
+// T2: Check the value, with a player ID (as it would be in-session)
+parsed_template = GetTemplateDataHelper(template, cmpPlayer.GetPlayerID(), {}, Resources);
+TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, {"stone": 100, "wood": 50, "time": 100});
+
+// T3: Check that the value is correct within the Update Component
+TS_ASSERT_UNEVAL_EQUALS(cmpUpgrade.GetUpgrades()[0].cost, {"stone": 100, "wood": 50, "time": 100});
+
+/**
+ * Tell the Upgrade component to start the Upgrade,
+ * then research the technology that will alter the value of the upgrade cost.
+ */
+cmpUpgrade.Upgrade("structures/"+civCode+"_defense_tower");
+cmpTechnologyManager.ResearchTechnology("alter_tower_upgrade_cost");
+
+// T4: Check that the player-less value hasn't increased...
+parsed_template = GetTemplateDataHelper(template, null, {}, Resources);
+TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, {"stone": 100, "wood": 50, "time": 100});
+
+// T5: ...but the player-backed value has.
+parsed_template = GetTemplateDataHelper(template, cmpPlayer.GetPlayerID(), {}, Resources);
+TS_ASSERT_UNEVAL_EQUALS(parsed_template.upgrades[0].cost, {"stone": 160, "wood": 25, "time": 90});
+
+// T6: The upgrade component should still be using the old resource cost (but new time cost) for the upgrade in progress...
+TS_ASSERT_UNEVAL_EQUALS(cmpUpgrade.GetUpgrades()[0].cost, {"stone": 100, "wood": 50, "time": 90});
+
+// T7: ...but with the upgrade cancelled, it now uses the modified value.
+cmpUpgrade.CancelUpgrade(cmpPlayer.GetPlayerID());
+TS_ASSERT_UNEVAL_EQUALS(cmpUpgrade.GetUpgrades()[0].cost, {"stone": 160, "wood": 25, "time": 90});