Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions BannerlordModEditor.Common.Tests/Services/ModProjectServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Xunit;
using BannerlordModEditor.Common.Services;
using BannerlordModEditor.Common.Models.SubModule;

namespace BannerlordModEditor.Common.Tests.Services
{
public class ModProjectServiceTests
{
private readonly ModProjectService _service;

public ModProjectServiceTests()
{
_service = new ModProjectService(new SubModuleLoader());
}

[Fact]
public void ValidateModName_ReturnsTrueForValidName()
{
Assert.True(_service.ValidateModName("MyMod"));
Assert.True(_service.ValidateModName("My_Cool_Mod"));
Assert.True(_service.ValidateModName("Mod123"));
}

[Fact]
public void ValidateModName_ReturnsFalseForInvalidName()
{
Assert.False(_service.ValidateModName(""));
Assert.False(_service.ValidateModName("A"));
Assert.False(_service.ValidateModName("Mod with spaces"));
Assert.False(_service.ValidateModName("Mod@Special"));
}

[Fact]
public void GenerateModId_ReturnsCorrectId()
{
Assert.Equal("MyMod", _service.GenerateModId("My Mod"));
Assert.Equal("MyCoolMod", _service.GenerateModId("My-Cool-Mod"));
Assert.Equal("TestMod123", _service.GenerateModId("Test Mod 123"));
}

[Fact]
public async Task CreateModProjectAsync_CreatesProjectStructure()
{
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempDir);

try
{
var options = new ModProjectOptions
{
ModName = "TestMod",
ModId = "TestMod",
OutputDirectory = tempDir,
UseButrTemplate = true
};

var result = await _service.CreateModProjectAsync(options);

Assert.True(result.Success);
Assert.True(Directory.Exists(result.ProjectPath));
Assert.True(File.Exists(Path.Combine(result.ProjectPath, "SubModule.xml")));
Assert.True(File.Exists(Path.Combine(result.ProjectPath, "TestMod.csproj")));
Assert.True(Directory.Exists(Path.Combine(result.ProjectPath, "ModuleData")));
Assert.True(File.Exists(Path.Combine(result.ProjectPath, ".gitignore")));
Assert.True(File.Exists(Path.Combine(result.ProjectPath, "README.md")));
}
finally
{
if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
}

[Fact]
public void CreateModProject_ReturnsErrorForInvalidName()
{
var options = new ModProjectOptions
{
ModName = "A",
OutputDirectory = "/tmp"

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test hard-codes "/tmp" as the output directory, which will fail on Windows environments. Use Path.GetTempPath() (optionally combined with a GUID subfolder) to keep the test cross-platform and consistent with the other tests in this file.

Suggested change
OutputDirectory = "/tmp"
OutputDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())

Copilot uses AI. Check for mistakes.
};

var result = _service.CreateModProject(options);

Assert.False(result.Success);
Assert.Contains("Invalid mod name", result.ErrorMessage);
}

[Fact]
public void CreateModProject_ReturnsErrorForExistingDirectory()
{
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
var modDir = Path.Combine(tempDir, "ExistingMod");
Directory.CreateDirectory(modDir);

try
{
var options = new ModProjectOptions
{
ModName = "ExistingMod",
OutputDirectory = tempDir
};

var result = _service.CreateModProject(options);

Assert.False(result.Success);
Assert.Contains("already exists", result.ErrorMessage);
}
finally
{
if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
}

[Fact]
public void CreateModProject_CreatesValidSubModuleXml()
{
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempDir);

try
{
var options = new ModProjectOptions
{
ModName = "XmlTestMod",
ModId = "XmlTestMod",
OutputDirectory = tempDir,
DependedModules = new() { "Native", "Sandbox" }
};

var result = _service.CreateModProject(options);

Assert.True(result.Success);

var loader = new SubModuleLoader();
var subModule = loader.Load(Path.Combine(result.ProjectPath, "SubModule.xml"));

Assert.NotNull(subModule);
Assert.Equal("XmlTestMod", subModule.Name);
Assert.Equal("XmlTestMod", subModule.Id);
Assert.Equal(2, subModule.DependedModules.Count);
}
finally
{
if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
}
}
}
184 changes: 184 additions & 0 deletions BannerlordModEditor.Common/Services/ModProjectService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BannerlordModEditor.Common.Models.SubModule;

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Release)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Release)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Debug)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Debug)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / unit-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / unit-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / large-xml-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / large-xml-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / concurrency-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / concurrency-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / performance-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / performance-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / memory-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / memory-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / regression-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / regression-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / error-handling-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / error-handling-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / ui-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

Check failure on line 6 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / ui-tests

The type or namespace name 'SubModule' does not exist in the namespace 'BannerlordModEditor.Common.Models' (are you missing an assembly reference?)

