/****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_io.reef Authors: Chris Tusa License: Description: Tests: registry file I/O ******************************************************************************/ import config import test.framework import core.result_generic as rg import io.dir as iodir import io.file as iofile import sys.process as pr proc main() let runner = new framework.TestRunner() // Set up a fresh temp dir as fake $HOME let tmp: string = "/tmp/repoman-test-load-init" // Wipe and recreate let _w: int = pr.process_wait(pr.process_spawn("rm", ["-rf", tmp])) let _c: bool = iodir.create_dir_all(tmp) // First call: no .config/repoman/repoman.toml exists → init writes default let r1 = config.load_or_init(tmp) runner.assert_eq_bool(rg.is_ok(r1), true, "load_or_init creates default") if rg.is_ok(r1) let reg = rg.unwrap_ok(r1) runner.assert_eq_int(reg.schema, 3, "default schema = 3") runner.assert_eq_int(reg.projects.length(), 0, "default has no projects") runner.assert_eq_string(reg.defaults.incus_project, "repoman", "default incus_project") end if // The file should now exist on disk. let cfg_path: string = tmp + "/.config/repoman/repoman.toml" runner.assert_eq_bool(iofile.fileExists(cfg_path), true, "registry file written") // Second call: should load the existing file. let r2 = config.load_or_init(tmp) runner.assert_eq_bool(rg.is_ok(r2), true, "second load reads existing") // Cleanup let _w2: int = pr.process_wait(pr.process_spawn("rm", ["-rf", tmp])) runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_merge.reef Authors: Chris Tusa License: Description: Tests: project + override merge logic ******************************************************************************/ import config import test.framework import sys.env fn make_defaults(): config.Defaults return config.Defaults { repos_root: "/home/u/repos", backup_root: "/nfs/repos", logdir: "/home/u/.local/state/repoman", incus_project: "repoman", default_image: "images:ubuntu/26.04/cloud", profiles: ["default", "claude-share"] } end make_defaults fn empty_override(): config.Override return config.Override { image: "", profiles: new [string](0), has_profiles: false, mounts: new [config.Mount](0), env_keys: new [string](0), env_values: new [string](0) } end empty_override proc main() let runner = new framework.TestRunner() // Pin HOME so paths.expand_home is deterministic across environments. env.set_env("HOME", "/home/test") let d = make_defaults() // 1. flag wins over override and defaults mut ov1: config.Override = empty_override() ov1.image = "images:debian/12/cloud" let e1 = config.merge_with_defaults("isurus", "isurus-project", "images:custom/x", ov1, d) runner.assert_eq_string(e1.image, "images:custom/x", "flag wins") runner.assert_eq_string(e1.repo_path, "/home/u/repos/isurus-project", "repo_path computed") // 2. override.image wins over defaults when no flag let e2 = config.merge_with_defaults("isurus", "isurus-project", "", ov1, d) runner.assert_eq_string(e2.image, "images:debian/12/cloud", "override.image wins") // 3. defaults when no flag, no override.image let e3 = config.merge_with_defaults("isurus", "isurus-project", "", empty_override(), d) runner.assert_eq_string(e3.image, "images:ubuntu/26.04/cloud", "defaults.image wins") // 4. profiles: override replaces defaults when has_profiles mut ov2: config.Override = empty_override() ov2.profiles = ["default", "claude-share", "node-dev"] ov2.has_profiles = true let e4 = config.merge_with_defaults("isurus", "isurus-project", "", ov2, d) runner.assert_eq_int(e4.profiles.length(), 3, "override profiles count") runner.assert_eq_string(e4.profiles[2], "node-dev", "override profiles[2]") // 5. profiles fall back to defaults let e5 = config.merge_with_defaults("isurus", "isurus-project", "", empty_override(), d) runner.assert_eq_int(e5.profiles.length(), 2, "defaults profiles count") // 6. mounts: auto bind always present, override appended mut m1: [config.Mount] = new [config.Mount](1) m1[0] = config.Mount { source: "~/.npm", path: "/home/u/.npm" } mut ov3: config.Override = empty_override() ov3.mounts = m1 let e6 = config.merge_with_defaults("isurus", "isurus-project", "", ov3, d) runner.assert_eq_int(e6.mounts.length(), 2, "auto bind + 1 override mount") runner.assert_eq_string(e6.mounts[0].source, "/home/u/repos/isurus-project", "auto bind source") runner.assert_eq_string(e6.mounts[0].path, "/home/u/repos/isurus-project", "auto bind dest") runner.assert_eq_string(e6.mounts[1].source, "/home/test/.npm", "override mount expanded") // 7. env: passed through mut keys: [string] = ["NODE_ENV"] mut vals: [string] = ["development"] mut ov4: config.Override = empty_override() ov4.env_keys = keys ov4.env_values = vals let e7 = config.merge_with_defaults("isurus", "isurus-project", "", ov4, d) runner.assert_eq_int(e7.env_keys.length(), 1, "env_keys count") runner.assert_eq_string(e7.env_keys[0], "NODE_ENV", "env_keys[0]") runner.assert_eq_string(e7.env_values[0], "development", "env_values[0]") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_migrate.reef Authors: Chris Tusa License: Description: Tests: schema 1 -> 2 -> 3 migration ******************************************************************************/ import config import test.framework import core.result_generic as rg proc main() let runner = new framework.TestRunner() // v1 (no llm block, no host block) → v3 with empty lan_ip let v1: string = "[repoman]\nschema = 1\noutput = \"quiet\"\n\n[defaults]\nrepos_root = \"~/repos\"\nbackup_root = \"/nfs/repos\"\nlogdir = \"~/.local/state/repoman\"\nincus_project = \"repoman\"\ndefault_image = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\", \"claude-share\"]\n" let r1 = config.parse_registry(v1) runner.assert_eq_bool(rg.is_ok(r1), true, "v1 parses ok") if rg.is_ok(r1) let reg = rg.unwrap_ok(r1) runner.assert_eq_int(reg.schema, 3, "v1 schema migrates to 3") runner.assert_eq_string(reg.host.lan_ip, "", "v1 has no lan_ip") end if // v2 with [defaults.llm.ollama_url] → v3 with extracted lan_ip let v2: string = "[repoman]\nschema = 2\noutput = \"quiet\"\n\n[defaults]\nrepos_root = \"~/repos\"\nbackup_root = \"/nfs/repos\"\nlogdir = \"~/.local/state/repoman\"\nincus_project = \"repoman\"\ndefault_image = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\"]\n\n[defaults.llm]\nenabled = true\nhermes_default = false\nollama_url = \"http://192.168.168.124:11434\"\nhermes_seed = []\n" let r2 = config.parse_registry(v2) runner.assert_eq_bool(rg.is_ok(r2), true, "v2 parses ok") if rg.is_ok(r2) let reg = rg.unwrap_ok(r2) runner.assert_eq_int(reg.schema, 3, "v2 schema migrates to 3") runner.assert_eq_string(reg.host.lan_ip, "192.168.168.124", "v2 lan_ip extracted from ollama_url") end if // v2 without [defaults.llm.ollama_url] (LLM was disabled) → v3 with empty lan_ip let v2_no_llm: string = "[repoman]\nschema = 2\noutput = \"quiet\"\n\n[defaults]\nrepos_root = \"~/repos\"\nbackup_root = \"/nfs/repos\"\nlogdir = \"~/.local/state/repoman\"\nincus_project = \"repoman\"\ndefault_image = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\"]\n" let r3 = config.parse_registry(v2_no_llm) runner.assert_eq_bool(rg.is_ok(r3), true, "v2 without llm block parses ok") if rg.is_ok(r3) let reg = rg.unwrap_ok(r3) runner.assert_eq_string(reg.host.lan_ip, "", "v2 without ollama_url has empty lan_ip") end if // v3 native (with [host].lan_ip) round-trip let v3: string = "[repoman]\nschema = 3\noutput = \"quiet\"\n\n[host]\nlan_ip = \"10.0.0.5\"\n\n[defaults]\nrepos_root = \"~/repos\"\nbackup_root = \"/nfs/repos\"\nlogdir = \"~/.local/state/repoman\"\nincus_project = \"repoman\"\ndefault_image = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\"]\n" let r4 = config.parse_registry(v3) runner.assert_eq_bool(rg.is_ok(r4), true, "v3 parses ok") if rg.is_ok(r4) let reg = rg.unwrap_ok(r4) runner.assert_eq_string(reg.host.lan_ip, "10.0.0.5", "v3 native lan_ip read directly") end if runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_mutate.reef Authors: Chris Tusa License: Description: Tests: registry mutation helpers ******************************************************************************/ import config import test.framework import core.result_generic as rg fn empty_defaults(): config.Defaults return config.Defaults { repos_root: "/r", backup_root: "/b", logdir: "/l", incus_project: "p", default_image: "img", profiles: new [string](0) } end empty_defaults proc main() let runner = new framework.TestRunner() let reg0: config.Registry = config.Registry { schema: 1, host: config.Host { lan_ip: "" }, output: "quiet", defaults: empty_defaults(), projects: new [config.Project](0) } let p1: config.Project = config.Project { name: "isurus", repo: "isurus", image: "img", profiles: new [string](0), created: "t", last_sync: "", backup: true } let r1 = config.add_project(reg0, p1) runner.assert_eq_bool(rg.is_ok(r1), true, "add new project ok") if rg.is_ok(r1) let reg1 = rg.unwrap_ok(r1) runner.assert_eq_int(reg1.projects.length(), 1, "1 project after add") runner.assert_eq_string(reg1.projects[0].name, "isurus", "project added") runner.assert_eq_string(reg1.output, "quiet", "output preserved through add_project") // duplicate add fails let r2 = config.add_project(reg1, p1) runner.assert_eq_bool(rg.is_err(r2), true, "duplicate name rejected") // update_last_sync let r3 = config.update_last_sync(reg1, "isurus", "2026-04-29T12:00:00Z") runner.assert_eq_bool(rg.is_ok(r3), true, "update existing ok") if rg.is_ok(r3) let reg3 = rg.unwrap_ok(r3) runner.assert_eq_string(reg3.projects[0].last_sync, "2026-04-29T12:00:00Z", "last_sync updated") runner.assert_eq_string(reg3.output, "quiet", "output preserved through update_last_sync") end if // update unknown name fails let r4 = config.update_last_sync(reg1, "nope", "t") runner.assert_eq_bool(rg.is_err(r4), true, "unknown name rejected") // remove_project let r5 = config.remove_project(rg.unwrap_ok(r1), "isurus") runner.assert_eq_bool(rg.is_ok(r5), true, "remove existing ok") if rg.is_ok(r5) let reg5 = rg.unwrap_ok(r5) runner.assert_eq_int(reg5.projects.length(), 0, "0 projects after remove") runner.assert_eq_string(reg5.output, "quiet", "output preserved through remove_project") end if // remove unknown name fails let r6 = config.remove_project(rg.unwrap_ok(r1), "ghost") runner.assert_eq_bool(rg.is_err(r6), true, "remove unknown rejected") end if runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_override.reef Authors: Chris Tusa License: Description: Tests: per-project override parsing and application ******************************************************************************/ import config import test.framework import core.result_generic as rg proc main() let runner = new framework.TestRunner() let toml_input: string = "[container]\nimage = \"images:debian/12/cloud\"\nprofiles = [\"default\", \"claude-share\", \"node-dev\"]\n\n[[mount]]\nsource = \"~/.npm\"\npath = \"/home/ctusa/.npm\"\n\n[[mount]]\nsource = \"~/.cache/yarn\"\npath = \"/home/ctusa/.cache/yarn\"\n\n[env]\nNODE_ENV = \"development\"\nDEBUG = \"1\"\n" let r = config.parse_override(toml_input) runner.assert_eq_bool(rg.is_ok(r), true, "override parse succeeds") if rg.is_ok(r) let ov = rg.unwrap_ok(r) runner.assert_eq_string(ov.image, "images:debian/12/cloud", "override.image") runner.assert_eq_bool(ov.has_profiles, true, "has_profiles set") runner.assert_eq_int(ov.profiles.length(), 3, "profiles count") runner.assert_eq_string(ov.profiles[2], "node-dev", "profiles[2]") runner.assert_eq_int(ov.mounts.length(), 2, "mount count") runner.assert_eq_string(ov.mounts[0].source, "~/.npm", "mount[0].source") runner.assert_eq_string(ov.mounts[0].path, "/home/ctusa/.npm", "mount[0].path") runner.assert_eq_int(ov.env_keys.length(), 2, "env count") // env keys are not order-guaranteed by TOML; check both possibilities let k0: string = ov.env_keys[0] runner.assert_eq_bool(k0 == "NODE_ENV" or k0 == "DEBUG", true, "env_keys[0] is one of expected") end if // empty override let r2 = config.parse_override("") runner.assert_eq_bool(rg.is_ok(r2), true, "empty override is valid") if rg.is_ok(r2) let ov2 = rg.unwrap_ok(r2) runner.assert_eq_string(ov2.image, "", "empty override.image") runner.assert_eq_bool(ov2.has_profiles, false, "no profiles") runner.assert_eq_int(ov2.mounts.length(), 0, "no mounts") runner.assert_eq_int(ov2.env_keys.length(), 0, "no env") end if runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_parse.reef Authors: Chris Tusa License: Description: Tests: registry TOML parsing ******************************************************************************/ import config import test.framework import core.result_generic as rg proc main() let runner = new framework.TestRunner() let toml_input: string = "[repoman]\nschema = 1\noutput = \"verbose\"\n\n[defaults]\nrepos_root = \"/home/u/repos\"\nbackup_root = \"/nfs/repos\"\nlogdir = \"/var/log/repoman\"\nincus_project = \"repoman\"\ndefault_image = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\", \"claude-share\"]\n\n[[project]]\nname = \"isurus\"\nrepo = \"isurus-project\"\nimage = \"images:ubuntu/26.04/cloud\"\nprofiles = [\"default\"]\ncreated = \"2026-04-28T15:00:00Z\"\nlast_sync = \"\"\nbackup = true\n" let r = config.parse_registry(toml_input) runner.assert_eq_bool(rg.is_ok(r), true, "parse succeeds") if rg.is_ok(r) let reg = rg.unwrap_ok(r) runner.assert_eq_int(reg.schema, 3, "schema after parse = 3 (migrated)") runner.assert_eq_string(reg.output, "verbose", "output preserved") runner.assert_eq_string(reg.defaults.repos_root, "/home/u/repos", "repos_root") runner.assert_eq_string(reg.defaults.backup_root, "/nfs/repos", "backup_root") runner.assert_eq_string(reg.defaults.logdir, "/var/log/repoman", "logdir preserved") runner.assert_eq_string(reg.defaults.incus_project, "repoman", "incus_project") runner.assert_eq_string(reg.defaults.default_image, "images:ubuntu/26.04/cloud", "default_image") runner.assert_eq_int(reg.defaults.profiles.length(), 2, "defaults.profiles count") runner.assert_eq_string(reg.defaults.profiles[0], "default", "defaults.profiles[0]") runner.assert_eq_string(reg.defaults.profiles[1], "claude-share", "defaults.profiles[1]") runner.assert_eq_int(reg.projects.length(), 1, "1 project") let p = reg.projects[0] runner.assert_eq_string(p.name, "isurus", "project.name") runner.assert_eq_string(p.repo, "isurus-project", "project.repo") runner.assert_eq_string(p.image, "images:ubuntu/26.04/cloud", "project.image") runner.assert_eq_int(p.profiles.length(), 1, "project.profiles count") runner.assert_eq_string(p.profiles[0], "default", "project.profiles[0]") runner.assert_eq_string(p.last_sync, "", "project.last_sync") runner.assert_eq_bool(p.backup, true, "project.backup") end if // schema rejection let bad_schema: string = "[repoman]\nschema = 99\n[defaults]\nrepos_root = \"/r\"\nbackup_root = \"/b\"\nincus_project = \"x\"\ndefault_image = \"y\"\nprofiles = []\n" let r2 = config.parse_registry(bad_schema) runner.assert_eq_bool(rg.is_err(r2), true, "schema 99 rejected") // Forward-compatibility: registry without output/logdir loads with defaults let legacy: string = "[repoman]\nschema = 1\n\n[defaults]\nrepos_root = \"/r\"\nbackup_root = \"/b\"\nincus_project = \"p\"\ndefault_image = \"img\"\nprofiles = []\n" let r3 = config.parse_registry(legacy) runner.assert_eq_bool(rg.is_ok(r3), true, "legacy registry parses") if rg.is_ok(r3) let reg3 = rg.unwrap_ok(r3) runner.assert_eq_string(reg3.output, "quiet", "output defaults to quiet when missing") runner.assert_eq_string(reg3.defaults.logdir, "~/.local/state/repoman", "logdir defaults when missing") end if // Invalid output value rejected let bad_output: string = "[repoman]\nschema = 1\noutput = \"loud\"\n[defaults]\nrepos_root = \"/r\"\nbackup_root = \"/b\"\nincus_project = \"p\"\ndefault_image = \"img\"\nprofiles = []\n" let r4 = config.parse_registry(bad_output) runner.assert_eq_bool(rg.is_err(r4), true, "invalid output value rejected") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_roundtrip.reef Authors: Chris Tusa License: Description: Tests: parse-then-serialize roundtrip ******************************************************************************/ import config import test.framework import core.result_generic as rg proc main() let runner = new framework.TestRunner() let defaults: config.Defaults = config.Defaults { repos_root: "/home/u/repos", backup_root: "/nfs/repos", logdir: "/home/u/.local/state/repoman", incus_project: "repoman", default_image: "images:ubuntu/26.04/cloud", profiles: ["default", "claude-share"] } mut projects: [config.Project] = new [config.Project](2) projects[0] = config.Project { name: "isurus", repo: "isurus-project", image: "images:ubuntu/26.04/cloud", profiles: ["default"], created: "2026-04-28T15:00:00Z", last_sync: "", backup: true } projects[1] = config.Project { name: "tools", repo: "tools", image: "images:debian/12/cloud", profiles: ["default", "claude-share"], created: "2026-04-29T10:00:00Z", last_sync: "2026-04-29T11:00:00Z", backup: false } let reg: config.Registry = config.Registry { schema: 1, host: config.Host { lan_ip: "" }, output: "quiet", defaults: defaults, projects: projects } let serialized: string = config.serialize_registry(reg) let parsed_r = config.parse_registry(serialized) runner.assert_eq_bool(rg.is_ok(parsed_r), true, "round-trip parse succeeds") if rg.is_ok(parsed_r) let reg2 = rg.unwrap_ok(parsed_r) runner.assert_eq_int(reg2.schema, 3, "schema after parse = 3 (migrated)") runner.assert_eq_string(reg2.defaults.repos_root, reg.defaults.repos_root, "defaults.repos_root preserved") runner.assert_eq_int(reg2.defaults.profiles.length(), 2, "defaults.profiles len preserved") runner.assert_eq_int(reg2.projects.length(), 2, "project count preserved") runner.assert_eq_string(reg2.projects[0].name, "isurus", "project[0].name") runner.assert_eq_string(reg2.projects[1].name, "tools", "project[1].name") runner.assert_eq_bool(reg2.projects[0].backup, true, "project[0].backup") runner.assert_eq_bool(reg2.projects[1].backup, false, "project[1].backup") runner.assert_eq_string(reg2.projects[1].last_sync, "2026-04-29T11:00:00Z", "last_sync preserved") end if runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_save.reef Authors: Chris Tusa License: Description: Tests: registry save semantics ******************************************************************************/ import config import test.framework import core.result_generic as rg import io.dir as iodir import io.file as iofile import sys.process as pr proc main() let runner = new framework.TestRunner() let tmp: string = "/tmp/repoman-test-save" let _w: int = pr.process_wait(pr.process_spawn("rm", ["-rf", tmp])) let _c: bool = iodir.create_dir_all(tmp) let cfg_path: string = tmp + "/repoman.toml" let reg: config.Registry = config.default_registry("/home/u") let r1 = config.save(reg, cfg_path) runner.assert_eq_bool(rg.is_ok(r1), true, "save returns Ok") runner.assert_eq_bool(iofile.fileExists(cfg_path), true, "target file exists") runner.assert_eq_bool(iofile.fileExists(cfg_path + ".tmp"), false, "tmp removed after rename") // Round-trip: read what we wrote let contents: string = iofile.readFile(cfg_path) let r2 = config.parse_registry(contents) runner.assert_eq_bool(rg.is_ok(r2), true, "saved file parses") if rg.is_ok(r2) let reg2 = rg.unwrap_ok(r2) runner.assert_eq_int(reg2.schema, 3, "schema after parse = 3 (migrated)") end if let _w2: int = pr.process_wait(pr.process_spawn("rm", ["-rf", tmp])) runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_schema_v3.reef Authors: Chris Tusa License: Description: Tests: schema 3 ([host].lan_ip) parse/serialize ******************************************************************************/ import config import test.framework import core.result_generic as rg import core.str proc main() let runner = new framework.TestRunner() // Build a schema-3 registry, serialize, parse round-trip let reg = config.Registry { schema: 3, host: config.Host { lan_ip: "192.168.168.124" }, output: "quiet", defaults: config.Defaults { repos_root: "~/repos", backup_root: "/nfs/repos", logdir: "~/.local/state/repoman", incus_project: "repoman", default_image: "images:ubuntu/26.04/cloud", profiles: ["default"] }, projects: new [config.Project](0) } let s = config.serialize_registry(reg) runner.assert_contains_string(s, "schema = 3", "writes schema = 3") runner.assert_contains_string(s, "[host]", "writes [host] block") runner.assert_contains_string(s, "lan_ip = \"192.168.168.124\"", "writes lan_ip") let neg = str.contains(s, "[defaults.llm]") runner.assert_eq_bool(neg, false, "does NOT write [defaults.llm]") let r2 = config.parse_registry(s) runner.assert_eq_bool(rg.is_ok(r2), true, "round-trip parses ok") if rg.is_ok(r2) let reg2 = rg.unwrap_ok(r2) runner.assert_eq_int(reg2.schema, 3, "round-trip schema = 3") runner.assert_eq_string(reg2.host.lan_ip, "192.168.168.124", "round-trip host.lan_ip") end if runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_config_serialize.reef Authors: Chris Tusa License: Description: Tests: registry TOML serialization ******************************************************************************/ import config import test.framework proc main() let runner = new framework.TestRunner() let defaults: config.Defaults = config.Defaults { repos_root: "/home/u/repos", backup_root: "/nfs/repos", logdir: "/home/u/.local/state/repoman", incus_project: "repoman", default_image: "images:ubuntu/26.04/cloud", profiles: ["default", "claude-share"] } mut projects: [config.Project] = new [config.Project](1) projects[0] = config.Project { name: "isurus", repo: "isurus-project", image: "images:ubuntu/26.04/cloud", profiles: ["default"], created: "2026-04-28T15:00:00Z", last_sync: "", backup: true } let reg: config.Registry = config.Registry { schema: 1, host: config.Host { lan_ip: "" }, output: "quiet", defaults: defaults, projects: projects } let out: string = config.serialize_registry(reg) runner.assert_contains_string(out, "[repoman]", "has [repoman] header") runner.assert_contains_string(out, "schema = 1", "has schema field") runner.assert_contains_string(out, "[defaults]", "has [defaults] header") runner.assert_contains_string(out, "repos_root = \"/home/u/repos\"", "has repos_root field") runner.assert_contains_string(out, "[[project]]", "has [[project]] header") runner.assert_contains_string(out, "name = \"isurus\"", "has project.name") runner.assert_contains_string(out, "backup = true", "has backup = true") runner.assert_contains_string(out, "profiles = [\"default\", \"claude-share\"]", "defaults.profiles array") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_hermes_classify.reef Authors: Chris Tusa License: Description: Tests: hermes path classification helper ******************************************************************************/ import hermes import test.framework proc main() let runner = new framework.TestRunner() let copy = hermes.SEED_KIND_COPY() let link = hermes.SEED_KIND_SYMLINK() // Runtime dirs (hermes-agent/, node/, bin/) used to be SYMLINK for // host-upgrade pass-through, but symlinks loop back into themselves // across the bind-mount boundary inside containers. v0.3 falls back // to COPY for all entries (spec O-3). runner.assert_eq_int(hermes.classify_seed_entry("hermes-agent/"), copy, "hermes-agent/ copy (was symlink, see O-3)") runner.assert_eq_int(hermes.classify_seed_entry("node/"), copy, "node/ copy (was symlink)") runner.assert_eq_int(hermes.classify_seed_entry("bin/"), copy, "bin/ copy (was symlink)") // Credentials/config/customizations → copy runner.assert_eq_int(hermes.classify_seed_entry(".env"), copy, ".env copy") runner.assert_eq_int(hermes.classify_seed_entry("config.yaml"), copy, "config.yaml copy") runner.assert_eq_int(hermes.classify_seed_entry("SOUL.md"), copy, "SOUL.md copy") runner.assert_eq_int(hermes.classify_seed_entry("skills/"), copy, "skills/ copy (user data, not runtime)") runner.assert_eq_int(hermes.classify_seed_entry("hooks/"), copy, "hooks/ copy") // Unknown entries → conservative default (copy) runner.assert_eq_int(hermes.classify_seed_entry("custom_thing"), copy, "unknown defaults to copy") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_hermes_paths.reef Authors: Chris Tusa License: Description: Tests: hermes path resolution helper ******************************************************************************/ import hermes import test.framework proc main() let runner = new framework.TestRunner() runner.assert_eq_string( hermes.state_dir_for("/home/ctusa", "isurus"), "/home/ctusa/.local/share/repoman/hermes/isurus", "state_dir_for layout" ) let seed = hermes.default_seed_list() runner.assert_eq_int(seed.length(), 5, "default seed list size = 5 (runtime dirs are bind-mounted, not seeded)") runner.assert_eq_string(seed[0], ".env", "first entry .env") runner.assert_eq_string(seed[1], "config.yaml", "second config.yaml") runner.assert_eq_string(seed[2], "SOUL.md", "third SOUL.md") runner.assert_eq_string(seed[3], "skills/", "fourth skills/") runner.assert_eq_string(seed[4], "hooks/", "fifth hooks/") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_incus_validate.reef Authors: Chris Tusa License: Description: Tests: incus container name validation ******************************************************************************/ import incus import test.framework import core.str proc main() let runner = new framework.TestRunner() runner.assert_eq_bool(incus.validate_name("foo"), true, "simple name") runner.assert_eq_bool(incus.validate_name("foo-bar"), true, "hyphenated") runner.assert_eq_bool(incus.validate_name("foo123"), true, "trailing digits") runner.assert_eq_bool(incus.validate_name("a"), true, "single char") runner.assert_eq_bool(incus.validate_name(""), false, "empty rejected") runner.assert_eq_bool(incus.validate_name("-foo"), false, "leading hyphen rejected") runner.assert_eq_bool(incus.validate_name("foo_bar"), false, "underscore rejected") runner.assert_eq_bool(incus.validate_name("foo.bar"), false, "dot rejected") runner.assert_eq_bool(incus.validate_name("Foo"), false, "uppercase rejected") runner.assert_eq_bool(incus.validate_name("foo bar"), false, "space rejected") // 63-char boundary (exactly 63 = ok, 64 = reject) let s63 = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabc" let s64 = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcd" runner.assert_eq_int(str.length(s63), 63, "s63 setup check") runner.assert_eq_int(str.length(s64), 64, "s64 setup check") runner.assert_eq_bool(incus.validate_name(s63), true, "63 chars accepted") runner.assert_eq_bool(incus.validate_name(s64), false, "64 chars rejected") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_paths.reef Authors: Chris Tusa License: Description: Tests: path helpers ******************************************************************************/ import paths import test.framework import sys.env proc main() let runner = new framework.TestRunner() // expand_home env.set_env("HOME", "/home/test") runner.assert_eq_string(paths.expand_home("~/foo"), "/home/test/foo", "~/foo expands") runner.assert_eq_string(paths.expand_home("~"), "/home/test", "bare ~ expands") runner.assert_eq_string(paths.expand_home("/abs/path"), "/abs/path", "abs path passthrough") runner.assert_eq_string(paths.expand_home("relative"), "relative", "relative passthrough") runner.assert_eq_string(paths.expand_home(""), "", "empty passthrough") // join runner.assert_eq_string(paths.join("/a", "b"), "/a/b", "join basic") runner.assert_eq_string(paths.join("/a/", "b"), "/a/b", "join trailing slash") runner.assert_eq_string(paths.join("/a", "/b"), "/a/b", "join leading slash") // exists / is_dir against a known dir runner.assert_eq_bool(paths.exists("/tmp"), true, "/tmp exists") runner.assert_eq_bool(paths.is_dir("/tmp"), true, "/tmp is dir") runner.assert_eq_bool(paths.exists("/this-does-not-exist-zzz"), false, "missing path → false") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_profile_paths.reef Authors: Chris Tusa License: Description: Tests: profile vendor/user dir resolution ******************************************************************************/ import profile import test.framework proc main() let runner = new framework.TestRunner() runner.assert_eq_string( profile.vendor_dir(), "/usr/local/share/repoman/profiles", "vendor_dir is the install layout" ) runner.assert_eq_string( profile.user_dir("/home/ctusa"), "/home/ctusa/.config/repoman/profiles.d", "user_dir under XDG config" ) runner.assert_eq_string( profile.user_dir(""), "/.config/repoman/profiles.d", "user_dir with empty home (still composes path)" ) runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_profile_render.reef Authors: Chris Tusa License: Description: Tests: profile ${VAR} substitution at install time ******************************************************************************/ import profile import test.framework import core.str proc main() let runner = new framework.TestRunner() let host = profile.HostFacts { lan_ip: "192.168.168.124", user: "ctusa", home: "/home/ctusa" } // All three substitutions let yaml: string = "config:\n environment.OLLAMA_HOST: \"http://\${HOST_LAN_IP}:11434\"\ndevices:\n state:\n source: \${HOME}/.ollama\n user: \${USER}\n" let out = profile.render(yaml, host) runner.assert_contains_string(out, "http://192.168.168.124:11434", "\${HOST_LAN_IP} substituted") runner.assert_contains_string(out, "/home/ctusa/.ollama", "\${HOME} substituted") runner.assert_contains_string(out, "user: ctusa", "\${USER} substituted") runner.assert_eq_bool(false, str.contains(out, "\${HOST_LAN_IP}"), "no leftover \${HOST_LAN_IP}") runner.assert_eq_bool(false, str.contains(out, "\${USER}"), "no leftover \${USER}") runner.assert_eq_bool(false, str.contains(out, "\${HOME}"), "no leftover \${HOME}") // No substitutions present — input is returned unchanged let plain: string = "name: foo\nconfig: {}\n" runner.assert_eq_string(profile.render(plain, host), plain, "no-substitution input passes through") // Empty input runner.assert_eq_string(profile.render("", host), "", "empty input") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_setup_planner.reef Authors: Chris Tusa License: Description: Tests: setup stage planner ******************************************************************************/ import setup import test.framework proc main() let runner = new framework.TestRunner() // Fresh host let fresh = setup.Environment { home_dir: "/home/ctusa", user: "ctusa", host_lan_ip: "192.168.168.42", incus_reachable: true, repoman_project_present: false } let stages = setup.plan_stages(fresh) runner.assert_eq_int(stages.length(), 2, "always 2 stages: incus_project + registry_defaults") runner.assert_eq_string(stages[0].id, "incus_project", "stage 0 = incus_project") runner.assert_eq_bool(stages[0].is_change, true, "incus_project will change on fresh host") runner.assert_eq_string(stages[1].id, "registry_defaults", "stage 1 = registry_defaults") runner.assert_eq_bool(stages[1].is_change, true, "registry_defaults always writes") // Already-set-up host let done = setup.Environment { home_dir: "/home/ctusa", user: "ctusa", host_lan_ip: "192.168.168.42", incus_reachable: true, repoman_project_present: true } let s2 = setup.plan_stages(done) runner.assert_eq_int(s2.length(), 2, "still 2 stages") runner.assert_eq_bool(s2[0].is_change, false, "incus_project no-op when present") runner.report() end main /****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2026, Leafscale, LLC - https://www.leafscale.com Project: repoman Filename: tests/test_sync_args.reef Authors: Chris Tusa License: Description: Tests: sync argv parsing ******************************************************************************/ import sync import test.framework proc main() let runner = new framework.TestRunner() // Basic: no flags let a1: [string] = sync.build_rsync_args("/src/", "/dst/", false, false, false, new [string](0)) runner.assert_eq_bool(contains_str(a1, "-aHAX"), true, "has -aHAX") runner.assert_eq_bool(contains_str(a1, "--info=stats2"), true, "has --info=stats2") runner.assert_eq_bool(contains_str(a1, "--delete"), true, "delete on by default") runner.assert_eq_bool(contains_str(a1, "--exclude=node_modules/"), true, "node_modules excluded") runner.assert_eq_bool(contains_str(a1, "--exclude=.cache/"), true, ".cache excluded") runner.assert_eq_bool(last_two(a1, "/src/", "/dst/"), true, "src and dst end positional") // Dry run: --dry-run + --itemize-changes + --info=stats2 (NOT progress2) let a2: [string] = sync.build_rsync_args("/src/", "/dst/", true, false, true, new [string](0)) runner.assert_eq_bool(contains_str(a2, "--dry-run"), true, "dry-run flag") runner.assert_eq_bool(contains_str(a2, "--itemize-changes"), true, "itemize-changes flag") runner.assert_eq_bool(contains_str(a2, "--info=stats2"), true, "info stats2") runner.assert_eq_bool(contains_str(a2, "--info=stats2,progress2"), false, "no progress in dry-run") // No delete let a3: [string] = sync.build_rsync_args("/src/", "/dst/", false, true, false, new [string](0)) runner.assert_eq_bool(contains_str(a3, "--delete"), false, "no --delete with no_delete") // TTY interactive: stats2,progress2 let a4: [string] = sync.build_rsync_args("/src/", "/dst/", false, false, true, new [string](0)) runner.assert_eq_bool(contains_str(a4, "--info=stats2,progress2"), true, "tty progress") // Excluded repos in whole-tree mode let a5: [string] = sync.build_rsync_args("/src/", "/dst/", false, false, false, ["repo-A", "repo-B"]) runner.assert_eq_bool(contains_str(a5, "--exclude=repo-A/"), true, "excluded repo A") runner.assert_eq_bool(contains_str(a5, "--exclude=repo-B/"), true, "excluded repo B") runner.report() end main fn contains_str(arr: [string], target: string): bool let n: int = arr.length() mut i: int = 0 while i < n if arr[i] == target return true end if i = i + 1 end while return false end contains_str fn last_two(arr: [string], a: string, b: string): bool let n: int = arr.length() if n < 2 return false end if return arr[n - 2] == a and arr[n - 1] == b end last_two