diff --git a/build/Version.props b/build/Version.props index 7178bdfc80..d37c328cda 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,7 +3,7 @@ - 5.13.6 + 5.13.7 4.7.1 9.11.0 6.0.0 diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index 1b74ccfe08..087503d68c 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -236,7 +236,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles } /// - public async Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + public async Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) { await EnsureDirectories(cancellationToken); var path = ValidateConfigRelativePath(configurationRelativePath); @@ -252,14 +252,14 @@ namespace Tgstation.Server.Host.Components.StaticFiles { IsDirectory = true, Path = ioManager.ConcatPath(configurationRelativePath, x), - }).OrderBy(file => file.Path)); + })); enumerator = synchronousIOManager.GetFiles(path, cancellationToken); result.AddRange(enumerator.Select(x => new ConfigurationFileResponse { IsDirectory = false, Path = ioManager.ConcatPath(configurationRelativePath, x), - }).OrderBy(file => file.Path)); + })); } using (SemaphoreSlimContext.TryLock(semaphore, out var locked)) @@ -276,7 +276,10 @@ namespace Tgstation.Server.Host.Components.StaticFiles await systemIdentity.RunImpersonated(ListImpl, cancellationToken); } - return result; + return result + .AsQueryable() + .OrderBy(configFile => !configFile.IsDirectory) + .ThenBy(configFile => configFile.Path); } /// diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs index 4fece676c9..b4ed9f59f9 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -38,8 +38,8 @@ namespace Tgstation.Server.Host.Components.StaticFiles /// The relative path in the Configuration directory. /// The for the operation. If , the operation will be performed as the user of the . /// The for the operation. - /// A resulting in the s for the items in the directory. and will both be . will be returned if the operation failed due to access contention. - Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); + /// A resulting in an of the s for the items in the directory. and will both be . will be returned if the operation failed due to access contention. + Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); /// /// Reads a given . diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index 1a7e2dab49..6be4c6df5f 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -190,10 +189,7 @@ namespace Tgstation.Server.Host.Controllers return new PaginatableResult( Conflict(new ErrorMessageResponse(ErrorCode.ConfigurationContendedAccess))); - return new PaginatableResult( - result - .AsQueryable() - .OrderBy(x => x)); // ordering performed by IConfiguration + return new PaginatableResult(result); } catch (NotImplementedException ex) { diff --git a/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs b/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs index cec38f9875..68241988d1 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/StaticFiles/TestConfiguration.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -57,7 +58,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles.Tests await configuration.StartAsync(CancellationToken.None); - var listResponse = await configuration.ListDirectory(".", null, CancellationToken.None); + var listResponse = (await configuration.ListDirectory(".", null, CancellationToken.None)).ToList(); Assert.AreEqual(9, listResponse.Count); Assert.AreEqual("a", listResponse[0].Path[2..]); Assert.AreEqual("c", listResponse[1].Path[2..]); @@ -76,7 +77,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles.Tests await ioManager.WriteAllBytes("GameStaticFiles/config/unbuyableshuttles.txt", Array.Empty(), CancellationToken.None); await ioManager.WriteAllBytes("GameStaticFiles/config/spaceruinblacklist.txt", Array.Empty(), CancellationToken.None); - listResponse = await configuration.ListDirectory("GameStaticFiles/config", null, CancellationToken.None); + listResponse = (await configuration.ListDirectory("GameStaticFiles/config", null, CancellationToken.None)).ToList(); var substringStart = "GameStaticFiles/config".Length + 1; Assert.AreEqual(6, listResponse.Count); Assert.AreEqual("title_music", listResponse[0].Path[substringStart..]); diff --git a/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs index c921b2c9cd..1008406cd7 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/ConfigurationTest.cs @@ -1,9 +1,6 @@ using System; using System.IO; using System.Linq; -using System.Net.Http; -using System.Net.Mime; -using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -14,9 +11,7 @@ using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; -using Tgstation.Server.Common; using Tgstation.Server.Host.IO; -using Tgstation.Server.Host.System; namespace Tgstation.Server.Tests.Live.Instance { @@ -139,12 +134,49 @@ namespace Tgstation.Server.Tests.Live.Instance TaskCreationOptions.LongRunning, TaskScheduler.Current); - public Task RunPreWatchdog(CancellationToken cancellationToken) + async Task TestListing(CancellationToken cancellationToken) { - return Task.WhenAll( - TestUploadDownloadAndDeleteDirectory(cancellationToken), - SetupDMApiTests(cancellationToken), - TestPregeneratedFilesExist(cancellationToken)); + await using var uploadMs = new MemoryStream(Encoding.UTF8.GetBytes("Hello world!")); + await configurationClient.Write( + new ConfigurationFileRequest + { + Path = "f-TestFile.txt" + }, + uploadMs, + cancellationToken); + uploadMs.Seek(0, SeekOrigin.Begin); + await configurationClient.Write( + new ConfigurationFileRequest + { + Path = "f-TestDir/f-TestFile.txt" + }, + uploadMs, + cancellationToken); + + var baseList = await configurationClient.List(null, ".", cancellationToken); + Assert.AreEqual(5, baseList.Count); + + Assert.AreEqual($"/CodeModifications", baseList[0].Path); + Assert.IsTrue(baseList[0].IsDirectory); + Assert.AreEqual($"/EventScripts", baseList[1].Path); + Assert.IsTrue(baseList[1].IsDirectory); + Assert.AreEqual($"/f-TestDir", baseList[2].Path); + Assert.IsTrue(baseList[2].IsDirectory); + Assert.AreEqual($"/GameStaticFiles", baseList[3].Path); + Assert.IsTrue(baseList[3].IsDirectory); + Assert.AreEqual($"/f-TestFile.txt", baseList[4].Path); + Assert.IsFalse(baseList[4].IsDirectory); } + + async Task SequencedApiTests(CancellationToken cancellationToken) + { + await TestUploadDownloadAndDeleteDirectory(cancellationToken); + await TestListing(cancellationToken); + } + + public Task RunPreWatchdog(CancellationToken cancellationToken) => Task.WhenAll( + SequencedApiTests(cancellationToken), + SetupDMApiTests(cancellationToken), + TestPregeneratedFilesExist(cancellationToken)); } }