Files
ThothII/tools/tht/internal/lifecycle/lock_test.go
T

107 lines
3.2 KiB
Go

package lifecycle
import (
"errors"
"os"
"path/filepath"
"testing"
"github.com/aritmolab/thothii/tools/tht/internal/config"
"github.com/aritmolab/thothii/tools/tht/internal/safeio"
)
func TestLifecycleLockIsExclusivePerInstallationAndReusableAfterRelease(t *testing.T) {
root := lifecycleTestRoot(t)
installation := config.Installation{ProjectDirectory: root, Path: filepath.Join(root, "thothii-installation.yaml")}
if err := os.MkdirAll(installation.ControlDirectory(), 0o755); err != nil {
t.Fatal(err)
}
first, err := Acquire(installation)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = first.Release() })
for name, path := range map[string]string{
"lifecycle parent": filepath.Dir(installation.ControlDirectory()),
"lifecycle directory": installation.ControlDirectory(),
"owner file": first.Path(),
} {
t.Run(name, func(t *testing.T) {
var err error
if name == "owner file" {
err = safeio.ValidatePrivateRegular(path)
} else {
err = safeio.ValidatePrivateDirectory(path)
}
if err != nil {
t.Fatalf("%s protection = %v", name, err)
}
})
}
if _, err := Acquire(installation); !errors.Is(err, ErrLocked) {
t.Fatalf("second Acquire() error = %v, want ErrLocked", err)
}
if err := first.Release(); err != nil {
t.Fatal(err)
}
second, err := Acquire(installation)
if err != nil {
t.Fatalf("Acquire() after release: %v", err)
}
if err := second.Release(); err != nil {
t.Fatal(err)
}
}
func TestLifecycleLockReleaseDoesNotRemoveAnotherOwnersFile(t *testing.T) {
root := lifecycleTestRoot(t)
installation := config.Installation{ProjectDirectory: root, Path: filepath.Join(root, "thothii-installation.yaml")}
lock, err := Acquire(installation)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(lock.Path(), []byte(`{"token":"different-owner"}`), 0o600); err != nil {
t.Fatal(err)
}
if err := lock.Release(); !errors.Is(err, ErrOwnership) {
t.Fatalf("Release() error = %v, want ErrOwnership", err)
}
if _, err := os.Stat(lock.Path()); err != nil {
t.Fatalf("foreign lock was removed: %v", err)
}
}
func TestTransactionCapabilityIsInstallationBoundAndExpiresOnRelease(t *testing.T) {
root := lifecycleTestRoot(t)
installation := config.Installation{ProjectDirectory: root, Path: filepath.Join(root, "thothii-installation.yaml")}
transaction, err := AcquireTransaction(installation)
if err != nil {
t.Fatal(err)
}
if err := transaction.Verify(installation); err != nil {
t.Fatalf("Verify() active capability error = %v", err)
}
otherRoot := lifecycleTestRoot(t)
other := config.Installation{ProjectDirectory: otherRoot, Path: filepath.Join(otherRoot, "thothii-installation.yaml")}
if err := transaction.Verify(other); !errors.Is(err, ErrTransactionInstallation) {
t.Fatalf("Verify() for another installation error = %v, want ErrTransactionInstallation", err)
}
if err := transaction.Release(); err != nil {
t.Fatal(err)
}
if err := transaction.Verify(installation); !errors.Is(err, ErrTransactionInactive) {
t.Fatalf("Verify() after Release() error = %v, want ErrTransactionInactive", err)
}
}
func lifecycleTestRoot(t *testing.T) string {
t.Helper()
root, err := filepath.EvalSymlinks(t.TempDir())
if err != nil {
t.Fatal(err)
}
return root
}