feat: add deployment foundation and cross-device handoff
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package planner
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBuildPlan(t *testing.T) {
|
||||
now := time.Date(2026, 9, 25, 12, 0, 0, 0, time.UTC)
|
||||
i := validIntent()
|
||||
p, err := Build(i, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.ProjectName != "sd-git-one" || p.DataPath != "/var/lib/server-deploy/instances/git-one" {
|
||||
t.Fatalf("wrong instance resources: %+v", p)
|
||||
}
|
||||
if p.ExpiresAt.Sub(p.CreatedAt) != 15*time.Minute {
|
||||
t.Fatal("wrong expiration")
|
||||
}
|
||||
if err := p.Verify(i, now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
again, _ := Build(i, now)
|
||||
if again.Hash != p.Hash {
|
||||
t.Fatal("unstable plan hash")
|
||||
}
|
||||
i.InstanceID = "git-two"
|
||||
other, _ := Build(i, now)
|
||||
if other.Hash == p.Hash || other.ProjectName == p.ProjectName || other.DataPath == p.DataPath {
|
||||
t.Fatal("instances share identity")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyRejectsChangedOrExpiredPlan(t *testing.T) {
|
||||
now := time.Date(2026, 9, 25, 12, 0, 0, 0, time.UTC)
|
||||
i := validIntent()
|
||||
original, _ := Build(i, now)
|
||||
cases := []struct {
|
||||
name string
|
||||
change func(*Plan, *Intent, *time.Time)
|
||||
}{
|
||||
{"expired", func(p *Plan, i *Intent, n *time.Time) { *n = p.ExpiresAt }},
|
||||
{"future", func(p *Plan, i *Intent, n *time.Time) { *n = p.CreatedAt.Add(-time.Second) }},
|
||||
{"tampered hash", func(p *Plan, i *Intent, n *time.Time) { p.Hash = "bad" }},
|
||||
{"tampered path", func(p *Plan, i *Intent, n *time.Time) { p.DataPath = "/" }},
|
||||
{"tampered project", func(p *Plan, i *Intent, n *time.Time) { p.ProjectName = "other" }},
|
||||
{"tampered expiry", func(p *Plan, i *Intent, n *time.Time) { p.ExpiresAt = p.ExpiresAt.Add(time.Hour) }},
|
||||
{"state drift", func(p *Plan, i *Intent, n *time.Time) { i.ObservedStateDigest = i.PackageDigest }},
|
||||
{"host drift", func(p *Plan, i *Intent, n *time.Time) { i.HostID = "another-host" }},
|
||||
{"domain drift", func(p *Plan, i *Intent, n *time.Time) { i.Domain = "other.example.com" }},
|
||||
{"intent tamper", func(p *Plan, i *Intent, n *time.Time) { p.Intent.InstanceID = "other" }},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p, current, at := original, i, now
|
||||
tc.change(&p, ¤t, &at)
|
||||
if p.Verify(current, at) == nil {
|
||||
t.Fatal("accepted invalid plan")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRejectsInvalidIntent(t *testing.T) {
|
||||
i := validIntent()
|
||||
i.InstanceID = "../bad"
|
||||
if _, err := Build(i, time.Now()); err == nil {
|
||||
t.Fatal("built invalid plan")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user