File: RazorProjectFileSystemTest.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.AspNetCore.Razor.Language\test\Microsoft.AspNetCore.Razor.Language.UnitTests.csproj (Microsoft.AspNetCore.Razor.Language.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using Xunit;
 
namespace Microsoft.AspNetCore.Razor.Language;
 
public class RazorProjectFileSystemTest
{
    [Fact]
    public void NormalizeAndEnsureValidPath_DoesNotModifyPath()
    {
        // Arrange
        var project = new TestRazorProjectFileSystem();
 
        // Act
        var path = project.NormalizeAndEnsureValidPath("/Views/Home/Index.cshtml");
 
        // Assert
        Assert.Equal("/Views/Home/Index.cshtml", path);
    }
 
    [Fact]
    public void NormalizeAndEnsureValidPath_ThrowsIfPathIsNull()
    {
        // Arrange
        var project = new TestRazorProjectFileSystem();
 
        // Act and Assert
        Assert.Throws<ArgumentNullException>(paramName: "path", () => project.NormalizeAndEnsureValidPath(null!));
    }
 
    [Fact]
    public void NormalizeAndEnsureValidPath_ThrowsIfPathIsEmpty()
    {
        // Arrange
        var project = new TestRazorProjectFileSystem();
 
        // Act and Assert
        Assert.Throws<ArgumentException>(paramName: "path", () => project.NormalizeAndEnsureValidPath(""));
    }
 
    [Theory]
    [InlineData("foo")]
    [InlineData("~/foo")]
    [InlineData("\\foo")]
    public void NormalizeAndEnsureValidPath_ThrowsIfPathDoesNotStartWithForwardSlash(string path)
    {
        // Arrange
        var project = new TestRazorProjectFileSystem();
 
        // Act and Assert
        Assert.Throws<ArgumentException>(paramName: "path", () => project.NormalizeAndEnsureValidPath(path));
    }
 
    [Fact]
    public void FindHierarchicalItems_ReturnsEmptySequenceIfPathIsAtRoot()
    {
        // Arrange
        var project = new TestRazorProjectFileSystem();
 
        // Act
        var result = project.FindHierarchicalItems("/", "File.cshtml");
 
        // Assert
        Assert.Empty(result);
    }
 
    [Theory]
    [InlineData("_ViewStart.cshtml")]
    [InlineData("_ViewImports.cshtml")]
    public void FindHierarchicalItems_ReturnsItemsForPath(string fileName)
    {
        // Arrange
        var path = "/Views/Home/Index.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem($"/{fileName}"),
            CreateProjectItem($"/Views/{fileName}"),
            CreateProjectItem($"/Views/Home/{fileName}"));
 
        // Act
        var result = project.FindHierarchicalItems(path, $"{fileName}");
 
        // Assert
        Assert.Collection(
            result,
            item => Assert.Equal($"/{fileName}", item.FilePath),
            item => Assert.Equal($"/Views/{fileName}", item.FilePath),
            item => Assert.Equal($"/Views/Home/{fileName}", item.FilePath));
    }
 
    [Fact]
    public void FindHierarchicalItems_ReturnsItemsForPathAtRoot()
    {
        // Arrange
        var path = "/Index.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(path, "File.cshtml");
 
        // Assert
        Assert.Collection(
            result,
            item => Assert.Equal("/File.cshtml", item.FilePath));
    }
 
    [Fact]
    public void FindHierarchicalItems_DoesNotIncludePassedInItem()
    {
        // Arrange
        var path = "/Areas/MyArea/Views/Home/File.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/Areas/MyArea/Views/Home/File.cshtml"),
            CreateProjectItem("/Areas/MyArea/Views/File.cshtml"),
            CreateProjectItem("/Areas/MyArea/File.cshtml"),
            CreateProjectItem("/Areas/File.cshtml"),
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(path, "File.cshtml");
 
        // Assert
        Assert.Collection(
            result,
            item => Assert.Equal("/File.cshtml", item.FilePath),
            item => Assert.Equal("/Areas/File.cshtml", item.FilePath),
            item => Assert.Equal("/Areas/MyArea/File.cshtml", item.FilePath),
            item => Assert.Equal("/Areas/MyArea/Views/File.cshtml", item.FilePath));
    }
 
    [Fact]
    public void FindHierarchicalItems_ReturnsEmptySequenceIfPassedInItemWithFileNameIsAtRoot()
    {
        // Arrange
        var path = "/File.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(path, "File.cshtml");
 
        // Assert
        Assert.Empty(result);
    }
 
    [Fact]
    public void FindHierarchicalItems_IncludesNonExistentFiles()
    {
        // Arrange
        var path = "/Areas/MyArea/Views/Home/Test.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/Areas/MyArea/File.cshtml"),
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(path, "File.cshtml");
 
        // Assert
        Assert.Collection(
            result,
            item =>
            {
                Assert.Equal("/File.cshtml", item.FilePath);
                Assert.True(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/MyArea/File.cshtml", item.FilePath);
                Assert.True(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/MyArea/Views/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/MyArea/Views/Home/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            });
    }
 
    [Theory]
    [InlineData("/Areas")]
    [InlineData("/Areas/")]
    public void FindHierarchicalItems_WithBasePath(string basePath)
    {
        // Arrange
        var path = "/Areas/MyArea/Views/Home/Test.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/Areas/MyArea/File.cshtml"),
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(basePath, path, "File.cshtml");
 
        // Assert
        Assert.Collection(
            result,
            item =>
            {
                Assert.Equal("/Areas/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/MyArea/File.cshtml", item.FilePath);
                Assert.True(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/MyArea/Views/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/MyArea/Views/Home/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            });
    }
 
    [Theory]
    [InlineData("/Areas/MyArea/Views")]
    [InlineData("/Areas/MyArea/Views/")]
    public void FindHierarchicalItems_WithNestedBasePath(string basePath)
    {
        // Arrange
        var path = "/Areas/MyArea/Views/Home/Test.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/Areas/MyArea/File.cshtml"),
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(basePath, path, "File.cshtml");
 
        // Assert
        Assert.Collection(
            result,
            item =>
            {
                Assert.Equal("/Areas/MyArea/Views/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            },
            item =>
            {
                Assert.Equal("/Areas/MyArea/Views/Home/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            });
    }
 
    [Theory]
    [InlineData("/Areas/MyArea/Views/Home")]
    [InlineData("/Areas/MyArea/Views/Home/")]
    public void FindHierarchicalItems_WithFileAtBasePath(string basePath)
    {
        // Arrange
        var path = "/Areas/MyArea/Views/Home/Test.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/Areas/MyArea/File.cshtml"),
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(basePath, path, "File.cshtml");
 
        // Assert
        Assert.Collection(
            result,
            item =>
            {
                Assert.Equal("/Areas/MyArea/Views/Home/File.cshtml", item.FilePath);
                Assert.False(item.Exists);
            });
    }
 
    [Fact]
    public void FindHierarchicalItems_ReturnsEmptySequenceIfPathIsNotASubPathOfBasePath()
    {
        // Arrange
        var basePath = "/Pages";
        var path = "/Areas/MyArea/Views/Home/Test.cshtml";
        var project = new TestRazorProjectFileSystem(
            CreateProjectItem("/Areas/MyArea/File.cshtml"),
            CreateProjectItem("/File.cshtml"));
 
        // Act
        var result = project.FindHierarchicalItems(basePath, path, "File.cshtml");
 
        // Assert
        Assert.Empty(result);
    }
 
    private static RazorProjectItem CreateProjectItem(string path)
    {
        var projectItem = new Mock<RazorProjectItem>();
        projectItem.SetupGet(f => f.FilePath).Returns(path);
        projectItem.SetupGet(f => f.Exists).Returns(true);
        return projectItem.Object;
    }
 
    public sealed class TestRazorProjectFileSystem(params IEnumerable<RazorProjectItem> items) : RazorProjectFileSystem
    {
        private readonly Dictionary<string, RazorProjectItem> _lookup = items.ToDictionary(item => item.FilePath);
 
        public TestRazorProjectFileSystem()
            : this([])
        {
        }
 
        public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
        {
            throw new NotImplementedException();
        }
 
        public override RazorProjectItem GetItem(string path, RazorFileKind? fileKind)
        {
            if (!_lookup.TryGetValue(path, out var value))
            {
                value = new NotFoundProjectItem(path, fileKind);
            }
 
            return value;
        }
 
        public new string NormalizeAndEnsureValidPath(string path)
            => base.NormalizeAndEnsureValidPath(path);
    }
}