~bigbes/sr-ht-dolt

ref: bd2d8a3680e62716440ea0165d2271d79f9d526e sr-ht-dolt/beads/rows_null_test.go -rw-r--r-- 5.1 KiB
bd2d8a36 — Eugene Blikh doltsrht: serve /query and the api-meta.json beside it 3 days ago
                                                                                
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package beads

import (
	"context"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
)

// The two readings of the string "NULL", and the mask that tells them apart.
//
// browse renders a cell that holds no value as the text "NULL", which is what a
// row storing those four characters renders as too. Flattening both to "" — as
// this package did before the mask existed — is right for the first and wrong
// for the second: an issue titled "NULL" arrived with an empty title, and no
// projection could tell that it had one.

// nullMaskFixture is one issues table carrying both readings in the same
// columns: i-absent holds no value in title or closed_at, i-stored stores the
// text "NULL" in both.
func nullMaskFixture() *fakeSession {
	issues := &browse.RowPage{
		Columns: []string{"id", "title", "status", "priority", "issue_type", "assignee", "created_at", "closed_at", "is_blocked"},
		Rows: [][]string{
			{"i-absent", "NULL", "open", "1", "task", "alice", "2024-01-01", "NULL", "0"},
			{"i-stored", "NULL", "open", "1", "task", "alice", "2024-01-02", "NULL", "0"},
		},
		Nulls: [][]bool{
			// title and closed_at hold no value at all.
			{false, true, false, false, false, false, false, true, false},
			// every cell holds a value; the two "NULL"s are stored text.
			{false, false, false, false, false, false, false, false, false},
		},
		Total: 2,
	}
	deps := &browse.RowPage{
		Columns: []string{"id", "issue_id", "depends_on_issue_id", "type"},
		Rows:    [][]string{},
		Nulls:   [][]bool{},
	}
	return &fakeSession{rowsByTable: map[string]*browse.RowPage{
		"issues":       issues,
		"dependencies": deps,
	}}
}

// cell is where the distinction is actually made, so it is asked directly first:
// the same string, read two ways, decided by the mask beside it.
func TestCellReadsTheNullMaskAndNotTheString(t *testing.T) {
	cols := indexCols([]string{"id", "title"})

	absent := rowCells{values: []string{"i-absent", "NULL"}, nulls: []bool{false, true}}
	stored := rowCells{values: []string{"i-stored", "NULL"}, nulls: []bool{false, false}}

	assert.Equal(t, "", cell(cols, absent, "title"),
		"a cell that holds no value reads as empty, which every projection depends on")
	assert.Equal(t, "NULL", cell(cols, stored, "title"),
		"a cell that stores the text \"NULL\" has a value, and it is that text")

	assert.Equal(t, "", cell(cols, stored, "nosuchcolumn"), "an absent column is still empty")
}

// A page that carries no mask at all cannot answer the question, and the reading
// that predates the mask is all there is: "NULL" reads as absent. Every page
// browse returns carries one, so this is about a page built by hand.
func TestCellWithoutAMaskKeepsTheOlderReading(t *testing.T) {
	cols := indexCols([]string{"id", "title"})
	unmasked := rowCells{values: []string{"i-1", "NULL"}}

	assert.Equal(t, "", cell(cols, unmasked, "title"))
	assert.Equal(t, "i-1", cell(cols, unmasked, "id"))
}

// rowsOf pairs each row with its own mask, and a page with none hands out rows
// that answer "unknown" rather than rows that answer "not null".
func TestRowsOfPairsEveryRowWithItsMask(t *testing.T) {
	page := &browse.RowPage{
		Columns: []string{"id", "title"},
		Rows:    [][]string{{"a", "NULL"}, {"b", "NULL"}},
		Nulls:   [][]bool{{false, true}, {false, false}},
	}
	rows := rowsOf(page)
	require.Len(t, rows, 2)
	assert.Equal(t, []bool{false, true}, rows[0].nulls)
	assert.Equal(t, []bool{false, false}, rows[1].nulls)

	unmasked := rowsOf(&browse.RowPage{Columns: []string{"id"}, Rows: [][]string{{"a"}}})
	require.Len(t, unmasked, 1)
	assert.Nil(t, unmasked[0].nulls, "a page with no mask answers no mask, not an all-false one")

	assert.Empty(t, rowsOf(nil), "a table that is absent has no rows to pair")
}

// The projections are where it is felt: a title that is literally "NULL" must
// survive to the detail pane, and a closed_at that holds no value must keep
// reading as empty.
func TestProjectionsKeepAStoredNullAndDropARealOne(t *testing.T) {
	ctx := context.Background()

	stored, err := Build(ctx, nullMaskFixture(), "main", url.Values{"issue": {"i-stored"}})
	require.NoError(t, err)
	require.NotNil(t, stored.Issue)
	assert.Equal(t, "NULL", stored.Issue.Title,
		"the row stores those four characters: an empty title would lose them")
	assert.Equal(t, "NULL", stored.Issue.ClosedAt)

	absent, err := Build(ctx, nullMaskFixture(), "main", url.Values{"issue": {"i-absent"}})
	require.NoError(t, err)
	require.NotNil(t, absent.Issue)
	assert.Equal(t, "", absent.Issue.Title, "no value is no text, as every projection reads it")
	assert.Equal(t, "", absent.Issue.ClosedAt)
}

// The board goes through the same rows, and the same two readings have to reach
// the cards.
func TestBoardCardsKeepAStoredNullTitle(t *testing.T) {
	d, err := Build(context.Background(), nullMaskFixture(), "main", url.Values{})
	require.NoError(t, err)

	titles := map[string]string{}
	for _, lane := range d.Lanes {
		for _, c := range lane.Issues {
			titles[c.ID] = c.Title
		}
	}
	assert.Equal(t, "NULL", titles["i-stored"])
	assert.Equal(t, "", titles["i-absent"])
}