Browse Source
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.commaster
6 changed files with 512 additions and 12 deletions
@ -1,4 +1,4 @@ |
|||||||
[project] |
[project] |
||||||
name = "punkshell" |
name = "punkshell" |
||||||
version = "0.32.0" |
version = "0.32.1" |
||||||
license = "BSD-2-Clause" |
license = "BSD-2-Clause" |
||||||
|
|||||||
@ -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…
Reference in new issue