namespace BannerlordModEditor.Common.Services
{
public class ModProjectOptions
{
public string ModName { get; set; } = string.Empty;
public string ModId { get; set; } = string.Empty;
public string OutputDirectory { get; set; } = string.Empty;
public bool UseButrTemplate { get; set; } = true;
public string GameVersion { get; set; } = "Latest";
public bool CreateSubModuleXml { get; set; } = true;
public bool CreateCsproj { get; set; } = true;
public bool CreateModuleDataFolder { get; set; } = true;
public List<string> DependedModules { get; set; } = new() { "Native" };
}

public class ModProjectResult
{
public bool Success { get; set; }
public string ProjectPath { get; set; } = string.Empty;
public List<string> CreatedFiles { get; set; } = new();

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreatedFiles is currently used to store both file paths and directory paths (e.g., ModuleData). This makes the API ambiguous for callers. Consider either (a) renaming to something like CreatedPaths, or (b) splitting into CreatedFiles and CreatedDirectories (or similar) so consumers can reliably interpret the result.

Suggested change
public List<string> CreatedFiles { get; set; } = new();
public List<string> CreatedPaths { get; set; } = new();
[Obsolete("Use CreatedPaths instead. This property will be removed in a future version.")]
public List<string> CreatedFiles
{
get => CreatedPaths;
set => CreatedPaths = value ?? new();
}

Copilot uses AI. Check for mistakes.
public List<string> Errors { get; set; } = new();
public string? ErrorMessage { get; set; }
}

public interface IModProjectService
{
Task<ModProjectResult> CreateModProjectAsync(ModProjectOptions options);
ModProjectResult CreateModProject(ModProjectOptions options);
bool ValidateModName(string modName);
string GenerateModId(string modName);
}

public class ModProjectService : IModProjectService
{
private readonly ISubModuleLoader _subModuleLoader;

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Release)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Release)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Debug)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Debug)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / unit-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / unit-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / large-xml-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / large-xml-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / concurrency-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / concurrency-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / performance-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / performance-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / memory-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / memory-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / regression-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / regression-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / error-handling-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / error-handling-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / ui-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 42 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / ui-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

public ModProjectService(ISubModuleLoader subModuleLoader)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Release)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Release)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / macos-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / tmux-integration-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Debug)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / test (Debug)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / windows-tests (9.0.x)

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / unit-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / unit-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / large-xml-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / large-xml-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / concurrency-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / concurrency-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / integration-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / performance-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / performance-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / memory-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / memory-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / regression-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / regression-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / error-handling-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / error-handling-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / ui-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 44 in BannerlordModEditor.Common/Services/ModProjectService.cs

View workflow job for this annotation

GitHub Actions / ui-tests

The type or namespace name 'ISubModuleLoader' could not be found (are you missing a using directive or an assembly reference?)
{
_subModuleLoader = subModuleLoader;
}

