Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Milestone/1 #14

Merged
merged 13 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## v1.0.0

- Fix: `certficate` typo in config parser.
- Improvement: Invoke-HttpUnit pipeline input.
- Feature: Add ServerCertificate property to the TestResult object.
- Feature: Add TCP connection test.
- Feature: Add a type format file for `TestResult`

## v0.6.0

- Adds IPs functionality
Expand Down
45 changes: 25 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,24 @@ It also provides easy access to the Windows Certificate store for client certifi

[Full Docs](docs)

### TOML
### Config File

This configuration file is targeting compatibility with the original [httpunit file format](https://github.com/StackExchange/httpunit/tree/master#toml) but is partially implemented.
[TOML](https://github.com/toml-lang/toml), YAML, JSON, and PSD1 formats are supported for the config file.

The [toml file](https://github.com/toml-lang/toml) has two sections:
Each `plan` can have:

- `Plan` is a list of test plans.
- `IPs` are a table of search and replace regexes. **_Not implemented_**
- `label` A label for documentation purposes.
- `url` The URL to retrieve.
- `ips` For http/https, a list of IPs to send the URL to. Default is "use DNS". Otherwise the connection is made to the IP address listed, ignoring DNS. Pass `'*'` to test all resolved addresses.
- `code` For http/https, the expected status code, default 200.
- `string` For http/https, a string we expect to find in the result.
- `timeout` An optional timeout for the test in the format `"hh:mm:ss"`. Default is 3 seconds.
- `certificate` For http/https, a path to a certificate in the Windows Store to pass as a Client Certificate. If just a Thumbprint is provided, it will look in `cert:\LocalMachine\My`.
- `tags` An optional list of tags for the test. Used for when you want to only run a subset of tests with the `-tags` flag.
- `insecureSkipVerify` Will allow testing of untrusted or self-signed certificates.
- `plan.headers` For http/https, a list of keys and values to validate the response headers.

Each `[[plan]]` lists:

- `label =` A label for documentation purposes. It must be unique.
- `url =` The URL to retrieve.
- `ips =` For http/https, a list of IPs to send the URL to. Default is "use DNS". Otherwise the connection is made to the IP address listed, ignoring DNS. Pass `'*'` to test all resolved addresses.
- `code =` For http/https, the expected status code, default 200.
- `string =` For http/https, a string we expect to find in the result.
- `regex =` For http/https, a regular expression we expect to match in the result. **_Not implemented_**
- `timeout =` An optional timeout for the test in the format `"hh:mm:ss"`. Default is 3 seconds.
- `certificate =` For http/https, a path to a certificate in the Windows Store to pass as a Client Certificate. If just a Thumbprint is provided, it will look in `cert:\LocalMachine\My`.
- `tags =` An optional set of tags for the test. Used for when you want to only run a subset of tests with the `-tags` flag **_Not implemented_**
- `insecureSkipVerify = true` Will allow testing of untrusted or self-signed certificates.
- `[plan.headers]` For http/https, a list of keys and values to validate the response headers.

#### A sample config file
#### A sample TOML config file

```toml
[[plan]]
Expand All @@ -62,6 +56,17 @@ Each `[[plan]]` lists:
Server = "gws"
```

#### A sample YAML config file

```yaml
Plan:
- code: 200
label: google
timeout: "0:0:10"
url: https://www.google.com
tags: [run]
```

### Test-SSLCertificate

The SSLCertificate commands may be moved to a separate module in the future.
Expand Down
2 changes: 2 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ $manifest = @{
CmdletsToExport = ''
VariablesToExport = ''
AliasesToExport = @('httpunit', 'Test-Http', 'ihu')
FormatsToProcess = 'httpunitPS.format.ps1xml'
}

function Clean {
Expand Down Expand Up @@ -95,6 +96,7 @@ function Build {
New-Item -Path $publish -ItemType Directory -ErrorAction SilentlyContinue | Out-Null

Copy-Item -Path "$src/$module.psm1" -Destination $publish
Copy-Item -Path "$src/$module.format.ps1xml" -Destination $publish
Copy-Item -Path @("$parent/LICENSE", "$parent/README.md") -Destination $publish -ErrorAction SilentlyContinue

$publicFunctions = Get-ChildItem -Path "$src/public/*.ps1"
Expand Down
51 changes: 13 additions & 38 deletions docs/Invoke-HttpUnit.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This is not a 100% accurate port of httpunit. The goal of this module is to util

### Parameter Set 2

- `[String]` **Path** _Specifies a path to a configuration file with a list of tests. Supported types are .toml, .yml, and .psd1._ Mandatory, ValueFromPipeline
- `[String[]]` **Path** _Specifies a path to a configuration file with a list of tests. Supported types are .toml, .yml, json, and .psd1._ Mandatory, ValueFromPipeline
- `[String[]]` **Tag** _If specified, only runs plans that are tagged with one of the tags specified._
- `[Switch]` **Quiet** _Do not output ErrorRecords for failed tests._

Expand All @@ -30,49 +30,22 @@ Run an ad-hoc test against one Url.

```powershell
Invoke-HttpUnit -Url https://www.google.com -Code 200
Label : https://www.google.com/
Result :
Connected : True
GotCode : True
GotText : False
GotRegex : False
GotHeaders : False
InvalidCert : False
TimeTotal : 00:00:00.4695217
Label Result Connected GotCode GotText GotHeaders InvalidCert TimeTotal
----- ------ --------- ------- ------- ---------- ----------- ---------
https://www.google.com/ (142.250.190.132) True True False False False 00:00:00.2840173
```
### Example 2

Run all of the tests in a given config file.

```powershell
Invoke-HttpUnit -Path .\example.toml
Label : google
Result :
Connected : True
GotCode : True
GotText : False
GotRegex : False
GotHeaders : False
InvalidCert : False
TimeTotal : 00:00:00.3210709
Label : api
Result : Exception calling "GetResult" with "0" argument(s): "No such host is known. (api.example.com:80)"
Connected : False
GotCode : False
GotText : False
GotRegex : False
GotHeaders : False
InvalidCert : False
TimeTotal : 00:00:00.0280893
Label : redirect
Result : Unexpected status code: NotFound
Connected : True
GotCode : False
GotText : False
GotRegex : False
GotHeaders : False
InvalidCert : False
TimeTotal : 00:00:00.1021738
Label Result Connected GotCode GotText GotHeaders InvalidCert TimeTotal
----- ------ --------- ------- ------- ---------- ----------- ---------
google (142.250.190.132) True True False False False 00:00:00.2064638
redirect (93.184.216.34) InvalidResult True False False False False 00:00:00.0953043
redirect (10.11.22.33) OperationTimeout False False False False False 00:00:03.0100917
redirect (10.99.88.77) OperationTimeout False False False False False 00:00:03.0067049
```

## Links
Expand All @@ -84,4 +57,6 @@ TimeTotal : 00:00:00.1021738

A `$null` Results property signifies no error and all specified test criteria passed.

You can use the common variable -OutVariable to save the test results. Each TestResult object has a hidden Response property with the raw response from the server.
You can use the common variable _OutVariable_ to save the test results.
Each TestResult object has a Response property with the raw response from the server.
For HTTPS tests, the TestResult object will have the ServerCertificate populated with the certificate presented by the server.
5 changes: 2 additions & 3 deletions example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
[[plan]]
label = "api"
url = "http://api.example.com/"
tags = ["demo", "regex"]
text = "API for example.com"
regex = "some regex"
tags = ["demo", "string"]
string = "API for example.com"

# Verify that this URL returns a redirect. Send to both
# the IP address listed in DNS, plus 10.11.22.33 and 10.99.88.77.
Expand Down
57 changes: 57 additions & 0 deletions src/httpunitPS.format.ps1xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<Configuration>
<ViewDefinitions>
<View>
<Name>TestResult</Name>
<ViewSelectedBy>
<TypeName>TestResult</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader></TableColumnHeader>
<TableColumnHeader>
<Label>Result</Label>
<Width>17</Width>
</TableColumnHeader>
<TableColumnHeader></TableColumnHeader>
<TableColumnHeader></TableColumnHeader>
<TableColumnHeader></TableColumnHeader>
<TableColumnHeader></TableColumnHeader>
<TableColumnHeader></TableColumnHeader>
<TableColumnHeader></TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>Label</PropertyName>
</TableColumnItem>
<TableColumnItem>
<ScriptBlock>
$_.Result.CategoryInfo.Category.ToString()
</ScriptBlock>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Connected</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>GotCode</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>GotText</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>GotHeaders</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>InvalidCert</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>TimeTotal</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
47 changes: 42 additions & 5 deletions src/httpunitPS.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class TestPlan {
$planUrl = [uri]$this.URL

foreach ($item in $this.ExpandIpList()) {
Write-Debug ('Adding test case for "{0}"' -f $item)
$case = [TestCase]@{
URL = $planUrl
IP = $item
Expand All @@ -80,13 +81,9 @@ class TestPlan {
$case.ExpectHeaders = $this.Headers
}


$cases.Add($case)
}




return $cases
}
}
Expand All @@ -109,6 +106,7 @@ class TestCase {
switch ($this.URL.Scheme) {
http { return $this.TestHttp() }
https { return $this.TestHttp() }
tcp { return $this.TestTcp() }
file {
$fileTest = [TestResult]::new()
$exception = [Exception]::new(("URL Scheme '{0}' is not supported. Did you mean to use the -Path parameter?" -f $this.URL.Scheme ))
Expand All @@ -124,11 +122,45 @@ class TestCase {
return $noTest
}

[TestResult] TestTcp() {
if ([string]::IsNullOrEmpty($this.Plan.Label)) {
$this.Plan.Label = $this.URL
}
$result = [TestResult]::new($this.Plan.Label)
$result.Connected = $true
$time = Get-Date

$testName = $this.IP
$testPort = $this.URL.Port

$result.Label = '{0} ({1})' -f $result.Label, $testName

if ([System.Environment]::OSVersion.Platform.ToString() -eq 'Win32NT') {
$testOutput = Test-NetConnection -ComputerName $testName -Port $testPort


if (!$testOutput.TcpTestSucceeded) {
$result.Connected = $false
$exception = [Exception]::new(("TCP connect to ({0} : {1}) failed" -f $testName, $testPort ))
$result.Result = [System.Management.Automation.ErrorRecord]::new($exception, "10", "ConnectionError", $this.URL)
}

$result.Response = $testOutput
} else {
$exception = [Exception]::new(("Not yet implemented on this platform" ))
$result.Result = [System.Management.Automation.ErrorRecord]::new($exception, "100", "NotImplemented", $this.URL)
}
$result.TimeTotal = (Get-Date) - $time
return $result
}

[TestResult] TestHttp() {
if ([string]::IsNullOrEmpty($this.Plan.Label)) {
$this.Plan.Label = $this.URL
}
$result = [TestResult]::new($this.Plan.Label)

$result.Label = '{0} ({1})' -f $result.Label, $this.IP
$time = Get-Date

Write-Debug ('TestHttp: Url={0} ExpectCode={1}' -f $this.URL.AbsoluteUri, $this.ExpectCode)
Expand Down Expand Up @@ -229,6 +261,10 @@ class TestCase {
$result.Result = [System.Management.Automation.ErrorRecord]::new($_.Exception.GetBaseException(), "5", "ConnectionError", $content)
} finally {
$result.TimeTotal = (Get-Date) - $time

if ($this.URL.Scheme -eq 'https') {
$result.ServerCertificate = Get-SSLCertificate -ComputerName $this.URL.DnsSafeHost -Port $this.URL.Port
}
}

return $result
Expand All @@ -238,14 +274,15 @@ class TestCase {
class TestResult {
[string] $Label
[System.Management.Automation.ErrorRecord] $Result
hidden [object] $Response
[object] $Response

[bool] $Connected
[bool] $GotCode
[bool] $GotText
[bool] $GotRegex
[bool] $GotHeaders
[bool] $InvalidCert
[Security.Cryptography.X509Certificates.X509Certificate2] $ServerCertificate
[timespan] $TimeTotal

TestResult () {}
Expand Down
Loading
Loading