Skip to content

Commit ac0dd17

Browse files
committed
Add comprehensive Pester test suite (143 tests), fix 3 bugs found
New test files (16): - Per-script functional tests covering JSON output, temp fixtures, git repos - Tests for: Find-InProject, Get-GitQuick, Get-ProjectInfo, Get-ProjectContext, Get-PortProcess, Get-ServiceStatus, Get-ScriptConfig, Set-ProjectEnv, Copy-ToClipboard, Watch-LogFile, Invoke-QuickRequest, New-AIRules, Invoke-Artisan, Start-DevServer, helpme, recent-commands Bugs found and fixed during testing: - Find-InProject.ps1: foreach loop var shadowed the Pattern param (case-insensitive) - Get-ServiceStatus.ps1: OrderedDictionary uses Contains() not ContainsKey() - Start-DevServer.ps1: renamed Host param to BindHost to avoid PS automatic var conflict
1 parent b7b7cbf commit ac0dd17

19 files changed

Lines changed: 1113 additions & 11 deletions

Find-InProject.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ $files = Get-ChildItem -Path $searchPath -Recurse -File -ErrorAction SilentlyCon
105105
# Include by type if specified
106106
if ($includePatterns.Count -gt 0) {
107107
$matched = $false
108-
foreach ($pattern in $includePatterns) {
109-
if ($_.Name -like $pattern) {
108+
foreach ($incPattern in $includePatterns) {
109+
if ($_.Name -like $incPattern) {
110110
$matched = $true
111111
break
112112
}

Get-ServiceStatus.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ $checkList = if ($Services -and $Services.Count -gt 0) {
159159
# Check each service
160160
$results = @()
161161
foreach ($key in $checkList) {
162-
if (-not $serviceChecks.ContainsKey($key)) {
162+
if (-not $serviceChecks.Contains($key)) {
163163
$results += [ordered]@{
164164
id = $key
165165
name = $key

Start-DevServer.ps1

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ param(
3030

3131
[int]$Port,
3232

33-
[string]$Host = 'localhost'
33+
[string]$BindHost = 'localhost'
3434
)
3535

3636
# Detect project type
@@ -101,7 +101,7 @@ if ($portInUse) {
101101

102102
Write-Host ""
103103
Write-Host "Starting $Type dev server on " -NoNewline -ForegroundColor Cyan
104-
Write-Host "http://${Host}:${Port}" -ForegroundColor Yellow
104+
Write-Host "http://${BindHost}:${Port}" -ForegroundColor Yellow
105105
Write-Host "Press Ctrl+C to stop" -ForegroundColor DarkGray
106106
Write-Host ""
107107

@@ -141,41 +141,41 @@ switch ($Type) {
141141

142142
Write-Host "Document root: $docRoot" -ForegroundColor DarkGray
143143
Write-Host ""
144-
php -S "${Host}:${Port}" -t $docRoot
144+
php -S "${BindHost}:${Port}" -t $docRoot
145145
}
146146

147147
'laravel' {
148148
Write-Host "Running: php artisan serve --port=$Port" -ForegroundColor DarkGray
149149
Write-Host ""
150-
php artisan serve --host=$Host --port=$Port
150+
php artisan serve --host=$BindHost --port=$Port
151151
}
152152

153153
'python' {
154154
# Django
155155
if (Test-Path '.\manage.py') {
156156
Write-Host "Running: python manage.py runserver ${Port}" -ForegroundColor DarkGray
157157
Write-Host ""
158-
python manage.py runserver "${Host}:${Port}"
158+
python manage.py runserver "${BindHost}:${Port}"
159159
}
160160
# Flask
161161
elseif (Test-Path '.\app.py') {
162162
Write-Host "Running: flask run --port $Port" -ForegroundColor DarkGray
163163
Write-Host ""
164164
$env:FLASK_APP = 'app.py'
165165
$env:FLASK_ENV = 'development'
166-
flask run --host=$Host --port=$Port
166+
flask run --host=$BindHost --port=$Port
167167
}
168168
# FastAPI / Uvicorn
169169
elseif (Test-Path '.\main.py') {
170170
Write-Host "Running: uvicorn main:app --port $Port" -ForegroundColor DarkGray
171171
Write-Host ""
172-
uvicorn main:app --host $Host --port $Port --reload
172+
uvicorn main:app --host $BindHost --port $Port --reload
173173
}
174174
else {
175175
# Simple Python HTTP server
176176
Write-Host "Running: python -m http.server $Port" -ForegroundColor DarkGray
177177
Write-Host ""
178-
python -m http.server $Port --bind $Host
178+
python -m http.server $Port --bind $BindHost
179179
}
180180
}
181181
}

tests/Copy-ToClipboard.Tests.ps1

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
$scriptDir = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
2+
3+
Describe "Copy-ToClipboard" {
4+
It "Should copy file contents to clipboard" {
5+
$dir = Join-Path $env:TEMP "pester-clip-$(Get-Random)"
6+
New-Item -Path $dir -ItemType Directory -Force | Out-Null
7+
try {
8+
Set-Content "$dir\test.txt" "clipboard test content"
9+
& "$scriptDir\Copy-ToClipboard.ps1" -Path "$dir\test.txt" 2>$null | Out-Null
10+
$clip = Get-Clipboard
11+
($clip -match 'clipboard test content') | Should Be $true
12+
} finally {
13+
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
14+
}
15+
}
16+
17+
It "Should copy current directory with -Pwd" {
18+
$before = Get-Location
19+
try {
20+
& "$scriptDir\Copy-ToClipboard.ps1" -Pwd 2>$null | Out-Null
21+
$clip = Get-Clipboard
22+
$clip | Should Be (Get-Location).Path
23+
} finally {
24+
Set-Location $before
25+
}
26+
}
27+
28+
It "Should copy file path with -PathOnly" {
29+
$dir = Join-Path $env:TEMP "pester-clip-path-$(Get-Random)"
30+
New-Item -Path $dir -ItemType Directory -Force | Out-Null
31+
try {
32+
Set-Content "$dir\test.txt" "content"
33+
& "$scriptDir\Copy-ToClipboard.ps1" -Path "$dir\test.txt" -PathOnly 2>$null | Out-Null
34+
$clip = Get-Clipboard
35+
($clip -like "*test.txt") | Should Be $true
36+
} finally {
37+
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
38+
}
39+
}
40+
41+
It "Should exit 1 for missing file" {
42+
& "$scriptDir\Copy-ToClipboard.ps1" -Path "C:\nonexistent_file_xyz.txt" 2>$null | Out-Null
43+
$LASTEXITCODE | Should Be 1
44+
}
45+
46+
It "Should show usage with no arguments" {
47+
$output = & "$scriptDir\Copy-ToClipboard.ps1" *>&1 | Out-String
48+
($output -match 'Usage') | Should Be $true
49+
}
50+
}

tests/Find-InProject.Tests.ps1

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
$scriptDir = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
2+
3+
Describe "Find-InProject" {
4+
function New-SearchFixture {
5+
$dir = Join-Path $env:TEMP "pester-search-$(Get-Random)"
6+
New-Item -Path $dir -ItemType Directory -Force | Out-Null
7+
Set-Content "$dir\app.php" "<?php`nfunction login() {`n return true;`n}`n"
8+
Set-Content "$dir\utils.js" "export function login() {`n return false;`n}`n"
9+
Set-Content "$dir\readme.txt" "This is a readme file with no matches."
10+
New-Item -Path "$dir\node_modules" -ItemType Directory -Force | Out-Null
11+
Set-Content "$dir\node_modules\dep.js" "function login() { }`n"
12+
New-Item -Path "$dir\vendor" -ItemType Directory -Force | Out-Null
13+
Set-Content "$dir\vendor\lib.php" "function login() { }`n"
14+
return $dir
15+
}
16+
17+
It "Should find matches and return correct JSON structure" {
18+
$tempDir = New-SearchFixture
19+
try {
20+
$raw = & "$scriptDir\Find-InProject.ps1" -Pattern "login" -Path $tempDir -AsJson 2>$null
21+
$result = $raw | ConvertFrom-Json
22+
$result.pattern | Should Be "login"
23+
$result.totalMatches | Should BeGreaterThan 0
24+
$result.fileCount | Should BeGreaterThan 0
25+
($result.results | Measure-Object).Count | Should BeGreaterThan 0
26+
} finally {
27+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
28+
}
29+
}
30+
31+
It "Should return match line numbers" {
32+
$tempDir = New-SearchFixture
33+
try {
34+
$raw = & "$scriptDir\Find-InProject.ps1" -Pattern "login" -Path $tempDir -AsJson 2>$null
35+
$result = $raw | ConvertFrom-Json
36+
$firstMatch = $result.results[0].matches[0]
37+
$firstMatch.line | Should BeGreaterThan 0
38+
$firstMatch.content | Should Not BeNullOrEmpty
39+
} finally {
40+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
41+
}
42+
}
43+
44+
It "Should respect -Type filter for php" {
45+
$tempDir = New-SearchFixture
46+
try {
47+
$raw = & "$scriptDir\Find-InProject.ps1" "login" -Type "php" -Path $tempDir -AsJson 2>$null
48+
$result = $raw | ConvertFrom-Json
49+
$result.fileCount | Should Be 1
50+
$files = $result.results | ForEach-Object { $_.file }
51+
($files | Where-Object { $_ -like "*.php" } | Measure-Object).Count | Should Be 1
52+
} finally {
53+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
54+
}
55+
}
56+
57+
It "Should exclude node_modules and vendor directories" {
58+
$tempDir = New-SearchFixture
59+
try {
60+
$raw = & "$scriptDir\Find-InProject.ps1" -Pattern "login" -Path $tempDir -AsJson 2>$null
61+
$result = $raw | ConvertFrom-Json
62+
$files = $result.results | ForEach-Object { $_.file }
63+
$hasNodeModules = ($files | Where-Object { $_ -match 'node_modules' } | Measure-Object).Count
64+
$hasVendor = ($files | Where-Object { $_ -match 'vendor' } | Measure-Object).Count
65+
$hasNodeModules | Should Be 0
66+
$hasVendor | Should Be 0
67+
} finally {
68+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
69+
}
70+
}
71+
72+
It "Should be case-insensitive by default" {
73+
$tempDir = New-SearchFixture
74+
try {
75+
$raw = & "$scriptDir\Find-InProject.ps1" -Pattern "LOGIN" -Path $tempDir -AsJson 2>$null
76+
$result = $raw | ConvertFrom-Json
77+
$result.totalMatches | Should BeGreaterThan 0
78+
} finally {
79+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
80+
}
81+
}
82+
83+
It "Should respect -CaseSensitive flag" {
84+
$tempDir = New-SearchFixture
85+
try {
86+
$raw = & "$scriptDir\Find-InProject.ps1" -Pattern "LOGIN" -CaseSensitive -Path $tempDir -AsJson 2>$null
87+
$result = $raw | ConvertFrom-Json
88+
$result.totalMatches | Should Be 0
89+
} finally {
90+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
91+
}
92+
}
93+
94+
It "Should return zero matches for non-matching pattern" {
95+
$tempDir = New-SearchFixture
96+
try {
97+
$raw = & "$scriptDir\Find-InProject.ps1" -Pattern "zzz_no_match_zzz" -Path $tempDir -AsJson 2>$null
98+
$result = $raw | ConvertFrom-Json
99+
$result.totalMatches | Should Be 0
100+
$result.fileCount | Should Be 0
101+
} finally {
102+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
103+
}
104+
}
105+
106+
It "Should find matches in both php and js files" {
107+
$tempDir = New-SearchFixture
108+
try {
109+
$raw = & "$scriptDir\Find-InProject.ps1" -Pattern "login" -Path $tempDir -AsJson 2>$null
110+
$result = $raw | ConvertFrom-Json
111+
$result.fileCount | Should Be 2
112+
} finally {
113+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
114+
}
115+
}
116+
}

tests/Get-GitQuick.Tests.ps1

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
$scriptDir = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
2+
3+
function New-TempGitRepo {
4+
$dir = Join-Path $env:TEMP "pester-git-$(Get-Random)"
5+
New-Item -Path $dir -ItemType Directory -Force | Out-Null
6+
Push-Location $dir
7+
git init . 2>$null | Out-Null
8+
git config user.email "test@test.com" 2>$null
9+
git config user.name "Test" 2>$null
10+
git commit --allow-empty -m "init" 2>$null | Out-Null
11+
Pop-Location
12+
return $dir
13+
}
14+
15+
Describe "Get-GitQuick" {
16+
It "Should return correct JSON schema for clean repo" {
17+
$tempDir = New-TempGitRepo
18+
Push-Location $tempDir
19+
try {
20+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
21+
$result = $raw | ConvertFrom-Json
22+
($null -ne $result.branch) | Should Be $true
23+
($null -ne $result.ahead) | Should Be $true
24+
($null -ne $result.behind) | Should Be $true
25+
($null -ne $result.staged) | Should Be $true
26+
($null -ne $result.modified) | Should Be $true
27+
($null -ne $result.untracked) | Should Be $true
28+
($null -ne $result.clean) | Should Be $true
29+
($null -ne $result.files) | Should Be $true
30+
} finally {
31+
Pop-Location
32+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
33+
}
34+
}
35+
36+
It "Should report clean = true on clean repo" {
37+
$tempDir = New-TempGitRepo
38+
Push-Location $tempDir
39+
try {
40+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
41+
$result = $raw | ConvertFrom-Json
42+
$result.clean | Should Be $true
43+
$result.staged | Should Be 0
44+
$result.modified | Should Be 0
45+
$result.untracked | Should Be 0
46+
} finally {
47+
Pop-Location
48+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
49+
}
50+
}
51+
52+
It "Should detect untracked files" {
53+
$tempDir = New-TempGitRepo
54+
Push-Location $tempDir
55+
try {
56+
Set-Content "newfile.txt" "hello"
57+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
58+
$result = $raw | ConvertFrom-Json
59+
$result.clean | Should Be $false
60+
$result.untracked | Should Be 1
61+
} finally {
62+
Pop-Location
63+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
64+
}
65+
}
66+
67+
It "Should detect staged files" {
68+
$tempDir = New-TempGitRepo
69+
Push-Location $tempDir
70+
try {
71+
Set-Content "staged.txt" "content"
72+
git add staged.txt 2>$null
73+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
74+
$result = $raw | ConvertFrom-Json
75+
$result.staged | Should Be 1
76+
$result.clean | Should Be $false
77+
} finally {
78+
Pop-Location
79+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
80+
}
81+
}
82+
83+
It "Should detect modified files" {
84+
$tempDir = New-TempGitRepo
85+
Push-Location $tempDir
86+
try {
87+
Set-Content "tracked.txt" "original"
88+
git add tracked.txt 2>$null
89+
git commit -m "add tracked" 2>$null | Out-Null
90+
Set-Content "tracked.txt" "modified"
91+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
92+
$result = $raw | ConvertFrom-Json
93+
$result.modified | Should Be 1
94+
} finally {
95+
Pop-Location
96+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
97+
}
98+
}
99+
100+
It "Should report correct branch name" {
101+
$tempDir = New-TempGitRepo
102+
Push-Location $tempDir
103+
try {
104+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
105+
$result = $raw | ConvertFrom-Json
106+
($result.branch -eq 'main' -or $result.branch -eq 'master') | Should Be $true
107+
} finally {
108+
Pop-Location
109+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
110+
}
111+
}
112+
113+
It "Should report stash count" {
114+
$tempDir = New-TempGitRepo
115+
Push-Location $tempDir
116+
try {
117+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
118+
$result = $raw | ConvertFrom-Json
119+
$result.stashes | Should Be 0
120+
} finally {
121+
Pop-Location
122+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
123+
}
124+
}
125+
126+
It "Should have files object with sub-arrays" {
127+
$tempDir = New-TempGitRepo
128+
Push-Location $tempDir
129+
try {
130+
$raw = & "$scriptDir\Get-GitQuick.ps1" -AsJson 2>$null
131+
$result = $raw | ConvertFrom-Json
132+
($null -ne $result.files.staged) | Should Be $true
133+
($null -ne $result.files.modified) | Should Be $true
134+
($null -ne $result.files.deleted) | Should Be $true
135+
($null -ne $result.files.untracked) | Should Be $true
136+
($null -ne $result.files.conflicts) | Should Be $true
137+
} finally {
138+
Pop-Location
139+
Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)