fix: address PR reviews, add scheduler Windows tasks and Pester coverage
This commit is contained in:
@@ -9,7 +9,7 @@ def test_resolve_alfworld_gamefile_uses_alfworld_data_for_relative_paths(monkeyp
|
||||
|
||||
resolved = _resolve_alfworld_gamefile("json_2.1.1/valid_seen/task/game.tw-pddl")
|
||||
|
||||
assert resolved == os.path.join(str(data_root), "json_2.1.1/valid_seen/task/game.tw-pddl")
|
||||
assert resolved == os.path.normpath(os.path.join(str(data_root), "json_2.1.1/valid_seen/task/game.tw-pddl"))
|
||||
|
||||
|
||||
def test_resolve_alfworld_gamefile_keeps_absolute_paths(monkeypatch, tmp_path):
|
||||
|
||||
@@ -31,6 +31,7 @@ def _run(script, args, env, cwd):
|
||||
return proc.returncode, proc.stdout, proc.stderr
|
||||
|
||||
|
||||
@unittest.skipIf(sys.platform == "win32", "Unix bash runner tests require POSIX/bash environment")
|
||||
class TestRunnerSourceCheckout(unittest.TestCase):
|
||||
"""When skillopt_sleep/ is found in the repo, the source-checkout path is used."""
|
||||
|
||||
@@ -46,6 +47,7 @@ class TestRunnerSourceCheckout(unittest.TestCase):
|
||||
self.assertIn("status", out)
|
||||
|
||||
|
||||
@unittest.skipIf(sys.platform == "win32", "Unix bash runner tests require POSIX/bash environment")
|
||||
class TestRunnerCliFallback(unittest.TestCase):
|
||||
"""When no source dir is found, fall back to the skillopt-sleep CLI on PATH."""
|
||||
|
||||
@@ -101,6 +103,7 @@ class TestRunnerCliFallback(unittest.TestCase):
|
||||
self.assertIn("fake-cli invoked: status", out)
|
||||
|
||||
|
||||
@unittest.skipIf(sys.platform == "win32", "Unix bash runner tests require POSIX/bash environment")
|
||||
class TestRunnerNoEngine(unittest.TestCase):
|
||||
"""When no source dir, no CLI on PATH, and no importable package, error cleanly."""
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
Describe "run-sleep.ps1 runner" {
|
||||
BeforeAll {
|
||||
$script = Join-Path $PSScriptRoot "..\plugins\run-sleep.ps1"
|
||||
$tempDir = [System.IO.Path]::GetTempFileName()
|
||||
Remove-Item $tempDir
|
||||
New-Item -ItemType Directory -Path $tempDir | Out-Null
|
||||
|
||||
# Detect a working python to bypass Windows Store alias issues
|
||||
if (-not $env:SKILLOPT_SLEEP_PYTHON) {
|
||||
if (Test-Path "C:\Python314\python.exe") {
|
||||
$env:SKILLOPT_SLEEP_PYTHON = "C:\Python314\python.exe"
|
||||
} elseif (Test-Path "C:\Python313\python.exe") {
|
||||
$env:SKILLOPT_SLEEP_PYTHON = "C:\Python313\python.exe"
|
||||
} elseif (Test-Path "C:\Python312\python.exe") {
|
||||
$env:SKILLOPT_SLEEP_PYTHON = "C:\Python312\python.exe"
|
||||
} elseif (Test-Path "C:\Python311\python.exe") {
|
||||
$env:SKILLOPT_SLEEP_PYTHON = "C:\Python311\python.exe"
|
||||
} elseif (Test-Path "C:\Python310\python.exe") {
|
||||
$env:SKILLOPT_SLEEP_PYTHON = "C:\Python310\python.exe"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if (Test-Path $tempDir) {
|
||||
Remove-Item -Recurse -Force $tempDir
|
||||
}
|
||||
}
|
||||
|
||||
It "runs successfully in source checkout mode" {
|
||||
$env:SKILLOPT_SLEEP_REPO = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
# Run help
|
||||
$result = powershell -File $script "--help" 2>&1
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$result | Out-String | Should Match "skillopt_sleep"
|
||||
}
|
||||
|
||||
It "falls back to CLI on PATH" {
|
||||
# Create a temp dir for isolated testing
|
||||
$sandbox = Join-Path $tempDir "cli_fallback"
|
||||
New-Item -ItemType Directory -Path $sandbox | Out-Null
|
||||
$scriptCopy = Join-Path $sandbox "run-sleep.ps1"
|
||||
Copy-Item $script $scriptCopy
|
||||
|
||||
# Create fake CLI
|
||||
$binDir = Join-Path $sandbox "bin"
|
||||
New-Item -ItemType Directory -Path $binDir | Out-Null
|
||||
$fakeCli = Join-Path $binDir "skillopt-sleep.cmd"
|
||||
"@echo off`r`necho fake-cli invoked %*`r`nexit /b 0" | Out-File -FilePath $fakeCli -Encoding ascii
|
||||
|
||||
# Save existing env vars
|
||||
$oldPath = $env:PATH
|
||||
$oldRepo = $env:SKILLOPT_SLEEP_REPO
|
||||
$oldPlugin = $env:CLAUDE_PLUGIN_ROOT
|
||||
|
||||
$env:PATH = "$binDir;$oldPath"
|
||||
$env:SKILLOPT_SLEEP_REPO = $null
|
||||
$env:CLAUDE_PLUGIN_ROOT = $null
|
||||
|
||||
$oldLocation = Get-Location
|
||||
Set-Location $sandbox
|
||||
try {
|
||||
$result = powershell -File $scriptCopy "status" 2>&1
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$result | Out-String | Should Match "fake-cli invoked status"
|
||||
}
|
||||
finally {
|
||||
Set-Location $oldLocation
|
||||
$env:PATH = $oldPath
|
||||
$env:SKILLOPT_SLEEP_REPO = $oldRepo
|
||||
$env:CLAUDE_PLUGIN_ROOT = $oldPlugin
|
||||
}
|
||||
}
|
||||
|
||||
It "propagates exit code on failure" {
|
||||
$env:SKILLOPT_SLEEP_REPO = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
# Run a non-existent command to trigger python module failure
|
||||
$result = powershell -File $script "non-existent-subcommand" 2>&1
|
||||
$LASTEXITCODE | Should Not Be 0
|
||||
}
|
||||
|
||||
It "handles paths containing spaces correctly" {
|
||||
# Create path containing spaces
|
||||
$spaceDir = Join-Path $tempDir "path with spaces"
|
||||
New-Item -ItemType Directory -Path $spaceDir | Out-Null
|
||||
$scriptCopy = Join-Path $spaceDir "run-sleep.ps1"
|
||||
Copy-Item $script $scriptCopy
|
||||
|
||||
# Create fake CLI
|
||||
$binDir = Join-Path $spaceDir "bin"
|
||||
New-Item -ItemType Directory -Path $binDir | Out-Null
|
||||
$fakeCli = Join-Path $binDir "skillopt-sleep.cmd"
|
||||
"@echo off`r`necho fake-cli invoked %*`r`nexit /b 0" | Out-File -FilePath $fakeCli -Encoding ascii
|
||||
|
||||
# Save env
|
||||
$oldPath = $env:PATH
|
||||
$oldRepo = $env:SKILLOPT_SLEEP_REPO
|
||||
$oldPlugin = $env:CLAUDE_PLUGIN_ROOT
|
||||
|
||||
$env:PATH = "$binDir;$oldPath"
|
||||
$env:SKILLOPT_SLEEP_REPO = $null
|
||||
$env:CLAUDE_PLUGIN_ROOT = $null
|
||||
|
||||
$oldLocation = Get-Location
|
||||
Set-Location $spaceDir
|
||||
try {
|
||||
$result = powershell -File $scriptCopy "status" 2>&1
|
||||
$LASTEXITCODE | Should Be 0
|
||||
$result | Out-String | Should Match "fake-cli invoked status"
|
||||
}
|
||||
finally {
|
||||
Set-Location $oldLocation
|
||||
$env:PATH = $oldPath
|
||||
$env:SKILLOPT_SLEEP_REPO = $oldRepo
|
||||
$env:CLAUDE_PLUGIN_ROOT = $oldPlugin
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
class TestSchedulerWindows(unittest.TestCase):
|
||||
@mock.patch("sys.platform", "win32")
|
||||
@mock.patch("shutil.which", return_value="C:\\Windows\\System32\\schtasks.exe")
|
||||
def test_schedule_windows(self, mock_which):
|
||||
from skillopt_sleep.scheduler import schedule
|
||||
|
||||
calls = []
|
||||
def fake_run(cmd, **kwargs):
|
||||
calls.append(cmd)
|
||||
class Proc:
|
||||
returncode = 0
|
||||
stdout = "SUCCESS: The scheduled task ... has successfully been created."
|
||||
stderr = ""
|
||||
return Proc()
|
||||
|
||||
with mock.patch("subprocess.run", side_effect=fake_run):
|
||||
ok, msg = schedule("/p/my project", backend="mock", hour=3, minute=17)
|
||||
self.assertTrue(ok)
|
||||
self.assertIn("via Windows Task Scheduler", msg)
|
||||
self.assertEqual(len(calls), 1)
|
||||
cmd = calls[0]
|
||||
self.assertEqual(cmd[0], "schtasks")
|
||||
self.assertEqual(cmd[1], "/create")
|
||||
self.assertEqual(cmd[2], "/tn")
|
||||
self.assertTrue(cmd[3].startswith("SkillOpt-Sleep-"))
|
||||
self.assertIn("my_project", cmd[3])
|
||||
self.assertEqual(cmd[4], "/tr")
|
||||
self.assertIn("cmd.exe", cmd[5])
|
||||
self.assertEqual(cmd[6], "/sc")
|
||||
self.assertEqual(cmd[7], "daily")
|
||||
self.assertEqual(cmd[8], "/st")
|
||||
self.assertEqual(cmd[9], "03:17")
|
||||
self.assertEqual(cmd[10], "/f")
|
||||
|
||||
@mock.patch("sys.platform", "win32")
|
||||
@mock.patch("shutil.which", return_value="C:\\Windows\\System32\\schtasks.exe")
|
||||
def test_unschedule_windows(self, mock_which):
|
||||
from skillopt_sleep.scheduler import unschedule
|
||||
|
||||
calls = []
|
||||
def fake_run(cmd, **kwargs):
|
||||
calls.append(cmd)
|
||||
class Proc:
|
||||
returncode = 0
|
||||
stdout = "SUCCESS: The scheduled task ... was successfully deleted."
|
||||
stderr = ""
|
||||
return Proc()
|
||||
|
||||
with mock.patch("subprocess.run", side_effect=fake_run):
|
||||
ok, msg = unschedule("/p/my project")
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(len(calls), 1)
|
||||
cmd = calls[0]
|
||||
self.assertEqual(cmd[0], "schtasks")
|
||||
self.assertEqual(cmd[1], "/delete")
|
||||
self.assertEqual(cmd[2], "/tn")
|
||||
self.assertTrue(cmd[3].startswith("SkillOpt-Sleep-"))
|
||||
self.assertEqual(cmd[4], "/f")
|
||||
@@ -770,6 +770,7 @@ class TestCodexBackend(unittest.TestCase):
|
||||
def test_codex_resolve_path_windows(self):
|
||||
from skillopt_sleep.backend import resolve_codex_path
|
||||
with mock.patch("sys.platform", "win32"), \
|
||||
mock.patch("shutil.which", return_value=None), \
|
||||
mock.patch.dict("os.environ", {
|
||||
"APPDATA": r"C:\Users\Sparsh\AppData\Roaming",
|
||||
"USERPROFILE": r"C:\Users\Sparsh",
|
||||
|
||||
Reference in New Issue
Block a user