feat(multiplayer): deliver allocated server rosters

This commit is contained in:
Josh Creek
2026-09-01 15:54:07 +01:00
parent 863cf61f1a
commit 68e76b5feb
10 changed files with 267 additions and 22 deletions
+50
View File
@@ -68,6 +68,56 @@ func TestAllocatedStartInjectsDynamicEndpointAndCallsReadyAfterProbe(t *testing.
}
}
func TestAllocatedStartMaterializesWorkloadAuthenticatedRosterBeforeChild(t *testing.T) {
rosterPath := filepath.Join(t.TempDir(), "join-roster.json")
sdk := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/gameserver":
_, _ = w.Write([]byte(`{"object_meta":{"annotations":{"cosmic-clash.io/match-id":"match-1","cosmic-clash.io/workload-token":"workload-token"}},"status":{"address":"127.0.0.1","ports":[{"name":"game","port":31001}]}}`))
case "/ready-probe", "/ready":
w.WriteHeader(http.StatusOK)
default:
w.WriteHeader(http.StatusNotFound)
}
}))
defer sdk.Close()
controlPlane := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/servers/server-1/roster" {
if r.Method != http.MethodGet || r.Header.Get("Authorization") != "Bearer workload-token" {
w.WriteHeader(http.StatusUnauthorized)
return
}
_, _ = w.Write([]byte(`[{"authorisation":{"player_id":"player-1"},"signature":"sig"}]`))
return
}
if r.URL.Path == "/v1/servers/server-1/register" {
w.WriteHeader(http.StatusNoContent)
return
}
w.WriteHeader(http.StatusNotFound)
}))
defer controlPlane.Close()
command := []string{"/bin/sh", "-c", "test -s '" + rosterPath + "'"}
s, err := New(Config{
Command: command, SDKBaseURL: sdk.URL, ReadyURL: sdk.URL + "/ready-probe", ControlPlaneURL: controlPlane.URL,
ServerID: "server-1", ProtocolVersion: 1, ImageDigest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
RosterPath: rosterPath, ReadyTimeout: time.Second, PollInterval: time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
if err := s.Start(context.Background()); err != nil {
t.Fatal(err)
}
if err := s.Wait(); err != nil {
t.Fatal(err)
}
contents, err := os.ReadFile(rosterPath)
if err != nil || !strings.Contains(string(contents), "player-1") {
t.Fatalf("materialized roster=%q err=%v", contents, err)
}
}
func TestControlPlaneRegistrationRejectsIncompleteConfig(t *testing.T) {
base := Config{Command: []string{"/bin/true"}, ControlPlaneURL: "https://control-plane.invalid"}
if _, err := New(base); err == nil {