aboutsummaryrefslogtreecommitdiff
path: root/columnar/columnar_test.go
blob: 17dc96429528e9c22dd8e26d98a30df545f1dd55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
// SPDX-License-Identifier: Apache-2.0

package columnar_test

import (
	"encoding/json"
	"sync"
	"testing"

	"go.xyrillian.de/gg/columnar"
	. "go.xyrillian.de/gg/internal/test"
	"go.xyrillian.de/gg/jsonmatch"
)

func testSuccessfulJSONRoundtrip[V any](t *testing.T, list []V, encoded jsonmatch.Diffable) {
	buf, err := json.Marshal(columnar.List[V](list))
	if err != nil {
		t.Fatal(err)
	}
	for _, diff := range encoded.DiffAgainst(buf) {
		t.Error(diff.String())
	}

	var unmarshaled columnar.List[V]
	err = json.Unmarshal(buf, &unmarshaled)
	if err != nil {
		t.Fatal(err)
	}
	AssertEqual(t, []V(unmarshaled), list)
}

func TestJSONRoundtripBasic(t *testing.T) {
	// try the example from the package docstring
	type Person struct {
		ID        int    `json:"id"`
		FirstName string `json:"first_name"`
		LastName  string `json:"last_name"`
		Married   bool   `json:"married"`
	}

	testSuccessfulJSONRoundtrip(t,
		[]Person{
			{ID: 1, FirstName: "Alice", LastName: "Allison", Married: false},
			{ID: 2, FirstName: "Bob", LastName: "Burger", Married: true},
			{ID: 3, FirstName: "Carol", LastName: "Callagher", Married: true},
		},
		jsonmatch.Object{
			"id":         jsonmatch.Array{1, 2, 3},
			"first_name": jsonmatch.Array{"Alice", "Bob", "Carol"},
			"last_name":  jsonmatch.Array{"Allison", "Burger", "Callagher"},
			"married":    jsonmatch.Array{false, true, true},
		},
	)
}

func TestJSONRoundtripWithSpecialFields(t *testing.T) {
	type Point struct {
		X float64 `json:"x"`
		Y float64 `json:"y"`
	}
	type Event struct {
		*Point    `json:"location"` // embedded field, also with pointer
		Name      string            // without tag -> uses field name as key
		nameMutex sync.RWMutex      //nolint:unused // not exported -> ignored
	}

	testSuccessfulJSONRoundtrip(t,
		[]Event{
			{Point: &Point{X: 0, Y: 0}, Name: "origin"},
			{Point: &Point{X: 2, Y: 4}, Name: "somewhere"},
		},
		jsonmatch.Object{
			"location": jsonmatch.Array{jsonmatch.Object{"x": 0, "y": 0}, jsonmatch.Object{"x": 2, "y": 4}},
			"Name":     jsonmatch.Array{"origin", "somewhere"},
		},
	)
}

func TestJSONRoundtripResolvesPointers(t *testing.T) {
	type Record struct {
		Foo int
		Bar int
	}
	testSuccessfulJSONRoundtrip(t,
		[]***Record{
			PointerTo(PointerTo(PointerTo(Record{1, 2}))),
			PointerTo(PointerTo(PointerTo(Record{2, 4}))),
			PointerTo(PointerTo(PointerTo(Record{3, 6}))),
		},
		jsonmatch.Object{
			"Foo": jsonmatch.Array{1, 2, 3},
			"Bar": jsonmatch.Array{2, 4, 6},
		},
	)
}

func TestJSONRoundtripErrors(t *testing.T) {
	type nothingPublic struct {
		foo int
		bar int
	}

	_, err := json.Marshal(columnar.List[nothingPublic]{{1, 2}})
	AssertEqual(t, err.Error(), `json: error calling MarshalJSON for type columnar.List[go.xyrillian.de/gg/columnar_test.nothingPublic·5]: columnar_test.nothingPublic has no exported fields`)
	err = json.Unmarshal([]byte(`{}`), PointerTo(columnar.List[nothingPublic]{}))
	AssertEqual(t, err.Error(), `columnar_test.nothingPublic has no exported fields`)
}

func TestJSONUnmarshalFromInconsistentLengths(t *testing.T) {
	type Record struct {
		Foo int
		Bar int
	}

	err := json.Unmarshal([]byte(`{"Foo":[1,2],"Bar":[3,4,5]}`), PointerTo(columnar.List[Record]{}))
	AssertEqual(t, err.Error(), `cannot unmarshal from columns with inconsistent lengths [2 3]`)
}