diff --git a/.vscode/settings.json b/.vscode/settings.json
index 44eff6c..c6074e2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,10 +4,14 @@
"CICD",
"MYINPUT",
"SOLUTIONFOLDERPATH",
+ "SOURCESDIRECTORY",
"asaproj",
"asaql",
+ "aut",
"jsondiffpatch",
"prun",
+ "tabler",
+ "toolset",
"unittest"
]
}
\ No newline at end of file
diff --git a/ASAHelloWorld/ASAHelloWorld.asaproj b/ASAHelloWorld/ASAHelloWorld.asaproj
deleted file mode 100644
index 142a14b..0000000
--- a/ASAHelloWorld/ASAHelloWorld.asaproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
- InputMock
-
-
-
- InputMock
-
-
-
- JobConfig
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index 3e46d2f..7864a81 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Unit testing Azure Stream Analytics
-Unit testing for [Azure Stream Analytics](https://docs.microsoft.com/en-us/azure/stream-analytics/) (ASA), the serverless [complex-event-processing](https://en.wikipedia.org/wiki/Complex_event_processing) service running in Azure.
+[PowerShell Gallery module](https://www.powershellgallery.com/packages/asa.unittest/) for unit testing [Azure Stream Analytics](https://docs.microsoft.com/en-us/azure/stream-analytics/) (ASA) jobs, the serverless [complex-event-processing](https://en.wikipedia.org/wiki/Complex_event_processing) service running in Azure.
In this article:
@@ -18,6 +18,7 @@ In this article:
- [Troubleshooting](https://github.com/Fleid/asa.unittest#Troubleshooting)
- [Internal Details](https://github.com/Fleid/asa.unittest#Internal-Details)
- [Change Log](https://github.com/Fleid/asa.unittest#Change-Log)
+ - [Thanks](https://github.com/Fleid/asa.unittest#Thanks)
***
@@ -25,12 +26,12 @@ In this article:
### Context
-At the time of writing, the major IDEs that support ASA ([VSCode](https://code.visualstudio.com/) and [Visual Studio](https://visualstudio.microsoft.com/vs/)) don't offer unit testing for it natively.
+At the time of writing, the major IDEs that support ASA ([VSCode](https://code.visualstudio.com/) and [Visual Studio](https://visualstudio.microsoft.com/vs/)) do not offer native unit testing capabilities.
This solution intends to fill that gap by enabling:
- fully local, repeatable executions over multiple test cases
-- automated evaluation of the resulting outputs against the expected ones
+- automated evaluation of the resulting outputs against expected ones
For that it leverages the **local testing with sample data** capabilities of either [VSCode](https://docs.microsoft.com/en-us/azure/stream-analytics/visual-studio-code-local-run) or [Visual Studio](https://docs.microsoft.com/en-us/azure/stream-analytics/stream-analytics-vs-tools-local-run), as unit testing should not rely on external services (no live input).
@@ -44,7 +45,7 @@ The whole thing is wired together in a **PowerShell** script based on a predefin
*[figure 1 - High level overview](https://github.com/Fleid/fleid.github.io/blob/master/_posts/202001_asa_unittest/ut_overview.png?raw=true)*
-This repository provides an **installation script**, in addition to the test script, to automate most of the setup. This installation script also allows automated executions in a continuous build pipeline such as **Azure DevOps Pipelines**.
+This repository provides an **installation script** (`New-AutProject`), in addition to the test script (`Start-AutRun`), to automate most of the setup. This installation script also allows automated executions in a continuous build pipeline such as **Azure DevOps Pipelines**.
Please note that this solution is currently available **only on Windows** as it depends on *Microsoft.Azure.StreamAnalytics.CICD*.
@@ -76,24 +77,38 @@ From there, the installation script will take care of the other dependencies (in
To be noted that those requirements are installed by default on every Azure DevOps Pipelines agents.
+### Fixture structure
+
+The scripts will expect the following folder structure to run properly:
+
+- **mySolutionFolder** <- *Potentially new top solution folder*
+ - **ASATest1** <- *Existing ASA project folder, containing the `.asaql` file and inputs folder*
+ - **ASATest1.Tests** <- *New folder for the test project*
+ - 1_arrange <- *New folder that will contain test cases*
+ - 2_act <- *New folder that will contain dependencies and scripts*
+ - 3_assert <- *New folder that will contain test run results*
+
+The step-by-step processes below explain how to set up this environment from scratch.
+
### Hello World
The following steps show how to download and run the solution with the included Hello World ASA project:
-1. Check all requirements are installed
-1. Clone/download this repository (it includes a basic ASA project `ASAHelloWorld` and a couple of pre-configured tests in *unittest\1_arrange* )
-1. **Only once** - execute the installer in the *unittest\2_act* folder: `Install-AutToolset.ps1`
- - Open a **Powershell** host (terminal, ISE...)
- - Navigate to `asa.unittest\unittest\2_act`
- - Run `.\Install-AutToolset.ps1 -solutionPath "C:\Users\florian\Repos\asa.unittest" -verbose` with the right `-solutionPath` (absolute paths)
+1. Check all the [requirements](https://github.com/Fleid/asa.unittest#Requirements) are installed
+1. Import the module from the PowerShell Gallery
+ - Open a **Powershell** host (terminal, ISE, VSCode...)
+ - Run `Install-Module -Name asa.unittest` to import the module from the [PowerShell Gallery](https://www.powershellgallery.com/packages/asa.unittest)
+1. Clone/download the [Examples folder](https://github.com/Fleid/asa.unittest/tree/master/examples) from this repository, save it to a convenient location (`C:\temp\examples` from now on)
+1. **Only once** - execute the installer: `New-AutProject`
+ - In the **Powershell** host
+ - Run `New-AutProject -installPath "C:\temp\examples\ASAHelloWorld.Tests" -verbose` with `installPath` the absolute path to the **test folder**, **not** the ASA project folder
- ![Screenshot of a terminal run of the installation script](https://github.com/Fleid/fleid.github.io/blob/master/_posts/202001_asa_unittest/ut_install_terminal.png?raw=true)
- In case of issues see [troubleshooting](https://github.com/Fleid/asa.unittest#Troubleshooting)
-1. Execute the test runner in the *unittest\2_act* folder: `Start-AutRun.ps1`
- - Open a **Powershell** host (terminal, ISE...)
- - Navigate to `asa.unittest\unittest\2_act`
- - Run `.\Start-AutRun.ps1 -asaProjectName "ASAHelloWorld" -solutionPath "C:\Users\florian\Repos\asa.unittest" -assertPath "C:\Users\florian\Repos\asa.unittest\unittest\3_assert"-verbose` with the right `-solutionPath` and `-assertPath` (absolute paths)
+1. Execute the test runner: `Start-AutRun`
+ - In the **Powershell** host
+ - Run `Start-AutRun -solutionPath "C:\temp\examples" -asaProjectName "ASAHelloWorld" -verbose` with `solutionPath` the absolute path to the folder containing both the ASA and the Test projects. `Start-AutRun` offers additional parameters that can be discovered via its help
- ![Screenshot of a terminal run of the installation script](https://github.com/Fleid/fleid.github.io/blob/master/_posts/202001_asa_unittest/ut_prun_terminal.png?raw=true)
- - Here it is expected that the test ends with 2 errors, in test case *003*
+ - Here it is expected that the test ends with 2 errors, in test case *003*. The folder `C:\temp\examples\ASAHelloWorld.Tests\3_assert` will contain the full trace of the run
- In case of issues see [troubleshooting](https://github.com/Fleid/asa.unittest#Troubleshooting)
### Installation
@@ -101,17 +116,17 @@ The following steps show how to download and run the solution with the included
The following steps show how to download and run the solution on an existing ASA project:
1. Check all requirements are installed
-1. If it doesn't exist, **create a solution folder** (simple top folder)
1. Prepare the ASA Project
+ - If it doesn't exist, **create a solution folder** (simple top folder, `C:\temp\examples` in the HelloWorld above)
- Copy or move the existing ASA project to the solution folder
- - ~~If the project was developed with VSCode (not necessary for Visual Studio), add an `.asaproj` file to the ASA project as explained below~~
- In ASA, add local inputs for every source used in the query (see [VSCode](https://docs.microsoft.com/en-us/azure/stream-analytics/visual-studio-code-local-run) / [Visual Studio](https://docs.microsoft.com/en-us/azure/stream-analytics/stream-analytics-vs-tools-local-run))
-1. Clone/download this repository, copy or move the *unittest* folder to the solution folder
-1. **Only once** - execute the installer in the *unittest\2_act* folder: `Install-AutToolset.ps1`
- - Open a **Powershell** host (terminal, ISE...)
- - Navigate to `unittest\2_act` in the solution folder
- - Run `.\Install-AutToolset.ps1 -solutionPath "C:\" -verbose` with the right `-solutionPath` (absolute paths)
+1. **Only once** - Import the module from the PowerShell Gallery
+ - Open a **Powershell** host (terminal, ISE, VSCode...)
+ - Run `Install-Module -Name asa.unittest` to import the module from the [PowerShell Gallery](https://www.powershellgallery.com/packages/asa.unittest)
- In case of issues see [troubleshooting](https://github.com/Fleid/asa.unittest#Troubleshooting)
+1. **Only once** - execute the installer: `New-AutProject`
+ - In the **Powershell** host
+ - Run `New-AutProject -installPath "MySolutionPath\MyTestFolder" -verbose` with `installPath` the absolute path to the **test folder**, **not** the ASA project folder. Usually the `MyTestFolder` sub-folder is of the form `MyAsaProject.Tests`
***
@@ -122,13 +137,12 @@ The following steps show how to download and run the solution on an existing ASA
Once the [installation](https://github.com/Fleid/asa.unittest#Installation) is done:
1. [Configure a test case](https://github.com/Fleid/asa.unittest#Configuring-a-test-case)
-1. Execute the test runner in the *unittest\2_act* folder: `Start-AutRun.ps1`
+1. Execute the test runner: `Start-AutRun`
- Open a **Powershell** host (terminal, ISE...)
- - Navigate to `unittest\2_act` in the solution folder
- - Run `.\Start-AutRun.ps1 -asaProjectName "" -solutionPath "C:\" -assertPath "C:\\unittest\3_assert"-verbose` with the right `-solutionPath` and `-assertPath` (absolute paths)
+ - Run `Start-AutRun -solutionPath "MySolutionPath" -asaProjectName "MyAsaProject" -verbose` with `solutionPath` the absolute path to the folder containing both the ASA and the Test projects. `Start-AutRun` offers additional parameters that can be discovered via its help
- In case of issues see **troubleshooting**
-The recommended way of running jobs is via a terminal window.
+For local development, the recommended way of running jobs is via a terminal window.
### Configuring a test case
@@ -185,11 +199,12 @@ Note that both scripts use default values for most parameters. These default val
The mandatory parameters are:
-- For the **installation script** (`Install-AutToolset`)
- - `$unittestFolder` if it's not the default (`unittest`)
+- For the **installation script** (`New-AutProject`)
+ - `$installPath`, the folder containing the test fixture
- For the **test runner** (`Start-AutRun`)
- - `$asaProjectName`
- - `$unittestFolder` if it's not the default (`unittest`)
+ - `$asaProjectName`
+ - `$unittestFolder` is defaulted to `$asaProjectName.Tests`
+ - `$solutionPath` is defaulted to `$ENV:BUILD_SOURCESDIRECTORY`
### Troubleshooting
@@ -211,19 +226,16 @@ PowerShell [remoting for jobs](https://docs.microsoft.com/en-us/powershell/modul
## Internal details
-**(This needs to be updated to reflect the new parallel run workflow)**
-
-![figure 2 - Detailed overview](https://github.com/Fleid/fleid.github.io/blob/master/_posts/202001_asa_unittest/ut_overviewFull.png?raw=true)
-
-*[figure 2 - Detailed overview](https://github.com/Fleid/fleid.github.io/blob/master/_posts/202001_asa_unittest/ut_overviewFull.png?raw=true)*
-
### Change Log
-- February 2020 :
+- March 2020 :
+ - Full refactoring to allow publication in the PowerShell Gallery
+ - Complete unit test coverage (Pester) and linting
+- February 2020 :
- Automated the generation of the XML asaproj file (required by sa.exe) from the JSON one (for VSCode project)
- Renamed scripts to follow PowerShell standards, with backward compatibility
-### Scenario and components
+### Components
This solution uses the following components:
@@ -233,21 +245,14 @@ This solution uses the following components:
- the [npm CLI](https://docs.npmjs.com/cli-documentation/) to install the package above, available with [Node.JS](https://nodejs.org/en/download/)
- [PowerShell](https://github.com/PowerShell/PowerShell/releases) as the shell to run intermediary tasks and execute the required commands
-These components are used in a script as follow:
-
-![figure 1 - Schema of the unit testing setup - it is detailed below](https://github.com/Fleid/fleid.github.io/blob/master/_posts/202001_asa_unittest/ut_solution.png?raw=true)
-
-The script will expect the following folder structure to run properly:
-
-- **mySolutionFolder** <- *Potentially new top solution folder*
- - **ASATest1** <- *Existing ASA project folder, containing the `.asaql` file and inputs folder*
- - **unittest** <- *New folder for the test project*
- - 1_arrange <- *New folder that will contain test cases*
- - 2_act <- *New folder that will contain dependencies and scripts*
- - 3_assert <- *New folder that will contain test run results*
-
### Shortcomings
- Slow execution
- No integration to the usual IDEs
- No cross-platform options
+
+### Thanks
+
+- [Kevin Marquette](https://twitter.com/KevinMarquette) for his [invaluable](https://powershellexplained.com/2017-01-21-powershell-module-continious-delivery-pipeline/) blog on PowerShell
+- [jsondiffpatch](https://github.com/benjamine/JsonDiffPatch) : Diff & patch JavaScript objects
+- [Tabler icons](https://github.com/tabler/tabler-icons) : A set of over 400 free MIT-licensed high-quality SVG icons for you to use in your web projects
diff --git a/asa.unittest.tests/Get-AutFieldFromFileInfo.Tests.ps1 b/asa.unittest.tests/Get-AutFieldFromFileInfo.Tests.ps1
new file mode 100644
index 0000000..8fc062b
--- /dev/null
+++ b/asa.unittest.tests/Get-AutFieldFromFileInfo.Tests.ps1
@@ -0,0 +1,98 @@
+### If tests are in ModuleRoot\Whatever, and scripts reachable via a module at ModuleRoot\Module\Module.psm1
+
+$projectRoot = Resolve-Path "$PSScriptRoot\.." #ModuleRoot\Whatever becomes \ModuleRoot with ..
+$moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psd1") #Look for the manifest to get to ModuleRoot\Module
+$moduleName = Split-Path $moduleRoot -Leaf #Extract the module name from above
+Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force
+
+#############################################################################################################
+# Invoke-Pester .\Get-AutFieldFromFileInfo.Tests.ps1 -CodeCoverage .\..\asa.unittest\private\Get-AutFieldFromFileInfo.ps1
+
+
+Describe "Get-AutFieldFromFileInfo parameters" {
+ InModuleScope $moduleName {
+ function GetFullPath {
+ Param(
+ [string] $Path
+ )
+ return $Path.Replace('TestDrive:', (Get-PSDrive TestDrive).Root)
+ }
+
+
+ $t_testPath = "TestDrive:\users\fleide\Repos\asa.unittest\examples\ASAHelloWorld.Tests\1_arrange"
+ New-Item -Path $t_testPath -ItemType Directory
+ New-item -Path $t_testPath -ItemType File -Name "001~Input~hwsource~nominal.csv"
+ New-item -Path $t_testPath -ItemType File -Name "001~Output~outputall.json"
+
+ $t_thisFileInfo = (Get-ChildItem -Path $t_testPath -File)
+ $t_separatorOfFields = "~"
+ $t_numberOfFields = 4
+
+ $Output_1 = New-Object PSObject -Property @{
+ FullName="001~Input~hwsource~nominal.csv"
+ FilePath= GetFullPath("TestDrive:\Users\fleide\Repos\asa.unittest\examples\ASAHelloWorld.Tests\1_arrange\001~Input~hwsource~nominal.csv")
+ Basename= "001~Input~hwsource~nominal"
+ basename0="001"
+ basename1="Input"
+ basename2="hwsource"
+ basename3="nominal"
+ }
+
+ $Output_2 = New-Object PSObject -Property @{
+ FullName="001~Output~outputall.json"
+ FilePath= GetFullPath("TestDrive:\Users\fleide\Repos\asa.unittest\examples\ASAHelloWorld.Tests\1_arrange\001~Output~outputall.json")
+ Basename= "001~Output~outputall"
+ basename0="001"
+ basename1="Output"
+ basename2="outputall"
+ }
+
+ #Mock Test-Path {return $true}
+
+ It "runs with a valid set of parameters (output1)" {
+ $actual = Get-AutFieldFromFileInfo `
+ -t $t_thisFileInfo[0] `
+ -s $t_separatorOfFields `
+ -n $t_numberOfFields
+
+ $t = $true
+
+ foreach($actual_properties in $actual.PSObject.Properties)
+ {
+ $t = $t -and ($Output_1.($actual_properties.Name) -eq $actual_properties.value)
+ }
+
+ $t | Should -be $true
+ }
+
+ $t_numberOfFields = 3
+ It "runs with a valid set of parameters (output2)" {
+ $actual = Get-AutFieldFromFileInfo `
+ -t $t_thisFileInfo[1] `
+ -s $t_separatorOfFields `
+ -n $t_numberOfFields
+
+ $t = $true
+
+ foreach($actual_properties in $actual.PSObject.Properties)
+ {
+ $t = $t -and ($Output_2.($actual_properties.Name) -eq $actual_properties.value)
+ }
+
+ $t | Should -be $true
+ }
+
+ It "runs from the pipeline" {
+ $actual = $t_thisFileInfo[0] | Get-AutFieldFromFileInfo -s "~" -n 4
+
+ $t = $true
+
+ foreach($actual_properties in $actual.PSObject.Properties)
+ {
+ $t = $t -and ($Output_1.($actual_properties.Name) -eq $actual_properties.value)
+ }
+
+ $t | Should -be $true
+ }
+ }
+}
diff --git a/asa.unittest.tests/Get-AutRunResult.Tests.ps1 b/asa.unittest.tests/Get-AutRunResult.Tests.ps1
new file mode 100644
index 0000000..535e870
--- /dev/null
+++ b/asa.unittest.tests/Get-AutRunResult.Tests.ps1
@@ -0,0 +1,305 @@
+### If tests are in ModuleRoot\Whatever, and scripts reachable via a module at ModuleRoot\Module\Module.psm1
+
+$projectRoot = Resolve-Path "$PSScriptRoot\.." #ModuleRoot\Whatever becomes \ModuleRoot with ..
+$moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psd1") #Look for the manifest to get to ModuleRoot\Module
+$moduleName = Split-Path $moduleRoot -Leaf #Extract the module name from above
+Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force
+
+#############################################################################################################
+# Invoke-Pester .\Get-AutRunResult.Tests.ps1 -CodeCoverage .\..\asa.unittest\private\Get-AutRunResult.ps1
+
+
+Describe "Get-AutRunResult Nominal" {
+ InModuleScope $moduleName {
+
+ $t_solutionPath = "TestDrive:\foo"
+ $t_asaProjectName = "bar"
+ $t_unittestFolder = "bar.Tests"
+ $t_testID = "yyyymmddhhmmss"
+ $t_testCase = "123"
+
+ Mock Test-Path {return $true}
+ Mock Get-ChildItem {return 1}
+ Mock Get-AutFieldFromFileInfo {}
+ Mock Get-Content {return (@{FilePath="foobar"} | ConvertTo-Json)}
+ Mock Add-Content {}
+ Mock jsondiffpatch {}
+ Mock Out-File {}
+
+ It "tries to get a list of files" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase | Out-Null
+
+ Assert-MockCalled Get-ChildItem -Times 1 -Exactly -Scope It
+ }
+
+ Mock Get-AutFieldFromFileInfo {}
+ It "tries nothing if it gets nothing" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase | Out-Null
+
+ Assert-MockCalled Out-File -Times 0 -Exactly -Scope It
+ }
+
+ Mock Get-ChildItem {return 1}
+ It "calls Get-AutFieldFromFileInfo if find files" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase | Out-Null
+
+ Assert-MockCalled Get-AutFieldFromFileInfo -Times 1 -Exactly -Scope It
+ }
+
+ Mock Get-AutFieldFromFileInfo {return @(`
+ @{Basename0="003";FilePath="foobar";Basename1="Output";Basename2="fb3"},`
+ @{Basename0="001";FilePath="foobar1";Basename1="Output";Basename2="fb11"},`
+ @{Basename0="001";FilePath="foobar2";Basename1="Output";Basename2="fb12"},`
+ @{Basename0="002";FilePath="foobar";Basename1="Output";Basename2="fb2"}`
+ )}
+ It "Generates N testable files for N output files" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase | Out-Null
+
+ Assert-MockCalled Get-Content -Times 4 -Exactly -Scope It
+ Assert-MockCalled Add-Content -Times 4 -Exactly -Scope It
+ }
+
+ Mock Get-AutFieldFromFileInfo {return @(`
+ @{Basename0="003";FilePath="foobar";Basename1="Output";Basename2="fb3"},`
+ @{Basename0="001";FilePath="foobar1";Basename1="Output";Basename2="fb11"},`
+ @{Basename0="001";FilePath="foobar2";Basename1="Output";Basename2="fb12"},`
+ @{Basename0="002";FilePath="foobar";Basename1="Output";Basename2="fb2"}`
+ )}
+ Mock jsondiffpatch {return $null}
+ It "Tests and generates N result files for N output files" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase | Out-Null
+
+ Assert-MockCalled jsondiffpatch -Times 4 -Exactly -Scope It
+ Assert-MockCalled Out-File -Times 4 -Exactly -Scope It
+ }
+
+ Mock Get-AutFieldFromFileInfo {return @(`
+ @{Basename0="003";FilePath="foobar";Basename1="Output";Basename2="fb3"},`
+ @{Basename0="001";FilePath="foobar1";Basename1="Output";Basename2="fb11"},`
+ @{Basename0="001";FilePath="foobar2";Basename1="Output";Basename2="fb12"},`
+ @{Basename0="001";FilePath="foobar3";Basename1="Output";Basename2="fb13"},`
+ @{Basename0="002";FilePath="foobar";Basename1="Output";Basename2="fb2"}`
+ )}
+ Mock jsondiffpatch {return "a"}
+ It "returns N for N errors" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase |
+ Should -be 5
+ }
+ }
+}
+
+Describe "New-AutRunFixture empty folders" {
+ InModuleScope $moduleName {
+
+ $t_solutionPath = "TestDrive:\foo"
+ $t_asaProjectName = "bar"
+ $t_unittestFolder = "bar.Tests"
+ $t_testID = "yyyymmddhhmmss"
+ $t_testCase = "123"
+
+ Mock Test-Path {return $true}
+ Mock Get-ChildItem {}
+ Mock Get-AutFieldFromFileInfo {}
+ Mock Get-Content {}
+ Mock Add-Content {}
+ Mock jsondiffpatch {}
+ Mock Out-File {}
+
+ It "provides 0 error in output pipeline on an empty folder" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase |
+ Should -be 0
+ }
+
+ It "generates no file on an empty folder" {
+ Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase | Out-Null
+
+ Assert-MockCalled Out-File -Times 0 -Exactly -Scope It
+ }
+ }
+}
+
+
+Describe "Get-AutRunResult parameters" {
+ InModuleScope $moduleName {
+
+ $t_solutionPath = "TestDrive:\foo"
+ $t_asaProjectName = "bar"
+ $t_unittestFolder = "bar.Tests"
+ $t_testID = "yyyymmddhhmmss"
+ $t_testCase = "123"
+
+ Mock Test-Path {return $true}
+ Mock Get-ChildItem {return 1}
+ Mock Get-AutFieldFromFileInfo {}
+ Mock Get-Content {return (@{FilePath="foobar"} | ConvertTo-Json)}
+ Mock Add-Content {}
+ Mock jsondiffpatch {}
+ Mock Out-File {}
+
+ It "runs with a valid set of parameters" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -not -throw "-* is required"
+ }
+
+ It "fails without -solutionPath" {
+ { Get-AutRunResult `
+ #-solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -throw "-solutionPath is required"
+ }
+
+ It "fails without -asaProjectName" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ #-asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -throw "-asaProjectName is required"
+ }
+
+ It "fails without -unittestFolder" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ #-unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -throw "-unittestFolder is required"
+ }
+
+ It "fails without -testID" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ #-testID $t_testID `
+ -testCase $t_testCase } |
+ Should -throw "-testID is required"
+ }
+
+ It "fails without -testCase" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ #-testCase $t_testCase
+ } |
+ Should -throw "-testCase is required"
+ }
+ }
+}
+
+Describe "Get-AutRunResult paths" {
+ InModuleScope $moduleName {
+
+ $t_solutionPath = "TestDrive:\foo"
+ $t_asaProjectName = "bar"
+ $t_unittestFolder = "bar.Tests"
+ $t_testID = "yyyymmddhhmmss"
+ $t_testCase = "123"
+
+ Mock Test-Path {return $true}
+ Mock Get-ChildItem {return 1}
+ Mock Get-AutFieldFromFileInfo {}
+ Mock Get-Content {return (@{FilePath="foobar"} | ConvertTo-Json)}
+ Mock Add-Content {}
+ Mock jsondiffpatch {}
+ Mock Out-File {}
+
+ Mock Test-Path {return $true}
+ It "runs with a valid set of paths" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -not -throw
+ }
+
+ Mock Test-Path {return $false}
+ It "fails when solutionPath is not a valid path" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -throw "$t_solutionPath is not a valid path"
+ }
+
+ $t_testPath = "$t_solutionPath\$t_unittestFolder\3_assert\$t_testID\$t_testCase"
+ Mock Test-Path {return $true} -ParameterFilter {$path -eq $t_solutionPath}
+ It "fails when testPath is not a valid path" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -throw "$t_testPath is not a valid path"
+ }
+
+ $t_outputSourcePath = "$t_testPath\$t_asaProjectName\Inputs"
+ Mock Test-Path {return $true} -ParameterFilter {$path -eq $t_testPath}
+ It "fails when outputSourcePath is not a valid path" {
+ { Get-AutRunResult `
+ -solutionPath $t_solutionPath `
+ -asaProjectName $t_asaProjectName `
+ -unittestFolder $t_unittestFolder `
+ -testID $t_testID `
+ -testCase $t_testCase } |
+ Should -throw "$t_outputSourcePath is not a valid path"
+ }
+ }
+}
diff --git a/asa.unittest.tests/Install-AutToolset.Tests.ps1 b/asa.unittest.tests/Install-AutToolset.Tests.ps1
new file mode 100644
index 0000000..09f277a
--- /dev/null
+++ b/asa.unittest.tests/Install-AutToolset.Tests.ps1
@@ -0,0 +1,165 @@
+### If tests are in ModuleRoot\Whatever, and scripts reachable via a module at ModuleRoot\Module\Module.psm1
+
+$projectRoot = Resolve-Path "$PSScriptRoot\.." #ModuleRoot\Whatever becomes \ModuleRoot with ..
+$moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psd1") #Look for the manifest to get to ModuleRoot\Module
+$moduleName = Split-Path $moduleRoot -Leaf #Extract the module name from above
+Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force
+
+#############################################################################################################
+# Invoke-Pester .\Install-AutToolset.Tests.ps1 -CodeCoverage .\..\asa.unittest\private\Install-AutToolset.ps1
+
+Describe "Install-AutToolset paramater installPath" {
+ InModuleScope $moduleName {
+
+ $t_installPath = "foo"
+
+ Mock Test-Path {return $true} -ParameterFilter {$Path -eq $t_installPath}
+ Mock Test-Path {return $true} -ParameterFilter { $PathType -and $PathType -eq "Leaf" }
+ Mock New-Item {}
+ Mock Invoke-WebRequest {}
+ Mock Invoke-External {} -ParameterFilter {$LiteralPath -like "*nuget*"}
+ Mock Invoke-External {} -ParameterFilter {$LiteralPath -eq "npm"}
+
+ It "fails if installPath is missing" {
+ { Install-AutToolset } |
+ Should -throw "-installPath is required"
+ }
+
+ It "doesn't create a folder if it exists" {
+ Install-AutToolset -installPath $t_installPath |
+ Assert-MockCalled New-Item -Times 0 -Exactly -Scope It -ParameterFilter {$Path -eq $t_installPath}
+ }
+
+ Mock Test-Path {return $false} -ParameterFilter {$Path -eq $t_installPath}
+ It "does create a folder it doesn't" {
+ Install-AutToolset -installPath $t_installPath |
+ Assert-MockCalled New-Item -Times 1 -Exactly -Scope It -ParameterFilter {$Path -eq $t_installPath}
+ }
+
+ }
+}
+
+Describe "Install-AutToolset behavior nuget" {
+ InModuleScope $moduleName {
+
+ $t_installPath = "foo"
+ $t_nugetPackages = "bar"
+
+ # The test folder exists
+ Mock Test-Path {return $true} -ParameterFilter { $Path -and $Path -eq $t_installPath }
+ # Nuget.exe is not there
+ Mock Test-Path {return $false} -ParameterFilter { $PathType -and $PathType -eq "Leaf" }
+ Mock New-Item {}
+ Mock Invoke-WebRequest {} #Nuget download
+ Mock Invoke-External {}
+
+ It "does not download nuget on default parameter" {
+ Install-AutToolset -installPath $t_installPath |
+ Assert-MockCalled Invoke-WebRequest -Times 0 -Exactly -Scope It
+ }
+
+ $t_nugetPackages = "bar"
+ It "does download nuget once for 1 package" {
+ Install-AutToolset -installPath $t_installPath -nugetPackages $t_nugetPackages |
+ Assert-MockCalled Invoke-WebRequest -Times 1 -Exactly -Scope It
+ }
+
+ $t_nugetPackages = "bar1","bar2"
+ It "does download nuget once for N packages" {
+ Install-AutToolset -installPath $t_installPath -nugetPackages $t_nugetPackages |
+ Assert-MockCalled Invoke-WebRequest -Times 1 -Exactly -Scope It
+ }
+
+ $t_nugetPackages = "bar"
+ # Nuget.exe is already there
+ Mock Test-Path {return $true} -ParameterFilter { $PathType -and $PathType -eq "Leaf" }
+ It "does not download nuget when needed but already there" {
+ Install-AutToolset -installPath $t_installPath -nugetPackages $t_nugetPackages |
+ Assert-MockCalled Invoke-WebRequest -Times 0 -Exactly -Scope It
+ }
+
+ $t_nugetPackages =
+ It "does not invoke nuget on default parameter" {
+ Install-AutToolset -installPath $t_installPath |
+ Assert-MockCalled Invoke-External -Times 0 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "$t_installPath\nuget*" }
+ }
+
+ $t_nugetPackages = "bar"
+ It "does invoke nuget once for 1 package" {
+ Install-AutToolset -installPath $t_installPath -nugetPackages $t_nugetPackages |
+ Assert-MockCalled Invoke-External -Times 1 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "$t_installPath\nuget*" }
+ }
+
+ $t_nugetPackages = "bar1","bar2"
+ It "does invoke nuget N times for N packages (N=2)" {
+ Install-AutToolset -installPath $t_installPath -nugetPackages $t_nugetPackages |
+ Assert-MockCalled Invoke-External -Times 2 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "$t_installPath\nuget*" }
+ }
+
+
+ }
+}
+
+Describe "Install-AutToolset npm" {
+ InModuleScope $moduleName {
+
+ $t_installPath = "foo"
+
+ # The test folder exists
+ Mock Test-Path {return $true} -ParameterFilter { $Path -and $Path -eq $t_installPath }
+ Mock New-Item {}
+ Mock Invoke-WebRequest {} #Nuget download
+ Mock Invoke-External {}
+
+ It "does not invoke npm on default parameter" {
+ Install-AutToolset -installPath $t_installPath |
+ Assert-MockCalled Invoke-External -Times 0 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "npm*" }
+ }
+
+ $t_npmPackages = "bar"
+ It "does invoke npm once for 1 package" {
+ Install-AutToolset -installPath $t_installPath -npmPackages $t_npmPackages |
+ Assert-MockCalled Invoke-External -Times 1 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "npm*" }
+ }
+
+ $t_npmPackages = "bar1","bar2"
+ It "does invoke npm N times for N packages (N=2)" {
+ Install-AutToolset -installPath $t_installPath -npmPackages $t_npmPackages |
+ Assert-MockCalled Invoke-External -Times 2 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "npm*" }
+ }
+
+ Mock Invoke-External {Throw "npm: The term 'npm' is not"} -ParameterFilter { $LiteralPath -like "npm*" }
+ $t_npmPackages = "bar"
+ It "fails if npm is not installed" {
+ {Install-AutToolset -installPath $t_installPath -npmPackages $t_npmPackages} |
+ Should -throw "npm: The term 'npm' is not"
+ }
+ Mock Invoke-External {} #Nuget or npm executions
+ }
+}
+
+Describe "Install-AutToolset nuget + npm" {
+ InModuleScope $moduleName {
+
+ $installPath = "foo"
+
+ # The test folder exists
+ Mock Test-Path {return $true} -ParameterFilter { $Path -and $Path -eq $installPath }
+ # Nuget.exe is not there
+ Mock Test-Path {return $false} -ParameterFilter { $PathType -and $PathType -eq "Leaf" }
+ Mock New-Item {}
+ Mock Invoke-WebRequest {} #Nuget download
+ Mock Invoke-External {}
+
+ It "does invoke nuget for nuget + npm" {
+ Install-AutToolset -installPath $installPath -nugetPackages "bar1","bar2" -npmPackages "bar1","bar2" |
+ Assert-MockCalled Invoke-External -Times 2 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "*nuget*" }
+ }
+
+ It "does invoke npm for nuget + npm" {
+ Install-AutToolset -installPath $installPath -nugetPackages "bar1","bar2" -npmPackages "bar1","bar2" |
+ Assert-MockCalled Invoke-External -Times 2 -Exactly -Scope It -ParameterFilter { $LiteralPath -like "npm*" }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/asa.unittest.tests/New-AutAsaprojXML.Tests.ps1 b/asa.unittest.tests/New-AutAsaprojXML.Tests.ps1
new file mode 100644
index 0000000..b526aef
--- /dev/null
+++ b/asa.unittest.tests/New-AutAsaprojXML.Tests.ps1
@@ -0,0 +1,135 @@
+### If tests are in ModuleRoot\Whatever, and scripts reachable via a module at ModuleRoot\Module\Module.psm1
+
+$projectRoot = Resolve-Path "$PSScriptRoot\.." #ModuleRoot\Whatever becomes \ModuleRoot with ..
+$moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psd1") #Look for the manifest to get to ModuleRoot\Module
+$moduleName = Split-Path $moduleRoot -Leaf #Extract the module name from above
+Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force
+
+#############################################################################################################
+# Invoke-Pester .\New-AutAsaprojXML.Tests.ps1 -CodeCoverage .\..\asa.unittest\public\New-AutAsaprojXML.ps1
+
+
+Describe "New-AutAsaprojXML Nominal" {
+
+ $sourceAsaprojMockJSON = "{
+ `"name`": `"asaproject`",
+ `"startFile`": `"asaproject.asaql`",
+ `"configurations`": [
+ {
+ `"filePath`": `"Inputs\\Local_input.json`",
+ `"subType`": `"InputMock`"
+ },
+ {
+ `"filePath`": `"Inputs\\Local_input2.json`",
+ `"subType`": `"InputMock`"
+ },
+ {
+ `"filePath`": `"JobConfig.json`",
+ `"subType`": `"JobConfig`"
+ }
+ ]
+ }"
+ $sourceAsaprojMock = $sourceAsaprojMockJSON | ConvertFrom-Json
+
+ $outputXMLstring ="
+
+