public async Task<ModProjectResult> CreateModProjectAsync(ModProjectOptions options)
{
return await Task.Run(() => CreateModProject(options));
Comment on lines +49 to +51

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping the synchronous, IO-heavy scaffolding in Task.Run will consume a thread-pool thread for the entire duration and doesn’t provide true async IO. Prefer either (1) making the method synchronous-only, or (2) implementing a genuinely async version that uses async file APIs (e.g., WriteAllTextAsync / async stream usage) and avoids Task.Run.

Suggested change
public async Task<ModProjectResult> CreateModProjectAsync(ModProjectOptions options)
{
return await Task.Run(() => CreateModProject(options));
public Task<ModProjectResult> CreateModProjectAsync(ModProjectOptions options)
{
return Task.FromResult(CreateModProject(options));

Copilot uses AI. Check for mistakes.
}

public ModProjectResult CreateModProject(ModProjectOptions options)
{
var result = new ModProjectResult();

try
{
if (!ValidateModName(options.ModName))
{
result.Success = false;
result.ErrorMessage = "Invalid mod name. Use only letters, numbers, and underscores.";

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation error message is inconsistent with the actual validation rules: ValidateModName currently allows -, but the message says “underscores” only (and it also doesn’t mention the length constraints). Update the message to reflect the real rules or adjust the validator to match the message.

Suggested change
result.ErrorMessage = "Invalid mod name. Use only letters, numbers, and underscores.";
result.ErrorMessage = "Invalid mod name. Use only letters, numbers, underscores, and hyphens, and ensure it meets the required length constraints.";

Copilot uses AI. Check for mistakes.
result.Errors.Add(result.ErrorMessage);
return result;
}

var modId = string.IsNullOrEmpty(options.ModId)
? GenerateModId(options.ModName)
: options.ModId;

var projectPath = Path.Combine(options.OutputDirectory, options.ModName);
if (Directory.Exists(projectPath))
{
result.Success = false;
result.ErrorMessage = $"Directory already exists: {projectPath}";
result.Errors.Add(result.ErrorMessage);
return result;
}

Directory.CreateDirectory(projectPath);
result.ProjectPath = projectPath;

if (options.CreateSubModuleXml)
{
var subModule = _subModuleLoader.CreateDefault(options.ModName, modId);
subModule.DependedModules = options.DependedModules
.Select(d => new Models.SubModule.DependedModuleDO { Id = d })
.ToList();

var subModulePath = Path.Combine(projectPath, "SubModule.xml");
_subModuleLoader.Save(subModule, subModulePath);
result.CreatedFiles.Add(subModulePath);
}

if (options.CreateCsproj)
{
var csprojPath = Path.Combine(projectPath, $"{options.ModName}.csproj");
var csprojContent = GenerateCsprojContent(options.ModName, modId, options.UseButrTemplate);
File.WriteAllText(csprojPath, csprojContent);
result.CreatedFiles.Add(csprojPath);
}

if (options.CreateModuleDataFolder)
{
var moduleDataPath = Path.Combine(projectPath, "ModuleData");
Directory.CreateDirectory(moduleDataPath);
result.CreatedFiles.Add(moduleDataPath);

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreatedFiles is currently used to store both file paths and directory paths (e.g., ModuleData). This makes the API ambiguous for callers. Consider either (a) renaming to something like CreatedPaths, or (b) splitting into CreatedFiles and CreatedDirectories (or similar) so consumers can reliably interpret the result.

Suggested change
result.CreatedFiles.Add(moduleDataPath);

Copilot uses AI. Check for mistakes.

var emptyXmlPath = Path.Combine(moduleDataPath, "example.xml");
File.WriteAllText(emptyXmlPath, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n</root>");
result.CreatedFiles.Add(emptyXmlPath);
}

var binPath = Path.Combine(projectPath, "bin");
Directory.CreateDirectory(binPath);

var objPath = Path.Combine(projectPath, "obj");
Directory.CreateDirectory(objPath);

Comment on lines +115 to +120

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-creating bin/ and obj/ in a project template is generally undesirable because these are build output directories and can confuse tooling/users (and may create noise in scaffolding results). Prefer not creating them at scaffold time; keep the .gitignore entries and let the build generate these folders.

Suggested change
var binPath = Path.Combine(projectPath, "bin");
Directory.CreateDirectory(binPath);
var objPath = Path.Combine(projectPath, "obj");
Directory.CreateDirectory(objPath);

Copilot uses AI. Check for mistakes.
var gitignorePath = Path.Combine(projectPath, ".gitignore");
File.WriteAllText(gitignorePath, "bin/\nobj/\n*.user\n.vs/");
result.CreatedFiles.Add(gitignorePath);

var readmePath = Path.Combine(projectPath, "README.md");
File.WriteAllText(readmePath, $"# {options.ModName}\n\nA Bannerlord Mod created with BannerlordModEditor.");
result.CreatedFiles.Add(readmePath);

result.Success = true;
}
catch (Exception ex)
{
result.Success = false;
result.ErrorMessage = ex.Message;
result.Errors.Add(ex.Message);
}

return result;
}

public bool ValidateModName(string modName)
{
if (string.IsNullOrWhiteSpace(modName))
return false;

if (modName.Length < 2 || modName.Length > 50)
return false;

return modName.All(c => char.IsLetterOrDigit(c) || c == '_' || c == '-');

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation error message is inconsistent with the actual validation rules: ValidateModName currently allows -, but the message says “underscores” only (and it also doesn’t mention the length constraints). Update the message to reflect the real rules or adjust the validator to match the message.

Suggested change
return modName.All(c => char.IsLetterOrDigit(c) || c == '_' || c == '-');
return modName.All(c => char.IsLetterOrDigit(c) || c == '_');

Copilot uses AI. Check for mistakes.
}

public string GenerateModId(string modName)
{
var id = modName.Replace(" ", "").Replace("-", "");
return id.Length > 0 ? id : "Mod";
}

private string GenerateCsprojContent(string modName, string modId, bool useButrTemplate)
{
var targetFramework = "net9.0";
var packageReferences = useButrTemplate
? @"
<PackageReference Include=""Bannerlord.ButterLib"" Version=""2.*"" />
<PackageReference Include=""Bannerlord.Harmony"" Version=""2.*"" />"
: "";
Comment on lines +161 to +165

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a new behavioral branch controlled by UseButrTemplate that changes the generated .csproj content, but the tests don’t currently assert that these conditional package references are present/absent. Add a test that creates a project with UseButrTemplate = false and verifies the generated .csproj does not contain the BUTR references (and optionally one that asserts they are present when true).

Copilot uses AI. Check for mistakes.

return $@"<Project Sdk=""Microsoft.NET.Sdk"">

<PropertyGroup>
<TargetFramework>{targetFramework}</TargetFramework>
<AssemblyName>{modName}</AssemblyName>
<RootNamespace>{modName}</RootNamespace>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include=""Bannerlord.MountAndBlade"" Version=""1.*"" />{packageReferences}
</ItemGroup>

</Project>";
}
}
}
Loading