diff --git a/ZLocation.Tests/ZLocation.Service.Tests.ps1 b/ZLocation.Tests/ZLocation.Service.Tests.ps1 new file mode 100644 index 0000000..c661621 --- /dev/null +++ b/ZLocation.Tests/ZLocation.Service.Tests.ps1 @@ -0,0 +1,90 @@ +Describe 'ZLocation.Service' { + Import-Module $PSScriptRoot\..\ZLocation\ZLocation.Service.psd1 -Force + . "$PSScriptRoot/_mocks.ps1" + + Context 'Testing database init' { + InModuleScope ZLocation.Service { + $dbpath = Get-ZLocationDatabaseFilePath + $testdbpattern = '*z-location-tests.db' + if ($dbpath -notlike $testdbpattern) {throw 'Not using test database, aborting tests'} + if (-not (Test-ZLocationDBUnlocked)) {throw 'Database is locked, aborting tests'} + + # It 'Is referring to test DB' { + # $dbpath | Should -BeLike $testdbpattern + # } + + It 'Initializes a database' { + if (Test-Path ($dbpath)) {Remove-Item $dbpath} + + Initialize-ZLocationDB + Assert-MockCalled Get-ZLocationDatabaseFilePath + Test-Path $dbpath | Should -Be $true + } + } + } + + Context 'Testing database functionality' { + InModuleScope ZLocation.Service { + $dbpath = Get-ZLocationDatabaseFilePath + $testdbpattern = '*z-location-tests.db' + if ($dbpath -notlike $testdbpattern) {throw 'Not using test database, aborting tests'} + if (-not (Test-ZLocationDBUnlocked)) {throw 'Database is locked, aborting tests'} + + BeforeEach { + if (Test-Path $dbpath) {Remove-Item $dbpath} + Initialize-ZLocationDB + } + + $path = [guid]::NewGuid().Guid + + It 'Adds and retrieves a location' { + Update-ZDBLocation -Path $path + Get-ZDBLocation | Should -HaveCount 1 + $l = [Location]::new() + $l.path = $path + $l.weight = 1 + Get-ZDBLocation | Select-Object -First 1 | ConvertTo-Json | Should -Be ($l | ConvertTo-Json) + } + + It 'Adds and removes a location' { + Update-ZDBLocation -Path $path + Remove-ZDBLocation $path + Get-ZDBLocation | Should -BeNullOrEmpty + } + } + } + + # Context "Malformed DB entries" { + # try { + # # Connect to the database and add some malformed entries + # Add-Type -Path $PSScriptRoot\..\ZLocation\LiteDB\LiteDB.dll + # $connectionString = "Filename=$($testDb); Mode=Shared" + # $db = [LiteDB.LiteDatabase]::new($connectionString) + # $collection = $db.GetCollection('Location') + # $oidquery = [LiteDB.Query]::Where('_id',{$args -like '{"$oid":"*"}'}) + + # # Create and insert a malformed location + # $bsondocument = [LiteDB.BsonDocument]::new() + # $bsondocument['weight'] = 1234 + # $collection.Insert($bsondocument) + + # It "confirms malformed entries inserted" { + # # This actually tests the query more than anything. + # $malformedEntries = (,$collection.Find($oidquery)) + # $malformedEntries | Should -HaveCount 1 + # } + + # It "can remove malformed location entries" { + # # Ensure nothing else can be connecting to $db to placate AppVeyor. + # $db.Dispose() + # Get-ZDBLocation + # $db = [LiteDB.LiteDatabase]::new($connectionString) + # $collection = $db.GetCollection('Location') + # $malformedEntries = $collection.Find($oidquery) + # $malformedEntries | Should -HaveCount 0 + # } + # } finally { + # $db.Dispose() + # } + # } +} diff --git a/ZLocation/ZLocation.Service.psd1 b/ZLocation/ZLocation.Service.psd1 index fca9090..1b7624e 100644 --- a/ZLocation/ZLocation.Service.psd1 +++ b/ZLocation/ZLocation.Service.psd1 @@ -4,7 +4,7 @@ GUID = '3d256bab-55d1-459c-8673-1d9d7ca8554a' # Assembly must be loaded first or else powershell class will fail to compile RequiredAssemblies = @("$PSScriptRoot/LiteDB/LiteDB.dll") RootModule = 'ZLocation.Service.psm1' -FunctionsToExport = @('Get-ZService') +FunctionsToExport = @('Get-ZDBLocation','Update-ZDBLocation','Remove-ZDBLocation') CmdletsToExport = @() VariablesToExport = @() AliasesToExport = @() diff --git a/ZLocation/ZLocation.Service.psm1 b/ZLocation/ZLocation.Service.psm1 index 9c982a7..9382cb4 100644 --- a/ZLocation/ZLocation.Service.psm1 +++ b/ZLocation/ZLocation.Service.psm1 @@ -2,8 +2,8 @@ Set-StrictMode -Version Latest Import-Module -Prefix DB (Join-Path $PSScriptRoot 'ZLocation.LiteDB.psd1') -class Service { - [Collections.Generic.IEnumerable[Location]] Get() { +# Get the locations in the database and their weights as [Location]s +function Get-ZDBLocation { return (dboperation { # Return an enumerator of all location entries try { @@ -28,34 +28,47 @@ class Service { } } }) - } - [void] Add([string]$path, [double]$weight) { - dboperation { - $l = DBGetById $collection $path ([Location]) - if($l) { - $l.weight += $weight - DBUpdate $collection $l - } else { - $l = [Location]::new() - $l.path = $path - $l.weight = $weight - DBInsert $collection $l - } +} + +# Increase the weight of a location in the database, adding it if not present. +function Update-ZDBLocation { + param ( + # The location to update or add + [Parameter(Mandatory=$true)] [string]$Path, + # The amount to increase the path's weight by + [double]$Weight = 1.0 + ) + dboperation { + $l = DBGetById $collection $path ([Location]) + if($l) { + $l.weight += $weight + DBUpdate $collection $l + } else { + $l = [Location]::new() + $l.path = $path + $l.weight = $weight + DBInsert $collection $l } } - [void] Remove([string]$path) { - dboperation { - # Use DB's internal column name, not mapped name - DBDelete $collection ([LiteDB.Query]::EQ('_id', [LiteDB.BSONValue]::new($path))) - } +} + +# Remove a location from the database +function Remove-ZDBLocation { + param ( + # The location to remove from the database + [Parameter(Mandatory=$true)] [string]$Path + ) + dboperation { + # Use DB's internal column name, not mapped name + DBDelete $collection ([LiteDB.Query]::EQ('_id', [LiteDB.BSONValue]::new($path))) } } class Location { [LiteDB.BsonId()] - [string] $path; + [string] $Path; - [double] $weight; + [double] $Weight; } function Get-ZLocationDatabaseFilePath @@ -105,26 +118,35 @@ function dboperation { } } -$dbExists = Test-Path (Get-ZLocationDatabaseFilePath) -$legacyBackupPath = Get-ZLocationLegacyBackupFilePath -$legacyBackupExists = ($legacyBackupPath -ne $null) -and (Test-Path $legacyBackupPath) - -# Create empty db, collection, and index if it doesn't exist -dboperation { - $collection.EnsureIndex('path') +function Test-ZLocationDBUnlocked { + try { + [IO.File]::OpenWrite((Get-ZLocationDatabaseFilePath)).close() + $true + } catch { + $false + } } -$service = [Service]::new() +function Initialize-ZLocationDB { + $dbExists = Test-Path (Get-ZLocationDatabaseFilePath) + $legacyBackupPath = Get-ZLocationLegacyBackupFilePath + $legacyBackupExists = ($null -ne $legacyBackupPath) -and (Test-Path $legacyBackupPath) -# Migrate legacy backup into database if appropriate -if((-not $dbExists) -and $legacyBackupExists) { - Write-Warning "ZLocation changed storage from $legacyBackupPath to $(Get-ZLocationDatabaseFilePath), feel free to remove the old txt file" - Get-Content $legacyBackupPath | Where-Object { $_ -ne $null } | ForEach-Object { - $split = $_ -split "`t" - $service.add($split[0], $split[1]) + if (-not($dbExists)) { + # Create empty db, collection, and index if it doesn't exist + dboperation { + $collection.EnsureIndex('path') + } + + # Migrate legacy backup into database if appropriate + if ($legacyBackupExists) { + Write-Warning "ZLocation changed storage from $legacyBackupPath to $(Get-ZLocationDatabaseFilePath), feel free to remove the old txt file" + Get-Content $legacyBackupPath | Where-Object { $_ -ne $null } | ForEach-Object { + $split = $_ -split "`t" + Add-ZDBLocation $split[0] $split[1] + } + } } } -Function Get-ZService { - ,$service -} +Initialize-ZLocationDB diff --git a/ZLocation/ZLocation.Storage.psm1 b/ZLocation/ZLocation.Storage.psm1 index c027648..83155aa 100644 --- a/ZLocation/ZLocation.Storage.psm1 +++ b/ZLocation/ZLocation.Storage.psm1 @@ -5,9 +5,8 @@ Import-Module (Join-Path $PSScriptRoot 'ZLocation.Search.psm1') function Get-ZLocation($Match) { - $service = Get-ZService $hash = [Collections.HashTable]::new() - foreach ($item in $service.Get()) + foreach ($item in (Get-ZDBLocation)) { $hash.add($item.path, $item.weight) } @@ -28,20 +27,14 @@ function Add-ZWeight { [Parameter(Mandatory=$true)] [string]$Path, [Parameter(Mandatory=$true)] [double]$Weight ) - $service = Get-ZService - $service.Add($path, $weight) + Update-ZDBLocation $path $weight } function Remove-ZLocation { param ( [Parameter(Mandatory=$true)] [string]$Path ) - $service = Get-ZService - $service.Remove($path) -} - -$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { - Write-Warning "[ZLocation] module was removed, but service was not closed." + Remove-ZDBLocation $path } Export-ModuleMember -Function @("Get-ZLocation", "Add-ZWeight", "Remove-ZLocation")