Browse Source

libfetch characterization test + isolation seams (punkshell 0.32.1)

make.tcl libfetch gains env-only isolation seams - PUNK_LIBFETCH_CONFIG
(alternate declarations file) and PUNK_LIBFETCH_PACKAGES (alternate tier
root), the PUNK_RUNTIME_PLATFORM idiom - so fixture/mirror runs never read
the real declarations or write the real bin/packages tier. Neither seam
bypasses the -trust-server consent gate.

New shell/testsuites/punkexe/maketcllibfetch.test (piped maketcl* harness +
testsupport/httpfixture.tcl + a file:// mirror; the canonical origin is
never contacted) pins the G-139 behaviours that previously had only manual
session evidence: the server-trust gate refusing non-canonical origins with
exit 3 before any network access or tier write, -serverurl-over-PUNKBIN_URL
precedence, consented fetch/verify/materialize (sha1sums + zip + sidecar +
installed-shape tree with embedded record), idempotent re-run vs -force,
declared-revision-change re-materialization keyed to the tree record (with
the sidecar-not-listed note lane), sha1-MISMATCH rejection without residue,
missing-from-server-sha1sums failure, and the no-config no-op. 8/8 pass in
both runner modes (singleproc + -jobs); full punkexe subtree green (93
runnable, 0 failed); the real bin/packages tier verified untouched.

Also refreshes the stale src/runtime/libpackages.toml header (r1 IS
published as of 2026-07-30; the G-139 reference now points at the archive)
and documents the seams there and in the libfetch help text.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 2 days ago
parent
commit
d50115517f
  1. 16
      CHANGELOG.md
  2. 2
      punkproject.toml
  3. 25
      src/make.tcl
  4. 23
      src/runtime/libpackages.toml
  5. 1
      src/tests/shell/AGENTS.md
  6. 457
      src/tests/shell/testsuites/punkexe/maketcllibfetch.test

16
CHANGELOG.md

@ -5,6 +5,22 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy.
## [0.32.1] - 2026-07-30
- `make.tcl libfetch` gains env-only isolation seams: `PUNK_LIBFETCH_CONFIG`
(alternate declarations file) and `PUNK_LIBFETCH_PACKAGES` (alternate tier
root), so fixture/mirror runs never read the real declarations or write the
real `bin/packages` tier; neither seam bypasses the `-trust-server` consent
gate. New characterization suite
`src/tests/shell/testsuites/punkexe/maketcllibfetch.test` pins the G-139
behaviours previously verified only by manual session evidence - the
server-trust gate (refusal with exit 3 before any network access),
`-serverurl`-over-`PUNKBIN_URL` precedence, fetch/verify/materialize,
idempotency vs `-force`, declared-revision-change re-materialization,
sha1-mismatch rejection and the no-config no-op - against
httpfixture-served and `file://` fixture origins (the canonical origin is
never contacted).
## [0.32.0] - 2026-07-30
- Kit payloads switched to punkbin lib-tier artifacts (G-139): the 8.6-family

2
punkproject.toml

@ -1,4 +1,4 @@
[project]
name = "punkshell"
version = "0.32.0"
version = "0.32.1"
license = "BSD-2-Clause"

25
src/make.tcl

