mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-20 02:17:24 +00:00
* feat(packages): backend pip + npm update flow (#900) Extend Phase 1 update infrastructure to pip + npm sources. Register checkers/executors behind edition gate (Lite edition stays github-only). Per-source sentinel errors + stderr classifier; strict package-name validators reject @version suffix. Shared PackageLocker serializes install + update paths. HTTP response surfaces per-source availability from LookPath detection. Closes part of #900 (Phase 2a). * feat(packages): frontend multi-source updates UI (#900) Unified flat updates list with source pill (github/pip/npm) + filter dropdown. Summary bar shows per-source counts, hiding sources whose backend availability=false. 30 i18n keys with full en/vi/zh parity. Mobile-safe table (overflow-x-auto + min-w-[600px]). Part of #900 (Phase 2a). * test(packages): pip + npm integration e2e (#900) Optional real-runtime integration test behind `pipnpm_e2e` build tag. Skipped by default CI; exercises full check + apply cycle with real pip3/npm in Alpine container. Part of #900 (Phase 2a). * docs(packages): document pip + npm update flow (#900) Adds packages-pip-npm.md covering command matrix, exit codes, stderr error classes, pre-release handling, availability detection, runbook for EACCES/ERESOLVE/externally-managed, min versions, fixture regen. Cross-link from packages-github.md. Changelogs updated. Part of #900 (Phase 2a). * fix(packages): set exec bit on testdata npm/pip scripts
82 lines
2.7 KiB
Go
82 lines
2.7 KiB
Go
package skills
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/edition"
|
|
)
|
|
|
|
// TestEditionGate_LitePreventsRegistration mirrors the wiring logic in
|
|
// cmd/gateway_packages_wiring.go and asserts that the pip/npm checkers are
|
|
// NOT registered when edition.Current().SupportsPipNpm == false (Lite desktop).
|
|
//
|
|
// This is the unit-level guard for P2A-H6: "Lite edition runs useless pip/npm
|
|
// checkers". The wiring file gates registration like:
|
|
//
|
|
// if edition.Current().SupportsPipNpm {
|
|
// registry.RegisterChecker(NewPipUpdateChecker())
|
|
// registry.RegisterExecutor(NewPipUpdateExecutor())
|
|
// registry.RegisterChecker(NewNpmUpdateChecker())
|
|
// registry.RegisterExecutor(NewNpmUpdateExecutor())
|
|
// }
|
|
func TestEditionGate_LitePreventsRegistration(t *testing.T) {
|
|
// Temporarily set edition to Lite; restore Standard on exit.
|
|
edition.SetCurrent(edition.Lite)
|
|
t.Cleanup(func() { edition.SetCurrent(edition.Standard) })
|
|
|
|
// Replicate wiring logic.
|
|
registry := NewUpdateRegistry(nil, "", time.Hour)
|
|
|
|
// Always register github (no edition gate in wiring).
|
|
// Use a fakeChecker so we don't need a real GitHubInstaller.
|
|
registry.RegisterChecker(&fakeChecker{source: "github", available: true})
|
|
|
|
// Gate pip+npm behind edition flag — same condition as wiring.
|
|
if edition.Current().SupportsPipNpm {
|
|
registry.RegisterChecker(NewPipUpdateChecker())
|
|
registry.RegisterExecutor(NewPipUpdateExecutor())
|
|
registry.RegisterChecker(NewNpmUpdateChecker())
|
|
registry.RegisterExecutor(NewNpmUpdateExecutor())
|
|
}
|
|
|
|
sources := registry.Sources()
|
|
|
|
if len(sources) != 1 || sources[0] != "github" {
|
|
t.Errorf("Lite edition: want sources=[github], got %v", sources)
|
|
}
|
|
|
|
// pip and npm must not appear.
|
|
for _, s := range sources {
|
|
if s == "pip" || s == "npm" {
|
|
t.Errorf("Lite edition: unexpected source %q in registry", s)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestEditionGate_StandardAllowsRegistration verifies the positive case:
|
|
// Standard edition registers all three sources.
|
|
func TestEditionGate_StandardAllowsRegistration(t *testing.T) {
|
|
edition.SetCurrent(edition.Standard)
|
|
t.Cleanup(func() { edition.SetCurrent(edition.Standard) })
|
|
|
|
registry := NewUpdateRegistry(nil, "", time.Hour)
|
|
registry.RegisterChecker(&fakeChecker{source: "github", available: true})
|
|
|
|
if edition.Current().SupportsPipNpm {
|
|
registry.RegisterChecker(NewPipUpdateChecker())
|
|
registry.RegisterExecutor(NewPipUpdateExecutor())
|
|
registry.RegisterChecker(NewNpmUpdateChecker())
|
|
registry.RegisterExecutor(NewNpmUpdateExecutor())
|
|
}
|
|
|
|
sources := registry.Sources() // sorted: github, npm, pip
|
|
want := map[string]bool{"github": true, "pip": true, "npm": true}
|
|
for _, s := range sources {
|
|
delete(want, s)
|
|
}
|
|
if len(want) != 0 {
|
|
t.Errorf("Standard edition: missing sources %v in %v", want, sources)
|
|
}
|
|
}
|