mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 04:14:56 +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
140 lines
4.7 KiB
Go
140 lines
4.7 KiB
Go
//go:build pipnpm_e2e
|
|
|
|
// Package integration contains optional end-to-end tests for pip + npm update flow.
|
|
// These tests require real pip3 and npm on PATH. They are excluded from default CI
|
|
// and must be opted into via: go test -tags pipnpm_e2e ./tests/integration/...
|
|
//
|
|
// Typical pre-conditions in a test container:
|
|
//
|
|
// pip3 install --break-system-packages "requests==2.25.0"
|
|
// npm install -g "typescript@4.0.0"
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"os/exec"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/skills"
|
|
)
|
|
|
|
// TestPipUpdateChecker_E2E verifies that PipUpdateChecker detects a known-stale
|
|
// package and PipUpdateExecutor upgrades it successfully.
|
|
//
|
|
// Pre-condition: pip3 must be on PATH and "requests==2.25.0" must be installed.
|
|
// The test installs the old version itself if pip3 is available.
|
|
func TestPipUpdateChecker_E2E(t *testing.T) {
|
|
if _, err := exec.LookPath("pip3"); err != nil {
|
|
t.Skip("pip3 not on PATH — skipping pip e2e test")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
|
defer cancel()
|
|
|
|
// Install a known-stale version of requests.
|
|
installCmd := exec.CommandContext(ctx, "pip3", "install",
|
|
"--break-system-packages", "--quiet", "requests==2.25.0")
|
|
if out, err := installCmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("pre-condition: install requests==2.25.0 failed: %v\n%s", err, out)
|
|
}
|
|
|
|
// Check: PipUpdateChecker should detect requests as outdated.
|
|
checker := skills.NewPipUpdateChecker()
|
|
result := checker.Check(ctx, nil)
|
|
|
|
if !result.Available {
|
|
t.Fatal("PipUpdateChecker: Available=false with pip3 on PATH")
|
|
}
|
|
if result.Err != nil {
|
|
t.Fatalf("PipUpdateChecker: unexpected error: %v", result.Err)
|
|
}
|
|
|
|
var requestsUpdate *skills.UpdateInfo
|
|
for i := range result.Updates {
|
|
if result.Updates[i].Name == "requests" {
|
|
requestsUpdate = &result.Updates[i]
|
|
break
|
|
}
|
|
}
|
|
if requestsUpdate == nil {
|
|
t.Fatal("PipUpdateChecker: 'requests' not listed as outdated (expected >=2.25.0 to have update)")
|
|
}
|
|
if requestsUpdate.CurrentVersion != "2.25.0" {
|
|
t.Errorf("CurrentVersion = %q, want 2.25.0", requestsUpdate.CurrentVersion)
|
|
}
|
|
t.Logf("requests: %s → %s", requestsUpdate.CurrentVersion, requestsUpdate.LatestVersion)
|
|
|
|
// Apply: PipUpdateExecutor should upgrade requests.
|
|
executor := skills.NewPipUpdateExecutor()
|
|
if err := executor.Update(ctx, "requests", requestsUpdate.LatestVersion, requestsUpdate.Meta); err != nil {
|
|
t.Fatalf("PipUpdateExecutor: Update failed: %v", err)
|
|
}
|
|
|
|
// Re-check: requests should no longer be in the outdated list.
|
|
result2 := checker.Check(ctx, nil)
|
|
for _, u := range result2.Updates {
|
|
if u.Name == "requests" {
|
|
t.Errorf("requests still outdated after update: current=%s latest=%s",
|
|
u.CurrentVersion, u.LatestVersion)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNpmUpdateChecker_E2E verifies that NpmUpdateChecker detects a known-stale
|
|
// global npm package and NpmUpdateExecutor upgrades it.
|
|
//
|
|
// Pre-condition: npm must be on PATH and "typescript@4.0.0" must be globally installed.
|
|
func TestNpmUpdateChecker_E2E(t *testing.T) {
|
|
if _, err := exec.LookPath("npm"); err != nil {
|
|
t.Skip("npm not on PATH — skipping npm e2e test")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
|
defer cancel()
|
|
|
|
// Install a known-stale version of typescript globally.
|
|
installCmd := exec.CommandContext(ctx, "npm", "install", "-g", "typescript@4.0.0")
|
|
if out, err := installCmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("pre-condition: install typescript@4.0.0 failed: %v\n%s", err, out)
|
|
}
|
|
|
|
// Check: NpmUpdateChecker should detect typescript as outdated.
|
|
checker := skills.NewNpmUpdateChecker()
|
|
result := checker.Check(ctx, nil)
|
|
|
|
if !result.Available {
|
|
t.Fatal("NpmUpdateChecker: Available=false with npm on PATH")
|
|
}
|
|
if result.Err != nil {
|
|
t.Fatalf("NpmUpdateChecker: unexpected error: %v", result.Err)
|
|
}
|
|
|
|
var tsUpdate *skills.UpdateInfo
|
|
for i := range result.Updates {
|
|
if result.Updates[i].Name == "typescript" {
|
|
tsUpdate = &result.Updates[i]
|
|
break
|
|
}
|
|
}
|
|
if tsUpdate == nil {
|
|
t.Fatal("NpmUpdateChecker: 'typescript' not listed as outdated (expected 4.0.0 to have update)")
|
|
}
|
|
t.Logf("typescript: %s → %s", tsUpdate.CurrentVersion, tsUpdate.LatestVersion)
|
|
|
|
// Apply: NpmUpdateExecutor should upgrade typescript.
|
|
executor := skills.NewNpmUpdateExecutor()
|
|
if err := executor.Update(ctx, "typescript", tsUpdate.LatestVersion, tsUpdate.Meta); err != nil {
|
|
t.Fatalf("NpmUpdateExecutor: Update failed: %v", err)
|
|
}
|
|
|
|
// Re-check: typescript should no longer be in the outdated list.
|
|
result2 := checker.Check(ctx, nil)
|
|
for _, u := range result2.Updates {
|
|
if u.Name == "typescript" {
|
|
t.Errorf("typescript still outdated after update: current=%s latest=%s",
|
|
u.CurrentVersion, u.LatestVersion)
|
|
}
|
|
}
|
|
}
|