package sdkserver import ( "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" ) func TestInventoryMaterialsUpdateIsCatalogBoundAndPreservesSave(t *testing.T) { root := t.TempDir() t.Chdir(root) if err := os.Mkdir("resources", 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join("resources", "InventoryItemConfig.json"), []byte(`{"item_ids":[101,102,301]}`), 0o644); err != nil { t.Fatal(err) } eidolonConfig := `{"avatars":{"1001":{"ranks":[{"cost":[{"ItemID":11001,"ItemNum":1}]}]}},"ex_materials":{}}` if err := os.WriteFile(filepath.Join("resources", "EidolonConfig.json"), []byte(eidolonConfig), 0o644); err != nil { t.Fatal(err) } original := `{"avatars":{"1001":{"level":80}},"battle_config":{},"materials":{"101":4}}` if err := os.WriteFile("freesr-data.json", []byte(original), 0o644); err != nil { t.Fatal(err) } request := httptest.NewRequest(http.MethodPost, "/api/inventory/materials", strings.NewReader(`{"updates":[{"item_id":101,"count":0},{"item_id":301,"count":9}]}`)) response := httptest.NewRecorder() apiInventoryMaterials(response, request) if response.Code != http.StatusOK { t.Fatalf("POST status = %d, body=%s", response.Code, response.Body.String()) } var saved map[string]json.RawMessage raw, err := os.ReadFile("freesr-data.json") if err != nil { t.Fatal(err) } if err := json.Unmarshal(raw, &saved); err != nil { t.Fatal(err) } var avatars map[string]any if err := json.Unmarshal(saved["avatars"], &avatars); err != nil || avatars["1001"] == nil { t.Fatalf("existing avatar data was not preserved: %s", saved["avatars"]) } var materials map[string]uint32 if err := json.Unmarshal(saved["materials"], &materials); err != nil { t.Fatal(err) } if materials["101"] != 0 || materials["301"] != 9 { t.Fatalf("saved materials = %#v", materials) } reset := httptest.NewRequest(http.MethodPost, "/api/inventory/materials", strings.NewReader(`{"updates":[{"item_id":101,"reset":true}]}`)) resetResponse := httptest.NewRecorder() apiInventoryMaterials(resetResponse, reset) if resetResponse.Code != http.StatusOK { t.Fatalf("reset status = %d, body=%s", resetResponse.Code, resetResponse.Body.String()) } raw, err = os.ReadFile("freesr-data.json") if err != nil { t.Fatal(err) } if err := json.Unmarshal(raw, &saved); err != nil { t.Fatal(err) } materials = nil if err := json.Unmarshal(saved["materials"], &materials); err != nil { t.Fatal(err) } if _, exists := materials["101"]; exists || materials["301"] != 9 { t.Fatalf("reset did not restore default while preserving other edits: %#v", materials) } before := string(raw) bad := httptest.NewRequest(http.MethodPost, "/api/inventory/materials", strings.NewReader(`{"updates":[{"item_id":999,"count":1}]}`)) badResponse := httptest.NewRecorder() apiInventoryMaterials(badResponse, bad) if badResponse.Code != http.StatusBadRequest { t.Fatalf("uncatalogued item HTTP status = %d, body=%s", badResponse.Code, badResponse.Body.String()) } var errResponse struct { Status int `json:"status"` } if err := json.Unmarshal(badResponse.Body.Bytes(), &errResponse); err != nil || errResponse.Status != http.StatusBadRequest { t.Fatalf("uncatalogued item response = %s", badResponse.Body.String()) } after, err := os.ReadFile("freesr-data.json") if err != nil { t.Fatal(err) } if string(after) != before { t.Fatal("uncatalogued item update modified freesr-data.json") } }