package sdkserver import ( "encoding/base64" "net/http" "net/http/httptest" "os" "path/filepath" "testing" "gac-hkrpg-go/internal/common" pb "gac-hkrpg-go/protocol/proto" "google.golang.org/protobuf/proto" ) func TestLuaOverlayGatewayAndLookup(t *testing.T) { base := t.TempDir() cfg := common.LuaOverlayConfig{Enabled: true, StageDir: filepath.Join(base, "stage"), HotfixDir: filepath.Join(base, "hotfix"), StreamingDir: filepath.Join(base, "streaming")} for _, dir := range []string{cfg.StageDir, cfg.HotfixDir, cfg.StreamingDir} { if err := os.Mkdir(dir, 0700); err != nil { t.Fatal(err) } } for _, data := range []struct{ dir, name, content string }{{cfg.StageDir, "M_LuaV.bytes", "stage"}, {cfg.HotfixDir, "M_LuaV.bytes", "old"}, {cfg.StreamingDir, "0123456789abcdef0123456789abcdef.bytes", "base"}} { if err := os.WriteFile(filepath.Join(data.dir, data.name), []byte(data.content), 0600); err != nil { t.Fatal(err) } } h := luaOverlayHandler(cfg) for _, tt := range []struct { path, want string code int }{{luaOverlayRoute + "/client/Windows/M_LuaV.bytes", "stage", 200}, {luaOverlayRoute + "/0123456789abcdef0123456789abcdef.bytes", "base", 200}, {luaOverlayRoute + "/client/Windows/../../secret", "404 page not found\n", 404}} { w := httptest.NewRecorder() h(w, httptest.NewRequest(http.MethodGet, tt.path, nil)) if w.Code != tt.code || w.Body.String() != tt.want { t.Fatalf("%s: %d %q", tt.path, w.Code, w.Body.String()) } } // The existing SDK has product-name wildcard routes; overlay dispatch must // coexist with them without a ServeMux registration panic. mux := http.NewServeMux() mux.HandleFunc("/{product_name}/mdk/shield/api/login", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) }) wrapped := withLuaOverlay(mux, cfg) for _, tt := range []struct { path string want int }{{"/game/mdk/shield/api/login", http.StatusNoContent}, {luaOverlayRoute + "/M_LuaV.bytes", http.StatusOK}} { w := httptest.NewRecorder() wrapped.ServeHTTP(w, httptest.NewRequest(http.MethodGet, tt.path, nil)) if w.Code != tt.want { t.Fatalf("%s: got %d want %d", tt.path, w.Code, tt.want) } } state := &appState{runtime: common.RuntimeConfig{PublicSDKURL: "https://sdk.example.invalid", PublicGameHost: "host.example.invalid", GamePort: 1, LuaOverlay: cfg}, hotfixMap: map[string]VersionConfig{"fixture": {LuaURL: "https://upstream.example.invalid"}}} w := httptest.NewRecorder() queryGateway(state)(w, httptest.NewRequest(http.MethodGet, "/query_gateway?version=fixture", nil)) wire, err := base64.StdEncoding.DecodeString(w.Body.String()) if err != nil { t.Fatal(err) } var gateway pb.Gateserver if err := proto.Unmarshal(wire, &gateway); err != nil { t.Fatal(err) } if gateway.LuaUrl != state.runtime.PublicSDKURL+luaOverlayRoute { t.Fatalf("wrong Lua URL: %q", gateway.LuaUrl) } if state.hotfixMap["fixture"].LuaURL != "https://upstream.example.invalid" { t.Fatal("upstream config was mutated") } }