@ -2685,7 +2685,12 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
(never an interactive prompt - unattended runs stay unattended).
A file:// origin is supported for local mirrors/fixtures.
Transport for http(s): Tcl http+tls when loadable, else curl,
else PowerShell (windows)."
else PowerShell (windows).
Isolation seams (env-only): PUNK_LIBFETCH_CONFIG points at an
alternate declarations file and PUNK_LIBFETCH_PACKAGES at an
alternate tier root - fixture/test runs leave the real
declarations and bin/packages untouched. Neither bypasses the
-trust-server consent gate."
bin
"
Install executables from src/bin to <projectdir>/bin, then check vfs
@ -5286,6 +5291,20 @@ if {$::punkboot::command eq "libfetch"} {
}
set libfetch_config $sourcefolder/runtime/libpackages.toml
set libfetch_packagesroot $projectroot/bin/packages
#Isolation seams (env-only, the PUNK_RUNTIME_PLATFORM idiom): an alternate
#declarations file and an alternate tier root, so fixture-driven runs (the
#shell/testsuites/punkexe/maketcllibfetch.test characterization suite,
#local-mirror experiments) never read the real declarations or write the
#real bin/packages tier. The server-trust gate below is deliberately NOT
#seam-bypassable - fixtures consent with -trust-server like any other
#non-canonical origin.
if {[info exists ::env(PUNK_LIBFETCH_CONFIG)] && $::env(PUNK_LIBFETCH_CONFIG) ne ""} {
set libfetch_config [file normalize $::env(PUNK_LIBFETCH_CONFIG)]
}
if {[info exists ::env(PUNK_LIBFETCH_PACKAGES)] && $::env(PUNK_LIBFETCH_PACKAGES) ne ""} {
set libfetch_packagesroot [file normalize $::env(PUNK_LIBFETCH_PACKAGES)]
}
#Canonical origin: the punkbin raw-file base (the punk-runtime.cmd
#convention; lib tier addressed as <origin>/lib/<target>/...).
#Server-trust consent (G-139, the G-123 posture): the canonical origin
@ -5368,7 +5387,7 @@ if {$::punkboot::command eq "libfetch"} {
puts stderr "$A(BAD)LIBFETCH: artifact.$aname name '$artifact' does not match <pkgfolder>-tcl<N>-r<N>.zip$A(RST)"
exit 3
}
set tierdir $projectroot/bin/packages/$target
set tierdir $libfetch_packagesroot/$target
file mkdir $tierdir
#server truth per target: lib/<target>/sha1sums.txt (fetched once per
#run per target; the local copy in the tier mirrors the punk-runtime
@ -5505,7 +5524,7 @@ if {$::punkboot::command eq "libfetch"} {
puts stderr "$A(BAD)LIBFETCH: FAILED - one or more declared artifacts could not be fetched/verified/materialized (see above)$A(RST)"
exit 3
}
puts stdout "LIBFETCH: done ([llength $libfetch_actions] action(s)) -> $projectroot/bin/packages"
puts stdout "LIBFETCH: done ([llength $libfetch_actions] action(s)) -> $libfetch_packagesroot"
}
flush stdout
::exit 0

23
src/runtime/libpackages.toml

@ -20,15 +20,22 @@
# target - lib-tier subfolder: 'allplatforms' (platform-neutral, pure-tcl)
# or a canonical platform-dir name (binary packages)
#
# NOTE: the r1 artifacts below are emitted by the suite_tcl90/suite_tcl86
# 'library-artifacts' steps (G-138, achieved 2026-07-30) but PUBLICATION to the
# canonical punkbin origin is a deliberate user step - until it happens,
# libfetch against the canonical origin reports them missing from the server
# sha1sums. A local mirror (file:// origin + -trust-server) or a pre-populated
# bin/packages tier serves in the interim.
# The r1 artifacts below are emitted by the suite_tcl90/suite_tcl86
# 'library-artifacts' steps and published in the canonical punkbin lib tier
# (G-138 + G-139, both achieved 2026-07-30). Publication of any NEW revision
# remains a deliberate user step - a declared name not yet on the server
# reports as missing from its sha1sums; a local mirror (file:// origin +
# -trust-server) or a pre-populated bin/packages tier serves during
# development.
#
# Downstream consumption (lib_tcl<N> deployment, kit vfs declarations) is the
# remainder of G-139 - see goals/G-139-punkbin-library-consumption.md.
# Isolation seams (env-only): PUNK_LIBFETCH_CONFIG substitutes an alternate
# declarations file for this one, PUNK_LIBFETCH_PACKAGES an alternate tier
# root - used by the maketcllibfetch.test fixture characterization; neither
# bypasses the -trust-server consent gate.
#
# Downstream consumption (lib_tcl<N> deployment via the PACKAGES phase, kit
# vfs declarations in vendorlib_vfs.toml) - see
# goals/archive/G-139-punkbin-library-consumption.md.
[artifact.tcllib-tcl9]
name = "tcllib2.0-tcl9-r1.zip"

1
src/tests/shell/AGENTS.md

