| 1 | package home |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "testing" |
| 7 | "time" |
| 8 | |
| 9 | "amadan.net/oficina/home/internal/home/directorydata" |
| 10 | "amadan.net/oficina/home/internal/home/sealtest" |
| 11 | "amadan.net/oficina/home/roster" |
| 12 | "amadan.net/rastrillo/rastrillo/migrate" |
| 13 | ) |
| 14 | |
| 15 | func TestPendingRosterActualSignedHTTPAndLeaseCeiling(t *testing.T) { |
| 16 | f := newRosterIntegration(t, 1) |
| 17 | if _, e := migrate.Apply(context.Background(), f.f.p.directory.db, directorydata.OperatorSchema); e != nil { |
| 18 | t.Fatal(e) |
| 19 | } |
| 20 | cache, _, clock := f.cache(t, f.transport, rosterKeyID(f.key)) |
| 21 | start, _ := clock.Now() |
| 22 | f.f.p.directory.directory.roster.now = func() time.Time { at, _ := clock.Now(); return at } |
| 23 | digest := bytes.Repeat([]byte{33}, 32) |
| 24 | tx, e := f.f.p.directory.db.Writer().Begin() |
| 25 | if e != nil { |
| 26 | t.Fatal(e) |
| 27 | } |
| 28 | defer tx.Rollback() |
| 29 | for _, q := range []struct { |
| 30 | sql string |
| 31 | args []any |
| 32 | }{ |
| 33 | {`UPDATE memberships SET role='owner' WHERE team_id=? AND person_ref=?`, []any{f.team, chooserPerson}}, |
| 34 | {`UPDATE team_instances SET state='pending' WHERE team_id=?`, []any{f.team}}, |
| 35 | {`INSERT INTO roster_operator_receipts VALUES('provision-alpha',1,'provision','alpha-docs',?,?,?)`, []any{f.generation, digest, start.Unix()}}, |
| 36 | {`INSERT INTO directory_operator_consumer_records VALUES('alpha-docs','provision-alpha',1,?,?)`, []any{f.generation, digest}}, |
| 37 | {`INSERT INTO verification_attempts SELECT ?,'alpha-docs',id,'docs','https://alpha.docs.oficina.works',?,1,?,roster_epoch,?,?,?,? FROM teams WHERE id=?`, []any{rosterAtom(21), f.generation, digest, chooserPerson, start.Unix(), start.Unix() + 600, bytes.Repeat([]byte{34}, 32), f.team}}, |
| 38 | {`INSERT INTO verification_active VALUES('alpha-docs',?)`, []any{rosterAtom(21)}}, |
| 39 | } { |
| 40 | if _, e = tx.Exec(q.sql, q.args...); e != nil { |
| 41 | t.Fatal(e) |
| 42 | } |
| 43 | } |
| 44 | if e = tx.Commit(); e != nil { |
| 45 | t.Fatal(e) |
| 46 | } |
| 47 | clock.advance(599 * time.Second) |
| 48 | d := cache.Check(context.Background(), Origin, "A1", chooserPerson) |
| 49 | if d.State != roster.Member || !d.Lease.Valid() { |
| 50 | t.Fatal("pending signed snapshot not accepted", d.State) |
| 51 | } |
| 52 | clock.advance(time.Second) |
| 53 | status, _, b := f.get(t, "team=A1&fetch="+rosterTestChallenge, f.token) |
| 54 | if status != 503 || string(b) != `{"error":"unavailable"}` { |
| 55 | t.Fatal("expired pending differs", status) |
| 56 | } |
| 57 | clock.advance(59 * time.Second) |
| 58 | if next := cache.Check(context.Background(), Origin, "A1", chooserPerson); next.State != roster.Unknown || d.Lease.Valid() { |
| 59 | t.Fatal("pending expiry extended standing", next.State) |
| 60 | } |
| 61 | sealtest.AssertNoPlaintext(t, f.f.p.directoryPath) |
| 62 | } |
| 63 | |