174 lines
6.4 KiB
Go
174 lines
6.4 KiB
Go
package sqlite_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
catalogdata "git.zaynet.fr/DoGaMa/DoGaMa-serv/catalog"
|
|
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/catalog"
|
|
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/instance"
|
|
"git.zaynet.fr/DoGaMa/DoGaMa-serv/internal/persistence/sqlite"
|
|
)
|
|
|
|
func TestCatalogSyncIsImmutableAndDraftPinsSnapshot(t *testing.T) {
|
|
ctx := context.Background()
|
|
db, err := sqlite.Open(ctx, filepath.Join(t.TempDir(), "dogama.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
repository := sqlite.NewRepository(db)
|
|
snapshots, err := catalog.LoadFS(catalogdata.Files, ".")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repository.Sync(ctx, snapshots); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
summaries, err := repository.List(ctx)
|
|
if err != nil || len(summaries) != 1 || summaries[0].Digest != snapshots[0].Digest {
|
|
t.Fatalf("summaries = %#v, error = %v", summaries, err)
|
|
}
|
|
loaded, err := repository.Get(ctx, snapshots[0].Template.ID, snapshots[0].Template.Version)
|
|
if err != nil || loaded.Digest != snapshots[0].Digest {
|
|
t.Fatalf("loaded snapshot = %#v, error = %v", loaded, err)
|
|
}
|
|
tampered := snapshots[0]
|
|
tampered.Digest = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
|
if err := repository.Sync(ctx, []catalog.Snapshot{tampered}); !errors.Is(err, catalog.ErrImmutableSnapshot) {
|
|
t.Fatalf("immutable snapshot error = %v", err)
|
|
}
|
|
preview, err := instance.BuildPreview(snapshots[0], instance.PreviewRequest{
|
|
DisplayName: "Family Palworld", Slug: "family-palworld",
|
|
HostPorts: map[string]int{"game": 8211},
|
|
MountPaths: map[string]string{"saved": "/srv/game-servers/family-palworld/saved"},
|
|
DataOrigin: "new", BackupRetention: 7,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repository.CreateDraft(ctx, instance.Draft{ID: "opaque-instance-id", Preview: preview}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var state, digest string
|
|
if err := db.QueryRow("SELECT lifecycle_state, template_digest FROM instances WHERE id=?", "opaque-instance-id").Scan(&state, &digest); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if state != "draft" || digest != snapshots[0].Digest {
|
|
t.Fatalf("draft state=%q digest=%q", state, digest)
|
|
}
|
|
if _, err := repository.BeginOperation(ctx, "operation-one", "opaque-instance-id", "install", "installing"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repository.SetOperationStep(ctx, "operation-one", "installation", "running"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
progress, err := repository.GetOperationProgress(ctx, "operation-one")
|
|
if err != nil || progress.GlobalStatus != "running" || progress.CurrentStep != "installation" || len(progress.Steps) != 7 {
|
|
t.Fatalf("progress=%#v err=%v", progress, err)
|
|
}
|
|
if _, err := repository.BeginOperation(ctx, "operation-two", "opaque-instance-id", "start", "starting"); !errors.Is(err, instance.ErrOperationConflict) {
|
|
t.Fatalf("parallel operation error = %v", err)
|
|
}
|
|
if err := repository.FailOperation(ctx, "operation-one", "error", "test_failure"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
progress, err = repository.GetOperationProgress(ctx, "operation-one")
|
|
if err != nil || progress.GlobalStatus != "failed" || progress.ErrorCode != "test_failure" || progress.FinishedAt == "" {
|
|
t.Fatalf("failed progress=%#v err=%v", progress, err)
|
|
}
|
|
if _, err := repository.BeginOperation(ctx, "operation-three", "opaque-instance-id", "start", "starting"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repository.RecoverInterruptedOperations(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var operationState string
|
|
if err := db.QueryRow("SELECT state FROM instance_operations WHERE id='operation-three'").Scan(&operationState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.QueryRow("SELECT lifecycle_state FROM instances WHERE id='opaque-instance-id'").Scan(&state); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if operationState != "intervention_required" || state != "intervention_required" {
|
|
t.Fatalf("recovered operation=%q instance=%q", operationState, state)
|
|
}
|
|
}
|
|
|
|
func TestReplaceMakesLocalCatalogDiskStateAuthoritative(t *testing.T) {
|
|
ctx := context.Background()
|
|
db, err := sqlite.Open(ctx, filepath.Join(t.TempDir(), "dogama.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
repository := sqlite.NewRepository(db)
|
|
snapshots, err := catalog.LoadFS(catalogdata.Files, ".")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repository.Replace(ctx, snapshots); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
changed := snapshots[0]
|
|
changed.Template.Game.Name = "Palworld changed"
|
|
changed.CanonicalYAML = strings.Replace(changed.CanonicalYAML, "Palworld", "Palworld changed", 1)
|
|
changed.Digest = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
if err := repository.Replace(ctx, []catalog.Snapshot{changed}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
list, err := repository.List(ctx)
|
|
if err != nil || len(list) != 1 || list[0].GameName != "Palworld changed" {
|
|
t.Fatalf("updated list = %#v, %v", list, err)
|
|
}
|
|
if err := repository.Replace(ctx, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
list, err = repository.List(ctx)
|
|
if err != nil || len(list) != 0 {
|
|
t.Fatalf("removed list = %#v, %v", list, err)
|
|
}
|
|
}
|
|
|
|
func TestSyncActivatesCurrentBundledSnapshotWithoutDiscardingHistory(t *testing.T) {
|
|
ctx := context.Background()
|
|
db, err := sqlite.Open(ctx, filepath.Join(t.TempDir(), "dogama.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
repository := sqlite.NewRepository(db)
|
|
current, err := catalog.LoadFS(catalogdata.Files, ".")
|
|
if err != nil || len(current) != 1 {
|
|
t.Fatalf("current catalog = %#v, %v", current, err)
|
|
}
|
|
body, err := catalogdata.Files.ReadFile("palworld/template.yaml")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
oldBody := strings.Replace(string(body), "version: 1.1.1", "version: 1.1.0", 1)
|
|
old, err := catalog.Validate([]byte(oldBody), "palworld", catalogdata.Files)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repository.Replace(ctx, []catalog.Snapshot{old}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repository.Sync(ctx, current); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
summaries, err := repository.List(ctx)
|
|
if err != nil || len(summaries) != 1 {
|
|
t.Fatalf("summaries = %#v, error = %v", summaries, err)
|
|
}
|
|
if summaries[0].Version != current[0].Template.Version || summaries[0].Digest != current[0].Digest {
|
|
t.Fatalf("active summary = %#v, want %s@%s", summaries[0], current[0].Template.ID, current[0].Template.Version)
|
|
}
|
|
if preserved, err := repository.Get(ctx, old.Template.ID, old.Template.Version); err != nil || preserved.Digest != old.Digest {
|
|
t.Fatalf("historical snapshot = %#v, error = %v", preserved, err)
|
|
}
|
|
}
|