backend: the test suite is portable to Windows (2 product fixes + 15 test fixes) - #148
Open
kai-openswarm wants to merge 4 commits into
Open
backend: the test suite is portable to Windows (2 product fixes + 15 test fixes)#148kai-openswarm wants to merge 4 commits into
kai-openswarm wants to merge 4 commits into
Conversation
The suite had never run on Windows in CI, and a first run there failed twelve tests for reasons that have nothing to do with the code under test: - charmap: three tests read fixture/source files with the platform default encoding (test_browser_provider_agnostic x2, test_skill_replay_vs_send_script) -> read as utf-8, which is what the files are - POSIX-only mechanisms asserted as facts: the shutdown fuse (a documented no-op on Windows), SIGCONT in the ghost reaper, st_mode bits in the credential store -> skipif os.name == 'nt' with the reason spelled out - test_browser_login_handoff pointed at '/nonexistent-dir-xyz' to get an unwritable path; Windows can create it -> a path under a regular file, which is unwritable everywhere - test_the_mac_branch_does_not_try_gcm trusted the host's IS_WIN; on Windows the GCM branch runs (correctly) -> the test pins the flag it means to test Two of the failures were real portability defects in the code, not the tests: list_skill_files and check_publish_capability reported os.path.relpath paths, so on Windows the API answered backslash paths that the frontend joins with '/' and that the SKILL.md-first ordering compares as strings. Both now report posix relative paths on every OS. Ubuntu behaviour is unchanged (the touched tests and the consumers of the two code paths: 72 passed).
compose_turn_system_prompt built the <current_time> block with strftime('%-d')
and strftime('%-I'). Those are glibc extensions; on Windows the C runtime raises
ValueError("Invalid format string"), the surrounding except swallowed it, and
the pin was silently dropped, so on Windows the agent had no notion of "now"
(the very thing the block exists to provide). Format the day/hour without the
non-portable codes. test_system_prompt now asserts the pin carries a real date
and time, which is what fails on Windows without this.
Named by running the suite on windows-latest with the first batch applied: - swarm skill export (backend/apps/swarm/entities/skills.py): supporting-file keys are built with os.path.relpath, so a bundle exported on Windows carried "scripts\go.py" and the importer, which joins the key onto the destination, would recreate a file literally named that on macOS/Linux. Normalise to "/"; test_skills_folders asserted exactly this. - test_app_export_no_stale_files: the fixture wrote the workspace files in text mode, so on Windows the export bytes were CRLF and the byte comparison failed; write with newline="\n". - test_bundled_extracted_modules: the .bin/vite launch shim was a symlink with a forward-slash relative target, which Windows cannot resolve (exists() is False). A plain file is what npm writes there and is what the check reads. - test_disk_resilience: the directory-fsync test guarded on hasattr(os, "O_RDONLY"), which Windows has; json_store skips the directory fsync on Windows by design, so skip the test there. - test_browser_metrics: POSIX mode bits are asserted on POSIX only.
…file The autouse fixture pointed SETTINGS_FILE at tmp_path but left the store's DATA_DIR at the real data dir. atomic_write_settings stages its temp file in DATA_DIR and os.replace()s it onto SETTINGS_FILE, so on a runner where the repo checkout and the temp dir sit on different drives the replace fails with "[WinError 17] The system cannot move the file to a different disk drive"; p_get_install_id swallows that and falls back to an unpersisted id, and test_install_id_persisted fails on the KeyError. Redirect DATA_DIR alongside SETTINGS_FILE, as production keeps them, which also stops the suite from dropping temp files into backend/data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Running
backend/testsonwindows-latestagainstdevfails 19 tests and stalls at the end (the stall is a Linux problem too and is fixed in #147). This PR makes the suite green on Windows, and two of the failures were product bugs on Windows, not test bugs.Product fixes
compose_turn_system_prompt: the<current_time>pin never rendered on Windows. It was built withstrftime('%-d')/strftime('%-I'), which are glibc extensions; on Windows the C runtime raisesValueError, the surroundingexceptswallowed it, and the pin was silently dropped, so on Windows the agent had no notion of "now" — the very thing the block exists to provide. Formatted without the non-portable codes;test_system_promptnow asserts the pin carries a real date and time.os.path.relpath, so a bundle exported on Windows carriedscripts\go.py, and the importer (which joins the key onto the destination) would recreate a file literally named that on macOS/Linux. Same class of fix inskills.py(folder-skill listing) andoutputs/publish_capability.py(API-facing relative paths). Normalised to/.Test fixes (behaviour on Linux/macOS unchanged)
test_browser_cookies_windowsIS_WINwas trueIS_WIN=Falsefor the POSIX casetest_browser_login_handofftest_browser_provider_agnostic,test_skill_replay_vs_send_scriptopen()in the locale codec (charmap) on UTF-8 fixturesencoding="utf-8"test_credential_store::test_written_db_is_owner_only,test_reap_ghost_runtimes(SIGCONT),test_shutdown_fuse_disarms(SIGALRM)skipif os.name == "nt"with the reasontest_app_export_no_stale_filesnewline="\n"test_bundled_extracted_modules.bin/vitesymlink with a forward-slash relative target does not resolve on Windowsexists())test_disk_resilience::…fsyncs_directory…hasattr(os, "O_RDONLY"), which Windows has;json_storeskips the dir fsync on Windows by designnttest_browser_metrics::…secrets_are_scrubbed…test_service::test_install_id_persistedSETTINGS_FILEbut notDATA_DIR; the atomic writer's temp file was on another drive thantmp_path→WinError 17onos.replace, swallowed byp_get_install_idDATA_DIRtoo (also stops the suite dropping temp files intobackend/data)test_skills_folders,test_system_promptProof
Full suite on hosted
windows-latest(Python 3.13 frombackend/requirements.lock) with these commits: 2951 passed / 15 skipped / 0 failed, every collected test ran (collected=2966 ran=2966), 9 min 14 s.ubuntu-lateston the same tree: 2951 passed / 15 skipped, unchanged fromdev.Follow-up
Once this and #147 land, a
windows-latestleg forbackend-tests.ymlis a three-line matrix change (os: [ubuntu-latest, windows-latest],shell: bashon the run steps).