370 lines
15 KiB
C#
370 lines
15 KiB
C#
using System.Xml.Linq;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.LegacyWeb.Tests;
|
|
|
|
public sealed class LegacyRuntimeAssetDeploymentContractTests
|
|
{
|
|
private static readonly string RepositoryRoot = FindRepositoryRoot();
|
|
private static readonly string ProjectPath = Path.Combine(
|
|
RepositoryRoot,
|
|
"src",
|
|
"MBN_STOCK_WEBVIEW.LegacyParityApp",
|
|
"MBN_STOCK_WEBVIEW.LegacyParityApp.csproj");
|
|
|
|
[Fact]
|
|
public void LegacyParityProjectLinksCutsAndResIntoOutputPublishAndMsix()
|
|
{
|
|
var project = XDocument.Load(ProjectPath);
|
|
var contents = project.Descendants("Content").ToArray();
|
|
|
|
AssertLinkedRuntimeTree(contents, "$(LegacyCutsSourceRoot)\\**\\*", "Cuts\\");
|
|
AssertLinkedRuntimeTree(contents, "@(LegacyPackagedResAsset)", "Res\\");
|
|
|
|
var packagedResNames = project.Descendants("LegacyPackagedResAsset")
|
|
.Select(element => Path.GetFileName(element.Attribute("Include")?.Value))
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
string[] requiredUiMenus =
|
|
[
|
|
"setup.ini",
|
|
"기타.ini",
|
|
"업종.ini",
|
|
"업종_코스닥.ini",
|
|
"업종_코스피.ini",
|
|
"종목.ini",
|
|
"종목_0111.ini",
|
|
"종목비교.ini",
|
|
"지수.ini",
|
|
"지수_0318.ini",
|
|
"지수_250401.ini",
|
|
"해외.ini",
|
|
"환율.ini"
|
|
];
|
|
|
|
Assert.Equal(34, packagedResNames.Count);
|
|
Assert.All(requiredUiMenus, name => Assert.Contains(name, packagedResNames));
|
|
Assert.DoesNotContain("MmoneyCoder.ini", packagedResNames);
|
|
Assert.DoesNotContain("MmoneyCoder - 복사본.ini", packagedResNames);
|
|
Assert.DoesNotContain("MmoneyCoder.ini.bak", packagedResNames);
|
|
Assert.DoesNotContain("MmoneyCoder.zip", packagedResNames);
|
|
Assert.DoesNotContain("afiedt.buf.txt", packagedResNames);
|
|
}
|
|
|
|
[Fact]
|
|
public void GraphEPreviewsExposeOnlyFourAllowlistedBitmapsThroughTheTrustedWebRoot()
|
|
{
|
|
var project = XDocument.Load(ProjectPath);
|
|
var previewNames = project.Descendants("LegacyFinancialPreviewAsset")
|
|
.Select(element => Path.GetFileName(element.Attribute("Include")?.Value)!)
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
|
|
Assert.Equal(
|
|
new HashSet<string>(
|
|
["pie.bmp", "Grow.bmp", "sell.bmp", "profit.bmp"],
|
|
StringComparer.OrdinalIgnoreCase),
|
|
previewNames);
|
|
|
|
var previewContent = Assert.Single(
|
|
project.Descendants("Content"),
|
|
element => string.Equals(
|
|
element.Attribute("Include")?.Value,
|
|
"@(LegacyFinancialPreviewAsset)",
|
|
StringComparison.Ordinal));
|
|
Assert.Equal(
|
|
"Web\\Previews\\%(Filename)%(Extension)",
|
|
previewContent.Element("Link")?.Value);
|
|
Assert.Equal(
|
|
"Web\\Previews\\%(Filename)%(Extension)",
|
|
previewContent.Element("TargetPath")?.Value);
|
|
Assert.Equal(
|
|
"PreserveNewest",
|
|
previewContent.Element("CopyToOutputDirectory")?.Value);
|
|
Assert.Equal(
|
|
"PreserveNewest",
|
|
previewContent.Element("CopyToPublishDirectory")?.Value);
|
|
|
|
Assert.DoesNotContain(
|
|
project.Descendants("Content"),
|
|
element => string.Equals(
|
|
element.Attribute("Include")?.Value,
|
|
"$(LegacyResSourceRoot)\\**\\*",
|
|
StringComparison.OrdinalIgnoreCase));
|
|
|
|
var window = File.ReadAllText(Path.Combine(
|
|
RepositoryRoot,
|
|
"src",
|
|
"MBN_STOCK_WEBVIEW.LegacyParityApp",
|
|
"MainWindow.xaml.cs"));
|
|
Assert.Equal(
|
|
1,
|
|
window.Split(
|
|
"SetVirtualHostNameToFolderMapping(",
|
|
StringSplitOptions.None).Length - 1);
|
|
Assert.Contains("LegacyBridgeProtocol.TrustedHost", window, StringComparison.Ordinal);
|
|
Assert.Contains("webRoot", window, StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"SetVirtualHostNameToFolderMapping(\"res",
|
|
window,
|
|
StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void WebViewIgnoresItsPersistentCacheBeforeExactTrustedNavigation()
|
|
{
|
|
var window = File.ReadAllText(Path.Combine(
|
|
RepositoryRoot,
|
|
"src",
|
|
"MBN_STOCK_WEBVIEW.LegacyParityApp",
|
|
"MainWindow.xaml.cs"));
|
|
|
|
var mapping = window.IndexOf(
|
|
"SetVirtualHostNameToFolderMapping(",
|
|
StringComparison.Ordinal);
|
|
var disableCache = window.IndexOf(
|
|
"\"Network.setCacheDisabled\"",
|
|
StringComparison.Ordinal);
|
|
var navigation = window.IndexOf(
|
|
"Browser.Source = new Uri(",
|
|
StringComparison.Ordinal);
|
|
|
|
Assert.True(mapping >= 0);
|
|
Assert.True(disableCache > mapping);
|
|
Assert.True(navigation > disableCache);
|
|
Assert.Contains(
|
|
"CoreWebView2HostResourceAccessKind.DenyCors",
|
|
window,
|
|
StringComparison.Ordinal);
|
|
Assert.Equal(
|
|
1,
|
|
window.Split(
|
|
"\"Network.setCacheDisabled\"",
|
|
StringSplitOptions.None).Length - 1);
|
|
Assert.Contains(
|
|
"\"{\\\"cacheDisabled\\\":true}\"",
|
|
window,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"$\"https://{LegacyBridgeProtocol.TrustedHost}/index.html\"",
|
|
window,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"$\"https://{LegacyBridgeProtocol.TrustedHost}/index.html?",
|
|
window,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void ActiveDatabaseIniIsPostBuildOutputOnlyAndNeverAPackagingItem()
|
|
{
|
|
var project = XDocument.Load(ProjectPath);
|
|
Assert.DoesNotContain(
|
|
project.Descendants().Where(element =>
|
|
element.Name.LocalName is "Content" or "None"),
|
|
element => (element.Attribute("Include")?.Value ?? string.Empty)
|
|
.Contains("MmoneyCoder.ini", StringComparison.OrdinalIgnoreCase));
|
|
|
|
var upToDateInput = Assert.Single(project.Descendants("UpToDateCheckInput"));
|
|
Assert.Equal(
|
|
"$(LegacyResSourceRoot)\\MmoneyCoder.ini",
|
|
upToDateInput.Attribute("Include")?.Value);
|
|
var upToDateBuilt = Assert.Single(project.Descendants("UpToDateCheckBuilt"));
|
|
Assert.Equal(
|
|
"$(TargetDir)Res\\MmoneyCoder.ini",
|
|
upToDateBuilt.Attribute("Include")?.Value);
|
|
|
|
var copyTarget = Assert.Single(
|
|
project.Descendants("Target"),
|
|
element => string.Equals(
|
|
element.Attribute("Name")?.Value,
|
|
"CopyLegacyDatabaseIniToLocalOutput",
|
|
StringComparison.Ordinal));
|
|
Assert.Equal("Build", copyTarget.Attribute("AfterTargets")?.Value);
|
|
Assert.Contains(
|
|
"GenerateAppxPackageOnBuild",
|
|
copyTarget.Attribute("Condition")?.Value,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"PublishAppxPackage",
|
|
copyTarget.Attribute("Condition")?.Value,
|
|
StringComparison.Ordinal);
|
|
Assert.Equal(
|
|
"$(LegacyResSourceRoot)\\MmoneyCoder.ini",
|
|
copyTarget.Attribute("Inputs")?.Value);
|
|
Assert.Equal(
|
|
"$(TargetDir)Res\\MmoneyCoder.ini",
|
|
copyTarget.Attribute("Outputs")?.Value);
|
|
var copy = Assert.Single(copyTarget.Elements("Copy"));
|
|
Assert.Equal(
|
|
"$(LegacyResSourceRoot)\\MmoneyCoder.ini",
|
|
copy.Attribute("SourceFiles")?.Value);
|
|
Assert.Equal(
|
|
"$(TargetDir)Res\\MmoneyCoder.ini",
|
|
copy.Attribute("DestinationFiles")?.Value);
|
|
var fileWrite = Assert.Single(copyTarget.Descendants("FileWrites"));
|
|
Assert.Equal(
|
|
"$(TargetDir)Res\\MmoneyCoder.ini",
|
|
fileWrite.Attribute("Include")?.Value);
|
|
|
|
var cleanTarget = Assert.Single(
|
|
project.Descendants("Target"),
|
|
element => string.Equals(
|
|
element.Attribute("Name")?.Value,
|
|
"RemoveLegacyDatabaseIniFromLocalOutput",
|
|
StringComparison.Ordinal));
|
|
Assert.Equal("Clean", cleanTarget.Attribute("BeforeTargets")?.Value);
|
|
var delete = Assert.Single(cleanTarget.Elements("Delete"));
|
|
Assert.Equal(
|
|
"$(TargetDir)Res\\MmoneyCoder.ini",
|
|
delete.Attribute("Files")?.Value);
|
|
|
|
var rejectionTarget = Assert.Single(
|
|
project.Descendants("Target"),
|
|
element => string.Equals(
|
|
element.Attribute("Name")?.Value,
|
|
"RejectLegacyDatabaseIniFromPackagePayload",
|
|
StringComparison.Ordinal));
|
|
Assert.Equal(
|
|
"_ComputeAppxPackagePayload",
|
|
rejectionTarget.Attribute("BeforeTargets")?.Value);
|
|
var forbiddenPayload = Assert.Single(
|
|
rejectionTarget.Descendants("_ForbiddenLegacyDatabasePackagePayload"));
|
|
Assert.Contains(
|
|
"%(PackagingOutputs.Filename)%(PackagingOutputs.Extension)",
|
|
forbiddenPayload.Attribute("Condition")?.Value,
|
|
StringComparison.Ordinal);
|
|
Assert.Single(rejectionTarget.Elements("Error"));
|
|
}
|
|
|
|
[Fact]
|
|
public void LegacyRuntimeSourceRootIsOverrideableAndValidatedBeforeBuild()
|
|
{
|
|
var project = XDocument.Load(ProjectPath);
|
|
var sourceRoot = Assert.Single(
|
|
project.Descendants("LegacyRuntimeSourceRoot"),
|
|
element => element.Attribute("Condition") is not null);
|
|
|
|
Assert.Equal("'$(LegacyRuntimeSourceRoot)' == ''", sourceRoot.Attribute("Condition")!.Value);
|
|
Assert.Contains("MBN_STOCK_N\\MBN_STOCK_N\\bin\\Debug", sourceRoot.Value,
|
|
StringComparison.Ordinal);
|
|
|
|
var target = Assert.Single(
|
|
project.Descendants("Target"),
|
|
element => string.Equals(
|
|
element.Attribute("Name")?.Value,
|
|
"ValidateLegacyRuntimeAssets",
|
|
StringComparison.Ordinal));
|
|
Assert.Equal("PrepareForBuild", target.Attribute("BeforeTargets")?.Value);
|
|
|
|
var conditions = target.Elements("Error")
|
|
.Select(error => error.Attribute("Condition")?.Value)
|
|
.Where(condition => condition is not null)
|
|
.ToArray();
|
|
string[] requiredSentinels =
|
|
[
|
|
"$(LegacyRuntimeSourceRoot)",
|
|
"$(LegacyCutsSourceRoot)",
|
|
"$(LegacyResSourceRoot)",
|
|
"$(LegacyCutsSourceRoot)\\%(LegacyRequiredScene.Identity).t2s",
|
|
"$(LegacyCutsSourceRoot)\\%(LegacyRequiredBuiltInAsset.Identity)",
|
|
"$(LegacyResSourceRoot)\\MmoneyCoder.ini",
|
|
"$(LegacyResSourceRoot)\\종목.ini",
|
|
"$(LegacyResSourceRoot)\\업종_코스피.ini",
|
|
"$(LegacyResSourceRoot)\\업종_코스닥.ini",
|
|
"$(LegacyResSourceRoot)\\해외.ini",
|
|
"$(LegacyResSourceRoot)\\환율.ini",
|
|
"$(LegacyResSourceRoot)\\지수.ini"
|
|
];
|
|
|
|
Assert.All(requiredSentinels, sentinel =>
|
|
Assert.Contains(conditions, condition =>
|
|
condition!.Contains(sentinel, StringComparison.Ordinal)));
|
|
|
|
var requiredScenes = Assert.Single(project.Descendants("LegacyRequiredScene"))
|
|
.Attribute("Include")!.Value
|
|
.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
|
Assert.Equal(45, requiredScenes.Length);
|
|
Assert.Equal(requiredScenes.Length,
|
|
requiredScenes.Distinct(StringComparer.Ordinal).Count());
|
|
Assert.Contains("5001", requiredScenes);
|
|
Assert.Contains("5076", requiredScenes);
|
|
Assert.Contains("5079", requiredScenes);
|
|
Assert.Contains("6001", requiredScenes);
|
|
|
|
var requiredAssets = Assert.Single(
|
|
project.Descendants("LegacyRequiredBuiltInAsset"))
|
|
.Attribute("Include")!.Value
|
|
.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
|
Assert.Equal(9, requiredAssets.Length);
|
|
Assert.Contains(@"images\주유기merge.png", requiredAssets);
|
|
Assert.Contains(@"images\35752913_l.jpg", requiredAssets);
|
|
Assert.Contains(@"Images\KRX.png", requiredAssets);
|
|
Assert.Contains(@"Images\NXT.png", requiredAssets);
|
|
|
|
var databaseIniError = Assert.Single(
|
|
target.Elements("Error"),
|
|
error => (error.Attribute("Condition")?.Value ?? string.Empty)
|
|
.Contains("$(LegacyResSourceRoot)\\MmoneyCoder.ini", StringComparison.Ordinal));
|
|
Assert.Contains(
|
|
"GenerateAppxPackageOnBuild",
|
|
databaseIniError.Attribute("Condition")?.Value,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"PublishAppxPackage",
|
|
databaseIniError.Attribute("Condition")?.Value,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void RepositoryDoesNotContainCopiedLegacyRuntimeTrees()
|
|
{
|
|
Assert.False(Directory.Exists(Path.Combine(RepositoryRoot, "Cuts")));
|
|
Assert.False(Directory.Exists(Path.Combine(RepositoryRoot, "Res")));
|
|
Assert.False(Directory.Exists(Path.Combine(
|
|
RepositoryRoot,
|
|
"src",
|
|
"MBN_STOCK_WEBVIEW.LegacyParityApp",
|
|
"Cuts")));
|
|
Assert.False(Directory.Exists(Path.Combine(
|
|
RepositoryRoot,
|
|
"src",
|
|
"MBN_STOCK_WEBVIEW.LegacyParityApp",
|
|
"Res")));
|
|
}
|
|
|
|
private static void AssertLinkedRuntimeTree(
|
|
IReadOnlyCollection<XElement> contents,
|
|
string include,
|
|
string targetPrefix)
|
|
{
|
|
var content = Assert.Single(
|
|
contents,
|
|
element => string.Equals(
|
|
element.Attribute("Include")?.Value,
|
|
include,
|
|
StringComparison.Ordinal));
|
|
|
|
Assert.StartsWith(targetPrefix, content.Element("Link")?.Value, StringComparison.Ordinal);
|
|
Assert.StartsWith(
|
|
targetPrefix,
|
|
content.Element("TargetPath")?.Value,
|
|
StringComparison.Ordinal);
|
|
Assert.Equal("PreserveNewest", content.Element("CopyToOutputDirectory")?.Value);
|
|
Assert.Equal("PreserveNewest", content.Element("CopyToPublishDirectory")?.Value);
|
|
}
|
|
|
|
private static string FindRepositoryRoot()
|
|
{
|
|
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (current is not null)
|
|
{
|
|
if (File.Exists(Path.Combine(current.FullName, "MBN_STOCK_WEBVIEW.sln")))
|
|
{
|
|
return current.FullName;
|
|
}
|
|
|
|
current = current.Parent;
|
|
}
|
|
|
|
throw new DirectoryNotFoundException("Could not locate the repository root.");
|
|
}
|
|
}
|