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

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()

}