_TESTS/PSLocalBase.Followup-a526708.Tests.ps1
|
# Additional independent evidence for a526708, outside its original green runner. # All databases are in memory. No changes to the engine or previous assertions. $ErrorActionPreference = 'Stop' Import-Module (Join-Path (Split-Path $PSScriptRoot -Parent) 'PSLocalBase.psd1') -Force $script:passed = 0 $script:failed = 0 function Check-Followup([string]$Name, [scriptblock]$Body) { $db = New-LocalBase try { & $Body $db; $script:passed++; Write-Host "[PASS] $Name" } catch { $script:failed++; Write-Host "[FAIL] ${Name}: $($_.Exception.Message)" } finally { $db.close() } } foreach ($text in @('0.1', '0.2', '0.01', '1.0000000000000002')) { foreach ($kind in @('Double', 'Decimal')) { foreach ($operation in @('remove', 'union')) { Check-Followup "Numeric roundtrip: value=$text input=$kind operation=$operation" { param($db) $value = if ($kind -eq 'Double') { [double]::Parse($text, [Globalization.CultureInfo]::InvariantCulture) } else { [decimal]::Parse($text, [Globalization.CultureInfo]::InvariantCulture) } $db.collection('items').doc('one').set(@{ values = @($value) }) $stored = @($db.collection('items').doc('one').get().values) $storedType = $stored[0].GetType().Name $mutation = if ($operation -eq 'remove') { [PSLocalBase]::ArrayRemove($value) } else { [PSLocalBase]::ArrayUnion($value) } $db.collection('items').doc('one').update(@{ values = $mutation }) $actual = @($db.collection('items').doc('one').get().values) $expected = if ($operation -eq 'remove') { 0 } else { 1 } if ($actual.Count -ne $expected) { throw "stored=$storedType expected=$expected actual=$($actual.Count)" } } } } } Check-Followup 'Right branch reuse preserves original union snapshot and true nested-union is rejected' { param($db) # Contract: Each union forms an independent immutable snapshot query. # Reusing a branch source ($right) in a subsequent union does NOT mutate or invalidate prior unions. $db.collection('a').doc('a').set(@{ n = 1 }) $db.collection('b').doc('b').set(@{ n = 2 }) $db.collection('c').doc('c').set(@{ n = 3 }) $right = $db.collection('b') $query1 = $db.collection('a').union($right) $query2 = $right.union($db.collection('c')) # Both independent composite queries must return their respective 2 rows $rows1 = @($query1.get()) $rows2 = @($query2.get()) if ($rows1.Count -ne 2) { throw "First union snapshot corrupted: expected 2 rows, got $($rows1.Count)." } if ($rows2.Count -ne 2) { throw "Second union corrupted: expected 2 rows, got $($rows2.Count)." } # True nested union (calling union on an already composite query or passing a composite query) must be explicitly rejected $rejected1 = $false try { [void]$query1.union($db.collection('c')) } catch [System.InvalidOperationException] { $rejected1 = $true } if (-not $rejected1) { throw "Nested union on composite left branch must throw InvalidOperationException." } $rejected2 = $false try { [void]$db.collection('c').union($query1) } catch [System.InvalidOperationException] { $rejected2 = $true } if (-not $rejected2) { throw "Nested union passing composite right branch must throw InvalidOperationException." } } Check-Followup 'Imported legacy index with quoted doc must preserve its unrelated UNIQUE' { param($db) # This fixture requires an external/custom SQLite index definition. It is # not the unquoted definition emitted by the usual historical helper. $p1 = 'a.a_a_a_a_a_a_a_a.a.a.a.a.a_a.a_a_a.a' $p2 = 'a_a.a.a.a_a.a_a.a.a.a_a_a.a.a.a_a.a_a' $clean = $p1.Replace('.', '_') $oldName = "idx_users__${clean}_43CEB4E2" [void]$db.collection('users').create() [void]$db.query("CREATE UNIQUE INDEX ""$oldName"" ON users(json_extract(""doc"",'`$.$p1'));") [void]$db.collection('users').createIndex($p2, @{ unique = $true }) [void]$db.collection('users').dropIndex($p2) $indexes = $db.query("SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_%';") if ($indexes.Count -ne 1 -or $indexes[0].name -cne $oldName) { throw "Expected original imported index; remaining=$($indexes.Count)." } $insert = 'INSERT INTO users(key,doc) VALUES(?,json_set(''{}'',?,?,?,?));' [void]$db.query($insert, @('u1', "`$.$p1", 'same', "`$.$p2", 'one')) $blocked = $false try { [void]$db.query($insert, @('u2', "`$.$p1", 'same', "`$.$p2", 'two')) } catch [System.InvalidOperationException] { $blocked = $true } if (-not $blocked) { throw 'Imported first-path UNIQUE no longer enforced.' } } Check-Followup 'H01 - Subnormal floats must not equal Decimal zero' { param($db) if ([PSLocalBase]::_JsonValuesEqual([double]1e-35, [decimal]0)) { throw '1e-35 must not equal decimal 0' } if ([PSLocalBase]::_JsonValuesEqual([double]::Epsilon, [decimal]0)) { throw 'Epsilon must not equal decimal 0' } if ([PSLocalBase]::_JsonValuesEqual([double]-1e-35, [decimal]0)) { throw '-1e-35 must not equal decimal 0' } if (-not [PSLocalBase]::_JsonValuesEqual([double]0.0, [decimal]0)) { throw '0.0 must equal decimal 0' } } Check-Followup 'H02 - Deserialized JSON PSCustomObject array operations' { param($db) $col = $db.collection('h02_ops') $col.doc('d1').set(@{ tags = @('a', 'b') }) $jsonU = '{"tags": {"$arrayUnion": ["c", "a"]}}' $col.doc('d1').update((ConvertFrom-Json $jsonU)) $resU = $col.doc('d1').get() if (($resU.tags -join ',') -ne 'a,b,c') { throw "Expected a,b,c after PSCustomObject arrayUnion; got: $($resU.tags -join ',')" } $jsonR = '{"tags": {"$arrayRemove": ["b"]}}' $col.doc('d1').update((ConvertFrom-Json $jsonR)) $resR = $col.doc('d1').get() if (($resR.tags -join ',') -ne 'a,c') { throw "Expected a,c after PSCustomObject arrayRemove; got: $($resR.tags -join ',')" } } Check-Followup 'H04 - collectionGroup drop must reject with InvalidOperationException' { param($db) $col = $db.collection('cg_drop_target') $col.add(@{ val = 42 }) $cg = $db.collectionGroup('cg_drop_target') $threw = $false try { $cg.drop() } catch [System.InvalidOperationException] { if ($_.Exception.Message -match 'composite|collectionGroup|read-only') { $threw = $true } } if (-not $threw) { throw 'collectionGroup.drop() must throw InvalidOperationException' } if ($col.count() -ne 1) { throw 'collectionGroup.drop() must not have dropped the underlying collection' } } Write-Host "Followup a526708: passed=$script:passed; failed=$script:failed" if ($script:failed -gt 0) { exit 1 } |