diff --git a/internal/server/operation.go b/internal/server/operation.go index 73491fb8..85a8b70d 100644 --- a/internal/server/operation.go +++ b/internal/server/operation.go @@ -137,6 +137,22 @@ func (r *RepoOperation) migration() (err error) { } } + if version < 5 { + // Strip legacy .json.gz and .json suffixes from filenames + _, err = r.db.Exec(` + UPDATE operations SET filename = REPLACE(filename, '.json.gz', '') WHERE filename LIKE '%.json.gz'; + UPDATE operations SET filename = REPLACE(filename, '.json', '') WHERE filename LIKE '%.json'; + `) + if err != nil { + return fmt.Errorf("merge db to v5 failed (normalize filenames): %w", err) + } + + _, err = r.db.Exec(`INSERT INTO version (db) VALUES (5)`) + if err != nil { + return fmt.Errorf("failed to increase version 5: %w", err) + } + } + return nil } @@ -387,3 +403,4 @@ func (r *RepoOperation) UpdateMissionDuration(ctx context.Context, id int64, dur `UPDATE operations SET mission_duration = ? WHERE id = ?`, duration, id) return err } + diff --git a/internal/server/operation_test.go b/internal/server/operation_test.go index 5601c401..a9207133 100644 --- a/internal/server/operation_test.go +++ b/internal/server/operation_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestMigration(t *testing.T) { @@ -35,6 +36,54 @@ func TestMigrationV3StorageFormat(t *testing.T) { assert.ErrorIs(t, err, sql.ErrNoRows) } +func TestMigrationV5NormalizeFilenames(t *testing.T) { + dir := t.TempDir() + pathDB := filepath.Join(dir, "test.db") + + // Create DB manually with legacy filenames (pre-v5) + db, err := sql.Open("sqlite3", pathDB) + require.NoError(t, err) + + _, err = db.Exec(` + CREATE TABLE version (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, db INTEGER); + CREATE TABLE operations ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + world_name TEXT NOT NULL, mission_name TEXT NOT NULL, mission_duration INTEGER NOT NULL, + filename TEXT NOT NULL, date TEXT NOT NULL, tag TEXT NOT NULL DEFAULT '', + storage_format TEXT DEFAULT 'json', conversion_status TEXT DEFAULT 'completed', + schema_version INTEGER DEFAULT 1 + ); + INSERT INTO version (db) VALUES (4); + INSERT INTO operations (world_name, mission_name, mission_duration, filename, date) + VALUES ('altis', 'M1', 3600, 'mission_one.json', '2026-01-01'); + INSERT INTO operations (world_name, mission_name, mission_duration, filename, date) + VALUES ('altis', 'M2', 3600, 'mission_two.json.gz', '2026-01-02'); + INSERT INTO operations (world_name, mission_name, mission_duration, filename, date) + VALUES ('altis', 'M3', 3600, 'mission_clean', '2026-01-03'); + `) + require.NoError(t, err) + db.Close() + + // Open via NewRepoOperation which runs migrations + repo, err := NewRepoOperation(pathDB) + require.NoError(t, err) + defer repo.db.Close() + + ctx := context.Background() + ops, err := repo.Select(ctx, Filter{Older: "2099-12-31", Newer: "2000-01-01"}) + require.NoError(t, err) + require.Len(t, ops, 3) + + // All filenames should be normalized (newest first by default) + filenames := map[string]bool{} + for _, op := range ops { + filenames[op.Filename] = true + } + assert.True(t, filenames["mission_one"]) + assert.True(t, filenames["mission_two"]) + assert.True(t, filenames["mission_clean"]) +} + func TestOperationStorageFormat(t *testing.T) { dir := t.TempDir() pathDB := filepath.Join(dir, "test.db") @@ -387,7 +436,7 @@ func TestMigrationRerun(t *testing.T) { var version int err = repo2.db.QueryRow("SELECT db FROM version ORDER BY db DESC LIMIT 1").Scan(&version) assert.NoError(t, err) - assert.Equal(t, 4, version) + assert.Equal(t, 5, version) } func TestGetTypesEmpty(t *testing.T) {