package hostfloor import ( "context" "crypto/ed25519" "crypto/rand" "encoding/json" "github.com/carlosframework/platform/internal/release" "os" "path/filepath" "testing" ) func TestFloorPairAndPermanentArming(t *testing.T) { pub, key, _ := ed25519.GenerateKey(rand.Reader) sg := release.RootSigner(key) ctx := context.Background() s := release.NewDirStore(t.TempDir()) floor := Floor{1, "v1.2.3"} raw, _ := json.Marshal(floor) auth, _ := release.SignRootEnvelope(sg, Authorization{1, "moves-protocol", "bucket", Key, Digest(raw)}) if _, err := Verify(pub, "other", raw, auth); err == nil { t.Fatal("wrong deployment") } if _, err := Verify(pub, "bucket", append(raw, ' '), auth); err == nil { t.Fatal("changed bytes") } if _, err := s.Put(ctx, AuthKey, auth, ""); err != nil { t.Fatal(err) } if _, err := Read(ctx, s, pub, "bucket"); err == nil { t.Fatal("incomplete pair accepted") } if err := Publish(ctx, s, sg, "bucket", floor); err != nil { t.Fatal(err) } d := Disk{Dir: t.TempDir(), Bucket: "bucket", PublicKey: pub} _ = os.Mkdir(filepath.Join(d.Dir, "identities"), 0700) initial, _ := json.Marshal(Policy{Schema: 1, DeploymentBucket: "bucket", Mode: "unpublished"}) _ = os.WriteFile(filepath.Join(d.Dir, "policy.json"), initial, 0600) armed := Policy{1, "bucket", "armed", raw, auth} if err := d.Persist(armed, nil); err != nil { t.Fatal(err) } if err := d.Persist(Policy{Schema: 1, DeploymentBucket: "bucket", Mode: "unpublished"}, nil); err == nil { t.Fatal("floor lowered") } _ = os.Remove(filepath.Join(d.Dir, "policy.json")) if _, _, err := d.Load(); err == nil { t.Fatal("missing is unpublished") } } func TestPendingAndLostControllersBlockUpdate(t *testing.T) { for _, state := range []string{"pending", "active", "lost", "broken"} { t.Run(state, func(t *testing.T) { d := Disk{Dir: t.TempDir(), Participation: floorParticipationFixture(t)} data := t.TempDir() _ = os.Mkdir(filepath.Join(data, ".hostexec"), 0700) _ = os.WriteFile(filepath.Join(data, ".hostexec", "host.json"), []byte(state), 0600) if err := d.Begin(t.Context(), "update", data); err == nil { t.Fatal("controller did not block") } if _, err := os.Stat(filepath.Join(d.Dir, "update-begun.json")); !os.IsNotExist(err) { t.Fatal("marker survived refusal", err) } }) } d := Disk{Dir: t.TempDir(), Participation: floorParticipationFixture(t)} data := t.TempDir() _ = os.Mkdir(filepath.Join(data, ".hostexec"), 0700) if err := d.Begin(t.Context(), "update", data); err != nil { t.Fatal(err) } if err := UpdateIdle(d.Dir, filepath.Join(data, "journal.json")); err == nil { t.Fatal("participant ignored begun update") } } func TestCanonicalReleaseTags(t *testing.T) { for _, tag := range []string{"dev", "abc123", "1.2.3", "vv1.2.3", "v01.2.3", "v1.2.3-rc1"} { if _, ok := release.ReleaseTag(tag); ok { t.Fatal(tag) } } a, _ := release.ReleaseTag("v1.10.0") b, _ := release.ReleaseTag("v1.9.9") if a.Compare(b) <= 0 { t.Fatal("lexical ordering") } }