|
| 1 | +package model |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "encoding/json" |
| 6 | + "strings" |
| 7 | + "testing" |
| 8 | + |
| 9 | + "github.com/urnetwork/server" |
| 10 | +) |
| 11 | + |
| 12 | +// A nil slice marshals as JSON `null`. The gomobile sdk binds these fields as |
| 13 | +// pointers, so `null` becomes a nil object the android client dereferences |
| 14 | +// inside a jni callback; the NPE cannot cross jni and ART aborts the process. |
| 15 | +// An API slice that can legitimately be empty must serialise as `[]`. |
| 16 | +// |
| 17 | +// These assert on the marshalled bytes, because `[]` versus `null` is the |
| 18 | +// property that actually reaches the client. |
| 19 | + |
| 20 | +func TestGetLeaderboardEmptyMarshalsAsArrayNotNull(t *testing.T) { |
| 21 | + server.DefaultTestEnv().Run(t, func(t testing.TB) { |
| 22 | + ctx := context.Background() |
| 23 | + |
| 24 | + // a fresh db has no payouts, so the query returns zero rows -- the |
| 25 | + // exact condition that crashed the android client |
| 26 | + earners, err := GetLeaderboard(ctx) |
| 27 | + if err != nil { |
| 28 | + t.Fatalf("GetLeaderboard: %s", err) |
| 29 | + } |
| 30 | + if earners == nil { |
| 31 | + t.Fatal("GetLeaderboard returned a nil slice; it marshals as null and crashes the client") |
| 32 | + } |
| 33 | + |
| 34 | + b, err := json.Marshal(LeaderboardResult{Earners: earners}) |
| 35 | + if err != nil { |
| 36 | + t.Fatal(err) |
| 37 | + } |
| 38 | + if !strings.Contains(string(b), `"earners":[]`) { |
| 39 | + t.Errorf("marshalled as %s, want \"earners\":[]", b) |
| 40 | + } |
| 41 | + }) |
| 42 | +} |
| 43 | + |
| 44 | +func TestFetchAccountPointsEmptyMarshalsAsArrayNotNull(t *testing.T) { |
| 45 | + server.DefaultTestEnv().Run(t, func(t testing.TB) { |
| 46 | + ctx := context.Background() |
| 47 | + |
| 48 | + // a network with no points at all -- feeds both `network_points` and |
| 49 | + // its `account_points` alias |
| 50 | + points := FetchAccountPoints(ctx, server.NewId()) |
| 51 | + if points == nil { |
| 52 | + t.Fatal("FetchAccountPoints returned a nil slice; it marshals as null and crashes the client") |
| 53 | + } |
| 54 | + |
| 55 | + b, err := json.Marshal(map[string]any{"network_points": points}) |
| 56 | + if err != nil { |
| 57 | + t.Fatal(err) |
| 58 | + } |
| 59 | + if strings.Contains(string(b), "null") { |
| 60 | + t.Errorf("marshalled as %s, want an empty array", b) |
| 61 | + } |
| 62 | + }) |
| 63 | +} |
0 commit comments