@ -18,6 +18,7 @@ Tests for shell-level behavior, command-line execution, and stdin/stdout interac
- Tests that spawn the built executable must be hang-proof: use the event-loop `punk_run` pattern (pipe stdin, half-close for EOF, timeout then force-kill) rather than plain `exec`, because known failure modes leave the child waiting on a reopened console.
- Three punkexe suites drive the WORKING TREE's `src/make.tcl` through the built executable's `script` subcommand with output captured through a pipe, pinning its reporting surfaces rather than running builds: `maketclcolour.test` (G-113 piped colour policy), `maketclbakelist.test` (G-121 kit matrix report + the selective-bake no-build guard) and `maketclplatform.test` (G-122 host/target platform split - the `check` derivation line, per-kit store tier/target, and cross-target artifact naming). Their per-kit row expectations are characterization of the current `src/runtime/mapvfs.config` matrix, so a deliberate config change legitimately updates them. `maketclplatform.test` additionally carries self-gating cygwin-host tests: they discover an msys2/cygwin-runtime tclsh (`env(PUNK_MSYS_TCLSH)`, else the usual msys2/cygwin roots) and PROBE it - a candidate only counts when it really reports `tcl_platform(platform)` `unix` with a cygwin-family os string - then assert that host derives the same win32 target, store and kit set as the native run. Such a tclsh is a POSIX Tcl: pass it a `/c/...`-spelled script path, never `C:/...`.
- `testsuites/punkexe/maketclbootgate.test` (G-125) pins the make.tcl side of the boot-precondition gate through the same piped `script` harness: `check` reports the gate as `ACTIVE` or `UNAVAILABLE` (the documented degradation when the bootsupport `punkboot::utils` snapshot lacks `vfs_boot_library_report`), names both tcl-library conventions, and states the never-deployed guarantee - ESC-free per the G-113 piped policy. The gate's VERDICT is characterized against directory fixtures in `modules/punkboot/utils/testsuites/utils/bootlibrary.test`; the predicate lives in `punkboot::utils` precisely so it is testable without running a bake.
- `testsuites/punkexe/maketcllibfetch.test` (G-139 follow-up) characterizes `make.tcl libfetch` through the same piped `script` harness against FIXTURE origins only - a `testsupport/httpfixture.tcl` server plus a `file://` mirror of a crafted punkbin `lib/<target>` layout; the real canonical origin is never contacted. Pins: the server-trust consent gate (any non-canonical origin - `PUNKBIN_URL` env or `-serverurl` - without `-trust-server` refuses with exit 3 BEFORE any network access or tier write, naming both the offending and canonical origins), `-serverurl`-beats-`PUNKBIN_URL` precedence, consented fetch/verify/materialize (per-target sha1sums + zip + sidecar into the tier, installed-shape tree with its embedded `punkbin-artifact.toml`), idempotent re-run vs `-force`, declared-revision-change re-materialization keyed to the tree's embedded record (including the sidecar-not-listed note lane), sha1-MISMATCH rejection without residue, artifact-missing-from-server-sha1sums failure, and the no-config no-op. Isolation rides the env seams `PUNK_LIBFETCH_CONFIG` + `PUNK_LIBFETCH_PACKAGES` (all state in tcltest tempdirs - the real `src/runtime/libpackages.toml` and `bin/packages` tier are never touched; the seams deliberately cannot bypass the consent gate). The asserted canonical-origin url is characterization of the constant in `src/make.tcl` - a deliberate canonical-origin change legitimately updates it.
- `testsuites/punkexe/shellexit.test` guards piped-stdin termination behaviour of the built executable (no `invalid thread handle` on shutdown races, no console-reopen hang on eof/error paths, exitcode propagation); do not weaken these tests - a red result means a regression in punk::repl shutdown or app-punkshell eof handling.
- `testsuites/punkexe/shellnavns.test` proves the namespace-navigation session contract end-to-end over a piped 'src'-mode session (dev modules - deliberate, so current punk::repl/nav source is exercised without a rebuild): n/ sets ns_current, the current namespace continues into `subshell punk` and back out (currently via the documented first-subshell shared-code-interp asymmetry - the SHARED marker pins it; the repl::start seeding template for genuinely separate interps is proven in modules/punk/repl nscurrent.test), the punk::repl 0.5.2 stray-namespace fix holds live, and the session exits cleanly. KNOWN GAP pinned there: piped submissions evaluate at :: despite ns_current (interactive and module-level inscope behaviour differ) - investigate alongside G-038.
- `testsuites/punkexe/scriptexec.test` covers the `script` subcommand (app-punkscript, G-015): piped execution + honest exit codes, result echo, file-form argv, the `lib:<name>` scriptlib-resolution mechanism (incl. punk::path 0.3.0 extensionless-tcl matching: first-line identification, called-spelling precedence over a `.tcl` sibling, sh-trampoline shebang acceptance, and the non-tcl skip note on not-found), and the tclsh-matching Tk main-loop behaviour. The `lib:` and Tk cases resolve committed fixtures under `scriptlib/_punktest/` (test-owned; a `lib:`-resolvable fixture cannot live in a tcltest temp dir since resolution only searches scriptlib locations relative to the exe). Tk cases are gated by the `punktk` constraint, auto-detected by probing whether the kit can load Tk.

457
src/tests/shell/testsuites/punkexe/maketcllibfetch.test

