mirror of
https://github.com/luxfi/chains.git
synced 2026-09-24 00:26:00 +00:00
The fix in aa4c375 was correct and unpinned -- reverting it left the suite green, which is the same failure this estate has now hit six times. Two tests close it: an unreadable tip must surface its cause and must not report a first run, and a tip of the wrong width must be refused as corruption rather than rounded down to absence. Restoring the old condition now fails both. The absent-tip half was already covered by TestOpenOnAnEmptyStoreStartsAtGenesisAndSaysSo, so it is not restated here.
63 lines
2.0 KiB
Go
63 lines
2.0 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package chain
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/luxfi/database"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// unreadableDB fails the tip read specifically, which is how a dead volume or a
|
|
// closed handle presents: the key is there, the bytes will not come back.
|
|
type unreadableDB struct {
|
|
database.Database
|
|
err error
|
|
short bool
|
|
}
|
|
|
|
var errUnreadable = errors.New("volume gone")
|
|
|
|
func (d *unreadableDB) Get(key []byte) ([]byte, error) {
|
|
if string(key) == string(tipKey) {
|
|
if d.short {
|
|
return []byte{1, 2, 3}, nil
|
|
}
|
|
return nil, d.err
|
|
}
|
|
return d.Database.Get(key)
|
|
}
|
|
|
|
// A chain that cannot read its tip does not know where it is. Reading that
|
|
// failure as "nothing recorded yet" starts a live chain over at genesis and
|
|
// lets it build height 1 on state it cannot see — durably, with the caller
|
|
// told it succeeded. Only ABSENCE means a fresh chain.
|
|
func TestOpenRefusesAnUnreadableTip(t *testing.T) {
|
|
f := newFixture(t)
|
|
require.NoError(t, f.store.Accept(newBlock(1, f.genesis.ID(), 1)))
|
|
|
|
// The same database, reopened behind a handle that cannot answer for the tip.
|
|
broken := New[*testBlock](&unreadableDB{Database: f.base, err: errUnreadable}, nil)
|
|
|
|
_, fresh, err := broken.Open(f.genesis, f.parse)
|
|
require.ErrorIs(t, err, errUnreadable, "the cause must survive the wrap")
|
|
require.False(t, fresh, "an unreadable tip is not a first run")
|
|
}
|
|
|
|
// A tip of the wrong width is corruption, not absence. Rounding it down to a
|
|
// fresh chain would discard a live chain because one field was damaged.
|
|
func TestOpenRefusesAWrongWidthTip(t *testing.T) {
|
|
f := newFixture(t)
|
|
require.NoError(t, f.store.Accept(newBlock(1, f.genesis.ID(), 1)))
|
|
|
|
broken := New[*testBlock](&unreadableDB{Database: f.base, short: true}, nil)
|
|
|
|
_, fresh, err := broken.Open(f.genesis, f.parse)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "want")
|
|
require.False(t, fresh)
|
|
}
|