| 1 | package mail |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "errors" |
| 6 | "sync" |
| 7 | "testing" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/release" |
| 10 | ) |
| 11 | |
| 12 | // okAdmitter admits nothing and refuses nothing: these tests exercise the gate. |
| 13 | type okAdmitter struct{} |
| 14 | |
| 15 | func (okAdmitter) AdmitClaimMailApps(ctx context.Context, _ ClaimMailHold, _ []ClaimMailApp) (ClaimMailAdmission, error) { |
| 16 | var n int |
| 17 | var mu sync.Mutex |
| 18 | return fixtureAdmission{ctx: ctx, released: &n, mu: &mu}, nil |
| 19 | } |
| 20 | |
| 21 | func TestClaimMailGateSerializesAndRetainsUncertain(t *testing.T) { |
| 22 | ctx := t.Context() |
| 23 | base := release.NewDirStore(t.TempDir()) |
| 24 | gate := &memGate{State: "free"} |
| 25 | one, err := NewClaimMailStore(base, gate, okAdmitter{}, ClaimMailOwner{Console: "c", Enrollment: "e", Controller: "one"}) |
| 26 | if err != nil { |
| 27 | t.Fatal(err) |
| 28 | } |
| 29 | two, err := NewClaimMailStore(base, gate, okAdmitter{}, ClaimMailOwner{Console: "c", Enrollment: "e", Controller: "two"}) |
| 30 | if err != nil { |
| 31 | t.Fatal(err) |
| 32 | } |
| 33 | calls := 0 |
| 34 | if err := WithClaimMailResource(ctx, one, func(held context.Context) error { |
| 35 | if err := WithClaimMailResource(ctx, two, func(context.Context) error { calls++; return nil }); !errors.Is(err, ErrClaimMailPending) { |
| 36 | t.Fatalf("competing gate: %v", err) |
| 37 | } |
| 38 | return WithClaimMailResource(held, one, func(context.Context) error { calls++; return nil }) |
| 39 | }); err != nil { |
| 40 | t.Fatal(err) |
| 41 | } |
| 42 | if calls != 1 { |
| 43 | t.Fatalf("calls=%d", calls) |
| 44 | } |
| 45 | uncertain := errors.New("backend outcome unknown") |
| 46 | if err := WithClaimMailResource(ctx, two, func(held context.Context) error { markClaimMailDirty(held); return uncertain }); !errors.Is(err, uncertain) { |
| 47 | t.Fatal(err) |
| 48 | } |
| 49 | if g := gate.snapshot(); g.State != "held" { |
| 50 | t.Fatalf("uncertain call released the gate: %+v", &g) |
| 51 | } |
| 52 | if err := WithClaimMailResource(ctx, one, func(context.Context) error { t.Fatal("uncertain owner replaced"); return nil }); !errors.Is(err, ErrClaimMailPending) { |
| 53 | t.Fatal(err) |
| 54 | } |
| 55 | } |
| 56 | func TestClaimMailGateFlagOffWritesNothing(t *testing.T) { |
| 57 | ctx := t.Context() |
| 58 | base := release.NewDirStore(t.TempDir()) |
| 59 | calls := 0 |
| 60 | if err := WithClaimMailResource(ctx, base, func(context.Context) error { calls++; return nil }); err != nil { |
| 61 | t.Fatal(err) |
| 62 | } |
| 63 | keys, err := base.List(ctx, "") |
| 64 | if err != nil { |
| 65 | t.Fatal(err) |
| 66 | } |
| 67 | if calls != 1 || len(keys) != 0 { |
| 68 | t.Fatalf("calls=%d keys=%v", calls, keys) |
| 69 | } |
| 70 | } |
| 71 | |