add checkpoint format compatibility checks · Entire
add checkpoint format compatibility checks
206bb2e→main·
pfleidi·3w ago·12 files·+441 added/-14 removed
Parse committed checkpoint version strings and reject unsupported formats before decoding checkpoint data.
Wire the check into explain, export, resume, and rewind read paths so user-driven commands fail clearly when a checkpoint requires a newer CLI.
Sessions
77f1d4ade515View transcript
Changes
12
cmd/entire/cli
Acheckpoint_policy_read_test.go+40
checkpointpolicy
Aformat.go+60
Aformat_test.go+51
Aversion.go+46
Mexplain.go+4
Mexplain_export.go+7
Mexplain_export_test.go+86
Mexplain_test.go+18
Mresume.go+36/-6
Mresume_test.go+69/-4
Mrewind.go+20/-4
strategy
Mmanual_commit_rewind.go+4
package cli
import (
"context"
"testing"
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
"github.com/stretchr/testify/require"
)
func TestReadCheckpointInfoFromStoreRejectsUnsupportedCheckpointVersion(t *testing.T) {
t.Parallel()
cpID := id.MustCheckpointID("111111111111")
_, err := readCheckpointInfoFromStore(context.Background(), checkpointInfoPolicyStub{
summary: &checkpoint.CheckpointSummary{
CheckpointID: cpID,
CheckpointVersion: "refs-v1",
},
}, cpID)
require.ErrorContains(t, err, `checkpoint 111111111111 uses unsupported checkpoint_version "refs-v1"`)
}
type checkpointInfoPolicyStub struct {
summary *checkpoint.CheckpointSummary
}
func (s checkpointInfoPolicyStub) ReadCommitted(context.Context, id.CheckpointID) (*checkpoint.CheckpointSummary, error) {
return s.summary, nil
}
func (s checkpointInfoPolicyStub) ReadSessionContent(context.Context, id.CheckpointID, int) (*checkpoint.SessionContent, error) {
return nil, checkpoint.ErrCheckpointNotFound
}
func (s checkpointInfoPolicyStub) ReadSessionMetadata(context.Context, id.CheckpointID, int) (*checkpoint.CommittedMetadata, error) {
return nil, checkpoint.ErrCheckpointNotFound
}
package checkpointpolicy
import ( "fmt" "strconv" "strings" )
type CheckpointFamily string
const ( CheckpointFamilyBranch CheckpointFamily = "branch" CheckpointFamilyRefs CheckpointFamily = "refs" )
type CheckpointFormat struct { Family CheckpointFamily Major int }
func ParseFormat(raw string) (CheckpointFormat, error) { familyRaw, majorRaw, ok := strings.Cut(raw, "-v") if !ok || familyRaw == "" || majorRaw == "" { return CheckpointFormat{}, fmt.Errorf("invalid checkpoint format %q", raw) }
family := CheckpointFamily(familyRaw) if _, ok := knownFamilies[family]; !ok { return CheckpointFormat{}, fmt.Errorf("unknown checkpoint family %q", familyRaw) }
major, err := strconv.Atoi(majorRaw) if err != nil || major <= 0 { return CheckpointFormat{}, fmt.Errorf("invalid checkpoint major %q", majorRaw) }
return CheckpointFormat{Family: family, Major: major}, nil }
func (f CheckpointFormat) String() string { if f.Family == "" || f.Major == 0 { return "" } return fmt.Sprintf("%s-v%d", f.Family, f.Major) }
func CanRead(format CheckpointFormat) bool { return readFormats[format] }
var knownFamilies = map[CheckpointFamily]bool{ CheckpointFamilyBranch: true, CheckpointFamilyRefs: true, }
var branchV1Format = CheckpointFormat{Family: CheckpointFamilyBranch, Major: 1}
var readFormats = map[CheckpointFormat]bool{ branchV1Format: true, }
```go
package checkpointpolicy_test
import (
"testing"
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
"github.com/stretchr/testify/require"
)
func TestParseFormat(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
want checkpointpolicy.CheckpointFormat
wantErr string
}{
{name: "branch v1", input: "branch-v1", want: checkpointpolicy.CheckpointFormat{Family: checkpointpolicy.CheckpointFamilyBranch, Major: 1}},
{name: "refs v2", input: "refs-v2", want: checkpointpolicy.CheckpointFormat{Family: checkpointpolicy.CheckpointFamilyRefs, Major: 2}},
{name: "unknown family", input: "unknown-v1", wantErr: "unknown checkpoint family"},
{name: "missing v", input: "branch-1", wantErr: "invalid checkpoint format"},
{name: "zero major", input: "branch-v0", wantErr: "invalid checkpoint major"},
{name: "non numeric major", input: "branch-vx", wantErr: "invalid checkpoint major"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := checkpointpolicy.ParseFormat(tt.input)
if tt.wantErr != "" {
require.ErrorContains(t, err, tt.wantErr)
return
}
require.NoError(t, err)
require.Equal(t, tt.want, got)
require.Equal(t, tt.input, got.String())
})
}
}
func TestCanReadFormat(t *testing.T) {
t.Parallel()
branchV1, err := checkpointpolicy.ParseFormat(checkpoint.CheckpointVersionBranchV1)
require.NoError(t, err)
refsV1, err := checkpointpolicy.ParseFormat("refs-v1")
require.NoError(t, err)
require.True(t, checkpointpolicy.CanRead(branchV1))
require.False(t, checkpointpolicy.CanRead(refsV1))
}
package checkpointpolicy
import (
"errors"
"fmt"
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
)
func IsUnsupportedVersion(err error) bool {
var unsupported *unsupportedVersionError
return errors.As(err, &unsupported)
}
func EnsureCanReadVersion(checkpointID, version string) error {
if version == "" {
version = checkpoint.CheckpointVersionBranchV1
}
format, err := ParseFormat(version)
if err != nil {
return fmt.Errorf("checkpoint %s has invalid checkpoint_version %q: %w", checkpointID, version, err)
}
if !CanRead(format) {
return &unsupportedVersionError{
CheckpointID: checkpointID,
Version: version,
Err: errors.New("not read-supported by this Entire CLI"),
}
}
return nil
}
type unsupportedVersionError struct {
CheckpointID string
Version string
Err error
}
func (e unsupportedVersionError) Error() string {
return fmt.Sprintf("checkpoint %s uses unsupported checkpoint_version %q: %v", e.CheckpointID, e.Version, e.Err)
}
func (e unsupportedVersionError) Unwrap() error {
return e.Err
}
package checkpointpolicy
import (
"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
)
func IsUnsupportedVersion(err error) bool {
var unsupported *unsupportedVersionError
return errors.As(err, &unsupported)
}
// Not parallel: uses t.Chdir() and package-level var stubs.
func TestGenerateCheckpointSummary_AdvancesV1Metadata(t *testing.T) {
ctx := context.Background()
}