#[test] fn quiet_update_keeps_prebuilt_enabled_by_default() { let install = include_str!("../install.sh"); assert!(install.contains("use_prebuilt=\"${DOSH_USE_PREBUILT:-1}\"")); assert!( !install.contains("use_prebuilt=0"), "quiet updates must not force source builds" ); } #[test] fn release_scripts_skip_stale_artifacts_when_auto_discovering() { let upload = include_str!("../scripts/upload-gitea-release.sh"); assert!(upload.contains("skipping $artifact: version")); assert!(upload.contains("expected_version")); let publish = include_str!("../scripts/publish-gitea-release.sh"); assert!(publish.contains("skipping stale artifact")); assert!(publish.contains("actual=\"$(artifact_version \"$artifact\" || true)\"")); } #[test] fn release_upload_refuses_partial_platform_sets_by_default() { let upload = include_str!("../scripts/upload-gitea-release.sh"); assert!(upload.contains("require_complete_release_artifacts \"$@\"")); assert!(upload.contains("DOSH_RELEASE_ALLOW_PARTIAL=1")); assert!(upload.contains("refusing partial release upload")); for name in [ "dosh-linux-x86_64.tar.gz", "dosh-macos-aarch64.tar.gz", "dosh-macos-x86_64.tar.gz", "dosh-windows-x86_64.zip", ] { assert!( upload.contains(name), "release upload completeness check must require {name}" ); } } #[test] fn release_upload_marks_prerelease_tags_as_prereleases() { let upload = include_str!("../scripts/upload-gitea-release.sh"); assert!(upload.contains("release_prerelease=false")); assert!(upload.contains("*-*) release_prerelease=true")); assert!(upload.contains("\"prerelease\":%s")); assert!(upload.contains("metadata_payload=")); assert!( upload.contains("-X PATCH"), "existing Gitea releases must be corrected when prerelease metadata changes" ); } #[test] fn release_gates_test_all_targets() { let makefile = include_str!("../Makefile"); let ci = include_str!("../.github/workflows/ci.yml"); assert!(makefile.contains("test:\n\tcargo test --all-targets")); assert!(makefile.contains("cargo test --all-targets\n\t$(MAKE) tui-harness")); assert!(ci.contains("run: sh -n install.sh scripts/*.sh")); assert!(ci.contains("run: cargo clippy --all-targets -- -D warnings")); assert!(ci.contains("run: cargo test --all-targets")); assert!(ci.contains("run: sh scripts/tui-harness.sh")); } #[test] fn one_dot_zero_gates_default_to_full_length_soaks() { let makefile = include_str!("../Makefile"); let reconnect_check = makefile .split("reconnect-check:") .nth(1) .and_then(|tail| tail.split("\nhostile-network-check:").next()) .expect("reconnect-check target"); assert!(reconnect_check.contains("DOSH_SOAK_SECONDS=$${DOSH_SOAK_SECONDS:-1800}")); assert!(!reconnect_check.contains("DOSH_SOAK_SECONDS=$${DOSH_SOAK_SECONDS:-300}")); let hostile_check = makefile .split("hostile-network-check:") .nth(1) .and_then(|tail| tail.split("\npersistence-check:").next()) .expect("hostile-network-check target"); assert!(hostile_check.contains("DOSH_BADNET_SOAK_SECONDS=$${DOSH_BADNET_SOAK_SECONDS:-1800}")); assert!(!hostile_check.contains("DOSH_BADNET_SOAK_SECONDS=$${DOSH_BADNET_SOAK_SECONDS:-300}")); } #[test] fn installers_skip_stale_latest_release_prebuilts() { let install = include_str!("../install.sh"); assert!(install.contains("latest_release_is_stale")); assert!(install.contains("current_source_version")); assert!(install.contains("latest release $latest is older than source $current")); assert!(install.contains("version_prerelease")); assert!(install.contains("prerelease_less_than")); assert!( install.contains("if latest_release_is_stale; then"), "unix installer must skip stale latest before downloading a prebuilt" ); let ps1 = include_str!("../install.ps1"); assert!(ps1.contains("Latest-ReleaseIsStale")); assert!(ps1.contains("Current-SourceVersion")); assert!(ps1.contains("latest release $latestVersion is older than source $current")); assert!(ps1.contains("Version-Prerelease")); assert!(ps1.contains("Compare-Prerelease")); assert!( ps1.contains("if (Latest-ReleaseIsStale)"), "windows installer must skip stale latest before downloading a prebuilt" ); } #[test] fn installers_refuse_prebuilts_without_checksums() { let install = include_str!("../install.sh"); assert!(install.contains("verify_archive_checksum")); assert!(install.contains("refusing unverified binary for $url")); assert!( !install.contains("continuing without sidecar verification"), "unix installer must not silently install an unverifiable prebuilt" ); let ps1 = include_str!("../install.ps1"); assert!(ps1.contains("Verify-ArchiveChecksum")); assert!(ps1.contains("refusing unverified binary for $Url")); assert!( !ps1.contains("continuing without sidecar verification"), "windows installer must not silently install an unverifiable prebuilt" ); } #[test] fn windows_installer_reuses_persistent_source_update_cache() { let ps1 = include_str!("../install.ps1"); assert!(ps1.contains("DOSH_UPDATE_CACHE")); assert!(ps1.contains("Assert-NoRelativePathSegments($Path)")); assert!(ps1.contains("Assert-NoRelativePathSegments $Path")); assert!(ps1.contains("$segment -eq \".\" -or $segment -eq \"..\"")); assert!(ps1.contains("$sourceCache = Assert-SafeUpdateCache $UpdateCache")); assert!(ps1.contains("git -C $sourceCache fetch --depth 1 origin main")); assert!(ps1.contains("git -C $sourceCache checkout -q -B main FETCH_HEAD")); assert!(ps1.contains("git clone --depth 1 --branch main $Repo $sourceCache")); assert!( !ps1.contains("git clone --depth 1 $Repo $tmp"), "Windows source updates should reuse a persistent cache instead of recloning into temp" ); } #[test] fn unix_installer_rejects_unsafe_source_update_cache_paths() { let install = include_str!("../install.sh"); assert!(install.contains("normalize_update_cache_path()")); assert!(install.contains("safe_update_cache_path()")); assert!(install.contains("update_cache=\"$(safe_update_cache_path \"$update_cache\")\"")); for pattern in [ "\"\"|\"/\"|\"$home_norm\"|\"$home_cache_norm\"", "\".\"|\"..\"|./*|../*|*/.|*/..|*/./*|*/../*", ] { assert!( install.contains(pattern), "Unix installer must reject unsafe update cache pattern {pattern}" ); } assert!( install.contains("rm -rf \"$update_cache\""), "source cache replacement remains the destructive operation guarded by safe_update_cache_path" ); } #[test] fn installers_generate_same_native_auth_client_defaults() { let install = include_str!("../install.sh"); let ps1 = include_str!("../install.ps1"); for setting in [ "auth_preference = \"native,ssh\"", "trust_on_first_use = false", "native_auth_timeout_ms = 700", "known_hosts = \"~/.config/dosh/known_hosts\"", "identity_files = [\"~/.ssh/id_ed25519\"]", "use_ssh_agent = true", "forward_agent = false", ] { assert!( install.contains(setting), "Unix installer missing client default {setting}" ); assert!( ps1.contains(setting), "Windows installer missing client default {setting}" ); } } #[test] fn windows_installer_generates_hosts_config_like_unix() { let install = include_str!("../install.sh"); let ps1 = include_str!("../install.ps1"); for setting in [ "hosts.toml", "# default_command = \"tm\"", "[default]", "predict = true", ] { assert!( install.contains(setting), "Unix installer missing host config setting {setting}" ); assert!( ps1.contains(setting), "Windows installer missing host config setting {setting}" ); } assert!(ps1.contains("$hostUdp = if ($DoshHost) { $DoshHost } else { $defaultServer }")); assert!(ps1.contains("Set-Content -NoNewline -Encoding utf8 $hostsConfig")); } #[test] fn package_check_verifies_only_artifacts_it_builds() { let makefile = include_str!("../Makefile"); let verify = include_str!("../scripts/verify-release-artifacts.sh"); assert!( verify.contains("if [ \"$#\" -gt 0 ]; then"), "release verifier must accept an explicit artifact list" ); let package_check = makefile .split("package-check:") .nth(1) .and_then(|tail| tail.split("\ninstall:").next()) .expect("package-check target"); assert!(package_check.contains("package-release-linux")); assert!(package_check.contains("package-release-windows")); assert!(package_check.contains("dosh-linux-x86_64.tar.gz")); assert!(package_check.contains("dosh-windows-x86_64.zip")); assert!( !package_check.contains("dosh-macos-aarch64.tar.gz"), "Linux package-check does not build macOS artifacts, so it must not depend on one" ); } #[test] fn complete_release_verifier_requires_both_macos_architectures() { let verify = include_str!("../scripts/verify-release-artifacts.sh"); assert!(verify.contains("dosh-linux-x86_64.tar.gz")); assert!(verify.contains("dosh-macos-aarch64.tar.gz")); assert!(verify.contains("dosh-macos-x86_64.tar.gz")); assert!(verify.contains("dosh-windows-x86_64.zip")); } #[test] fn release_verifier_checks_checksums_and_expected_binaries() { let verify = include_str!("../scripts/verify-release-artifacts.sh"); assert!(verify.contains("verify_checksum()")); assert!(verify.contains("release checksum mismatch")); for member in [ "dosh/bin/dosh", "dosh/bin/dosh-client", "dosh/bin/dosh-server", "dosh/bin/dosh-auth", "dosh/bin/dosh-bench", "dosh/bin/dosh.exe", "dosh/bin/dosh-client.exe", "dosh/bin/dosh-bench.exe", ] { assert!( verify.contains(member), "release verifier must require archive member {member}" ); } assert!(verify.contains("artifact missing expected member")); } #[test] fn package_release_stamps_git_build_info() { let package = include_str!("../scripts/package-release.sh"); let build = include_str!("../build.rs"); for name in [ "DOSH_BUILD_GIT_HASH", "DOSH_BUILD_COMMIT_DATE", "DOSH_BUILD_GIT_DIRTY", ] { assert!(package.contains(name), "package script must export {name}"); assert!(build.contains(name), "build.rs must consume {name}"); } assert!(package.contains("git rev-parse --short=12 HEAD")); assert!(package.contains("git show -s --format=%cs HEAD")); assert!(build.contains("cargo:rerun-if-env-changed=DOSH_BUILD_GIT_HASH")); } #[test] fn systemd_service_does_not_sandbox_remote_shells() { let service = include_str!("../packaging/systemd/dosh-server.service"); let install = include_str!("../install.sh"); for raw in [service, install] { assert!(raw.contains("KillMode=process")); assert!( raw.contains("Restart=always"), "dosh-server should come back after accidental SIGTERM while preserving child shells" ); for directive in [ "NoNewPrivileges=", "PrivateTmp=", "ProtectSystem=", "ProtectHome=", "RestrictAddressFamilies=", "RestrictRealtime=", "LockPersonality=", "MemoryDenyWriteExecute=", ] { assert!( !raw.contains(directive), "dosh sessions are user shells; systemd sandbox directive {directive} changes terminal/app behavior" ); } assert!( !raw.contains("ReadWritePaths="), "remote shells must not be restricted to dosh config/data paths" ); } }