From 3b133a296bcdf16754fe77ba3b8c0ca27bee0590 Mon Sep 17 00:00:00 2001 From: codaamok Date: Sat, 26 Feb 2022 20:27:08 +0000 Subject: [PATCH] See CHANGELOG.md, pipeline changes & added tests --- .github/workflows/{build.yml => pipeline.yml} | 15 +++- .vscode/tasks.json | 8 +-- CHANGELOG.md | 5 ++ src/PSShlink.psd1 | 6 +- src/Public/Remove-ShlinkTag.ps1 | 18 ++--- src/Public/Save-ShlinkUrlQrCode.ps1 | 12 +++- .../Get-ShlinkDomains.Acceptance.Tests.ps1 | 24 +++++++ .../Get-ShlinkServer.Acceptance.Tests.ps1 | 23 +++++++ .../Get-ShlinkTags.Acceptance.Tests.ps1 | 35 ++++++++++ .../Public/Get-ShlinkUrl.Acceptance.Tests.ps1 | 30 ++++++++ .../Get-ShlinkVisits.Acceptance.Tests.ps1 | 69 +++++++++++++++++++ ...ShlinkVisitsNonOrphan.Acceptance.Tests.ps1 | 44 ++++++++++++ ...et-ShlinkVisitsOrphan.Acceptance.Tests.ps1 | 54 +++++++++++++++ ...ps1 => New-ShlinkUrl.Acceptance.Tests.ps1} | 39 ++++++----- .../Remove-ShlinkTag.Acceptance.Tests.ps1 | 48 +++++++++++++ .../Remove-ShlinkUrl.Acceptance.Tests.ps1 | 50 ++++++++++++++ .../Save-ShlinkUrlQrCode.Acceptance.Tests.ps1 | 39 +++++++++++ .../Public/Set-ShlinkTag.Acceptance.Tests.ps1 | 35 ++++++++++ .../Public/Set-ShlinkUrl.Acceptance.Tests.ps1 | 40 +++++++++++ tests/invoke.tests.ps1 | 19 +++++ 20 files changed, 574 insertions(+), 39 deletions(-) rename .github/workflows/{build.yml => pipeline.yml} (80%) create mode 100644 tests/Public/Get-ShlinkDomains.Acceptance.Tests.ps1 create mode 100644 tests/Public/Get-ShlinkServer.Acceptance.Tests.ps1 create mode 100644 tests/Public/Get-ShlinkTags.Acceptance.Tests.ps1 create mode 100644 tests/Public/Get-ShlinkUrl.Acceptance.Tests.ps1 create mode 100644 tests/Public/Get-ShlinkVisits.Acceptance.Tests.ps1 create mode 100644 tests/Public/Get-ShlinkVisitsNonOrphan.Acceptance.Tests.ps1 create mode 100644 tests/Public/Get-ShlinkVisitsOrphan.Acceptance.Tests.ps1 rename tests/Public/{New-ShlinkUrl.Tests.ps1 => New-ShlinkUrl.Acceptance.Tests.ps1} (57%) create mode 100644 tests/Public/Remove-ShlinkTag.Acceptance.Tests.ps1 create mode 100644 tests/Public/Remove-ShlinkUrl.Acceptance.Tests.ps1 create mode 100644 tests/Public/Save-ShlinkUrlQrCode.Acceptance.Tests.ps1 create mode 100644 tests/Public/Set-ShlinkTag.Acceptance.Tests.ps1 create mode 100644 tests/Public/Set-ShlinkUrl.Acceptance.Tests.ps1 create mode 100644 tests/invoke.tests.ps1 diff --git a/.github/workflows/build.yml b/.github/workflows/pipeline.yml similarity index 80% rename from .github/workflows/build.yml rename to .github/workflows/pipeline.yml index eb74919..bfeafd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/pipeline.yml @@ -1,4 +1,4 @@ -name: "Create GitHub release and publish to the PowerShell Gallery" +name: "CI/CD Pipeline" on: push: @@ -7,6 +7,8 @@ on: branches: - main - master + schedule: + - cron: '11 7 * * *' workflow_dispatch: jobs: @@ -51,17 +53,24 @@ jobs: Invoke-Build -File "custom.build.ps1" @Params -Task PostBuild shell: pwsh + - name: Pester Tests + if: hashFiles('tests/invoke.tests.ps1') != '' + run: pwsh -File "tests/invoke.tests.ps1" + - name: Custom pre-release tasks + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} run: Invoke-Build -File "custom.build.ps1" -ModuleName $env:GH_PROJECTNAME -Author $env:GH_USERNAME -Version $env:GitVersion_SemVer -NewRelease $true -Task PreRelease shell: pwsh - name: Publish to PowerShell Gallery + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} run: Invoke-Build -File "invoke.build.ps1" -ModuleName $env:GH_PROJECTNAME -Task "PublishModule" shell: pwsh env: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} - name: Create release + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} id: create_release uses: actions/create-release@v1 env: @@ -74,6 +83,7 @@ jobs: prerelease: false - name: Upload release asset + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} id: upload_release_asset uses: actions/upload-release-asset@v1 env: @@ -85,6 +95,7 @@ jobs: asset_content_type: application/zip - name: Commit CHANGELOG.md and module manifest + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} run: | git config --global user.email "action@github.com" git config --global user.name "GitHub Action" @@ -92,10 +103,12 @@ jobs: git commit -m "Released ${{ env.GitVersion_SemVer }}" - name: Push commit + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} uses: ad-m/github-push-action@master with: github_token: ${{ secrets.GITHUB_TOKEN }} - name: Custom post-release tasks + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} run: Invoke-Build -File "custom.build.ps1" -ModuleName $env:GH_PROJECTNAME -Author $env:GH_USERNAME -Version $env:GitVersion_SemVer -NewRelease $true -Task PostRelease shell: pwsh \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 722ae54..8b72a06 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -42,7 +42,7 @@ "label": "Tests", "type": "process", "command": "pwsh", - "args": ["-noprofile","-command","Import-Module ${input:CodeType}; $env:ShlinkServer = '${input:ShlinkServer}'; $env:ShlinkAPIKey = '${input:ShlinkAPIKey}' | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString; Invoke-Pester -Path './tests/*' -Output Detailed"], + "args": ["-noprofile", "-command", "$env:ShlinkServer = '${input:ShlinkServer}'; $env:ShlinkAPIKey = '${input:ShlinkAPIKey}' | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString; ./tests/invoke.tests.ps1"], "group": { "kind": "test", "isDefault": true @@ -63,13 +63,13 @@ "description": "Please enter Shlink server name", "id": "ShlinkServer", "type": "promptString", - "default": "https://acook.io" + "default": "http://psshlink.codaamok" }, { "description": "Please enter Shlink API key", "id": "ShlinkAPIKey", "type": "promptString", - "default": "", + "default": "18c65bc9-e4fb-449d-b3e0-c6427cbac735", "password": true, }, { @@ -80,7 +80,7 @@ "./src/PSShlink.psd1", "./build/PSShlink/PSShlink.psd1" ], - "default": "./src/PSShlink.psd1" + "default": "./build/PSShlink/PSShlink.psd1" }, ] } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bd1b351..27d724d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- New parameter `-PassThru` added to function `Save-ShlinkUrlQrCode` which will return a `System.IO.FileSystemInfo` object for each QR code image file it creates when used. Default behaviour has not changed (return no object if successful). + +### Changed +- Renamed parameter `-Tags` to be `-Tag` for function `Remove-ShlinkTag` ## [0.9.1] - 2022-02-14 ### Fixed diff --git a/src/PSShlink.psd1 b/src/PSShlink.psd1 index bda2487..6369dd8 100644 --- a/src/PSShlink.psd1 +++ b/src/PSShlink.psd1 @@ -69,11 +69,7 @@ FormatsToProcess = 'PSShlink.Format.ps1xml' # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = 'Get-ShlinkDomains', 'Get-ShlinkServer', 'Get-ShlinkTags', - 'Get-ShlinkUrl', 'Get-ShlinkVisits', 'Get-ShlinkVisitsOrphan', - 'New-ShlinkTag', 'New-ShlinkUrl', 'Remove-ShlinkTag', - 'Remove-ShlinkUrl', 'Save-ShlinkUrlQrCode', - 'Set-ShlinkDomainRedirects', 'Set-ShlinkTag', 'Set-ShlinkUrl' +FunctionsToExport = '*' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @() diff --git a/src/Public/Remove-ShlinkTag.ps1 b/src/Public/Remove-ShlinkTag.ps1 index 930ff7e..07a1598 100644 --- a/src/Public/Remove-ShlinkTag.ps1 +++ b/src/Public/Remove-ShlinkTag.ps1 @@ -4,7 +4,7 @@ function Remove-ShlinkTag { Remove a tag from an existing Shlink server. .DESCRIPTION Remove a tag from an existing Shlink server. - .PARAMETER Tags + .PARAMETER Tag Name(s) of the tag(s) you want to remove. .PARAMETER ShlinkServer The URL of your Shlink server (including schema). For example "https://example.com". @@ -38,7 +38,7 @@ function Remove-ShlinkTag { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] - [String[]]$Tags, + [String[]]$Tag, [Parameter()] [String]$ShlinkServer, @@ -63,18 +63,18 @@ function Remove-ShlinkTag { process { $QueryString = [System.Web.HttpUtility]::ParseQueryString('') - foreach ($Tag in $Tags) { - if ($AllTags.tag -notcontains $Tag) { + foreach ($_Tag in $Tag) { + if ($AllTags.tag -notcontains $_Tag) { $WriteErrorSplat = @{ - Message = "Tag '{0}' does not exist on Shlink server '{1}'" -f $Tag, $Script:ShlinkServer + Message = "Tag '{0}' does not exist on Shlink server '{1}'" -f $_Tag, $Script:ShlinkServer Category = "ObjectNotFound" - TargetObject = $Tag + TargetObject = $_Tag } Write-Error @WriteErrorSplat continue } else { - $QueryString.Add("tags[]", $Tag) + $QueryString.Add("tags[]", $_Tag) } $Params = @{ @@ -85,9 +85,9 @@ function Remove-ShlinkTag { } if ($PSCmdlet.ShouldProcess( - ("Would delete tag '{0}' from Shlink server '{1}'" -f $Tag, $Script:ShlinkServer), + ("Would delete tag '{0}' from Shlink server '{1}'" -f $_Tag, $Script:ShlinkServer), "Are you sure you want to continue?", - ("Removing tag '{0}' from Shlink server '{1}'" -f $Tag, $Script:ShlinkServer))) { + ("Removing tag '{0}' from Shlink server '{1}'" -f $_Tag, $Script:ShlinkServer))) { try { InvokeShlinkRestMethod @Params } diff --git a/src/Public/Save-ShlinkUrlQrCode.ps1 b/src/Public/Save-ShlinkUrlQrCode.ps1 index 0863219..5e60d6e 100644 --- a/src/Public/Save-ShlinkUrlQrCode.ps1 +++ b/src/Public/Save-ShlinkUrlQrCode.ps1 @@ -38,6 +38,8 @@ function Save-ShlinkUrlQrCode { .PARAMETER ShlinkApiKey A SecureString object of your Shlink server's API key. It is not required to use this parameter for every use of this function. When it is used once for any of the functions in the PSShlink module, its value is retained throughout the life of the PowerShell session and its value is only accessible within the module's scope. + .PARAMETER PassThru + Returns a System.IO.FileSystemInfo object of each QR image file it creates .EXAMPLE PS C:\> Save-ShlinkUrlQrCode -ShortCode "profile" -Domain "example.com" -Size 1000 -Format svg -Path "C:\temp" @@ -51,7 +53,7 @@ function Save-ShlinkUrlQrCode { Expects PSObjects with PSTypeName of 'PSTypeName', typically from Get-ShlinkUrl. .OUTPUTS - System.Management.Automation.PSObject + System.IO.FileSystemInfo #> [CmdletBinding()] param ( @@ -89,7 +91,10 @@ function Save-ShlinkUrlQrCode { [String]$ShlinkServer, [Parameter(ParameterSetName="SpecifyProperties")] - [SecureString]$ShlinkApiKey + [SecureString]$ShlinkApiKey, + + [Parameter()] + [Switch]$PassThru ) begin { $QueryString = [System.Web.HttpUtility]::ParseQueryString('') @@ -192,6 +197,9 @@ function Save-ShlinkUrlQrCode { try { Set-Content @Params + if ($PassThru) { + Get-Item $FileName + } } catch { Write-Error -ErrorRecord $_ diff --git a/tests/Public/Get-ShlinkDomains.Acceptance.Tests.ps1 b/tests/Public/Get-ShlinkDomains.Acceptance.Tests.ps1 new file mode 100644 index 0000000..162a7c1 --- /dev/null +++ b/tests/Public/Get-ShlinkDomains.Acceptance.Tests.ps1 @@ -0,0 +1,24 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Get-ShlinkDomains" { + It "Returns domains configured with Shlink" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkDomains @Params + $Object.data.domain | Should -Be 'psshlink.codaamok' + $Object.data.isDefault | Should -BeTrue + } +} diff --git a/tests/Public/Get-ShlinkServer.Acceptance.Tests.ps1 b/tests/Public/Get-ShlinkServer.Acceptance.Tests.ps1 new file mode 100644 index 0000000..80bc95a --- /dev/null +++ b/tests/Public/Get-ShlinkServer.Acceptance.Tests.ps1 @@ -0,0 +1,23 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Get-ShlinkServer" { + It "Returns Shlink instance information" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ErrorAction = 'Stop' + } + $Object = Get-ShlinkServer @Params + $Object.status | Should -Be 'Pass' + [System.Version]$Object.version | Should -BeOfType [System.Version] + } +} \ No newline at end of file diff --git a/tests/Public/Get-ShlinkTags.Acceptance.Tests.ps1 b/tests/Public/Get-ShlinkTags.Acceptance.Tests.ps1 new file mode 100644 index 0000000..30696c8 --- /dev/null +++ b/tests/Public/Get-ShlinkTags.Acceptance.Tests.ps1 @@ -0,0 +1,35 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Get-ShlinkTags" { + It "Returns all tags on the Shlink instance" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkTags @Params + $Object.tag | Should -Be 'psshlinktag1', 'psshlinktag2' + $Object.shortUrlsCount | Should -Be '1','1' + } + + It "Returns tags on Shlink instance using search term '<_>'" -ForEach 1,2 { + $Params = @{ + SearchTerm = $_ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkTags @Params + $Object.tag | Should -Be ('psshlinktag{0}' -f $_) + } +} \ No newline at end of file diff --git a/tests/Public/Get-ShlinkUrl.Acceptance.Tests.ps1 b/tests/Public/Get-ShlinkUrl.Acceptance.Tests.ps1 new file mode 100644 index 0000000..dbdc523 --- /dev/null +++ b/tests/Public/Get-ShlinkUrl.Acceptance.Tests.ps1 @@ -0,0 +1,30 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Get-ShlinkUrl" { + It "Retrieve 'PSShlink-Test' short URL " -ForEach @( + @{ Type = 'exactly'; Parameter = 'ShortCode'; Value = 'PSShlink-Test' } + @{ Type = 'vaguely'; Parameter = 'SearchTerm'; Value = 'test' } + ) { + $Params = @{ + $Parameter = $Value + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkUrl @Params + $Object.shortCode | Should -Be 'PSShlink-Test' + $Object.shortUrl | Should -Be ('{0}/PSShlink-Test' -f $env:ShlinkServer) + $Object.tags | Should -Be 'PSShlinkTag1', 'PSShlinkTag2' + $Object.longUrl | Should -Be 'https://google.co.uk' + } +} \ No newline at end of file diff --git a/tests/Public/Get-ShlinkVisits.Acceptance.Tests.ps1 b/tests/Public/Get-ShlinkVisits.Acceptance.Tests.ps1 new file mode 100644 index 0000000..dc38735 --- /dev/null +++ b/tests/Public/Get-ShlinkVisits.Acceptance.Tests.ps1 @@ -0,0 +1,69 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Get-ShlinkVisits" { + It "Get summary of all the server's visits" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisits @Params + $Object.Server | Should -Be $env:ShlinkServer + [int]$Object.visitsCount | Should -BeOfType [int] + } + + It "Get visit data for a specific shortcode" { + $Params = @{ + ShortCode = 'PSShlink-Test' + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisits @Params + $Count = $Object.count + Invoke-WebRequest 'http://psshlink.codaamok/PSShlink-Test' -ErrorAction 'Stop' + $Object = Get-ShlinkVisits @Params + $Object.Count | Should -Be ($Count + 1) + } + + It "Get visit data for tag 'psshlinktag<_>'" -TestCases 1,2 { + $Params = @{ + Tag = 'psshlinktag{0}' -f $_ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisits @Params + $Count = $Object.count + Invoke-WebRequest 'http://psshlink.codaamok/PSShlink-Test' -ErrorAction 'Stop' + $Object = Get-ShlinkVisits @Params + $Object.Count | Should -Be ($Count + 1) + } + + It "Get visits data after a specified date" { + Start-Sleep -Seconds 1 + $Date = Get-Date + $Params = @{ + ShortCode = 'PSShlink-Test' + StartDate = $Date + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisits @Params + $Object | Should -BeNullOrEmpty + Invoke-WebRequest 'http://psshlink.codaamok/PSShlink-Test' -ErrorAction 'Stop' + $Object = Get-ShlinkVisits @Params + $Object.Count | Should -Be 1 + } +} \ No newline at end of file diff --git a/tests/Public/Get-ShlinkVisitsNonOrphan.Acceptance.Tests.ps1 b/tests/Public/Get-ShlinkVisitsNonOrphan.Acceptance.Tests.ps1 new file mode 100644 index 0000000..caaac7c --- /dev/null +++ b/tests/Public/Get-ShlinkVisitsNonOrphan.Acceptance.Tests.ps1 @@ -0,0 +1,44 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Get-ShlinkVisitsNonOrphan" { + It "Get (non orphan) visit data for the Shlink instance" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisitsNonOrphan @Params + $Count = $Object.Count + $Url = '{0}/PSShlink-Test' -f $env:ShlinkServer + Invoke-WebRequest $Url -ErrorAction 'Stop' + $Object = Get-ShlinkVisitsNonOrphan @Params + $Object.Count | Should -Be ($Count + 1) + } + + It "Get (non orphan) visit data for the Shlink instance after a specified date" { + Start-Sleep -Seconds 1 + $Date = Get-Date + $Params = @{ + StartDate = $Date + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisitsNonOrphan @Params + $Object | Should -BeNullOrEmpty + $Url = '{0}/PSShlink-Test' -f $env:ShlinkServer + Invoke-WebRequest $Url -ErrorAction 'Stop' + $Object = Get-ShlinkVisitsNonOrphan @Params + $Object.Count | Should -Be 1 + } +} diff --git a/tests/Public/Get-ShlinkVisitsOrphan.Acceptance.Tests.ps1 b/tests/Public/Get-ShlinkVisitsOrphan.Acceptance.Tests.ps1 new file mode 100644 index 0000000..48eb7c5 --- /dev/null +++ b/tests/Public/Get-ShlinkVisitsOrphan.Acceptance.Tests.ps1 @@ -0,0 +1,54 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Get-ShlinkVisitsOrphan" { + It "Get (orphan) visit data for the Shlink instance" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisitsOrphan @Params + $Count = $Object.Count + try { + $Url = '{0}/{1}' -f $env:ShlinkServer, (New-Guid).Guid + Invoke-WebRequest $Url -ErrorAction 'Stop' + } + catch { + $_.Exception.Response.StatusCode | Should -Be 'NotFound' + } + $Object = Get-ShlinkVisitsOrphan @Params + $Object.Count | Should -Be ($Count + 1) + } + + It "Get (orphan) visit data for the Shlink instance after a specified date" { + Start-Sleep -Seconds 1 + $Date = Get-Date + $Params = @{ + StartDate = $Date + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkVisitsOrphan @Params + $Object | Should -BeNullOrEmpty + try { + $Url = '{0}/{1}' -f $env:ShlinkServer, (New-Guid).Guid + Invoke-WebRequest $Url -ErrorAction 'Stop' + } + catch { + $_.Exception.Response.StatusCode | Should -Be 'NotFound' + } + $Object = Get-ShlinkVisitsOrphan @Params + $Object.Count | Should -Be 1 + } +} diff --git a/tests/Public/New-ShlinkUrl.Tests.ps1 b/tests/Public/New-ShlinkUrl.Acceptance.Tests.ps1 similarity index 57% rename from tests/Public/New-ShlinkUrl.Tests.ps1 rename to tests/Public/New-ShlinkUrl.Acceptance.Tests.ps1 index b09ef17..8a42e80 100644 --- a/tests/Public/New-ShlinkUrl.Tests.ps1 +++ b/tests/Public/New-ShlinkUrl.Acceptance.Tests.ps1 @@ -1,47 +1,50 @@ BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test # and you might not want to cloober it with the non-built code - if (-not (Get-Module PSShlink)) { - Import-Module $PSScriptRoot/../../src/PSShlink.psd1 -Force + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force } } -Describe "Create new short URLs" { +Describe "New-ShlinkUrl" { It "New valid short URL" { - $Guid = (New-Guid).Guid - $Splat = @{ + $Params = @{ LongUrl = 'https://google.co.uk' - CustomSlug = $Guid + CustomSlug = 'PSShlink-Test' + Tags = 'psshlinktag1', 'psshlinktag2' ShlinkServer = $env:ShlinkServer ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString ErrorAction = 'Stop' } - $Object = New-ShlinkUrl @Splat - $Object.shortCode | Should -Be $Guid - $Object.shortUrl | Should -Be ('{0}/{1}' -f $env:ShlinkServer, $Guid) + $Object = New-ShlinkUrl @Params + $Object.shortCode | Should -Be 'PSShlink-Test' + $Object.shortUrl | Should -Be ('{0}/PSShlink-Test' -f $env:ShlinkServer) + $Object.tags | Should -Be 'psshlinktag1', 'psshlinktag2' $Object.longUrl | Should -Be 'https://google.co.uk' } It "New invalid short URL" { $Guid = (New-Guid).Guid - $Splat = @{ - LongUrl = 'https://{0}.com' -f (New-Guid).Guid + $Params = @{ + LongUrl = 'https://{0}.com' -f ([String[]]$Guid * 3 -join '-') CustomSlug = $Guid ShlinkServer = $env:ShlinkServer ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ValidateUrl = $true ErrorAction = 'Stop' } - { New-ShlinkUrl @Splat } | Should -Throw -ExceptionType ([System.ArgumentException]) + { New-ShlinkUrl @Params } | Should -Throw -ExceptionType ([System.ArgumentException]) } It "New invalid short URL with validation off" { $Guid = (New-Guid).Guid - # Multiply guid string by 3 because ideally we *really* do not want a valid URL - $Url = 'https://{0}.com' -f ($Guid * 3) - + $Url = 'https://{0}.com' -f ([String[]]$Guid * 3 -join '-') { Invoke-WebRequest -Uri $Url -ErrorAction 'Stop' } | Should -Throw -ExceptionType ([System.Net.Http.HttpRequestException]) - - $Splat = @{ + $Params = @{ LongUrl = $Url CustomSlug = $Guid ShlinkServer = $env:ShlinkServer @@ -49,7 +52,7 @@ Describe "Create new short URLs" { ValidateUrl = $false ErrorAction = 'Stop' } - $Object = New-ShlinkUrl @Splat + $Object = New-ShlinkUrl @Params $Object.shortCode | Should -Be $Guid $Object.shortUrl | Should -Be ('{0}/{1}' -f $env:ShlinkServer, $Guid) $Object.longUrl | Should -Be $Url diff --git a/tests/Public/Remove-ShlinkTag.Acceptance.Tests.ps1 b/tests/Public/Remove-ShlinkTag.Acceptance.Tests.ps1 new file mode 100644 index 0000000..fb1c357 --- /dev/null +++ b/tests/Public/Remove-ShlinkTag.Acceptance.Tests.ps1 @@ -0,0 +1,48 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Remove-ShlinkTag" { + It "Removes a tag from the Shlink instance (WhatIf enabled: )" -TestCases @( + @{ WhatIf = $true } + @{ WhatIf = $false } + ) { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + + Remove-ShlinkTag -Tag 'psshlinktag1' -Confirm:$false -WhatIf:$WhatIf @Params + + switch ($WhatIf) { + $true { + Get-ShlinkTags -SearchTerm 'psshlinktag1' @Params | Should -Not -BeNullOrEmpty + } + $false { + Get-ShlinkTags -SearchTerm 'psshlinktag1' @Params | Should -BeNullOrEmpty + } + } + } + + It "Remove all tags" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + + Get-ShlinkTags @Params | Remove-ShlinkTag @Params -Confirm:$false + + Get-ShlinkTags @Params | Should -BeNullOrEmpty + } + +} \ No newline at end of file diff --git a/tests/Public/Remove-ShlinkUrl.Acceptance.Tests.ps1 b/tests/Public/Remove-ShlinkUrl.Acceptance.Tests.ps1 new file mode 100644 index 0000000..74a472e --- /dev/null +++ b/tests/Public/Remove-ShlinkUrl.Acceptance.Tests.ps1 @@ -0,0 +1,50 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Remove-ShlinkUrl" { + It "Removes a short code URL from the Shlink instance (WhatIf enabled: )" -TestCases @( + @{ WhatIf = $true } + @{ WhatIf = $false } + ) { + $Params = @{ + ShortCode = 'PSShlink-Test' + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + Confirm = $false + WhatIf = $WhatIf + ErrorAction = 'Stop' + } + Remove-ShlinkUrl @Params + + switch ($WhatIf) { + $true { + Get-ShlinkUrl -ShortCode 'PSShlink-Test' | Should -Not -BeNullOrEmpty + } + $false { + { Get-ShlinkUrl -ShortCode 'PSShlink-Test' -ErrorAction 'Stop' } | Should -Throw -ExceptionType ([System.Management.Automation.ItemNotFoundException]) + } + } + + } + + It "Remove all short codes" { + $Params = @{ + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + + Get-ShlinkUrl @Params | Remove-ShlinkUrl @Params -Confirm:$false + + Get-ShlinkUrl @Params | Should -BeNullOrEmpty + } +} \ No newline at end of file diff --git a/tests/Public/Save-ShlinkUrlQrCode.Acceptance.Tests.ps1 b/tests/Public/Save-ShlinkUrlQrCode.Acceptance.Tests.ps1 new file mode 100644 index 0000000..e4b702e --- /dev/null +++ b/tests/Public/Save-ShlinkUrlQrCode.Acceptance.Tests.ps1 @@ -0,0 +1,39 @@ +BeforeAll { + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + if (-not (Get-Module PSShlink)) { + Import-Module $PSScriptRoot/../../src/PSShlink.psd1 -Force + } + + $TempFile = New-TemporaryFile -ErrorAction "Stop" +} + +AfterAll { + Remove-Item $TempFile -ErrorAction 'Continue' +} + +Describe "Save-ShlinkUrlQrCode" { + It "Create '' QR code image file of expected hash" -TestCases @( + @{ Format = 'png'; SHA256 = 'A3D4EA74661878D8AB15AD864705AB43A095D5B60416F0B33F3E5212EEB527EF' } + @{ Format = 'svg'; SHA256 = '380E532048FD830255CF8C34579D92813CB0B78F131D0E5790257A3155943391' } + ) { + $Params = @{ + Path = $TempFile.DirectoryName + ShortCode = 'PSShlink-Test' + Format = $Format + Size = 100 + Margin = 10 + ErrorCorrection = 'L' + RoundBlockSize = $false + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + PassThru = $true + ErrorAction = 'Stop' + } + $File = Save-ShlinkUrlQrCode @Params + + (Get-FileHash $File -Algorithm SHA256).Hash | Should -Be $SHA256 + + Remove-Item $File -ErrorAction 'Continue' + } +} \ No newline at end of file diff --git a/tests/Public/Set-ShlinkTag.Acceptance.Tests.ps1 b/tests/Public/Set-ShlinkTag.Acceptance.Tests.ps1 new file mode 100644 index 0000000..3a78024 --- /dev/null +++ b/tests/Public/Set-ShlinkTag.Acceptance.Tests.ps1 @@ -0,0 +1,35 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Set-ShlinkTag" { + It "Rename an existing tag" { + $Params = @{ + OldTagName = 'psshlinktag3' + NewTagName = 'psshlinktag5' + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + Set-ShlinkTag @Params + + $Params = @{ + OldTagName = 'psshlinktag3' + NewTagName = 'psshlinktag5' + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Get-ShlinkTags -SearchTerm 'psshlinktag5' + $Object.Count | Should -Be 1 + $Object.tag | Should -Be 'psshlinktag5' + } +} \ No newline at end of file diff --git a/tests/Public/Set-ShlinkUrl.Acceptance.Tests.ps1 b/tests/Public/Set-ShlinkUrl.Acceptance.Tests.ps1 new file mode 100644 index 0000000..2a57761 --- /dev/null +++ b/tests/Public/Set-ShlinkUrl.Acceptance.Tests.ps1 @@ -0,0 +1,40 @@ +BeforeAll { + # Always use built code if running in a pipeline + if ($env:USER -eq 'runner') { + Import-Module "$PSScriptRoot/../../build/PSShlink/PSShlink.psd1" -Force + } + # Check if module is already imported, as it can be via VSCode task where you can choose what code base to test + # and you might not want to cloober it with the non-built code + elseif (-not (Get-Module PSShlink)) { + Import-Module "$PSScriptRoot/../../src/PSShlink.psd1" -Force + } +} + +Describe "Set-ShlinkUrl" { + It "Update an existing short code" { + $Date = Get-Date '2000-01-01 00:00:00' + $Params = @{ + ShortCode = 'PSShlink-Test' + LongUrl = 'https://google.com' + Tags = 'psshlinktag3','psshlinktag4' + ValidSince = $Date.AddDays(-1) + ValidUntil = $Date.AddDays(1) + MaxVisits = 100 + Title = 'Google (USA)' + ForwardQuery = $false + Crawlable = $true + ShlinkServer = $env:ShlinkServer + ShlinkApiKey = $env:ShlinkAPIKey | ConvertTo-SecureString + ErrorAction = 'Stop' + } + $Object = Set-ShlinkUrl @Params + $Object.longUrl | Should -Be 'https://google.com' + $Object.tags | Should -Be 'psshlinktag3','psshlinktag4' + $Object.meta.validSince | Should -Be $Date.AddDays(-1) + $Object.meta.validUntil | Should -Be $Date.AddDays(1) + $Object.meta.maxVisits | Should -Be 100 + $Object.title | Should -be 'Google (USA)' + $Object.forwardQuery | Should -BeFalse + $Object.crawlable | Should -BeTrue + } +} \ No newline at end of file diff --git a/tests/invoke.tests.ps1 b/tests/invoke.tests.ps1 new file mode 100644 index 0000000..11b6562 --- /dev/null +++ b/tests/invoke.tests.ps1 @@ -0,0 +1,19 @@ +$PesterConfig = New-PesterConfiguration -Hashtable @{ + Run = @{ + Path = @( + "{0}/Public/New-*.Acceptance.Tests.ps1" -f $PSScriptRoot + "{0}/Public/Get-*.Acceptance.Tests.ps1" -f $PSScriptRoot + "{0}/Public/Save-*.Acceptance.Tests.ps1" -f $PSScriptRoot + "{0}/Public/Set-*Url.Acceptance.Tests.ps1" -f $PSScriptRoot + "{0}/Public/Set-*Tag.Acceptance.Tests.ps1" -f $PSScriptRoot + "{0}/Public/Remove-*.Acceptance.Tests.ps1" -f $PSScriptRoot + ) + Exit = $true + SkipRemainingOnFailure = 'Block' + } + Output = @{ + Verbosity = 'Detailed' + } +} + +Invoke-Pester -Configuration $PesterConfig -ErrorAction 'Stop'