package main // Item 22: the reflective Clone must be a perfect substitute for the // JSON round-trip clone — byte-identical serialization on the real // seed database, and fully independent of the original afterwards. import ( "bytes" "encoding/json" "os" "testing" ) func TestCloneEquivalenceOnSeed(t *testing.T) { raw, err := os.ReadFile("difftest/seed.json") if err != nil { t.Fatal(err) } var d Data if err := unmarshalJSONUseNumber(raw, &d); err != nil { t.Fatal(err) } fast := d.Clone() slow := d.cloneViaJSON() fastJSON, err := marshalJSON(fast) if err != nil { t.Fatal(err) } slowJSON, err := marshalJSON(slow) if err != nil { t.Fatal(err) } if !bytes.Equal(fastJSON, slowJSON) { t.Fatalf("clone mismatch:\nfast: %.400s\nslow: %.400s", fastJSON, slowJSON) } origJSON, err := marshalJSON(&d) if err != nil { t.Fatal(err) } if !bytes.Equal(fastJSON, origJSON) { t.Fatal("clone must serialize identically to the source data") } // Deep independence: mutating the clone at several depths must not // leak into the source. if len(fast.Customers) > 0 { fast.Customers[0].Name = "MUTATED" if fast.Customers[0].Credentials != nil { fast.Customers[0].Credentials.RelayPassword = "MUTATED" } } if len(fast.Nodes) > 0 { fast.Nodes[0].Name = "MUTATED" } for key := range fast.KeyStore { fast.KeyStore[key].PrivateKey = "MUTATED" break } afterJSON, err := marshalJSON(&d) if err != nil { t.Fatal(err) } if !bytes.Equal(afterJSON, origJSON) { t.Fatal("clone mutation leaked into the source data") } } func TestCloneEquivalenceSynthetic(t *testing.T) { d := perfSeedData() // Add the shapes the perf seed lacks: activity detail with nested // generic values, keyStore entries, unknown (Extra) fields. d.Activity = []*Activity{{ID: "act_1", At: NowISO(), Actor: "admin", Action: "x", Target: "y", Detail: map[string]any{"n": json.Number("42"), "nested": map[string]any{"list": []any{"a", json.Number("1.5"), true, nil}}}}} d.KeyStore["reality-nod_0"] = &KeyEntry{PrivateKey: "priv", PublicKey: "pub", UpdatedAt: NowISO()} fastJSON, err := marshalJSON(d.Clone()) if err != nil { t.Fatal(err) } slowJSON, err := marshalJSON(d.cloneViaJSON()) if err != nil { t.Fatal(err) } if !bytes.Equal(fastJSON, slowJSON) { t.Fatalf("synthetic clone mismatch:\nfast: %.300s\nslow: %.300s", fastJSON, slowJSON) } if d.Clone() == nil || EmptyData().Clone() == nil { t.Fatal("clone of empty data must be non-nil") } var nilData *Data if nilData.Clone() != nil { t.Fatal("clone of nil must be nil") } }