mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
`Mount` meant two unrelated things across the fleet at once: zip/remote.go func Mount(prefix, addr string, decl ...Declaration) (*App, error) 112 subsystems func Mount(app cloud.Router, deps cloud.Deps) error The second is the fleet's registration contract, stated in hanzoai/cloud's package doc and checked by cloud.MountFunc. The first is a leftover: Mount was one of five composition verbs (Listen/Mount/Add/Graft/Use), the other four were dissolved into Use, and this one survived by being the only one that pointed at another process. A reader seeing `Mount` in a diff could not tell which was meant without counting arguments. Renamed to Proxy, which is what it BUILDS. Every route it registers runs the one handler this file calls `proxy`, whose whole body is forward() to the address given; "proxy" is the term of art for a local stand-in whose handlers hand the request to someone else, so it needs no gloss. It is also a noun, which a function returning a value should be — composition is Use's job now, and the doc has said so since the four verbs were dissolved. NOT Remote: [Remote] already exists here and is the CALLER's side, a client executing commands against a service elsewhere. This is the SERVER's side, a definition standing in for that service inside this program's routing table. Two sides of one wire are two things. Also renamed the tests that named it, and mount_test.go -> adapt_net_http_test.go with it: those pin AdaptNetHTTP, a THIRD sense of "mount", and TestMount_* named none of the three. Regression set empty: TestServePathAllocsAreChainDepthInvariant fails identically on the parent commit (an allocation bound, untouched here). Co-authored-by: Hanzo Dev <[email protected]>
91 lines
3.1 KiB
Go
91 lines
3.1 KiB
Go
package zip_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/zap-proto/zip"
|
|
)
|
|
|
|
// Two definitions cannot own one address. That rule used to live in a private
|
|
// prefix-claim ledger (App.claim/release) that only Proxy and Load consulted;
|
|
// it now lives where every OTHER address collision is already decided — the
|
|
// walk, at build. One mechanism, so there is no second answer to disagree with
|
|
// the first, and the report is strictly better: every collision at once, with
|
|
// both claimants and their call sites named.
|
|
//
|
|
// The defect the old ledger closed is still closed, and these prove it.
|
|
|
|
func plugin(t *testing.T, name, prefix string) *zip.App {
|
|
t.Helper()
|
|
p, err := zip.Load(zip.Plugin{Name: name, Addr: "/run/zip/" + name + ".sock"}, prefix)
|
|
if err != nil {
|
|
t.Fatalf("Load(%s): %v", name, err)
|
|
}
|
|
return p
|
|
}
|
|
|
|
// A second definition claiming a held prefix is refused. Before, the second
|
|
// claim returned nil and the first registration kept answering, so the losing
|
|
// plugin's whole surface served the winner's 404 and nothing said so.
|
|
func TestASecondClaimOfAPrefixIsRefused(t *testing.T) {
|
|
app := zip.New(zip.Config{AppName: "host", DisableStartupMessage: true})
|
|
app.Use(plugin(t, "a", "/v1/x"), plugin(t, "b", "/v1/x"))
|
|
|
|
err := app.Build()
|
|
if err == nil {
|
|
t.Fatal("two definitions claiming /v1/x were both accepted")
|
|
}
|
|
for _, want := range []string{"/v1/x", `"a"`, `"b"`} {
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Errorf("refusal does not name %s: %v", want, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A refused composition changes nothing: the live generation is whatever was
|
|
// valid, and a half-applied plugin is not a state that exists.
|
|
func TestARefusedCompositionIsRolledBack(t *testing.T) {
|
|
app := zip.New(zip.Config{AppName: "host", DisableStartupMessage: true})
|
|
app.Use(plugin(t, "first", "/v1/a"))
|
|
|
|
// Serve IS the build — generation 0. Include is a HOST verb.
|
|
h, err := zip.Serve(app, "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("Serve: %v", err)
|
|
}
|
|
defer func() { _ = h.Close() }()
|
|
if err := h.Include(plugin(t, "second", "/v1/a")); err == nil {
|
|
t.Fatal("a colliding plugin was included")
|
|
}
|
|
if n, _ := app.Generation(); n != 0 {
|
|
t.Errorf("a refused Include advanced the generation to %d", n)
|
|
}
|
|
// The plugin table still holds exactly the one that was valid.
|
|
names := []string{}
|
|
for _, p := range app.Plugins() {
|
|
names = append(names, p.Name)
|
|
}
|
|
if len(names) != 1 || names[0] != "first" {
|
|
t.Errorf("plugins = %v, want [first]", names)
|
|
}
|
|
}
|
|
|
|
// Proxy and Load are the same kind of thing now — definition constructors — so
|
|
// they collide with each other exactly as two Loads do. There is no separate
|
|
// register for either.
|
|
func TestProxyAndLoadCollideThroughOneMechanism(t *testing.T) {
|
|
remote, err := zip.Proxy("/v1/y", "/run/zip/y.sock")
|
|
if err != nil {
|
|
t.Fatalf("Proxy: %v", err)
|
|
}
|
|
app := zip.New(zip.Config{AppName: "host", DisableStartupMessage: true})
|
|
app.Use(remote, plugin(t, "z", "/v1/y"))
|
|
|
|
if err := app.Build(); err == nil {
|
|
t.Fatal("a Proxy and a Load claiming /v1/y were both accepted")
|
|
} else if !strings.Contains(err.Error(), "/v1/y") {
|
|
t.Errorf("refusal does not name the address: %v", err)
|
|
}
|
|
}
|