@ -0,0 +1,457 @@
package require tcltest
package require punk::zip
package require sha1
#Piped characterization of 'make.tcl libfetch' (G-139): the punkbin lib-tier
#fetch/verify/materialize entrypoint, driven ONLY against fixture origins - a
#child-process http server (testsupport/httpfixture.tcl) serving a crafted
#punkbin lib/<target> layout, plus a file:// mirror of the same layout and
#deliberately-unreachable urls. The real canonical origin is never contacted.
#Runs the WORKING TREE's src/make.tcl under the built punk executable's
#'script' subcommand with output captured through a pipe (the maketcl* family
#harness), and pins:
# - the server-trust consent gate: any NON-canonical origin (PUNKBIN_URL env
# or -serverurl flag) without -trust-server refuses with exit 3 BEFORE any
# network access or tier write (proven with an unreachable origin), naming
# the offending origin and the canonical one
# - -serverurl beats PUNKBIN_URL (documented precedence): a consented fetch
# succeeds against the fixture while the env var points at a dead address
# - consented fetch: per-target sha1sums + artifact zip + .toml sidecar
# fetched and sha1-verified into the tier, installed-shape tree
# materialized under <packagesroot>/<target>/tcl<N>/<pkg> with pkgIndex.tcl
# and the embedded punkbin-artifact.toml record, exit 0, ESC-free output
# (G-113 piped policy)
# - idempotent re-run: verified-present + tree-current, no re-fetch, no
# re-materialization; -force re-fetches and re-materializes
# - editing the declared name to a new revision re-materializes WITHOUT
# -force (tree currency is keyed to the tree's embedded record name), with
# immutable revisions side-by-side in the tier; a sidecar missing from the
# server sha1sums is noted and skipped without failing the artifact
# - sha1 MISMATCH against the server sha1sums: artifact rejected (no .part
# residue, no zip, no tree), run FAILED with exit 3
# - an artifact missing from the server sha1sums fails the run (exit 3)
# - no config at the declarations path: 'nothing declared' no-op, exit 0
# - a file:// origin works end-to-end via the native copy transport (and
# still needs -trust-server like any non-canonical origin)
#The canonical-origin url asserted in the gate output is characterization of
#the constant in src/make.tcl - a deliberate canonical-origin change
#legitimately updates it.
#Isolation: the env seams PUNK_LIBFETCH_CONFIG + PUNK_LIBFETCH_PACKAGES point
#every run at tcltest-tempdir fixture state - the real
#src/runtime/libpackages.toml and bin/packages tier are never touched.
#
#Target executable resolved from env(PUNK_SHELL_TEST_EXE), else <projectroot>/bin/punk902z.exe
#then <projectroot>/bin/punkshell902. Skipped (constraint punkexeavailable) if none found.
namespace eval ::testspace {
namespace import ::tcltest::*
variable testdir [file dirname [file normalize [info script]]]
#<projectroot>/src/tests/shell/testsuites/punkexe -> 5 levels up to <projectroot>
variable projectroot [file normalize [file join $testdir .. .. .. .. ..]]
variable testsroot [file normalize [file join $testdir .. .. ..]]
variable maketcl [file join $projectroot src make.tcl]
variable punkexe ""
if {[info exists ::env(PUNK_SHELL_TEST_EXE)] && $::env(PUNK_SHELL_TEST_EXE) ne ""} {
set punkexe [file normalize $::env(PUNK_SHELL_TEST_EXE)]
} else {
foreach candidate [list [file join $projectroot bin punk902z.exe] [file join $projectroot bin punkshell902]] {
if {[file exists $candidate]} {
set punkexe $candidate
break
}
}
}
testConstraint punkexeavailable [expr {$punkexe ne "" && [file exists $punkexe]}]
variable maketcl_run_timeout_ms 60000
variable runstate
array set runstate {}
proc maketcl_run_read {chan} {
variable runstate
append runstate(output) [read $chan]
if {[chan eof $chan]} {
chan event $chan readable {}
set runstate(done) eof
}
}
#Run <punkexe> script src/make.tcl <subcommand...> with output captured through a
#pipe (stdin half-closed for immediate EOF - make.tcl must never wait on stdin for
#these subcommands; the child inherits ::env, which carries the seams and the
#per-case PUNKBIN_URL state). Returns dict: timedout 0|1, exitcode <int|"">,
#output <combined stdout+stderr>.
proc maketcl_run {cmdargs} {
variable runstate
variable maketcl_run_timeout_ms
variable punkexe
variable maketcl
array unset runstate
set runstate(output) ""
set runstate(done) ""
set chan [open |[list $punkexe script $maketcl {*}$cmdargs 2>@1] r+]
chan configure $chan -blocking 0 -translation binary
catch {chan close $chan write} ;#no stdin for the child - immediate EOF
set timerid [after $maketcl_run_timeout_ms [list set [namespace current]::runstate(done) timeout]]
chan event $chan readable [list [namespace current]::maketcl_run_read $chan]
while {$runstate(done) eq ""} {
vwait [namespace current]::runstate(done)
}
after cancel $timerid
set timedout [expr {$runstate(done) eq "timeout"}]
set exitcode ""
if {$timedout} {
catch {exec {*}[auto_execok taskkill] /F /T /PID [lindex [pid $chan] 0]}
catch {chan close $chan}
} else {
chan configure $chan -blocking 1
if {[catch {chan close $chan} errdata errdict]} {
set exitcode [lindex [dict get $errdict -errorcode] end]
} else {
set exitcode 0
}
}
return [dict create timedout $timedout exitcode $exitcode output $runstate(output)]
}
proc esc_count {text} {
return [regexp -all {\x1b} $text]
}
proc writefile_raw {path content} {
set fd [open $path w]
fconfigure $fd -translation binary
puts -nonewline $fd $content
close $fd
}
proc readfile {path} {
set fd [open $path r]
set data [read $fd]
close $fd
return $data
}
#--- fixture punkbin lib-tier layout (server side) -----------------------------
variable plat testplat-x86_64
variable serverroot ""
variable configdir ""
variable configfile ""
variable packagesroot ""
variable serverchan ""
variable baseurl ""
variable saved_env [dict create]
variable fixtureok 0
#a minimal installed-shape package folder: pkgIndex.tcl + one script + the
#embedded punkbin-artifact.toml record whose name line keys tree currency
proc fixture_record {artifactname} {
set l {}
lappend l "# punkbin artifact record (test fixture)"
lappend l "schema = \"2\""
lappend l "name = \"$artifactname\""
lappend l "class = \"library\""
return "[join $l \n]\n"
}
proc build_fixture_zip {stageparent pkgfolder artifactname payload zipfile} {
set pdir [file join $stageparent $pkgfolder]
file mkdir $pdir
writefile_raw [file join $pdir pkgIndex.tcl] "package ifneeded fixturepkg 1.0 \[list source \[file join \$dir fixturepkg.tcl\]\]\n"
writefile_raw [file join $pdir fixturepkg.tcl] $payload
writefile_raw [file join $pdir punkbin-artifact.toml] [fixture_record $artifactname]
punk::zip::mkzip -directory $stageparent $zipfile
file delete -force $pdir
}
proc write_config {entries} {
variable configfile
set lines [list "# maketcllibfetch.test fixture declarations"]
foreach e $entries {
lassign $e key name target
lappend lines "\[artifact.$key\]" "name = \"$name\"" "target = \"$target\"" ""
}
writefile_raw $configfile "[join $lines \n]\n"
}
proc reset_packages {} {
variable packagesroot
file delete -force $packagesroot
file mkdir $packagesroot
}
proc server_start {} {
variable serverroot
variable serverchan
variable baseurl
variable testsroot
set helper [file join $testsroot testsupport httpfixture.tcl]
if {![file exists $helper]} { return 0 }
if {[catch {open |[list [info nameofexecutable] $helper $serverroot] r+} chan]} {
return 0
}
chan configure $chan -blocking 1 -buffering line
set line [gets $chan]
if {[regexp {^PORT (\d+)$} $line -> p]} {
set serverchan $chan
set baseurl "http://127.0.0.1:$p"
return 1
}
catch {close $chan}
return 0
}
proc server_stop {} {
variable serverchan
if {$serverchan ne ""} {
#closing the pipe EOFs the child's stdin - it exits on that
catch {close $serverchan}
set serverchan ""
}
}
#--- one-time fixture build + server launch ------------------------------------
if {[testConstraint punkexeavailable]} {
variable serverroot [file normalize [makeDirectory lf_server]]
variable configdir [file normalize [makeDirectory lf_config]]
variable packagesroot [file normalize [makeDirectory lf_packages]]
variable configfile [file join $configdir libpackages.toml]
set tplat [file join $serverroot lib $plat]
file mkdir $tplat
set zipstage [file normalize [makeDirectory lf_zipstage]]
#two immutable revisions of the same package folder (r2 = new bytes, new
#embedded record) + a corrupt-served artifact for the mismatch case
build_fixture_zip $zipstage fixturepkg fixturepkg-tcl9-r1.zip \
"proc fixturepkg::rev {} {return r1}\n" [file join $tplat fixturepkg-tcl9-r1.zip]
build_fixture_zip $zipstage fixturepkg fixturepkg-tcl9-r2.zip \
"proc fixturepkg::rev {} {return r2}\n" [file join $tplat fixturepkg-tcl9-r2.zip]
writefile_raw [file join $tplat fixturepkg-tcl9-r1.toml] [fixture_record fixturepkg-tcl9-r1.zip]
writefile_raw [file join $tplat badpkg-tcl9-r1.zip] "these served bytes do not match the sha1 the server sha1sums promises\n"
#server sha1sums: r1 zip + r1 sidecar listed with true hashes; r2 zip
#listed with a true hash but its sidecar deliberately NOT listed (the
#sidecar-skipped note lane); badpkg listed with a WRONG hash (mismatch
#lane). ghostpkg-tcl9-r1.zip is declared by a config but never listed.
set sums [list]
foreach fname [list fixturepkg-tcl9-r1.zip fixturepkg-tcl9-r1.toml fixturepkg-tcl9-r2.zip] {
lappend sums "[string tolower [sha1::sha1 -hex -file [file join $tplat $fname]]] *$fname"
}
lappend sums "[string repeat 0 40] *badpkg-tcl9-r1.zip"
writefile_raw [file join $tplat sha1sums.txt] "[join $sums \n]\n"
variable fixtureok [server_start]
#shield the runs from ambient state, then point the seams at the fixture
#config + packages tier (restored at end of file)
foreach v {PUNKBIN_URL PUNK_LIBFETCH_CONFIG PUNK_LIBFETCH_PACKAGES} {
if {[info exists ::env($v)]} {
dict set saved_env $v $::env($v)
unset ::env($v)
}
}
set ::env(PUNK_LIBFETCH_CONFIG) $configfile
set ::env(PUNK_LIBFETCH_PACKAGES) $packagesroot
}
testConstraint fixtureserver $fixtureok
variable deadurl "http://127.0.0.1:1"
#added 2026-07-30 (agent, G-139) - post-achievement follow-up: fixture-driven
#characterization of the libfetch consent gate, verification and idempotency
#behaviours (the goal's live verification was manual session evidence)
test maketcl_libfetch_trustgate_env {a non-canonical PUNKBIN_URL without -trust-server refuses with exit 3 before any network access or tier write - proven against an unreachable origin} -constraints {punkexeavailable} -setup {
reset_packages
set ::env(PUNKBIN_URL) $::testspace::deadurl
} -body {
variable deadurl
variable packagesroot
set r [maketcl_run {libfetch}]
set out [dict get $r output]
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result esc [esc_count $out]
lappend result origin_named [string match "*LIBFETCH: origin '$deadurl' is not the canonical punkbin origin*" $out]
lappend result canonical_named [string match "*LIBFETCH: canonical: https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master*" $out]
lappend result consent_stated [string match "*requires the explicit -trust-server flag*Nothing was fetched.*" $out]
lappend result tier_untouched [expr {[llength [glob -nocomplain -directory $packagesroot *]] == 0}]
set result
} -cleanup {
unset -nocomplain ::env(PUNKBIN_URL)
} -result {timedout 0 exitcode 3 esc 0 origin_named 1 canonical_named 1 consent_stated 1 tier_untouched 1}
test maketcl_libfetch_trustgate_flag {a non-canonical -serverurl without -trust-server refuses naming that origin} -constraints {punkexeavailable fixtureserver} -setup {
reset_packages
unset -nocomplain ::env(PUNKBIN_URL)
} -body {
variable baseurl
variable packagesroot
set r [maketcl_run [list libfetch -serverurl $baseurl]]
set out [dict get $r output]
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result origin_named [string match "*LIBFETCH: origin '$baseurl' is not the canonical punkbin origin*" $out]
lappend result tier_untouched [expr {[llength [glob -nocomplain -directory $packagesroot *]] == 0}]
set result
} -cleanup {
} -result {timedout 0 exitcode 3 origin_named 1 tier_untouched 1}
test maketcl_libfetch_fetch_idempotent_force {consented http fetch materializes the declared artifact (with -serverurl beating a dead PUNKBIN_URL); a re-run is idempotent; -force re-fetches and re-materializes} -constraints {punkexeavailable fixtureserver} -setup {
reset_packages
write_config {{fixture fixturepkg-tcl9-r1.zip testplat-x86_64}}
#the dead env origin proves flag-over-env precedence: only -serverurl
#reaching the fixture lets this succeed
set ::env(PUNKBIN_URL) $::testspace::deadurl
} -body {
variable baseurl
variable packagesroot
variable plat
set tier [file join $packagesroot $plat]
set result [list]
set r [maketcl_run [list libfetch -serverurl $baseurl -trust-server]]
set out [dict get $r output]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result esc [esc_count $out]
lappend result fetched [string match "*LIBFETCH: fetched lib/$plat/fixturepkg-tcl9-r1.zip*" $out]
lappend result materialized [string match "*LIBFETCH: materialized $plat/tcl9/fixturepkg*" $out]
lappend result done_line [string match "*LIBFETCH: done (2 action(s)) -> $packagesroot*" $out]
lappend result tier_zip [file isfile [file join $tier fixturepkg-tcl9-r1.zip]]
lappend result tier_sidecar [file isfile [file join $tier fixturepkg-tcl9-r1.toml]]
lappend result tier_sums [file isfile [file join $tier sha1sums.txt]]
lappend result tree_pkgindex [file isfile [file join $tier tcl9 fixturepkg pkgIndex.tcl]]
lappend result tree_record [regexp -line {^name = "fixturepkg-tcl9-r1\.zip"$} [readfile [file join $tier tcl9 fixturepkg punkbin-artifact.toml]]]
lappend result no_part [expr {[llength [glob -nocomplain -directory $tier *.part]] == 0}]
set r2 [maketcl_run [list libfetch -serverurl $baseurl -trust-server]]
set out2 [dict get $r2 output]
lappend result rerun_exitcode [dict get $r2 exitcode]
lappend result rerun_verified [string match "*LIBFETCH: verified-present lib/$plat/fixturepkg-tcl9-r1.zip*" $out2]
lappend result rerun_treecurrent [string match "*LIBFETCH: tree-current $plat/tcl9/fixturepkg*" $out2]
lappend result rerun_no_refetch [expr {![string match "*LIBFETCH: fetched *" $out2] && ![string match "*LIBFETCH: materialized *" $out2]}]
set r3 [maketcl_run [list libfetch -serverurl $baseurl -trust-server -force]]
set out3 [dict get $r3 output]
lappend result force_exitcode [dict get $r3 exitcode]
lappend result force_refetched [string match "*LIBFETCH: fetched lib/$plat/fixturepkg-tcl9-r1.zip*" $out3]
lappend result force_rematerialized [string match "*LIBFETCH: materialized $plat/tcl9/fixturepkg*" $out3]
set result
} -cleanup {
unset -nocomplain ::env(PUNKBIN_URL)
} -result {timedout 0 exitcode 0 esc 0 fetched 1 materialized 1 done_line 1 tier_zip 1 tier_sidecar 1 tier_sums 1 tree_pkgindex 1 tree_record 1 no_part 1 rerun_exitcode 0 rerun_verified 1 rerun_treecurrent 1 rerun_no_refetch 1 force_exitcode 0 force_refetched 1 force_rematerialized 1}
test maketcl_libfetch_declared_revision_change {editing the declared name to a new revision re-materializes without -force (tree currency keys off the embedded record); revisions sit side-by-side in the tier; a sidecar absent from the server sha1sums is noted and skipped without failing} -constraints {punkexeavailable fixtureserver} -setup {
reset_packages
write_config {{fixture fixturepkg-tcl9-r1.zip testplat-x86_64}}
unset -nocomplain ::env(PUNKBIN_URL)
} -body {
variable baseurl
variable packagesroot
variable plat
set tier [file join $packagesroot $plat]
set result [list]
set r [maketcl_run [list libfetch -serverurl $baseurl -trust-server]]
lappend result prime_exitcode [dict get $r exitcode]
write_config {{fixture fixturepkg-tcl9-r2.zip testplat-x86_64}}
set r2 [maketcl_run [list libfetch -serverurl $baseurl -trust-server]]
set out2 [dict get $r2 output]
lappend result exitcode [dict get $r2 exitcode]
lappend result fetched_r2 [string match "*LIBFETCH: fetched lib/$plat/fixturepkg-tcl9-r2.zip*" $out2]
lappend result rematerialized [string match "*LIBFETCH: materialized $plat/tcl9/fixturepkg*" $out2]
lappend result sidecar_noted [string match "*LIBFETCH: note - fixturepkg-tcl9-r2.toml not listed in server sha1sums (artifact accepted; sidecar skipped)*" $out2]
lappend result tree_record_r2 [regexp -line {^name = "fixturepkg-tcl9-r2\.zip"$} [readfile [file join $tier tcl9 fixturepkg punkbin-artifact.toml]]]
lappend result r1_still_present [file isfile [file join $tier fixturepkg-tcl9-r1.zip]]
lappend result r2_present [file isfile [file join $tier fixturepkg-tcl9-r2.zip]]
lappend result no_r2_sidecar [expr {![file exists [file join $tier fixturepkg-tcl9-r2.toml]]}]
set result
} -cleanup {
} -result {prime_exitcode 0 exitcode 0 fetched_r2 1 rematerialized 1 sidecar_noted 1 tree_record_r2 1 r1_still_present 1 r2_present 1 no_r2_sidecar 1}
test maketcl_libfetch_sha1_mismatch {an artifact whose served bytes do not match the server sha1sums is rejected: no zip, no .part residue, no tree, run FAILED exit 3} -constraints {punkexeavailable fixtureserver} -setup {
reset_packages
write_config {{bad badpkg-tcl9-r1.zip testplat-x86_64}}
unset -nocomplain ::env(PUNKBIN_URL)
} -body {
variable baseurl
variable packagesroot
variable plat
set tier [file join $packagesroot $plat]
set result [list]
set r [maketcl_run [list libfetch -serverurl $baseurl -trust-server]]
set out [dict get $r output]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result mismatch_named [string match "*sha1 MISMATCH for badpkg-tcl9-r1.zip*- deleted*" $out]
lappend result failed_stated [string match "*LIBFETCH: FAILED - one or more declared artifacts*" $out]
lappend result no_zip [expr {![file exists [file join $tier badpkg-tcl9-r1.zip]]}]
lappend result no_part [expr {[llength [glob -nocomplain -directory $tier *.part]] == 0}]
lappend result no_tree [expr {![file exists [file join $tier tcl9 badpkg]]}]
set result
} -cleanup {
} -result {timedout 0 exitcode 3 mismatch_named 1 failed_stated 1 no_zip 1 no_part 1 no_tree 1}
test maketcl_libfetch_not_in_server_sums {a declared artifact absent from the server sha1sums fails the run} -constraints {punkexeavailable fixtureserver} -setup {
reset_packages
write_config {{ghost ghostpkg-tcl9-r1.zip testplat-x86_64}}
unset -nocomplain ::env(PUNKBIN_URL)
} -body {
variable baseurl
variable plat
set result [list]
set r [maketcl_run [list libfetch -serverurl $baseurl -trust-server]]
set out [dict get $r output]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result not_listed [string match "*'ghostpkg-tcl9-r1.zip' is not listed in the server's lib/$plat/sha1sums.txt*" $out]
lappend result failed_stated [string match "*LIBFETCH: FAILED - one or more declared artifacts*" $out]
set result
} -cleanup {
} -result {timedout 0 exitcode 3 not_listed 1 failed_stated 1}
test maketcl_libfetch_no_config {a missing declarations file is a stated no-op with exit 0 (canonical origin, no network involved)} -constraints {punkexeavailable} -setup {
reset_packages
unset -nocomplain ::env(PUNKBIN_URL)
set ::env(PUNK_LIBFETCH_CONFIG) [file join $::testspace::configdir absent_declarations.toml]
} -body {
set result [list]
set r [maketcl_run {libfetch}]
set out [dict get $r output]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result noop_stated [string match "*LIBFETCH: no config at *absent_declarations.toml - nothing declared, nothing to fetch*" $out]
set result
} -cleanup {
set ::env(PUNK_LIBFETCH_CONFIG) $::testspace::configfile
} -result {timedout 0 exitcode 0 noop_stated 1}
test maketcl_libfetch_file_origin {a consented file:// mirror origin fetches and materializes via the native copy transport (no server process involved)} -constraints {punkexeavailable} -setup {
reset_packages
write_config {{fixture fixturepkg-tcl9-r1.zip testplat-x86_64}}
unset -nocomplain ::env(PUNKBIN_URL)
} -body {
variable serverroot
variable packagesroot
variable plat
set tier [file join $packagesroot $plat]
set result [list]
set r [maketcl_run [list libfetch -serverurl "file://$serverroot" -trust-server]]
set out [dict get $r output]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result fetched [string match "*LIBFETCH: fetched lib/$plat/fixturepkg-tcl9-r1.zip*" $out]
lappend result materialized [string match "*LIBFETCH: materialized $plat/tcl9/fixturepkg*" $out]
lappend result tree_pkgindex [file isfile [file join $tier tcl9 fixturepkg pkgIndex.tcl]]
lappend result tree_record [regexp -line {^name = "fixturepkg-tcl9-r1\.zip"$} [readfile [file join $tier tcl9 fixturepkg punkbin-artifact.toml]]]
set result
} -cleanup {
} -result {timedout 0 exitcode 0 fetched 1 materialized 1 tree_pkgindex 1 tree_record 1}
#whole-file teardown: stop the fixture server, restore shielded env state
server_stop
if {[testConstraint punkexeavailable]} {
unset -nocomplain ::env(PUNKBIN_URL)
unset -nocomplain ::env(PUNK_LIBFETCH_CONFIG)
unset -nocomplain ::env(PUNK_LIBFETCH_PACKAGES)
variable saved_env
dict for {v val} $saved_env {
set ::env($v) $val
}
}
cleanupTests
}
namespace delete ::testspace
Loading…
Cancel
Save