package browse
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// The nulls fixture table, in primary-key order. note is a real NULL at id=2
// and id=5, and the literal four-character string "NULL" at id=3.
//
// id | note
// 1 | 'alpha'
// 2 | NULL
// 3 | 'NULL'
// 4 | 'delta'
// 5 | NULL
// 6 | 'zeta'
// TestRowsNullMaskSeparatesStoredNULLText is the whole point of the mask: the
// rendered strings of a real NULL and of a stored "NULL" are equal, and only
// Nulls tells a caller which is which.
func TestRowsNullMaskSeparatesStoredNULLText(t *testing.T) {
db := openFixture(t)
ctx := context.Background()
page, err := db.Rows(ctx, "main", "nulls", 0, 4)
require.NoError(t, err)
require.Len(t, page.Rows, 4)
require.Equal(t, []string{"id", "note"}, page.Columns)
realNull := page.Rows[1] // id=2
storedText := page.Rows[2] // id=3
assert.Equal(t, placeholderNull, realNull[1])
assert.Equal(t, placeholderNull, storedText[1])
assert.Equal(t, realNull[1], storedText[1],
"both must still render identically; the rendered strings are not allowed to change")
require.Len(t, page.Nulls, len(page.Rows))
assert.True(t, page.Nulls[1][1], "id=2 stores no value: the mask must say so")
assert.False(t, page.Nulls[2][1], "id=3 stores the text \"NULL\": the mask must not claim it is null")
// Ordinary values and primary keys are never null.
assert.False(t, page.Nulls[0][0])
assert.False(t, page.Nulls[0][1])
assert.False(t, page.Nulls[1][0], "a primary key cannot be null")
assert.False(t, page.Nulls[3][1])
}
// TestRowsNullMaskAllFalseWhenPopulated: a table with no NULL in it yields a
// mask that is present, correctly shaped, and entirely false.
func TestRowsNullMaskAllFalseWhenPopulated(t *testing.T) {
db := openFixture(t)
ctx := context.Background()
page, err := db.Rows(ctx, "main", "users", 0, 10)
require.NoError(t, err)
require.Len(t, page.Rows, 4)
require.Len(t, page.Nulls, len(page.Rows))
for i, mask := range page.Nulls {
for j, isNull := range mask {
assert.Falsef(t, isNull, "users row %d cell %d (%q) must not be null", i, j, page.Rows[i][j])
}
}
}
// TestRowsNullMaskShape: every mask row is as long as its row, which is as long
// as Columns — so a caller may index Nulls with any index valid for Rows.
func TestRowsNullMaskShape(t *testing.T) {
db := openFixture(t)
ctx := context.Background()
for _, table := range []string{"users", "nulls", "docs", "items"} {
page, err := db.Rows(ctx, "main", table, 0, 10)
require.NoErrorf(t, err, "table %q", table)
require.Lenf(t, page.Nulls, len(page.Rows), "table %q: one mask per row", table)
for i := range page.Rows {
assert.Lenf(t, page.Nulls[i], len(page.Rows[i]),
"table %q row %d: mask width must equal row width", table, i)
assert.Lenf(t, page.Nulls[i], len(page.Columns),
"table %q row %d: mask width must equal column count", table, i)
}
}
}
// TestRowsNullMaskPagingAlignment reads a page that neither starts at the first
// row nor ends at the last one: an off-by-one between the mask and the rows
// would show up here and nowhere else.
func TestRowsNullMaskPagingAlignment(t *testing.T) {
db := openFixture(t)
ctx := context.Background()
// Rows 3, 4, 5 of six: stored "NULL", 'delta', real NULL.
page, err := db.Rows(ctx, "main", "nulls", 2, 3)
require.NoError(t, err)
require.Equal(t, 2, page.Offset)
require.Equal(t, 6, page.Total)
require.Equal(t, [][]string{
{"3", placeholderNull},
{"4", "delta"},
{"5", placeholderNull},
}, page.Rows)
assert.Equal(t, [][]bool{
{false, false},
{false, false},
{false, true},
}, page.Nulls, "the mask must be aligned with the rows this page actually returned")
// A page past the end has no rows and therefore no mask rows.
past, err := db.Rows(ctx, "main", "nulls", 6, 3)
require.NoError(t, err)
assert.Empty(t, past.Rows)
assert.Empty(t, past.Nulls)
}