Skip to content

Commit aea470a

Browse files
committed
Improve project tooling, packaging, and test workflow
1 parent 1e7100b commit aea470a

10 files changed

Lines changed: 278 additions & 191 deletions

File tree

.github/workflows/logic-checks.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Logic Checks
2+
3+
on:
4+
pull_request:
5+
push:
6+
7+
jobs:
8+
logic-tests:
9+
runs-on: windows-latest
10+
11+
steps:
12+
- name: Check out repository
13+
uses: actions/checkout@v4
14+
15+
- name: Set up .NET SDK
16+
uses: actions/setup-dotnet@v4
17+
with:
18+
global-json-file: global.json
19+
20+
- name: Run logic tests
21+
run: dotnet test BetterModMenu.Tests/BetterModMenu.Tests.csproj

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ obj/
3030

3131
# Build, export, and temp files
3232
build/
33+
artifacts/
3334
export/
3435
*.log
3536
*.tmp

BetterModMenu.Tests/BetterModMenu.Tests.csproj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<OutputType>Exe</OutputType>
43
<TargetFramework>net8.0</TargetFramework>
54
<ImplicitUsings>enable</ImplicitUsings>
65
<Nullable>enable</Nullable>
76
<IsPackable>false</IsPackable>
87
</PropertyGroup>
98

9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
11+
<PackageReference Include="MSTest.TestAdapter" Version="3.10.4" />
12+
<PackageReference Include="MSTest.TestFramework" Version="3.10.4" />
13+
</ItemGroup>
14+
1015
<ItemGroup>
1116
<Compile Include="..\Data\ManifestScanner.cs" Link="Data\ManifestScanner.cs" />
1217
<Compile Include="..\Data\ProfileStateRules.cs" Link="Data\ProfileStateRules.cs" />

