fix(pi): isolate resolved package builds

This commit is contained in:
2026-08-16 00:20:30 +02:00
parent 5a657a67e8
commit 3e0c864c85
6 changed files with 96 additions and 5 deletions
+45
View File
@@ -53,6 +53,7 @@ func TestNPMRegistryClientRejectsInvalidResponses(t *testing.T) {
}{
{name: "prerelease only", status: http.StatusOK, body: `{"versions":{"1.0.0-rc.1":{}}}`, wantText: "no stable"},
{name: "malformed JSON", status: http.StatusOK, body: `{`, wantText: "invalid"},
{name: "malformed semantic version key", status: http.StatusOK, body: `{"versions":{"0.81.0":{},"latest":{}}}`, wantText: "invalid"},
{name: "no version data", status: http.StatusOK, body: `{"versions":{}}`, wantText: "no stable"},
{name: "package not found", status: http.StatusNotFound, body: `{"error":"not_found"}`, wantText: "not found"},
} {
@@ -68,6 +69,31 @@ func TestNPMRegistryClientRejectsInvalidResponses(t *testing.T) {
}
}
func TestNPMRegistryClientRejectsRedirectsOutsideTheAuthoritativePackageURL(t *testing.T) {
for _, destination := range []string{
"http://registry.npmjs.org/@earendil-works%2Fpi-coding-agent",
"https://mirror.example.invalid/@earendil-works%2Fpi-coding-agent",
} {
t.Run(destination, func(t *testing.T) {
calls := 0
client := newNPMRegistryClient(&http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) {
calls++
response := registryResponse(http.StatusFound, "")
response.Header.Set("Location", destination)
response.Request = request
return response, nil
})})
_, err := client.LatestStable(context.Background(), "@earendil-works/pi-coding-agent")
if err == nil || !strings.Contains(strings.ToLower(err.Error()), "redirect") {
t.Fatalf("LatestStable() redirect error = %v, want rejected redirect", err)
}
if calls != 1 {
t.Fatalf("LatestStable() followed rejected redirect %d times", calls-1)
}
})
}
}
func TestNPMRegistryClientRejectsAnOversizedResponse(t *testing.T) {
client := newNPMRegistryClient(&http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
return registryResponse(http.StatusOK, `{"versions":{"1.2.3":{}}}`+strings.Repeat(" ", registryBodyLimit+1)), nil
@@ -124,6 +150,7 @@ func TestUpdateWithResolvedVersionDoesNotMutateWhenDiscoveryFails(t *testing.T)
{name: "package missing", err: errors.New("package not found")},
{name: "malformed", err: errors.New("registry response is invalid")},
{name: "no stable version", err: errors.New("registry contains no stable version")},
{name: "malformed semantic version key", err: errors.New("registry version key is invalid")},
} {
t.Run(test.name, func(t *testing.T) {
fake := newFakeRunner()
@@ -144,6 +171,24 @@ func TestUpdateWithResolvedVersionDoesNotMutateWhenDiscoveryFails(t *testing.T)
}
}
func TestUpdateWithResolvedVersionDoesNotMutateForMalformedRegistryVersionKeys(t *testing.T) {
fake := newFakeRunner()
statePath := t.TempDir() + "/state/update.json"
registry := newNPMRegistryClient(&http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
return registryResponse(http.StatusOK, `{"versions":{"0.81.0":{},"not-a-semver":{}}}`), nil
})})
_, err := UpdateWithResolvedVersion(context.Background(), fake, Request{StatePath: statePath, Source: BuildSource, Confirm: true, Drain: true}, "@earendil-works/pi-coding-agent", registry)
if err == nil || !strings.Contains(err.Error(), "invalid") {
t.Fatalf("UpdateWithResolvedVersion() error = %v, want malformed registry version rejection", err)
}
if len(fake.calls) != 0 {
t.Fatalf("malformed registry version invoked Docker or lifecycle commands: %v", fake.calls)
}
if _, statErr := os.Stat(filepath.Dir(statePath)); !errors.Is(statErr, os.ErrNotExist) {
t.Fatalf("malformed registry version created update state directory: %v", statErr)
}
}
func TestReadRuntimePackageNameReadsThePiDependency(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, "docker", "pi-runtime")