Skip to content

Commit f05665c

Browse files
committed
adding additional tests and adding custom handlers project
1 parent edba5eb commit f05665c

18 files changed

+894
-19
lines changed

src/Cli/func/Actions/LocalActions/PackAction/NodePackSubcommandAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ private async Task RunNpmBuildIfExists()
121121
// Simple check if build script exists
122122
if (packageJsonContent.Contains("\"build\"") && packageJsonContent.Contains("scripts"))
123123
{
124-
ColoredConsole.WriteLine("Running npm run build...");
125124
await NpmHelper.RunNpmCommand("run build", ignoreError: false);
125+
Console.WriteLine();
126126
}
127127
else
128128
{

src/Cli/func/Actions/LocalActions/PackAction/PackAction.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
using Azure.Functions.Cli.Common;
55
using Azure.Functions.Cli.Helpers;
66
using Azure.Functions.Cli.Interfaces;
7-
using Colors.Net;
87
using Fclp;
9-
using Microsoft.Azure.AppService.Proxy.Common.Constants;
10-
using Microsoft.Azure.WebJobs.Script;
118

129
namespace Azure.Functions.Cli.Actions.LocalActions.PackAction
1310
{

src/Cli/func/Actions/LocalActions/PackAction/PythonPackSubcommandAction.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,20 @@ public async Task RunAsync(PackOptions packOptions, string[] args)
3838
{
3939
ParseArgs(args);
4040

41+
// Validate invalid flag combinations
42+
if (packOptions.NoBuild && BuildNativeDeps)
43+
{
44+
throw new CliException("Invalid options: --no-build cannot be used with --build-native-deps.");
45+
}
46+
4147
var functionAppRoot = PackHelpers.ResolveFunctionAppRoot(packOptions.FolderPath);
4248

4349
if (!Directory.Exists(functionAppRoot))
4450
{
4551
throw new CliException($"Directory not found to pack: {functionAppRoot}");
4652
}
4753

48-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
54+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !BuildNativeDeps)
4955
{
5056
ColoredConsole.WriteLine(WarningColor("Python function apps is supported only on Linux. Please use the --build-native-deps flag" +
5157
" when building on windows to ensure dependencies are properly restored."));

test/Cli/Func.E2ETests/Commands/FuncPack/BasePackTests.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4+
using Azure.Functions.Cli.Common;
45
using Azure.Functions.Cli.TestFramework.Assertions;
56
using Azure.Functions.Cli.TestFramework.Commands;
67
using FluentAssertions;
@@ -11,7 +12,7 @@ namespace Azure.Functions.Cli.E2ETests.Commands.FuncPack
1112
{
1213
internal static class BasePackTests
1314
{
14-
internal static void TestBasicPackFunctionality(string workingDir, string testName, string funcPath, ITestOutputHelper log, string[] filesToValidate)
15+
internal static void TestBasicPackFunctionality(string workingDir, string testName, string funcPath, ITestOutputHelper log, string[] filesToValidate, string[]? logStatementsToValidate = null)
1516
{
1617
// Run pack command
1718
var funcPackCommand = new FuncPackCommand(funcPath, testName, log);
@@ -44,5 +45,40 @@ internal static void TestBasicPackFunctionality(string workingDir, string testNa
4445

4546
File.Delete(zipFiles.First()); // Clean up the zip file after validation
4647
}
48+
49+
internal static async Task TestNoBuildCustomOutputPackFunctionality(
50+
string projectDir,
51+
string testName,
52+
string funcPath,
53+
ITestOutputHelper log,
54+
string zipOutputDirectory,
55+
string[] filesToValidate)
56+
{
57+
// Ensure publish output exists for --no-build scenario
58+
var exe = new Executable("dotnet", $"publish --output \"./output\"", workingDirectory: projectDir);
59+
var exitCode = await exe.RunAsync();
60+
exitCode.Should().Be(0);
61+
62+
// Run func pack pointing at publish output directory, no build, and custom output location
63+
var funcPackCommand = new FuncPackCommand(funcPath, testName, log);
64+
var packResult = funcPackCommand
65+
.WithWorkingDirectory(projectDir)
66+
.Execute([Path.Combine(projectDir, "output"), "--no-build", "--output", zipOutputDirectory]);
67+
68+
// Verify pack succeeded and build was skipped
69+
packResult.Should().ExitWith(0);
70+
packResult.Should().NotHaveStdOutContaining("Building .NET project...");
71+
packResult.Should().HaveStdOutContaining("Skipping build event for functions project (--no-build).");
72+
73+
// Find any zip files in the specified output directory
74+
var zipFiles = Directory.GetFiles(zipOutputDirectory, "*.zip");
75+
Assert.True(zipFiles.Length > 0, $"No zip files found in {zipOutputDirectory}");
76+
77+
// Validate zip contents
78+
packResult.Should().ValidateZipContents(zipFiles.First(), filesToValidate, log);
79+
80+
// Clean up
81+
File.Delete(zipFiles.First());
82+
}
4783
}
4884
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using System.IO.Compression;
5+
using Azure.Functions.Cli.E2ETests.Traits;
6+
using Azure.Functions.Cli.TestFramework.Assertions;
7+
using Azure.Functions.Cli.TestFramework.Commands;
8+
using FluentAssertions;
9+
using Xunit;
10+
using Xunit.Abstractions;
11+
12+
namespace Azure.Functions.Cli.E2ETests.Commands.FuncPack
13+
{
14+
[Trait(WorkerRuntimeTraits.WorkerRuntime, WorkerRuntimeTraits.Custom)]
15+
public class CustomHandlerPackTests : BaseE2ETests
16+
{
17+
public CustomHandlerPackTests(ITestOutputHelper log)
18+
: base(log)
19+
{
20+
}
21+
22+
private string CustomHandlerProjectPath => Path.Combine(TestProjectDirectory, "TestCustomHandlerProject");
23+
24+
[Fact]
25+
public void Pack_CustomHandler_TurnsBitExecutable()
26+
{
27+
var testName = nameof(Pack_CustomHandler_TurnsBitExecutable);
28+
29+
var packResult = new FuncPackCommand(FuncPath, testName, Log)
30+
.WithWorkingDirectory(CustomHandlerProjectPath)
31+
.Execute([]);
32+
33+
packResult.Should().ExitWith(0);
34+
packResult.Should().HaveStdOutContaining("Creating a new package");
35+
36+
var zipFiles = Directory.GetFiles(CustomHandlerProjectPath, "*.zip");
37+
Assert.True(zipFiles.Length > 0, $"No zip files found in {CustomHandlerProjectPath}");
38+
39+
var zipPath = zipFiles.First();
40+
41+
packResult.Should().ValidateZipContents(
42+
zipPath,
43+
new[]
44+
{
45+
"host.json",
46+
"GoCustomHandlers"
47+
},
48+
Log);
49+
50+
using (var archive = ZipFile.OpenRead(zipPath))
51+
{
52+
var entry = archive.Entries.FirstOrDefault(e => e.FullName.Replace('\\', '/').EndsWith("GoCustomHandlers"));
53+
entry.Should().NotBeNull("GoCustomHandlers should be present in the packaged zip");
54+
55+
int permissions = (entry!.ExternalAttributes >> 16) & 0xFFFF;
56+
permissions.Should().Be(Convert.ToInt32("100777", 8), "GoCustomHandlers should be marked as executable in the zip");
57+
}
58+
59+
File.Delete(zipPath);
60+
}
61+
62+
[Fact]
63+
public void Pack_CustomHandler_PreserveExecutables_SetsBit()
64+
{
65+
var testName = nameof(Pack_CustomHandler_PreserveExecutables_SetsBit);
66+
var execRelativePath = Path.GetFullPath(".\\TurnThisExecutable");
67+
68+
var packResult = new FuncPackCommand(FuncPath, testName, Log)
69+
.WithWorkingDirectory(CustomHandlerProjectPath)
70+
.Execute(["--preserve-executables", execRelativePath]);
71+
72+
packResult.Should().ExitWith(0);
73+
74+
var zipFiles = Directory.GetFiles(CustomHandlerProjectPath, "*.zip");
75+
Assert.True(zipFiles.Length > 0, $"No zip files found in {CustomHandlerProjectPath}");
76+
var zipPath = zipFiles.First();
77+
78+
using (var archive = ZipFile.OpenRead(zipPath))
79+
{
80+
var entry = archive.Entries.FirstOrDefault(e => e.FullName.Replace('\\', '/').EndsWith("TurnThisExecutable"));
81+
entry.Should().NotBeNull();
82+
int permissions = (entry!.ExternalAttributes >> 16) & 0xFFFF;
83+
permissions.Should().Be(Convert.ToInt32("100777", 8));
84+
}
85+
86+
File.Delete(zipPath);
87+
}
88+
}
89+
}

test/Cli/Func.E2ETests/Commands/FuncPack/DotnetInProc6PackTests.cs

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

44
using Azure.Functions.Cli.E2ETests.Traits;
5+
using Azure.Functions.Cli.TestFramework.Assertions;
6+
using Azure.Functions.Cli.TestFramework.Commands;
7+
using FluentAssertions;
8+
using System.IO.Compression;
59
using Xunit;
610
using Xunit.Abstractions;
711

@@ -21,7 +25,12 @@ public DotnetInProc6PackTests(ITestOutputHelper log)
2125
public void Pack_Dotnet6InProc_WorksAsExpected()
2226
{
2327
var testName = nameof(Pack_Dotnet6InProc_WorksAsExpected);
24-
Log.WriteLine(Dotnet6ProjectPath);
28+
29+
var logsToValidate = new[]
30+
{
31+
"Building .NET project...",
32+
"Determining projects to restore..."
33+
};
2534

2635
BasePackTests.TestBasicPackFunctionality(
2736
Dotnet6ProjectPath,
@@ -31,9 +40,68 @@ public void Pack_Dotnet6InProc_WorksAsExpected()
3140
new[]
3241
{
3342
"host.json",
34-
"Dotnet6InProc.cs",
35-
"TestNet6InProcProject.csproj"
43+
Path.Combine("bin", "extensions.json"),
44+
Path.Combine("bin", "function.deps.json"),
45+
Path.Combine("bin", "Microsoft.Azure.WebJobs.Host.Storage.dll"),
46+
Path.Combine("bin", "Microsoft.WindowsAzure.Storage.dll"),
47+
Path.Combine("bin", "TestNet6InProcProject.dll"),
48+
Path.Combine("bin", "TestNet6InProcProject.pdb"),
49+
Path.Combine("Dotnet6InProc", "function.json"),
50+
Path.Combine("bin", "runtimes", "browser", "lib", "net6.0", "System.Text.Encodings.Web.dll")
51+
},
52+
logsToValidate);
53+
}
54+
55+
[Fact]
56+
public async Task Pack_Dotnet6InProc_CustomOutput_NoBuild()
57+
{
58+
var testName = nameof(Pack_Dotnet6InProc_CustomOutput_NoBuild);
59+
60+
await BasePackTests.TestNoBuildCustomOutputPackFunctionality(
61+
Dotnet6ProjectPath,
62+
testName,
63+
FuncPath,
64+
Log,
65+
WorkingDirectory,
66+
new[]
67+
{
68+
"host.json",
69+
Path.Combine("bin", "extensions.json"),
70+
Path.Combine("bin", "function.deps.json"),
71+
Path.Combine("bin", "Microsoft.Azure.WebJobs.Host.Storage.dll"),
72+
Path.Combine("bin", "Microsoft.WindowsAzure.Storage.dll"),
73+
Path.Combine("bin", "TestNet6InProcProject.dll"),
74+
Path.Combine("bin", "TestNet6InProcProject.pdb"),
75+
Path.Combine("Dotnet6InProc", "function.json"),
76+
Path.Combine("bin", "runtimes", "browser", "lib", "net6.0", "System.Text.Encodings.Web.dll")
3677
});
3778
}
79+
80+
[Fact]
81+
public void Pack_Dotnet6InProc_PreserveExecutables_SetsBit()
82+
{
83+
var testName = nameof(Pack_Dotnet6InProc_PreserveExecutables_SetsBit);
84+
var execRelativePath = "TurnThisExecutable";
85+
86+
var packResult = new FuncPackCommand(FuncPath, testName, Log)
87+
.WithWorkingDirectory(Dotnet6ProjectPath)
88+
.Execute(["--preserve-executables", execRelativePath]);
89+
90+
packResult.Should().ExitWith(0);
91+
92+
var zipFiles = Directory.GetFiles(Dotnet6ProjectPath, "*.zip");
93+
Assert.True(zipFiles.Length > 0, $"No zip files found in {Dotnet6ProjectPath}");
94+
var zipPath = zipFiles.First();
95+
96+
using (var archive = ZipFile.OpenRead(zipPath))
97+
{
98+
var entry = archive.Entries.FirstOrDefault(e => e.FullName.Replace('\\', '/').EndsWith(execRelativePath));
99+
entry.Should().NotBeNull();
100+
int permissions = (entry!.ExternalAttributes >> 16) & 0xFFFF;
101+
permissions.Should().Be(Convert.ToInt32("100777", 8));
102+
}
103+
104+
File.Delete(zipPath);
105+
}
38106
}
39107
}

0 commit comments

Comments
 (0)