BetterModMenu.Tests/LogicTests.cs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using BetterModMenu.Data;
2+
using BetterModMenu.Patches;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
using System.IO;
5+
6+
namespace BetterModMenu.Tests;
7+
8+
[TestClass]
9+
public class LogicTests
10+
{
11+
[TestMethod]
12+
public void CanAdd_TrimsAndAcceptsValidNames()
13+
{
14+
var existingGroups = new List<string> { "Bosses" };
15+
16+
bool isValid = ModdingGroupRules.CanAdd(existingGroups, " New Group ", out string trimmedName);
17+
18+
Assert.IsTrue(isValid);
19+
Assert.AreEqual("New Group", trimmedName);
20+
}
21+
22+
[TestMethod]
23+
public void CanAdd_RejectsReservedGroupName()
24+
{
25+
bool isValid = ModdingGroupRules.CanAdd(Array.Empty<string>(), ModdingScreenConstants.UnassignedGroup, out _);
26+
27+
Assert.IsFalse(isValid);
28+
}
29+
30+
[TestMethod]
31+
public void ValidateRename_AllowsNoOpRename()
32+
{
33+
var result = ModdingGroupRules.ValidateRename(new[] { "Bosses" }, "Bosses", "Bosses", out string trimmedName);
34+
35+
Assert.AreEqual(GroupNameValidationResult.Unchanged, result);
36+
Assert.AreEqual("Bosses", trimmedName);
37+
}
38+
39+
[TestMethod]
40+
public void ValidateRename_RejectsDuplicateTarget()
41+
{
42+
var result = ModdingGroupRules.ValidateRename(new[] { "Bosses", "Elites" }, "Bosses", "Elites", out _);
43+
44+
Assert.AreEqual(GroupNameValidationResult.Duplicate, result);
45+
}
46+
47+
[TestMethod]
48+
public void FindManifestPath_ReturnsOnlyExactManifestNames()
49+
{
50+
string tempDirectory = CreateTempDirectory();
51+
try
52+
{
53+
File.WriteAllText(Path.Combine(tempDirectory, "RouteSuggestConfig.json"), "{ }");
54+
File.WriteAllText(Path.Combine(tempDirectory, "BetterModMenu.json"), "{ }");
55+
56+
string? manifestPath = ManifestScanner.FindManifestPath(tempDirectory, "BetterModMenu", new[] { ".json" });
57+
58+
Assert.AreEqual(Path.Combine(tempDirectory, "BetterModMenu.json"), manifestPath);
59+
}
60+
finally
61+
{
62+
Directory.Delete(tempDirectory, true);
63+
}
64+
}
65+
66+
[TestMethod]
67+
public void FindManifestPath_RejectsUnsafeManifestIds()
68+
{
69+
string tempDirectory = CreateTempDirectory();
70+
try
71+
{
72+
File.WriteAllText(Path.Combine(tempDirectory, "BetterModMenu.json"), "{ }");
73+
74+
string? traversed = ManifestScanner.FindManifestPath(tempDirectory, @"..\BetterModMenu", new[] { ".json" });
75+
string rootedId = Path.Combine(tempDirectory, "BetterModMenu");
76+
string? rooted = ManifestScanner.FindManifestPath(tempDirectory, rootedId, new[] { ".json" });
77+
78+
Assert.IsNull(traversed);
79+
Assert.IsNull(rooted);
80+
}
81+
finally
82+
{
83+
Directory.Delete(tempDirectory, true);
84+
}
85+
}
86+
87+
[TestMethod]
88+
public void TryReadAffectsGameplay_IgnoresMismatchedIds()
89+
{
90+
string tempPath = Path.Combine(Path.GetTempPath(), "BetterModMenuTests_" + Guid.NewGuid().ToString("N") + ".json");
91+
try
92+
{
93+
File.WriteAllText(tempPath, """
94+
{
95+
"id": "AnotherMod",
96+
"affects_gameplay": true
97+
}
98+
""");
99+
100+
bool found = ManifestScanner.TryReadAffectsGameplay(tempPath, "BetterModMenu", out bool affectsGameplay);
101+
102+
Assert.IsFalse(found);
103+
Assert.IsFalse(affectsGameplay);
104+
}
105+
finally
106+
{
107+
if (File.Exists(tempPath))
108+
File.Delete(tempPath);
109+
}
110+
}
111+
112+
[TestMethod]
113+
public void NormalizeGroups_RemovesStaleEntries()
114+
{
115+
var customGroups = new List<string> { "Bosses", "Elites" };
116+
var modGroups = new Dictionary<string, string>
117+
{
118+
["keep-mod"] = "Bosses",
119+
["missing-mod"] = "Bosses",
120+
["wrong-group"] = "Missing"
121+
};
122+
var collapsedGroups = new HashSet<string> { "Bosses", "Missing", "Unassigned" };
123+
124+
bool changed = ProfileStateRules.NormalizeGroups(customGroups, modGroups, collapsedGroups, new[] { "keep-mod", "other-mod" }, "Unassigned");
125+
126+
Assert.IsTrue(changed);
127+
Assert.AreEqual(1, modGroups.Count);
128+
Assert.AreEqual("Bosses", modGroups["keep-mod"]);
129+
Assert.IsTrue(collapsedGroups.Contains("Bosses"));
130+
Assert.IsFalse(collapsedGroups.Contains("Missing"));
131+
Assert.IsFalse(collapsedGroups.Contains("Unassigned"));
132+
}
133+
134+
[TestMethod]
135+
public void NormalizeGroups_ReportsNoOpWhenAlreadyValid()
136+
{
137+
var customGroups = new List<string> { "Bosses" };
138+
var modGroups = new Dictionary<string, string> { ["keep-mod"] = "Bosses" };
139+
var collapsedGroups = new HashSet<string> { "Bosses" };
140+
141+
bool changed = ProfileStateRules.NormalizeGroups(customGroups, modGroups, collapsedGroups, new[] { "keep-mod" }, "Unassigned");
142+
143+
Assert.IsFalse(changed);
144+
}
145+
146+
[TestMethod]
147+
public void BuildVisibleGroupOrder_OmitsEmptyUnassignedGroup()
148+
{
149+
var groups = new Dictionary<string, int>
150+
{
151+
[ModdingScreenConstants.UnassignedGroup] = 0,
152+
["Bosses"] = 1,
153+
["Elites"] = 0
154+
};
155+
156+
var names = ProfileStateRules.BuildVisibleGroupOrder(groups, new List<string> { "Bosses", "Elites" }, ModdingScreenConstants.UnassignedGroup);
157+
158+
CollectionAssert.AreEqual(new[] { "Bosses", "Elites" }, names);
159+
}
160+
161+
private static string CreateTempDirectory()
162+
{
163+
string tempDirectory = Path.Combine(Path.GetTempPath(), "BetterModMenuTests_" + Guid.NewGuid().ToString("N"));
164+
Directory.CreateDirectory(tempDirectory);
165+
return tempDirectory;
166+
}
167+
}

BetterModMenu.Tests/Program.cs

Lines changed: 0 additions & 171 deletions
This file was deleted.

0 commit comments

Comments
 (0)