carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package moves
2
3import (
4 "strings"
5 "testing"
6 "time"
7
8 "github.com/carlosframework/platform/internal/hostclaim"
9)
10
11// Every scope helper's EvidenceAt is the bound it was handed, never the
12// clock (claim rev 6.23 §2, INV-22): a bound years from now and one years
13// ago, in a zone that is not UTC, both come back as the same instant in UTC.
14// Each scope is a valid park for the host, names the incarnation it was
15// given, and carries the staging request limit.
16func TestScopeHelpersUseTheGenuineBound(t *testing.T) {
17 const host = "shop.bab.example.com"
18 inc := strings.Repeat("ab", 32)
19 zone := time.FixedZone("plus-one", 3600)
20 for _, bound := range []time.Time{
21 time.Date(2031, 3, 4, 5, 6, 7, 8, zone),
22 time.Date(2001, 9, 10, 11, 12, 13, 14, zone),
23 } {
24 for _, c := range []struct {
25 name string
26 got hostclaim.ParkScope
27 location string
28 }{
29 {"source location, holder with a slash", SourceLocationScope(hostclaim.Holder{Location: "instances/" + host + "/" + inc + "/", Incarnation: inc}, bound), "instances/" + host + "/" + inc},
30 {"source location, holder without one", SourceLocationScope(hostclaim.Holder{Location: "instances/" + host, Incarnation: inc}, bound), "instances/" + host},
31 {"target location, holder with a slash", TargetLocationScope(hostclaim.Holder{Location: "instances/" + host + "/" + inc + "/", Incarnation: inc}, bound), "instances/" + host + "/" + inc},
32 {"target location, holder without one", TargetLocationScope(hostclaim.Holder{Location: "instances/" + host, Incarnation: inc}, bound), "instances/" + host},
33 {"residue, holder with a slash", ResidueScope(hostclaim.Holder{Location: "instances/" + host + "/" + inc + "/", Incarnation: inc}, bound), "instances/" + host + "/" + inc},
34 {"residue, holder without one", ResidueScope(hostclaim.Holder{Location: "instances/" + host, Incarnation: inc}, bound), "instances/" + host},
35 {"staging", StagingScope(host, "move-1", inc, bound), "moves/" + host + "/move-1"},
36 {"abort", AbortScope(host, "move-1", inc, bound), "moves/" + host + "/move-1"},
37 {"return lane", ReturnLaneScope(host, "rb-1", inc, bound), "moves/" + host + "/rb-1"},
38 } {
39 t.Run(c.name+"/"+bound.Format("2006"), func(t *testing.T) {
40 if !c.got.EvidenceAt.Equal(bound) || c.got.EvidenceAt.Location() != time.UTC {
41 t.Fatalf("EvidenceAt = %v, want %v in UTC", c.got.EvidenceAt, bound)
42 }
43 if c.got.RequestLimit != 120*time.Second {
44 t.Fatalf("RequestLimit = %v, want 120s", c.got.RequestLimit)
45 }
46 if c.got.Location != c.location || c.got.Incarnation != inc {
47 t.Fatalf("scope = %q/%q, want %q/%q", c.got.Location, c.got.Incarnation, c.location, inc)
48 }
49 if err := c.got.Validate(host); err != nil {
50 t.Fatalf("Validate(%q): %v", host, err)
51 }
52 })
53 }
54 }
55 if zero := StagingScope(host, "move-1", inc, time.Time{}); zero.Validate(host) == nil {
56 t.Fatal("a zero bound made a valid scope; the helper must not fill one in")
57 }
58}
59
60// CommitScopes (Plan 5 Task 3, K9) is the one builder of a commit's two
61// parks, for the box and the console alike: the source's location from the
62// flip (SourceLocationScope) and the move's lane to the seal's bound
63// (StagingScope), read from the claim and the record's FlippedAt only. It
64// refuses without the flip's time, without a holder, and without a seal.
65func TestCommitScopes(t *testing.T) {
66 const host = "shop.bab.example.com"
67 inc := strings.Repeat("ab", 32)
68 seal := sealForTest()
69 flipped := seal.SealedAt.Add(40 * time.Second)
70 c := &hostclaim.Claim{Host: host,
71 Holder: &hostclaim.Holder{Location: "instances/" + host + "/" + inc + "/", Incarnation: inc},
72 Op: hostclaim.Op{ID: "move-1", Kind: hostclaim.KindLanding, Journal: map[string]string{hostclaim.JournalSeal: seal.Encode()}}}
73 source, staging, err := CommitScopes(c, &flipped)
74 if err != nil {
75 t.Fatal(err)
76 }
77 if want := SourceLocationScope(*c.Holder, flipped); source != want {
78 t.Fatalf("source %+v, want %+v", source, want)
79 }
80 if want := StagingScope(host, "move-1", inc, seal.Bound()); staging != want {
81 t.Fatalf("staging %+v, want %+v", staging, want)
82 }
83 for name, edit := range map[string]func(c *hostclaim.Claim) *time.Time{
84 "no flip": func(*hostclaim.Claim) *time.Time { return nil },
85 "no holder": func(c *hostclaim.Claim) *time.Time { c.Holder = nil; return &flipped },
86 "no seal": func(c *hostclaim.Claim) *time.Time { delete(c.Op.Journal, hostclaim.JournalSeal); return &flipped },
87 "no claim": nil,
88 } {
89 t.Run(name, func(t *testing.T) {
90 cc := *c
91 cc.Op.Journal = map[string]string{hostclaim.JournalSeal: seal.Encode()}
92 if edit == nil {
93 if _, _, err := CommitScopes(nil, &flipped); err == nil {
94 t.Fatal("scopes without a claim")
95 }
96 return
97 }
98 at := edit(&cc)
99 if _, _, err := CommitScopes(&cc, at); err == nil {
100 t.Fatal("scopes built")
101 }
102 })
103 }
104}
105