Recently added, shop sorting, and outfit cards with T5 products
CI / build (push) Successful in 34s

- The inventory's Recently Added puts T5 products added to the library in the last 30 days first.
- The shop's T5 listing follows its sort menu: Best Match ranks by name, Newest by upload date
  (oldest first ascending); ratings and prices (T5 products have neither) sort by newest.
- Outfit cards: outfit_products.php gets the outfit's community products added (not for sale),
  find_bundle.php is asked about IMVU's products only, and removing one in the card (remove_pids)
  keeps it from IMVU and forgets it in the outfit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-30 02:45:02 -07:00
1 parent af9158513b
commit 171e8173af
11 files changed
+463 -7

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
package products
import (
"encoding/json"
"fmt"
"strings"
"testing"
)
func TestOutfitCard(t *testing.T) {
h, _ := newHarness(t)
local := h.p.byProductID("blue-coat").local
h.do("POST", "https://secure.imvu.com/api/xmlrpc/client.php", call("test.avatarInfoForLogin2"), false, false)
save := []byte(fmt.Sprintf(`{"outfit_name": "Mine", "products": "80,1,%d"}`, local))
h.do("POST", "https://client-dynamic.imvu.com/api/outfits.php", save, true, false)
// The product list has the community product too, not for sale.
_, body := h.do("GET", "https://client-dynamic.imvu.com/api/outfit_products.php?outfit_id=555", nil, true, true)
var r struct{ Result map[string]map[string]any }
if json.Unmarshal(body, &r) != nil || len(r.Result) != 2 {
t.Fatalf("products %s", body)
}
if e := r.Result[fmt.Sprint(local)]; e == nil || e["name"] != "Blue Coat" || e["is_bundleable"] != float64(0) {
t.Fatalf("community entry %v", e)
}
// The bundle check is asked about IMVU's products only (re-signed).
h.do("GET", fmt.Sprintf("https://client-dynamic.imvu.com/api/find_bundle.php?pids=80,%d.0,1", local), nil, true, true)
if got := h.imvu.asked["find bundle"]; len(got) != 1 || got[0] != "pids=80,1" {
t.Fatalf("find_bundle: IMVU got %q", got)
}
// Removing it in the card: IMVU hears of IMVU's removals only, and the outfit forgets it.
h.do("POST", "https://client-dynamic.imvu.com/api/outfits.php",
[]byte(fmt.Sprintf(`{"outfit_id": "555", "outfit_name": "Mine", "remove_pids": [1, %d]}`, local)), true, false)
if got := h.imvu.asked["save outfit"]; len(got) != 2 || !strings.Contains(got[1], `"remove_pids":[1]`) {
t.Fatalf("IMVU got %q", got)
}
if left := h.p.state.outfit("555"); len(left) != 0 {
t.Fatalf("outfit still has %v", left)
}
}