$ErrorActionPreference = 'Stop';
Set-StrictMode -Version '2.0';

$currentScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path

Import-Module -Force "$currentScriptDir\..\..\modules\Uninstall"

Describe "when I create an uninstall entry" {
	$testRegPath = 'HKCU:\Software\Flexera Software\ScaffoldTests'

	Mock -CommandName 'Get-UninstallKeyPath' { $testRegPath }.GetNewClosure() -ModuleName 'Uninstall'

	BeforeEach {
		if (-not (Test-Path $testRegPath)) {
			New-Item -Path $testRegPath -Force | Out-Null;
		}
	}

	AfterEach {
		Remove-Item $testRegPath -Force -Recurse
	}

	function Get-TestUninstallKeyPropertyValue([string]$PropertyName, [string]$UninstallId = 'UninstallId') {
		# this will throw if the path doesn't exist
		return Get-ItemPropertyValue -Path (Join-Path $testRegPath $UninstallId) -Name $PropertyName
	}

	It "fails if the UninstallId parameter is null" {
		{ New-UninstallKey -UninstallId $null -DisplayName 'foo' -ScriptPath 'script' -Verbose } |
			Should Throw 'Cannot validate argument on parameter ''UninstallId''. The argument is null or empty.'
	}

	It "creates the uninstall registry key in the base path" {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'display' -ScriptPath 'script' -Verbose

		(Test-Path (Join-Path $testRegPath 'UninstallID')) | Should Be $true
	}

	It "fails if DisplayName is null" {
		{ New-UninstallKey -UninstallId 'UninstallID' -DisplayName $null -ScriptPath 'script' -Verbose } |
			Should Throw 'Cannot validate argument on parameter ''DisplayName''. The argument is null or empty.'
	}

	It "sets the DisplayName property to the supplied value" {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -Verbose

		Get-TestUninstallKeyPropertyValue 'DisplayName' | Should Be 'ExpectedValue'
	}

	It 'fails if the script path is null' {
		{ New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath $null -Verbose } |
			Should Throw 'Cannot validate argument on parameter ''ScriptPath''. The argument is null or empty.'
	}

	It 'sets the SourceLocation correctly based on the supplied script path' {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'c:\foo\script' -Verbose

		Get-TestUninstallKeyPropertyValue 'InstallSource' | Should BeExactly 'c:\foo'
	}

	It 'sets the UninstallString property using the supplied script path' {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -Verbose

		Get-TestUninstallKeyPropertyValue 'UninstallString' | Should BeLike '* script *'
	}

	It 'uses the default uninstall arguments if none are supplied' {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -Verbose

		Get-TestUninstallKeyPropertyValue 'UninstallString' | Should BeLike '* script -Uninstall "UninstallID"'
	}

	It 'uses supplied uninstall arguments when supplied' {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -UninstallArgs '|expected args|' -Verbose

		Get-TestUninstallKeyPropertyValue 'UninstallString' | Should BeLike '* script |expected args| "UninstallID"'
	}

	It 'invokes the uninstall script with Powershell correctly' {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -UninstallArgs '|expected args|' -Verbose

		Get-TestUninstallKeyPropertyValue 'UninstallString' | Should BeLike 'powershell -NonInteractive -File *'
	}

	It 'fails if the supplied uninstall arguments are null' {
		{ New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -UninstallArgs $null -Verbose } |
			Should Throw 'Cannot validate argument on parameter ''UninstallArgs''. The argument is null or empty.'
	}
}

Describe 'when I remove an uninstall entry' {
	$testRegPath = 'HKCU:\Software\Flexera Software\ScaffoldTests'

	Mock -CommandName 'Get-UninstallKeyPath' { $testRegPath }.GetNewClosure() -ModuleName 'Uninstall'

	BeforeEach {
		if (-not (Test-Path $testRegPath)) {
			New-Item -Path $testRegPath -Force | Out-Null;
		}
	}

	AfterEach {
		Remove-Item $testRegPath -Force -Recurse
	}

	It 'removes the installed entry' {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -Verbose
		
		(Test-Path (Join-Path $testRegPath 'UninstallID')) | Should Be $true

		Remove-UninstallKey -UninstallId 'UninstallID'

		(Test-Path (Join-Path $testRegPath 'UninstallID')) | Should Be $false
	}
	It 'leaves others behind' {
		New-UninstallKey -UninstallId 'UninstallID' -DisplayName 'ExpectedValue' -ScriptPath 'script' -Verbose
		New-UninstallKey -UninstallId 'UninstallID2' -DisplayName 'ExpectedValue' -ScriptPath 'script' -Verbose

		@(Get-ChildItem $testRegPath).Count | Should Be 2
		
		Remove-UninstallKey -UninstallId 'UninstallID'

		@(Get-ChildItem $testRegPath).Count | Should Be 1
	}
}


InModuleScope -ModuleName 'Uninstall' {

	## tests for functions that are not exported by the module.

	Describe 'when I convert a string into its PowerShell literal equivalent' {
		It 'escapes the <name> character' {
			param([string]$name, [string]$val, [string]$res)
			Get-PowershellLiteralString $val | Should BeExactly $res
		} -TestCases @( 
			@{ name = 'nul'; val = "`0"; res = '"`0"'; },
			@{ name = 'bel'; val = "`a"; res = '"`a"'; },
			@{ name = 'bspc'; val = "`b"; res = '"`b"'; },
			@{ name = 'tab'; val = "`t"; res = '"`t"'; },
			@{ name = 'nl'; val = "`n"; res = '"`n"'; },
			@{ name = 'vtab'; val = "`v"; res = '"`v"'; },
			@{ name = 'ff'; val = "`f"; res = '"`f"'; },
			@{ name = 'cr'; val = "`r"; res = '"`r"'; },
			@{ name = '"'; val = '"'; res = '"`""'; },
			@{ name = '$'; val = '$'; res = '"`$"'; },
			@{ name = '`'; val = '`'; res = '"``"'; }
		)

		It 'escapes a character in the middle of a string' {
			Get-PowershellLiteralString 'foo$bar' | Should BeExactly '"foo`$bar"'
		}
		It 'escapes all special characters in a string' {
			Get-PowershellLiteralString '$$$``' | Should BeExactly '"`$`$`$````"'
		}
		It 'escapes special characters at the beginning of the string' {
			Get-PowershellLiteralString '$$$``' | Should BeExactly '"`$`$`$````"'
		}
		It 'escapes only special characters' {
			Get-PowershellLiteralString 'f`o$o$b$a$r' | Should BeExactly '"f``o`$o`$b`$a`$r"'
		}
		It 'does not change a string with no special characters' {
			Get-PowershellLiteralString 'foobar' | Should BeExactly '"foobar"'
		}
		It 'returns literal $null for null input' {
			Get-PowershellLiteralString $null | Should BeExactly '$null'
		}
		It 'handles the empty string' {
			Get-PowershellLiteralString '' | Should BeExactly '""'
		}
	}
} # end module scope

